Configure WebORB with Log4Net

Posted by admin on January 17th, 2010

This will be a quick one because time is not on my side these days... If you've never worked with Log4Net, I suggest to go and read some documentation on this an try to set it up.

A while ago I was searching how I could merge the logging from WebORB with my Log4Net configuration. As it happens, I had two log files, one from WebORB and one from Log4Net. Having an overview of the logging, was difficult like this. After some initial help from Mark Piller (WebORB), I managed to merge the two loggings. However, in the beginning, some logs were still written to the WebORB log file. After some weeks I got another email from Mark explaining how it could be done so that all logging would go to the Log4Net logging.

So here's how you do it:

In the Weborb.config file, add next lines in the logging tag section:

XML:
  1. <!--<currentPolicy>Date Based Logging</currentPolicy>-->
  2. <currentPolicy>Log4NetPolicy</currentPolicy>
  3.  
  4. <loggingPolicy>
  5.   <policyName>Log4NetPolicy</policyName>
  6.   <className>Edu3.Weborb.Logging.Log4NetPolicy</className>
  7. </loggingPolicy>

The Date Based Logging is the default policy used by WebORB which I commented out. We'll implement a Log4NetPolicy (you can take another name if you wish so).

In the loggingPolicy tag you can map the policy to a className which we'll implement. This class needs to implement the interface Weborb.Util.Logging.ILoggingPolicy, which is an interface in the weborb dll.

Important is to set all the log enables to true. It's Log4Net that will handle levels. If you set, for instance, the WebORB INFO level to enable=false, then our custom Log4Net logger will not receive any INFO loggings from WebORB. Even if you then set INFO level to be logged in Log4Net, the WebORB INFO levels won't be logged.

XML:
  1. <log enable="yes">WEBORB INFO</log>
  2. <log enable="yes">WEBORB DEBUG</log>
  3. <log enable="yes">WEBORB ERROR</log>
  4. <log enable="yes">WEBORB SERIALIZATION</log>
  5. <log enable="yes">WEBORB EXCEPTION</log>
  6. <log enable="yes">WEBORB INSTRUMENTATION</log>
  7. <log enable="yes">WEBORB SECURITY</log>
  8. <log enable="yes">WEBORB MESSAGE SERVER</log>
  9. <log enable="yes">WEBORB THREADING</log>

So here's my Edu3.Weborb.Logging.Log4NetPolicy class:

C#:
  1. using Weborb.Util.Logging;
  2. using System.Collections;
  3.  
  4. namespace Edu3.Weborb.Logging
  5. {
  6.     class Log4NetPolicy : ILoggingPolicy
  7.     {
  8.         private readonly Hashtable _hashtable;
  9.         private const string PolicyName = "Log4NetPolicy";
  10.  
  11.         public Log4NetPolicy(Hashtable hashTable)
  12.         {
  13.             _hashtable = hashTable;
  14.         }
  15.  
  16.         public ILogger getLogger()
  17.         {
  18.             return new Log4NetLogger();
  19.         }
  20.  
  21.         public string getPolicyName()
  22.         {
  23.             return PolicyName;
  24.         }
  25.  
  26.         public Hashtable getPolicyParameters()
  27.         {
  28.             return _hashtable;
  29.         }
  30.     }
  31. }

getLogger() should return the logger object which will be used by WebORB
getPolicyName() should return the name of the policy. Make sure it is the same as you specified in the config file
getPolicyParameters() should return the same hashtable which was passed into your policy constructor

The Log4NetLogger is also a custom written class that looks like this:

