Artem Saveliev
ResumeWork About meDiary

Being an SaaS company from the start (web technologies were chosen to drive our main product), it only made sense for us to eventually completely embrace AJAX. In addition, the original application is basically ASP.NET 1.1, with an inconsistent graphical design. So sprinkling UpdatePanel on top would just add some dynamical elements to an outdated UI design, without any real noticeable changes to the user.

This, among other things, was the reason to go all the way and redo our application using a better UI library - naturally ExtJS was my first choice - I didn't want to invest too much into proof-of-concept application, or, frankly, lock us into one of proprietary component libraries. Version 2.0 just came out, and their component model is quite mature to build an enterprise LOB (line of business) application on top of it.

At the same time I want to keep some standards in place, and chose to use ASP.NET AJAX for the server side, in the web services area. That way we are developing same web service for our AJAX JSON application and for our SOAP integration, using same code in ASP.NET web service. In fact, it works transparently most of the time, and same service code can be accessed from both types of clients. One other thing it adds is nice JavaScript proxy object support on the client side. In the end, while we are developing the application, we get automatic web services API already developed for our partners.

Thirdly, to cut down on development time, an OR/M is used. Currently it's CodeSmith's .netTiers template - which automatically generates DAL, BOL and even "service" layer. More importantly, it creates web service (asmx) file, which can be used as-is, or copied from.

The database backend is MS SQL 2005, as always. However, I made some use of xml column type, based on previous experience, to try and accommodate some clients unique requests, which don't mandate a separate table schema change. The end result is that the product is generic enough to accommodate different scenarios, and fast enough with little overhead from all the XML parsing - it is only needed when a feature sees minor use in real life (and yes, I do know about xml indexing, but for inserting/updating some overhead is still incurred).

The resulting tiers look like this:

  • ExtJS UI
  • client-side code
  • ASP.NET AJAX JavaScript proxy classes
  • JSON data transfer
  • ASP.NET web service
  • CodeSmith .netTiers business and data objects
  • MS SQL database

 

I am very careful to re-evaluate each of the technologies used in this list - an experienced developer may question each and every one of the choices. There are very good (and possibly better) replacements for ExtJS, ASP.NET AJAX JSON, ASP.NET web services, or .netTiers. But by taking the possible changes into account, it's not that hard to design a system where all these things can be swapped later on. The only real investment that is made, is made into UI code and database designs. So, for example, any code that we custom-develop on top of OR/M can we migrated to a different OR/M solution - and the objects/field names are still driven by initial database design.

But things have to progress, so while designing for change, we move forward as quick as we can! That's one of XP mottos - programming only what is needed today, and implementing it as simply as possible. And not being afraid to change later on.

So with that, I'll finish this introduction into our next generation product, and get back to work :) There are tons of things to learn in all that new tech, and I will try to write about it - most of the user-created code can be found in ExtJS forums, and really all it takes is integration.

So today we got one of our arabic clients request - customize the order template to include the arabic text in the captions and text areas.

First thing i did was to try and just give FOP russian text - that just gets output as # signs. To get this working you need to follow this good old tutorial - it mainly talks about doing it manually (what i was doing first). The more extended, and newer (for versions 0.93 and 94) article is naturally located on official site. You will especially need it when you want to do it from code, not command line.

The font that you can use in Windows which covers almost all languages (per unicode) is located at c:\windows\fonts\arialuni.ttf . It's a huge (22Mb) font that generates a pretty big font metrics (about 700k for me). This file, however, is not standard in windows - it seems to be part of the Microsoft Office installation. None of our windows servers have it by default, for example. Or you can just use plain arial, which covers all european scripts.

Finally tried doing arabic script using this font technique. This doesn't work, and it doesn't look like it will work any time soon. The problem of course is BIDI (bi-directional text support) - FOP can use arabic characters, but can not reverse the direction (and one can not simply reverse character order before giving it to FOP - arabic non-capital letters have to be connected to each other in a very special way). There's no bidi support in FOP (although XSL-FO does have it in the spec). The mailing archive hints to some people working on it successfully, however the major obstacle seems to be lack of proper BIDI support in Java 1.3 itself - only 1.4 added needed methods, but FOP wants to stay 1.3-compatible.

Update: well, I just got a notification of 0.95 beta, and it does drop the 1.3 requirement. Good news! Hopefully the custom work that is done by other developers will be included eventually.

We had to revert to a workaround - the areas where arabic is needed are going to be bitmaps that our customers will be able to upload the images for.

Blog, site revived

| | Comments (0) | TrackBacks (0)

Hi, this is Artem again. I finally revived my web site. It's now "better" ©.

First of all, all of the content has been checked and validated against XHTML 1.0 Transitional specification (Thank you Visual Studio 2005 for automatic schema checking!).

