Home
Help
Register
Log in

Search

 
   Active Threads  

You are here: Home > General > General Chat> Business Rules Engine - any suggestions?
 

Pages: 1 2
General Chat
Business Rules Engine - any suggestions?
Page:1/2 

  Print all messages in this thread  
Poster Message
stoneyowl
User



Location:
Ft Leavenworth, KS USA
Joined on:
29-Jan-2004 21:28:51
Posted:
62 posts
# Posted on: 10-Aug-2006 17:27:12.  
I have just been handed a new set of conditions that complicates life tremendously Angry
I am looking into incorporating a business rules engine to let the users make these kind of changes, rather than re-compiling everytime someone has a bright idea.

Has anyone had any experience with business rules engines for .NET, in particular used with LLBLGen Pro objects? Preferably low cost (or open source).
When I become an evil overlord...in order to keep my subjects in a mindless trance, I will provide 24/7 high speed internet access.  Top
Answer
User



Location:
Phoenix AZ USA
Joined on:
28-Jun-2004 21:13:21
Posted:
363 posts
# Posted on: 10-Aug-2006 17:41:19.  
I have never used it, but it looks nice Laugh

http://sourceforge.net/projects/nxbre/


  Top
stoneyowl
User



Location:
Ft Leavenworth, KS USA
Joined on:
29-Jan-2004 21:28:51
Posted:
62 posts
# Posted on: 10-Aug-2006 18:46:29.  
Yes, I have looked at NXBRE, and will continue to experiment with it, but it's major problem (in my mind) is that it carries along an XML file as baggage. I would prefer either compiled rules generated by a stand alone designer, or something stored in an SQL database.
When I become an evil overlord...in order to keep my subjects in a mindless trance, I will provide 24/7 high speed internet access.  Top
Otis
LLBLGen Pro Team