C#:
  1. using System;
  2. using log4net;
  3. using Weborb.Util.Logging;
  4. using log4net.Core;
  5.  
  6. namespace Edu3.Weborb.Logging
  7. {
  8.     public class Log4NetLogger : AbstractLogger
  9.     {
  10.         private static readonly ILog Log = LogManager.GetLogger("WebORB");
  11.        
  12.         public override void fireEvent(string category, object eventObject
  13.             , DateTime timestamp)
  14.         {
  15.             Level activityLevel;
  16.             switch (category)
  17.             {
  18.                 case LoggingConstants.WEBORB_INFO:
  19.                     activityLevel =
  20.                         Log.Logger.Repository.LevelMap["WEBORB_INFO"];
  21.                     Log.Logger.Log(GetType(), activityLevel
  22.                         , eventObject, null);
  23.                     break;
  24.                 case LoggingConstants.WEBORB_DEBUG:
  25.                     activityLevel =
  26.                         Log.Logger.Repository.LevelMap["WEBORB_DEBUG"];
  27.                     Log.Logger.Log(GetType(), activityLevel
  28.                         , eventObject, null);
  29.                     break;
  30.                 case LoggingConstants.WEBORB_ERROR:
  31.                     activityLevel =
  32.                         Log.Logger.Repository.LevelMap["WEBORB_ERROR"];
  33.                     Log.Logger.Log(GetType(), activityLevel
  34.                         , eventObject, null);
  35.                     break;
  36.                 case LoggingConstants.WEBORB_SERIALIZATION:
  37.                     activityLevel =
  38.                         Log.Logger.Repository.LevelMap["WEBORB_SERIALIZATION"];
  39.                     Log.Logger.Log(GetType(), activityLevel
  40.                         , eventObject, null);
  41.                     break;
  42.                 case LoggingConstants.WEBORB_EXCEPTION:
  43.                     activityLevel =
  44.                         Log.Logger.Repository.LevelMap["WEBORB_EXCEPTION"];
  45.                     Log.Logger.Log(GetType(), activityLevel
  46.                         , eventObject, null);
  47.                     break;
  48.                 case LoggingConstants.WEBORB_INSTRUMENTATION:
  49.                     activityLevel =
  50.                         Log.Logger.Repository.LevelMap["WEBORB_INSTRUMENTATION"];
  51.                     Log.Logger.Log(GetType(), activityLevel
  52.                         , eventObject, null);
  53.                     break;
  54.                 case LoggingConstants.WEBORB_SECURITY:
  55.                     activityLevel =
  56.                         Log.Logger.Repository.LevelMap["WEBORB_SECURITY"];
  57.                     Log.Logger.Log(GetType(), activityLevel
  58.                         , eventObject, null);
  59.                     break;
  60.                 case LoggingConstants.WEBORB_MESSAGE_SERVER:
  61.                     activityLevel =
  62.                         Log.Logger.Repository.LevelMap["WEBORB_MESSAGE_SERVER"];
  63.                     Log.Logger.Log(GetType(), activityLevel
  64.                         , eventObject, null);
  65.                     break;
  66.                 case LoggingConstants.WEBORB_THREADING:
  67.                     activityLevel =
  68.                         Log.Logger.Repository.LevelMap["WEBORB_THREADING"];
  69.                     Log.Logger.Log(GetType(), activityLevel
  70.                         , eventObject, null);
  71.                     break;
  72.                 case LoggingConstants.WEBORB_MESSAGINGEXCEPTION:
  73.                     activityLevel =
  74.                         Log.Logger.Repository.LevelMap["WEBORB_MESSAGINGEXCEPTION"];
  75.                     Log.Logger.Log(GetType(), activityLevel
  76.                         , eventObject, null);
  77.                     break;
  78.                 default:
  79.                     throw new Exception("All LoggingConstants should " +
  80.                                         "have been catched previously.");
  81.             }
  82.         }
  83.     }
  84. }

As you'll see, I defined some custom log levels in Log4Net. You really don't need this, you could just use the existing Log4Net levels and map custom WebORB levels to one of the existing Log4Net levels.

Here's how my Log4Net configuration section looks like if you want to work with these custom levels.