Second, the new blogging engine is installed - instead of outdated and unsupported .Text, I now used MovableType, which coincidentally released a free version recently. This is a very cool blogging engine, with proper templating and alot of community support (for example the theme, I.e. everything under the header, is downloaded right from MT admin console and installed instantly). It even has comment spam blocker plugin set up by default, which checks against online database and does other neat stuff.

Frankly, I did look at many blogging engines, especially trying hard to find a good free themable .NET-based blogging engine, to no avail. The only one currently in active development is dasBlog, but it's only starting. And Community Server Free has stupid limitation on number of images you can upload - a feature i tend to use alot.

Finally, i am writing this from one of best looking and actually instantly working blogging writers available - Windows Live Writer. Surprisingly, it supports all blogging engines, including custom ones. With MovableType support i get WYSIWYG theming - right now i actually see how the post will look on the screen when posted (even with my custom blog theme)! It also does automatic image uploading, including uploading to a custom FTP site - huge for me, yet again :) it looks absolutely awesome, does spell checking, supports multiple blogs and so on and so forth. Long live w.Bloggar!

So, in the end, i am happy with my new setup. Very happy. Even that it doesn't use .NET anymore. Expect alot of updates.

"flows" in XSL-FO can have static areas surrounding them, which are naturaly used to make headers and footers on pages. Let's consider sales invoice like this:

sample invoice

Here, of course, you can position header and footer absolutely using <fo:block-container left="xx" top="xx" position="absolute">. However, when your invoice grows and you run out of space on the page, and you want to see something like this:

invoice how it should page correctly

In XSL-FO standard this is achived using special static sections of the page:

<fo:page-sequence-master master-name="all-pages">
  <fo:repeatable-page-master-alternatives>
    <fo:conditional-page-master-reference 
            page-position="any" master-reference="any-page-id"/>
    <fo:conditional-page-master-reference 
            page-position="last" master-reference="last-page-id"/>
  </fo:repeatable-page-master-alternatives>
</fo:page-sequence-master>

Here you use master-reference parameter values your page master definition , for example:

<fo:simple-page-master master-name="last-page-id">

Inside that master you can define different footer. A good example is given here http://www.xmlpdf.com/ibex-examples.html

Problem is, tracking which page is "last" is very tricky in free-flow documents. Take HTML for example, the common question about how to make something 100% high is hard to answer. The content can flow differently, and, more importantly, the element that is trying to make use of that position can affect its own position. For example, now in XSL-FO, a footer on the last page can be so high that the engine will have to create another page after the page that was considered "last" just to fit it. In which case, page before last will suddenly fit all content on the last page too, because it doesn't have footer anymore. So engine has to make a decision on how to fix that in one or another position! What I am getting to is that Apache FOP doesn't do that yet, and in general many complex things having to do with flow. It's working just fine with everything else it seems. This is why current version is in "maintanence" now and developers are working on redesigning for next version (which doesn't have name yet, but either 0.30 or 1.0 ;-)

So what do you do in the mid time? Well, you have 3 workarounds (none of which is perfect). First one that is normally suggested is to use fo:footnote element to emulate the footer on the last page. Don't forget that you have to have footnote reference text, otherwise it won't generate anything:

<fo:block id="end">
<fo:footnote><fo:inline color="white">.</fo:inline>
<fo:footnote-body>...

The problem with footnote is that you can't use absolutely positioned blocks in it, and it seems to only like tables for fine formatting. So the example that i gave above on the picture might be hard to implement.

The second workaround is to use some code inside your program (before FOP call) to tell which items will be on last page, and put them into a different page master. That may be hard for some applications, again, like in example I gave, each line in the invoice can have wrapping text and as a result starts taking more space than originally estimated.

The last solution is not perfect either - don't make special footer.This is what I ended up doing (and it was fine in my case, since every invoice page has same footer in my clients case). So in the end it looks like this:

result of implementing paging on invoice in FOP

You may think of switching to commercial XSL-FO generator, like RenderX or Antenna House, which seem to support flows better, and overall are high quality commercial products.

http://www.renderx.net/Content/tools/xep.html

http://www.antennahouse.com/product.htm

For my own software I am thinking of supporting many different renderers, based on clients ability to buy commercial renderer or to live with Apache FOP limitations (which will be fixed some day).

XML Cache

| | Comments (0) | TrackBacks (0)

Developers frequently want to get data structures out of their code and into XML. But that bears a performance hit, since every time you read the file you have to parse it. In case of client applications you can avoid it by placing XML objects in the memory. However, in case of web-apps, memory gets destroyed after the request. Besides, you can't monitor changes in the files themselves. ASP.NET introduced a very useful object called Cache, which is designed to help with precisely that problem. It stores objects on the application level (similar to Application object) but also allows you to monitor changes in the data. The following code is implementation of XmlDocument object storage and file change monitoring:

public static XmlDocument GetDOM(string path, Page page)
{
  if (page.Cache["xmlcache:dom:"+path] != null)
    return (XmlDocument)page.Cache["xmlcache:dom:"+path];
  else
  {
    XmlDocument x = new XmlDocument();
    x.Load(page.MapPath(path));
    page.Cache.Insert("xmlcache:dom:"+path,
      x,new CacheDependency(page.MapPath(path)));
    return x;
  }
}

The methods to store XslTransform and XPathDocument would look exactly the same. However, you should carefully monitor the contents of XslTransform document - CacheDependency object doesn't monitor internal links of XSLT language, like xsl:import. By the way, I couldn't find a way to automatically find all those tags in XSLT document and generate CacheDependancy for each of them.

Oh, and one other important thing. ASP.NET 2.0 extended on CacheDependency and introduced an object that monitors changes in SQL database! It's ideal for many applications where you fill drop-downs with data from database, for example.

Simple serializer/deserializer

public static string SerializeObject(object obj)
{
 MemoryStream temp = new MemoryStream();
 XmlSerializer serializer = new XmlSerializer(obj.GetType());
 serializer.Serialize(temp,obj);
 
 temp.Seek(0,SeekOrigin.Begin);
 byte[] byteArray = new byte[temp.Length];
 int count = 0;
 while (count < temp.Length)
 {
  byteArray[count++] = Convert.ToByte(temp.ReadByte());
 }
 ASCIIEncoding asciiEncoding = new ASCIIEncoding();
 char[] charArray = new char[asciiEncoding.GetCharCount(
  byteArray, 0, count)];
 asciiEncoding.GetDecoder().GetChars(
  byteArray, 0, count, charArray, 0);
 return new string(charArray);
}
public static object DeSerializeObject(string s,object obj)
{
 ASCIIEncoding asciiEncoding = new ASCIIEncoding();
 byte[] inp = asciiEncoding.GetBytes(s);
 MemoryStream temp = new MemoryStream();
 XmlSerializer serializer = new XmlSerializer(obj.GetType());
 
 temp.Write(inp,0, inp.Length);
 temp.Seek(0,SeekOrigin.Begin);
 return serializer.Deserialize(temp);
}

Update: apparently XmlSerializer is pretty limited in which types it can reflect, it's designed to fit current WDSL schema, which is limited itself. Well, not really, but in any case right now XmlSerializer can't serialize multidimentional arrays, for example. In the code above you can replace XmlSerializer with SoapFormatter, while keeping everything else exactly the same. That will generate different (SOAP) envelope and you will be able to serialize any type of data.

When generating reports from XML data using XSLT I have to perform complex mathematical operations. Or, rather, operations that generic XPath can't do. To achive that you can use variables in XSLT and fill node lists with them. So we have:

<xsl:variable name="totalCommission">
 <xsl:for-each 
  select="orderz/accounts/payment/orderzs[@o_status != -9 
  and @o_prodtype != 'J']">
  <accum><xsl:value-of select="(@o_extprice  - @o_discount - 
   itemmaster/itempricelist/@storecost*@qty) * 
   ../../../../@salescommission div 100"/></accum>
 </xsl:for-each>
</xsl:variable>

as a result we create a variable that generates its element using some algorithm. After that, we can run a summ operation against it:

$<xsl:value-of 
select="format-number(sum(msxsl:node-set($totalCommission)/accum), 
'#,##0.00')" />

Very important in this operation is support of node set generator msxsl:node-set(), which is supported in MSXML that i use in this case. To use it the root XML node will look like this:

<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform" 
version="1.0" xmlns:msxsl="urn:schemas-microsoft-com:xslt">

Did you know that updating UI controls from your working thread (function, whatever) is a big no-no? Specifiably if you are doing something like progress bar, showing messages while program is working, want to implement pause/cancel button.

Anyways, here's a good “starter kit” for people who have hard time understanding how to make it all happen:

http://www.codeproject.com/csharp/H5CsThreading.asp

string.Trim(char[])

| | Comments (0) | TrackBacks (0)

Apparently you should be more careful with Trim. How precisely it works i don't yet understand, but the following operation:

variable.Trim(”' “.ToCharArray())

does not always remove space or/and single quote from the ends of the string. I think it is related to the fact that in that case it doesn't use Char.IsWhiteSpace, like if you use Trim without parameters.

I saw it yesterday already, but .Text doesn't format date correctly for article URL generation. See, it uses HttpHandlers to make each entry and day have it's own URL, although they are really stored in database. This is exactly same as mod_rewrite for Apache, or writing ISAPI filter for IIS. But this is much simpler, configured in web.config and written in any .NET language. So, the URL format is basically date, however for Russian locale apparently date separator is dot, not slash. So the parser that was reading it was getting confused... using datetime.ToString(“someformat“,new CultureInfo("en-US")) I forced it to use the format it expects (American), and all is good now :)