Location:
The Hague, The Netherlands
Joined on:
17-Aug-2003 18:00:36
Posted:
37694 posts
# Posted on: 10-Aug-2006 20:37:49.  
If you can, you could check out WWF in the .NET 3.0 CTP (though you can't use it in production now, but if your project isn't scheduled to be released before dec 2006, it's definitely an option)

Frans Bouma
LLBLGen Pro / ORM Profiler Lead Developer | Blog | Twitter
 
Top
Jeff M
User



Location:

Joined on:
04-Aug-2004 17:36:52
Posted:
250 posts
# Posted on: 16-Aug-2006 00:46:01.  
Otis wrote:
If you can, you could check out WWF in the .NET 3.0 CTP (though you can't use it in production now, but if your project isn't scheduled to be released before dec 2006, it's definitely an option)


I think that Microsoft is offering GoLive licenses for WWF and WinFX.

Jeff
  Top
jtgooding
User



Location:
Orlando, USA
Joined on:
26-Apr-2004 14:42:01
Posted:
126 posts
# Posted on: 16-Aug-2006 14:22:24.  
They are but it is not for the latest release, so if you download the latest CTP it doesn't have a go live unfortunately, Go Live is only for Beta 2, and I think they are currently at Beta 2.2 or something silly like that.

John Gooding  Top
Devildog74
User



Location:
Westminster, CO
Joined on:
04-Feb-2004 20:27:48
Posted:
719 posts
# Posted on: 18-Aug-2006 06:54:42.  
InRule has been around for a while. It is a commercial product. http://www.inrule.com/

The current company that I am working for is already writing state machine workflows into its product line using windows workflow foundation RC2. It is really cool. You can create extensible workflows and create XOML based workflows that are compiled on the fly. The rules are XML based but they get compiled into code. Objects associated with the workflow are serialized and deserialized into SQL Server.

I have been using workflows to orchestrate communication and invoke behaviors on business objects in various business libraries and web services in our organization. So far, it has been pretty painless, and it is pretty robust.
  Top
Otis
LLBLGen Pro Team



Location:
The Hague, The Netherlands
Joined on:
17-Aug-2003 18:00:36
Posted:
37694 posts
# Posted on: 18-Aug-2006 10:17:47.  
Devildog, would you describe it as 'easy' to for example define rules for an LLBLGen Pro using system in WWF?

Frans Bouma
LLBLGen Pro / ORM Profiler Lead Developer | Blog | Twitter
 
Top
Devildog74
User



Location:
Westminster, CO
Joined on:
04-Feb-2004 20:27:48
Posted:
719 posts
# Posted on: 18-Aug-2006 14:51:38.  
Not really sure that I follow your question.

In a workflow I can create properties and build rules on those properties. So if the type of one of my properties was CustomerOrder:EntityBase and my CustomerOrder had a property "OrderItems" of type EntityCollection, I could create a rule called "HasOrderItems".

If I fetch data into the CustomerOrder and the OrderItems collection when the workflow begins execution, I can have access to the CustomerOrder property at any point during the execution of my workflow as long as its always created on startup.

When I create the rule I can say if CustomerOrder.OrderItems.Count > 0 then HasOrderItems == true, and thus write control of flow in my workflow using this rule.

So I am not too sure how easy it is, but it is feasable.

So far the biggest challenge comes when exposing a custom business object, i.e. a subclass of EntityBase or Csla.BusinessBase as a property of the workflow. The challenge lies when the workflow is persisted to the database. The object is serialized so that it may be re-created at a later date and time when the workflow instance is re-created.

  Top
Otis
LLBLGen Pro Team



Location:
The Hague, The Netherlands
Joined on:
17-Aug-2003 18:00:36
Posted:
37694 posts
# Posted on: 18-Aug-2006 16:01:16.  
Devildog74 wrote:
Not really sure that I follow your question.

In a workflow I can create properties and build rules on those properties. So if the type of one of my properties was CustomerOrder:EntityBase and my CustomerOrder had a property "OrderItems" of type EntityCollection, I could create a rule called "HasOrderItems".

If I fetch data into the CustomerOrder and the OrderItems collection when the workflow begins execution, I can have access to the CustomerOrder property at any point during the execution of my workflow as long as its always created on startup.

When I create the rule I can say if CustomerOrder.OrderItems.Count > 0 then HasOrderItems == true, and thus write control of flow in my workflow using this rule.

So I am not too sure how easy it is, but it is feasable.

Ah thanks Regular Smiley
What I was thinking about is if WWF is usable for business rules in a random application which also uses LLBLGen Pro. And if not, or it's cumbersome, what I have to do to make it easy. Regular Smiley. What I'm after is to avoid having to supply a business rule engine on top of llblgen pro entities but instead use what's inside .NET in a couple of months: WWF.

Quote:

So far the biggest challenge comes when exposing a custom business object, i.e. a subclass of EntityBase or Csla.BusinessBase as a property of the workflow. The challenge lies when the workflow is persisted to the database. The object is serialized so that it may be re-created at a later date and time when the workflow instance is re-created.

hmm. As a true Johnny Clueless, I have no idea why one would persist a workflow to the db when it runs, but perhaps there might be a reason. I'll do some reading this weekend to see what WWF is all about and what I need to do to make llblgen pro be very easy to use with WWF Regular Smiley


Frans Bouma
LLBLGen Pro / ORM Profiler Lead Developer | Blog | Twitter
 
Top
DvK
User



Location:
Delft (centre), Netherlands
Joined on:
22-Mar-2006 10:43:57
Posted:
292 posts
# Posted on: 18-Aug-2006 16:32:50.  
Whoaw....that would be great. A true rules-engine on top of LLBLGen ! Laugh
That would make your product even more interesting for developers because everybody has to deal with real-world workflow situations in professional applications.

Perhaps it's a good idea to come up with some constructive ideas about functional stuff / workflow characteristics ?!

Regards,
Danny
  Top
Devildog74
User



Location:
Westminster, CO
Joined on:
04-Feb-2004 20:27:48
Posted:
719 posts
# Posted on: 18-Aug-2006 21:17:06.  
Here is a snippet of a rule that gets put into the .rules file associated with a workflow when you use the rule builder in the VS.NET IDE.

Code:

        <RuleExpressionCondition Name="SubmitOrderToPartners">
            <RuleExpressionCondition.Expression>
                <ns0:CodeBinaryOperatorExpression Operator="ValueEquality" xmlns:ns0="clr-namespace:System.CodeDom;Assembly=System, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089">
                    <ns0:CodeBinaryOperatorExpression.Left>
                        <ns0:CodePropertyReferenceExpression PropertyName="SubmitOrderToPartnerSystems">
                            <ns0:CodePropertyReferenceExpression.TargetObject>
                                <ns0:CodePropertyReferenceExpression PropertyName="Order">
                                    <ns0:CodePropertyReferenceExpression.TargetObject>
                                        <ns0:CodeFieldReferenceExpression FieldName="SubmitOrder_WsIn_RequestMessage">
                                            <ns0:CodeFieldReferenceExpression.TargetObject>
                                                <ns0:CodeThisReferenceExpression />
                                            </ns0:CodeFieldReferenceExpression.TargetObject>
                                        </ns0:CodeFieldReferenceExpression>
                                    </ns0:CodePropertyReferenceExpression.TargetObject>
                                </ns0:CodePropertyReferenceExpression>
                            </ns0:CodePropertyReferenceExpression.TargetObject>
                        </ns0:CodePropertyReferenceExpression>
                    </ns0:CodeBinaryOperatorExpression.Left>
                    <ns0:CodeBinaryOperatorExpression.Right>
                        <ns0:CodePrimitiveExpression>
                            <ns0:CodePrimitiveExpression.Value>
                                <ns1:Boolean xmlns:ns1="clr-namespace:System;Assembly=mscorlib, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089">true</ns1:Boolean>
                            </ns0:CodePrimitiveExpression.Value>
                        </ns0:CodePrimitiveExpression>
                    </ns0:CodeBinaryOperatorExpression.Right>
                </ns0:CodeBinaryOperatorExpression>
            </RuleExpressionCondition.Expression>
        </RuleExpressionCondition>


This particular rule checks the value of a property associated with a request document serilaized with a workflow.

With regards to serialization of workflows, some workflows may take days weeks, or months to complete and they all cannot live in the servers memory until they are completed and they definately need to survive crashes and reboots. So the workflow runtime puts them into a database when they arent active and re-activates the same instance when they need to be used again.

I am not really sure how rules would be applicable to LLBlgen objects.

I do know that you may want to write your own workflow activities that workflow designers can use to interact with LLBLGen generated objects. For example, there are activities being created to communicate with SMTP Services and SQL Server for Data Access, so I dont see why there could be a set of LLBLGen provided activities to hook into state machine and sequential workflows.


  Top
pilotboba
User



Location:

Joined on:
05-Aug-2005 21:31:38
Posted:
434 posts
# Posted on: 18-Aug-2006 22:02:43.  
Otis wrote:
I have no idea why one would persist a workflow to the db when it runs, but perhaps there might be a reason.


Basically you can think of a workflow as a long running transaction. Many examples of this are a business process. Say, ordering a product is a work flow.

Create Purchase Requisition
Submitt Purchase Req
Approve/Reject Req
If approved, Create Purchase Order
Submitt Purchase Order
else rejected
Revise Req or Cancel Req
Recieve Purchase Order
etc, etc...

The workflow is not going to happen immediatly. So, you need to save the workflow at different states until it is discarded or completed.

BOb
  Top
swallace
User



Location:
Oklahoma City, Oklahoma, USA
Joined on:
18-Aug-2003 15:34:29
Posted:
648 posts
# Posted on: 18-Aug-2006 23:06:41.  
Great videos on WWF:

Quote:
http://wf.netfx3.com/files/folders/screencasts/default.aspx

View them from the beginning, as they build on each other. Starts with the simplest concepts, and eventually moves into persistence services and more. 24 videos, each about 10 minutes.


  Top
JimFoye
User



Location:
Austin, TX
Joined on:
22-Jun-2004 04:03:11
Posted:
656 posts
# Posted on: 19-Aug-2006 17:43:09.  
Wow WWF looks pretty exciting. I was actually just pondering enhancing my existing validation framwork I use for LLBLGenPro by putting rules in an XML file and generating them with a custom template, but I think it's better to wait for WWF!
  Top
Otis
LLBLGen Pro Team



Location:
The Hague, The Netherlands
Joined on:
17-Aug-2003 18:00:36
Posted:
37694 posts
# Posted on: 19-Aug-2006 18:29:43.  
Thanks Scott for the link!
DevilDog: thanks for the example, I have a better understanding now Regular Smiley

pilotboba wrote:
Otis wrote:
I have no idea why one would persist a workflow to the db when it runs, but perhaps there might be a reason.


Basically you can think of a workflow as a long running transaction. Many examples of this are a business process. Say, ordering a product is a work flow.

Create Purchase Requisition
Submitt Purchase Req
Approve/Reject Req
If approved, Create Purchase Order
Submitt Purchase Order
else rejected
Revise Req or Cancel Req
Recieve Purchase Order
etc, etc...

The workflow is not going to happen immediatly. So, you need to save the workflow at different states until it is discarded or completed.

BOb

OK, this makes sense. I was under the assumption that WWF could be a replacement for the actual BL in your app (which runs instantly also Wink). So instead of typing code etc., you could design some workflows and execute those, which could be preferable as you can visually discuss the flow of the application with the customer and the design is actually the program (which I personally find what computer science should be targetting at)

Though a workflow in the sense as you and devildog describe it is indeed making it SIMPLER to create / define /program these longer running tasks.

The only problem I see is with versioning of serialized entities/data. What if the assembly where the type of a given object gets updated? Won't that make saved workflows unloadable? So you can only update assemblies if running workflows are ended, but that would make maintaining the system hard.



Frans Bouma
LLBLGen Pro / ORM Profiler Lead Developer | Blog | Twitter
 
Top
Devildog74
User



Location:
Westminster, CO
Joined on:
04-Feb-2004 20:27:48
Posted:
719 posts
# Posted on: 20-Aug-2006 01:26:29.  
Actually that is one of the challenges that the people at microsoft havent quite overcome yet, and it is a bit cumbersome.

But basically when the runtime deserializes a workflow, its assembly is loaded using the standard assembly location techniques (probing, GAC, etc.). When making changes to workflows you have to use due care when deploying. At this point the reccomended approach is to deploy to the GAC or modify the probing paths, but I havent been able to get the probing paths functionality worked out.

For example, say I have workflows that were created under v1.0.1 and they are still waiting for completion. I can deploy v1.0.2 and run new workflows with the code in v1.0.2 but the v1.0.1 assembly still needs to live on the machine somewhere.

What I have found is that because my assemblies are named the same, and I use probing paths, the runtime finds some assembly using the assembly name and then stops looking even though it has found the wrong version.

So, Frans, yes you are correct, developers must be very careful when versioning and deploying workflows. When making changes to a given workflow, if you change the structure of the workflow, you need to increment the version number otherwise, existing workflows may be broken. So you almost need to treat published workflows like a published interface, whereby, once its published you dont take things away.

One thing that is really cool is that you can make workflows extensible by using activities that allow you to dynamically add and invoke other workflows into the execution chain of the executing workflow.

I havent tried using entities with workflows. Actually, I am not sure that I would want serialize an entity with fetched data into a workflow. My approach when using business objects in workflows is to make the business objects stateless, and only serialize the data needed to fetch / recreate / re-hydrate the business object / entity when the workflow starts up again. So, I may only serialize the key values of the entities into properties of the workflow, and then in other methods of the workflow, re-fetch the entitites using the previously serialize key values. One thing that I can see doing is using entities for CRUD operations that need to be invoked during the execution of an activity within a workflow.
  Top
sparmar2000
User



Location:
Aylesbury, UK
Joined on:
30-Nov-2003 14:50:22
Posted:
341 posts
# Posted on: 20-Aug-2006 06:14:35.  
Here is my ‘penny’s’ worth on this very interesting thread.

The Manual Process—Before Workflow

An application form arrives in the post. It is sorted by the mailroom and distributed to one (of many clerks), who checks it for completeness. The form might be returned to the customer for more information at this point, or rejected out-of-hand, or the clerk will ‘file’ the application into the Company’s ‘Customer Loan Application’ folder that includes a company form which has application status tick boxes, and then pass it to the next clerk.

The process of granting a loan can include many sub-processes. Some organizations, there is a clerk or team of clerks for each sub-process; in others, one clerk manages all the tasks for an individual application.

The point I am trying to make is that the ‘application form’ in my view is the LLBLGen Pro entity (or entities), the ‘clerks’ are the workflow runtime engine and the ‘process’ they follow is the workflow to fill in the application status tick boxes.

Each clerk performs an activity e.g. review applicant’s salary to access if the loan follows the company rule like ‘loans should be maximum 3 times the salary’.

A workflow system would perform this activity as part of a workflow (process) by retrieving the person entity and looking at the salary property and then changing the workflow status to either ‘acceptable’ or ‘not’ - the clerk would tick one of the boxes and pass it to some other clerk in the maual process.

So in conclusion in my opinion, the workflow engine, in this case MS WWF, persists the workflow ‘status’ of a particular application - the application status tick boxes, whilst the ‘application’ is in database where all the CRUS is performed using LLBLGen Pro.

Have I got this right?

Application status tick boxes assembly need to be backward compatible and the LLBLGen assemby is independent from the workflow so can change as long as the API between the 2 does not change.


  Top
Otis
LLBLGen Pro Team



Location:
The Hague, The Netherlands
Joined on:
17-Aug-2003 18:00:36
Posted:
37694 posts
# Posted on: 20-Aug-2006 12:21:30.  
Devildog74 wrote:
Actually that is one of the challenges that the people at microsoft havent quite overcome yet, and it is a bit cumbersome.

But basically when the runtime deserializes a workflow, its assembly is loaded using the standard assembly location techniques (probing, GAC, etc.). When making changes to workflows you have to use due care when deploying. At this point the reccomended approach is to deploy to the GAC or modify the probing paths, but I havent been able to get the probing paths functionality worked out.

For example, say I have workflows that were created under v1.0.1 and they are still waiting for completion. I can deploy v1.0.2 and run new workflows with the code in v1.0.2 but the v1.0.1 assembly still needs to live on the machine somewhere.

What I have found is that because my assemblies are named the same, and I use probing paths, the runtime finds some assembly using the assembly name and then stops looking even though it has found the wrong version.

This is seriously bad. That they don't check for assembly version AND fileversion is going to hurt this technique. We all know the .NET 1.1 SP1 change in the SortedList class which made data saved in a program running on Sp1 not deserializable on .NET 1.1 without Sp1. Thus serializable classes have to be able to deserialize themselves from data serialized in older versions, that's what this says IMHO. That will be a challenge Regular Smiley.

Quote:

So, Frans, yes you are correct, developers must be very careful when versioning and deploying workflows. When making changes to a given workflow, if you change the structure of the workflow, you need to increment the version number otherwise, existing workflows may be broken. So you almost need to treat published workflows like a published interface, whereby, once its published you dont take things away.

Even more restrictive I think: the interface might not even change, but the implementation did: crash Regular Smiley. So all the deserializer code needs stuff like I use in the designer, a set of methods which simply swallow an exception if a value isn't there, like:
object value = GeneralUtils.InfoGetValue(info, "_foo", typeof(int));
if(value==null)
{
// not found, old version
}

etc. and InfoGetValue simply tries to read _foo from info, which always throws an exception if it's not there (which is a retarded design, why isn't there some kind of ContainsKey or Contains method.. Angry), and if so, returns a default value, e.g. null

Quote:

One thing that is really cool is that you can make workflows extensible by using activities that allow you to dynamically add and invoke other workflows into the execution chain of the executing workflow.

I havent tried using entities with workflows. Actually, I am not sure that I would want serialize an entity with fetched data into a workflow. My approach when using business objects in workflows is to make the business objects stateless, and only serialize the data needed to fetch / recreate / re-hydrate the business object / entity when the workflow starts up again. So, I may only serialize the key values of the entities into properties of the workflow, and then in other methods of the workflow, re-fetch the entitites using the previously serialize key values. One thing that I can see doing is using entities for CRUD operations that need to be invoked during the execution of an activity within a workflow.

I think that's a good solution Regular Smiley you then avoid the serialization problem altogether Regular Smiley
Frans Bouma
LLBLGen Pro / ORM Profiler Lead Developer | Blog | Twitter
 
Top
Devildog74
User



Location:
Westminster, CO
Joined on:
04-Feb-2004 20:27:48
Posted:
719 posts
# Posted on: 20-Aug-2006 21:27:26.  
sparmar2000 wrote:
Have I got this right?


Dead on.


  Top
Devildog74
User



Location:
Westminster, CO
Joined on:
04-Feb-2004 20:27:48
Posted:
719 posts
# Posted on: 01-Sep-2006 16:49:49.  
If anyone is interested, I figured out how to use the <dependentAssembly> attribute in my application configuration file to determine how the workflow runtime can locate assemblies with the same name but different versions.

Basically, for each version of the workflow, I add a <dependentAssembly>, <assemblyIdentity>, and <codeBase> element to my configuration file, and viola, the runtime can find the assemblies.

Check it out on my blog. http://cbertolasio.blogspot.com/
  Top
Chester
Support Team



Location:
Chicago, IL USA
Joined on:
15-Jul-2005 22:38:41
Posted:
223 posts
# Posted on: 03-Sep-2006 00:55:22.  
I haven't yet investigated WWF, at least not hands-on (done a lot of reading). I thought it had basically two possible functions:

1) define sequential workflows (Clerk enters order, passes off to Order Picker, passes off to Shipping...)