XML:
  1. <log4net>
  2.   <level>
  3.     <name value="WEBORB_INFO" />
  4.     <value value="40000" />
  5.   </level>
  6.   <level>
  7.     <name value="WEBORB_DEBUG" />
  8.     <value value="30000" />
  9.   </level>
  10.   <level>
  11.     <name value="WEBORB_ERROR" />
  12.     <value value="70000" />
  13.   </level>
  14.   <level>
  15.     <name value="WEBORB_SERIALIZATION" />
  16.     <value value="40000" />
  17.   </level>
  18.   <level>
  19.     <name value="WEBORB_EXCEPTION" />
  20.     <value value="70000" />
  21.   </level>
  22.   <level>
  23.     <name value="WEBORB_MESSAGINGEXCEPTION" />
  24.     <value value="70000" />
  25.   </level>
  26.   <level>
  27.     <name value="WEBORB_INSTRUMENTATION" />
  28.     <value value="40000" />
  29.   </level>
  30.   <level>
  31.     <name value="WEBORB_SECURITY" />
  32.     <value value="40000" />
  33.   </level>
  34.   <level>
  35.     <name value="WEBORB_MESSAGE_SERVER" />
  36.     <value value="40000" />
  37.   </level>
  38.   <level>
  39.     <name value="WEBORB_THREADING" />
  40.     <value value="40000" />
  41.   </level>
  42.   <appender name="RollingFileAppender" type="log4net.Appender.RollingFileAppender">
  43.         <file value="Logs/Log4Net/"/>
  44.     <appendToFile value="true"/>
  45.     <rollingStyle value="Date"/>
  46.         <staticLogFileName value="false" />
  47.     <lockingModel type="log4net.Appender.FileAppender+MinimalLock" />
  48.     <datePattern value="yyyy-MM-dd-HH.lo\g" />
  49.     <maxSizeRollBackups value="10" />
  50.     <maximumFileSize value="1MB" />
  51.     <layout type="log4net.Layout.PatternLayout">
  52.       <conversionPattern value="%date [%thread] %-5level %logger - %message%newline"/>
  53.     </layout>
  54.   </appender>
  55.   <root>
  56.     <level value="ALL"/>
  57.     <appender-ref ref="RollingFileAppender"/>
  58.   </root>
  59. </log4net>

In this section you can configure not to log all levels. For more information on this see the Log4Net documentation.

Maybe that some of the Log Levels values (the integers) should be changed to meet your expectations. See Log4Net Levels for more information.

That's about it. If I forgot something, I'll update the post. If you have any question, please feel free to ask.

<

Nested TransactionScopes in .NET

Posted by admin on December 7th, 2009

In next example, the persistence to the database will not be executed.

C#:
  1. IBinaryAssetStructureRepository rep = new BinaryAssetStructureRepository();
  2. var userDto = new UserDto { id = 3345 };
  3. var dto = new BinaryAssetBranchNodeDto("name", userDto, userDto);
  4. using (var scope1 = new TransactionScope())
  5. {
  6.     using (var scope2 = new TransactionScope())
  7.     {
  8.         //Persist to database
  9.         rep.CreateRoot(dto, 1, false);
  10.         scope2.Complete();
  11.     }
  12.     scope1.Dispose();
  13. }
  14. dto = rep.GetByKey(dto.id, -1, false);

scope1.Dispose(); is not necessary in this example, but I added it for clearance.

<

Getting an embedded resource out of the assembly + convert to bytearray

Posted by admin on December 7th, 2009

My file is located under /images/donkey.jpg, so in that case, don't forget to add .images.

C#:
  1. var assemblyName =  typeof(BinaryAssetFileDataProviderTest).Assembly.GetName().Name;
  2. var str = typeof (BinaryAssetFileDataProviderTest).Assembly.GetManifestResourceStream(assemblyName + ".images." + "donkey.jpg");
  3. var blob = new byte[str.Length];
  4. str.Read(blob, 0, (int)str.Length);
  5. var size = (int)str.Length;
  6. str.Close();

<

Saving a connectionString at runtime to .NET app.config

Posted by admin on December 7th, 2009
C#:
  1. var config = ConfigurationManager.OpenExeConfiguration(ConfigurationUserLevel.None);
  2. var connectionStringSettings =
  3. new ConnectionStringSettings("ConnectionString", _arguments["connectionString"], "System.Data.SqlClient");
  4. config.ConnectionStrings.ConnectionStrings.Add(connectionStringSettings);
  5. config.Save(ConfigurationSaveMode.Modified);
  6. ConfigurationManager.RefreshSection("connectionStrings");

<

Database version control MSSQL C#[dot]NET Program

Posted by admin on November 5th, 2009

Some time ago I read an article on the importance of version control for databases by Jeff Atwood. I knew that in our team we had to start using something like this because updating different databases to a new release took a lot of time. We always used sql compare to look for the difference between the two databases and to update the database. Sometimes, with small changes, this wasn't that bad, but with big differences and with data that also needed to be inserted, it was a nightmare. And if you had done one database, you needed to do it all over on another... very bad, very bad, but hey, we learn, we learn. Now I've created a C#.NET Console Application that handles most of the things described in the blog of Jeff Atwood. It's certainly not done and lots of things can be improved, but due to a lack of time right now, this will currently have to do it. It does its job and that's all we needed. This is the source code and this is the release build. In order to follow this serie of posts, you'd have to read the article by Jeff Atwood because I will not repeat everything he has said. In the source code zip there's a folder lib with libraries I used and a folder files with the files I will explain further on.