2) define a finite state machine - the "turnstile" is an example of one and can be found at:

http://www.objectmentor.com/resources/articles/umlfsm.pdf#search=%22state%20machine%20turnstile%22


I'm currently on a BizTalk project, and it has it's own rules engine (which could be used in any application, not just BizTalk, except for licensing issues). The engine allows you to define Facts (databases, XML Schemas, etc.), and then Rules that operate on those Facts. e.g. Given an XML document of a certain Schema type, check that the node at such-and-such a path is less than such-a-value. If true, perform some action (set the value of another node, set the value of a database artifact, call some external assembly method...). It's not too bad, but I haven't had to deal with versioning, so I'm not sure about it.

I do know that all the BizTalk guys at MS seem to be looking forward to both WCF and WWF coming out. So I don't think WWF will replace what the BizTalk rules engine does now but rather augment it.


  Top
mkamoski
User



Location:
ZULU-5
Joined on:
06-Dec-2005 22:51:12
Posted:
116 posts
# Posted on: 05-Sep-2006 16:27:51.  
stoneyowl wrote:
I have just been handed a new set of conditions that complicates life tremendously Angry
I am looking into incorporating a business rules engine to let the users make these kind of changes, rather than re-compiling everytime someone has a bright idea.

Has anyone had any experience with business rules engines for .NET, in particular used with LLBLGen Pro objects? Preferably low cost (or open source).