What's not implemented?

  • Setting up a database from scratch using the baseline scripts is not implemented.
  • Configuring more things like folder structure
  • Support for databases other than mssql
  • Views are not implemented as we do not use them yet in our application. Implementing them however is pretty easy and you should be able to do it yourself with the source code.
  • ...

How does it work?

First you have to have a certain folderstructure because I haven't had time to configure that. Here's a screenshot of how it should look:

Screenshot Folder Structure

Screenshot Folder Structure

I quickly wrote the program so that we could use Database Version Control as quick as possible. Because of that default it works with this folder structure. Under Program is the Release build of the C# Console Application. Under the root of Scripts are the update scripts along with some files I'll discuss later. Under the functions and storedprocedures are the create (no alter scripts because the sp's and functions are removed when running the exe!) scripts for functions and stored procedures.

How does the root of Scripts look like:

Screenshot Files under Scripts folder

Screenshot Files under Scripts folder

  • DropStoredProceduresAndFunctions.sql: Script that will be used to delete all sp's and functions out of the database. This will be necessary before creating all sp's and functions.
  • excludeList.csv: This is a text file with comma seperated values. If one of the values appears in the name of a sp or function, then this sp or function will be ignored (will not be deleted and not be created).
  • sc.xx.xx.xxxx.sql: The update scripts. The name is important as the exe depends on that.
  • sc.template.sql: This is a template script that is best used to create an update script

Explanation of scripts

DropStoredProceduresAndFunctions.sql script:

SQL:
  1. -- variable to object name
  2. declare @name varchar(1000)
  3. -- variable to hold object type
  4. declare @xtype varchar(20)
  5. -- variable to hold sql string
  6. declare @sqlstring nvarchar(4000)
  7. declare SPViews_cursor cursor FOR
  8. SELECT QUOTENAME(ROUTINE_SCHEMA) + '.' + QUOTENAME(ROUTINE_NAME)
  9. AS name, ROUTINE_TYPE AS xtype
  10. FROM
  11. INFORMATION_SCHEMA.ROUTINES
  12. UNION
  13. SELECT QUOTENAME(TABLE_SCHEMA) + '.' + QUOTENAME(TABLE_NAME) AS
  14. name, 'VIEW' AS xtype
  15. FROM
  16. INFORMATION_SCHEMA.VIEWS
  17.  
  18. open SPViews_cursor
  19. fetch next FROM SPViews_cursor INTO @name, @xtype
  20. while @@fetch_status = 0
  21. begin
  22. -- test object type if it is a stored procedure
  23. IF @xtype = 'PROCEDURE' ###
  24. begin
  25. SET @sqlstring = 'drop procedure ' + @name
  26. exec sp_executesql @sqlstring
  27. SET @sqlstring = ' '
  28. end
  29. -- test object type if it is a function
  30. IF @xtype = 'FUNCTION' ###
  31. begin
  32. SET @sqlstring = 'drop FUNCTION ' + @name
  33. exec sp_executesql @sqlstring
  34. SET @sqlstring = ' '
  35. end
  36. -- test object type if it is a view
  37. --if @xtype = 'VIEW'
  38. -- begin
  39. -- set @sqlstring = 'drop view ' + @name
  40. -- exec sp_executesql @sqlstring
  41. -- set @sqlstring = ' '
  42. -- end
  43. -- get next record
  44. fetch next FROM SPViews_cursor INTO @name, @xtype
  45. end
  46. close SPViews_cursor
  47. deallocate SPViews_cursor

I've got this from here. The ### tokens will be replace by transact-sql by the program to exclude sp's and funtions with certain keywords (excludeList.csv).

Example of an update script:

SQL:
  1. /*
  2. Comment
  3. */
  4. SET NUMERIC_ROUNDABORT OFF
  5. GO
  6. SET ANSI_PADDING, ANSI_WARNINGS, CONCAT_NULL_YIELDS_NULL, ARITHABORT, QUOTED_IDENTIFIER, ANSI_NULLS ON
  7. GO
  8. IF EXISTS (SELECT * FROM tempdb..sysobjects WHERE id=OBJECT_ID('tempdb..#tmpErrors')) DROP TABLE #tmpErrors
  9. GO
  10. CREATE TABLE #tmpErrors (Error int)
  11. GO
  12. SET XACT_ABORT ON
  13. GO
  14. SET TRANSACTION ISOLATION LEVEL SERIALIZABLE
  15. GO
  16. BEGIN TRANSACTION
  17. GO
  18.  
  19. -- sql code
  20.  
  21. GO
  22.  
  23. INSERT INTO [dbo].[SchemaChanges]
  24. ([MajorReleaseNumber]
  25. ,[MinorReleaseNumber]
  26. ,[PointReleaseNumber]
  27. ,[ScriptName]
  28. ,[DateApplied])
  29. VALUES
  30. ('03'
  31. ,'01'
  32. ,'0011'
  33. ,'Comment'
  34. ,GETDATE())
  35.  
  36. GO
  37. IF EXISTS (SELECT * FROM #tmpErrors) ROLLBACK TRANSACTION
  38. GO
  39. IF @@TRANCOUNT&gt;0 BEGIN
  40. PRINT 'The database update succeeded'
  41. COMMIT TRANSACTION
  42. END
  43. ELSE PRINT 'The database update failed'
  44. GO
  45. DROP TABLE #tmpErrors
  46. GO

These sort of scripts will be executed by a SqlCommand (DbCommand from Enterprise Library). We are still experimenting and have seen that the Transactions do not work. Somehow there's a difference between these kind of scripts being executed in Management Studio and being executed by a SqlCommand. I think we'll have to use TransactionScrope in the C# Console Application to make the transactions work. These Transactions are important because if only a part of the script would be executed, then the build would be broken. You would then have to restore a previous backup, fix the script and run the exe again against the database. Later on I'll talk about some best practices to use database version control. The GO statements are also a program and the program filters them out before executing the script (again with some other keywords that cause problems). In a couple of months, when we have been able to work things out a little better, I'll post a new version. For now, this is what you get ;-) .

In the script there's place to put your sql code and then there's an important part, updating the SchemaChanges table. Each update script inserts a new record in the table SchemaChanges so that the update gets registered in the database. With this table, the program can check what the last update was and look for new scripts in the Script folder. It's very important that the values you insert into the SchemaChanges table correspond to the name you gave to the file, otherwise the whole thing will not work correctly.

sc.template.sql script:

SQL:
  1. /*
  2. Commentaar
  3. */
  4. SET NUMERIC_ROUNDABORT OFF
  5. GO
  6. SET ANSI_PADDING, ANSI_WARNINGS, CONCAT_NULL_YIELDS_NULL, ARITHABORT, QUOTED_IDENTIFIER, ANSI_NULLS ON
  7. GO
  8. IF EXISTS (SELECT * FROM tempdb..sysobjects WHERE id=OBJECT_ID('tempdb..#tmpErrors')) DROP TABLE #tmpErrors
  9. GO
  10. CREATE TABLE #tmpErrors (Error int)
  11. GO
  12. SET XACT_ABORT ON
  13. GO
  14. SET TRANSACTION ISOLATION LEVEL SERIALIZABLE
  15. GO
  16. BEGIN TRANSACTION
  17. GO
  18.  
  19. -- Plaats SQL Code hier
  20.  
  21. GO
  22.  
  23. INSERT INTO [dbo].[SchemaChanges]
  24. ([MajorReleaseNumber]
  25. ,[MinorReleaseNumber]
  26. ,[PointReleaseNumber]
  27. ,[ScriptName]
  28. ,[DateApplied])
  29. VALUES
  30. ('00'
  31. ,'00'
  32. ,'0000'
  33. ,'Short Description'
  34. ,GETDATE(), 'Delete this parameter (is to make sure you change the version numbers')
  35.  
  36. GO
  37. IF EXISTS (SELECT * FROM #tmpErrors) ROLLBACK TRANSACTION
  38. GO
  39. IF @@TRANCOUNT&gt;0 BEGIN
  40. PRINT 'The database update succeeded'
  41. COMMIT TRANSACTION
  42. END
  43. ELSE PRINT 'The database update failed'
  44. GO
  45. DROP TABLE #tmpErrors
  46. GO

This is the template, which will surely change in the next months. The INSERT query will not work like it is written because there's a value too much. I've done this so that my teammembers wouldn't forget to edit the release numbers.

Explanation of the program and source code

Let's start of with explaining how the exe (DatabaseVersionControl.exe) works:

Parameters that are required (one):

-connectionString="Data Source=OMITTED;Persist Security Info=True;Integrated Security=SSPI;Initial Catalog=OMITTED;MultipleActiveResultSets=True"

Parameters that are optional (two):

-include "value1,value2,..." : If one of these values are in the excludeList.csv, then they will be overruled so that they will not be excluded.

-scriptsDirectory="..." : Path to the directory which contains the scripts. Under this folder the two folders storedprocedures and functions must be existing, otherwise the program will not work. Maybe that in the future this can be configured, but for now it'll do. If this parameter is left out, then the exe searches for the scripts folder at ../Scripts. Change this if you want in the soruce code.

The source code itself is explained in the code. You'll need some 4.1 enterprise libraries, but I guess you'll see that if you try to compile the code. I have put them in a lib folder, but the project uses the dll's from the registry.

We use cruisecontrol to set up a part of continuous integration. One project of cruisecontrol is listening to the scripts svn repository and executes the exe with the proper connectionstring is something has changed.

Best practices that we use in our team

  1. Everybody that wants to work at the database has a local database.
  2. Before starting to write an update script or change a sp or function, update your svn repository and execute the exe against your local database (and make sure you use the right one if you also have maintenance branches). I create a local bat file for each branch and for the trunk.
  3. If you want to change an sp or function, open the corresponding file, edit it, save it and run the exe. If everything works, commit the changed sp or function.
  4. If you want to create an update script, copy the template and give it the right name. If the last update script is sc.03.01.0017.sql, then the new one should be sc.03.01.0008.sql or sc.03.02.0000.sql or sc.04.00.0000.sql, depending on what kind of change you are going to make. !Do not commit at this point!
  5. Open the change script in an editor or in management studio and put in your scripts. Change the release numbers and put in some comment. Save the script and run the exe. If everything works, commit the change script.

Executing a change script, sp script or function script in management studio is a bad idea. It may work in management studio but not with the exe. In the future we'll have to check some things that happen with the exe and not with executing them in management studio, but for now we use what we have. If you do want to test changes in management studio you should take a backup before you start doing that. If you think your script is done, then restore your db and run the exe. It's not fun for other developers if things go wrong on the development database after you committed.

<

Teach Yourself Programming in Ten Years

Posted by admin on October 26th, 2009

Just read a very interesting article on programming by Peter Norvig. If you're a programmer, read it and check out some of the links.

--> http://norvig.com/21-days.html

<

Educational software

Posted by admin on October 18th, 2009

I've been working almost 5 years now on educational software in two companies (first Indie Group, now Televic-Education). We mainly created CD-ROMS (that were enclosed with schoolbooks) and online solutions. All those applications were not built from scratch, instead we created a framework that could read in all exercises and hierarchy of the exercises. Those exercises were created in a back end were you could assemble them in folders and export them in a format for our framework (xml) or other formats like SCORM.

I haven't got any videos yet on those CD-ROMS, but you can see two of our online solutions in action. The first is Edumatic, the first application that was created and which has some 20 exercise types (multiple choice, fill in, cross word, dragndrop, translate, ...). It can be used online or exported to be used in CD-ROMS. On the site of edumatic, you can try out an online example of a multiple set of exercises (for each exercise type there are a lot of exercises). Our senior product manager (;-) Piet Santy) also shows an example each day created with Edumatic which you can follow on Twitter --> link. Please follow and try out his examples! Some of his past examples are shown here and here and here and here(demo). Edumatic back end is created in ASP.NET, the front end in Flash (AS2). Together with Christophe Herreman we created the framework for it, but a lot of other persons contributed to the framework, back end and exercise types like Kristof Neirynck(current technical lead), Bert Vandamme, Piet Santy, Sofie Deparcq and others.

Our other product, which is newer than Edumatic, is called Edumatic Exam. It is now used by three customers and has a lot of potential. It's more an assessment application than an exercise application and has currently some 15 exercise types. We are striving to support all exercise types from Edumatic, but that'll take some more time (we're nearly there). Client side is created in Flex, back end with .NET. This application is used by our government to test the knowledge of applicants. Daily there are multiple sessions with hundreds of candidates. We haven't got a demo online yet, but if you subscribe yourself at Selor, you can practice some of the language assessments with our application (you need to go to Mijn Selor, subscribe and then choose Taaloefenpakket... all in Dutch or French...) Try it out! The product is also online, but currently you can not register for it. That'll be something for the future. Currently working most on Edumatic Exam are Bert Vandamme, Sem Dendoncker, Wouter Vanden Berghe and me.

Cheers and practice!

<

Journey to Puimichel early this year watching Moon and Venus

Posted by admin on October 17th, 2009

This post is a little bit off topic (no RIA) but I had to write it down.

Early this year we went on a trip to Puimichel in France. My father has been living there a long time and is passionated by the stars and optics.

Puimichel :
Grotere kaart weergeven

On his site you can see some of his work he's done over the years.

When we were staying there early this year, we did an interference test which was way cool. In short, such an interference test looks how good the optics are. In this stadium there was still work to do which you can see by the circular lines in the test. Basically you see the surface of the optics (parabola) and where there are mountains and valleys (you have to think in lengts of light waves, very small... ;-) ).

Later on, with the 1 meter telescope, we looked at the moon. Incredible how you could almost walk the moon... During the day Venus was nice to watch at, but not like looking at the moon.

Some more pictures:

That's about it. If you ever get a change to go to Puimichel, certainly do. The view is incredible, surrounded by the Verdon, the Alpes, the Lubéron, the Mont Ventoux, ...

Cheers

<

Oldschool Flashing

Posted by admin on August 2nd, 2009

After some years of Flexing I've restarted to Flash. I'm creating a flash site for Peter Platel (know from 'Kleren maken de man') and it has been lots of fun and lots of frustration. Luckily I've gotten support from Ward De Langhe, one of the best Flash Developers around! (Check the Art he's creating with Flash! Awesome!).

Four and a half years ago I started working with Flash mainly to created cd-roms (in a Zinc of Director shell --- even more frustrating). After a while you get to know that Flash isn't always logic. Sometimes I would search hours for a bug just to find out that the bug was in Flash and all you could do is work around it. Other things I remember from working with Flash:

  • Debugging is a disaster because there is no debugger. You had to put traces all around to find a bug and those traces would only appear when you were building in Flash. So SOS logger was used to get some logs when you're site was online for example.
  • Quite often the library in Flash was like a battlefield after a few weeks of development. And once a battlefield is wasn't easy to find anything back.
  • Putting code on the timeline in Flash was a way of punishing the developer that came after you... We had projects from a previous developer that were like a puzzle with code all around... we called it 'name of developer'-code... :-)
  • Creating flash content that would adapt to the size of the stage is a hard one. Certainly when you worked with Flex were Layout Containers are part of the sdk going back to Flash is a adventure. With the help of some basic classes created by Ward De Langhe I created a liquid flash site. Some bugs are left in my flash site but they will be resolved in the near future. For a project I've worked on (Edumatic) we even created Layout Containers in AS2 (based on the Layout Containers in Java).

So, this in mind, I went on creating this Flash Site. Now I have:

  • Still no debugger but, but I started an ActionScript Project in FlexBuilder parallel to the Flash project. This ActionScript Project uses assets from an Swf file. This ActionScript project has the adventage that it can be debugged. The flash project still can't be debugged. Another thing I'd like to try is a Flex Project for creating a Flash Site. I wonder if this will work out... (to be continued).
  • My library is kept clean from in the beginning! If you import graphics from Illustrator, you sometimes get hunderds of groups in groups in groups... I always clean them out first.
  • Almost no code is found in the fla file. All code is in classes, except sometimes a stop() for animations (I could also do this in code but in this case I like it more in the fla).
  • The liquid design isn't easy when you've never done it. For example, if you have a Tween that's dependent of the stage size and during the tween you the stage is resized, you can get in trouble.
  • Thx to some classes provided by Ward, the site is localized. For each language there's a Xml file containing the text content.

I've tried to use Spring.Actionscript in the project but the Ioc framework needs some classes from the Flex Sdk so I could not compile it. Anyway, to keep the size small of the swf it's maybe better to leave it out.

Another thing I learned is that it's a lot of work compared to the result. What seems little work for the customer or graphical designer sometimes takes a lot of hours... But I'm happy with the result for now. The site is far from finished so check it out in the next months as new parts will be added.

<

SQL Server DateTime v’s .Net DateTime – Battle of Accuracy!

Posted by admin on June 10th, 2009

Link

<

Copyright © 2007 Johlero – Cardoen Lieven. All rights reserved.