Funny you should ask.

I have been building one for a client for a while now and will be done with a week or so.

I wrote a high-level, public, whitepaper on it, which you can find here...

http://www.weblogicarts.com/DemoList.aspx

(A lot has changed since I wrote that paper. For example, we replaced WilsonORMapper with LLBLGen. But, the general idea is the same.)

It is written in DotNet and can manage rules for any DotNet DLL or EXE that has objects that serialize to XML.

That is, we are using it with LLBLGen objects; but, it can work with any objects that serialize to XML.

Feel free to take the idea if you like and/or ask specific questions. I cannot share the code base; but, I am happy to discuss it.

It is not the be-all-end-all, do-everything type of rules-engine; but, it does work and it does do a lot.

HTH.

Thank you.

-- Mark Kamoski

http://www.WebLogicArts.com   Top
greenstone
User



Location:
Alexandria, VA, USA
Joined on:
20-Jun-2007 03:39:56
Posted:
126 posts
# Posted on: 06-Nov-2010 16:08:12.  
Hi,

This thread is a few years old now, but have a project where need to create a long-running workflow (essentially, a niche business ticketing system...with lots of specific rules), and have started to look at Windows Workflow Foundation (WF) 4.0. Especially because need to use SQLServer or Oracle (customer choice for a given installation) as the database backend, I would like to use llblgen (the database neutrality being just one of the many great features of llblgen) if it is feasible with WF.

Has anyone had some good success stories (or "didn't work" stories) using WF with llblgen?

Thanks!


Andy  Top
raist
User



Location:
Madrid, Spain
Joined on:
19-Apr-2010 23:57:10
Posted:
114 posts
# Posted on: 28-Oct-2011 15:07:18.  
We're trying that approach right now.
Basically:
- ORM Layer
- GUI Layer based on extended ASP.Net GUI Templates (llblgenangte.codeplex.com + roles, profiles, breadcrumbs, etc.).
- Asynchronous Business Logic based on Llblgen IoC (through auditors) connected to WF.

Let me know if you are interested
  Top
Pages: 1 2  


Powered by HnD ©2002-2007 Solutions Design
HnD uses LLBLGen Pro

Version: 2.1.12172008 Final.