20

Can anyone give a high level description of what is going on in the Composite C1 core? In particular I am interested in knowing how the plugin architecture works and what the core components are of the system i.e. when a request arrives what is happening in the architecture. The description doesn't have to be too verbose just a list of steps and the classes involved.

Hopefully one of the core development team would enlighten me... and maybe publish some more API (hint hint more class documentation please).

Michiel van Oosterhout
  • 22,839
  • 15
  • 90
  • 132
Paul Sullivan
  • 2,865
  • 2
  • 19
  • 25
  • Class Documentation is always a bitch, and its hard to find any open source product with a as big codebase as C1 that has any decent Class Documentation. Even a high-cost, closed source product as Sitecore sucks when it comes to documenting code. Its all about reading examples and asking in forums. And reading code... nothing beats sitting in Reflector and reading code :) – Pauli Østerø Oct 30 '11 at 15:06
  • Agreed Pauli - I've been sitting in ILSpy and working my way through the core. Really it doesn't need full class documentation but does need some core documentation either in UML or even just a couple of articles detailing the major internal functions and how it ties together. While the documentation on the C1 site is good it does need filling in in some places. Understandable that it is missing though given the size of the system. I am reading your amendments and will mark as answer shortly. regards – Paul Sullivan Oct 30 '11 at 15:18
  • The system is too big for any single person to grasp by him self, and "major internal functions" depends very much on what exactly you want to interact with. Just the trees and elements in the Console is a major study by itself. Workflows, action providers, hooks and security. The functions system is pretty easy to understand but then Data Providers is pretty heavy. Static types is also very interesting and widget providers for using in Form Markups. Man, there is just so much. – Pauli Østerø Oct 30 '11 at 15:26
  • :) Yup 'Man, there is just so much' - Huge system but until I have grasped what is going on internally (even at the surface level) I won't be happy developing against it (with the confidence I need when asked for something 'bespoke'. One of my early concerns that shook my confidence was of how to go about integrating a Membership/Role Provider (which was answered by a good example in the forums) but I am not one for wanting a solution to be provided more to have the ability to provide myself. Until I can understand the model using c1 si too risky for anything other than a personal project. – Paul Sullivan Oct 30 '11 at 16:06
  • ahh... not that i will compare C1 with like Windows, but you can't never fully grasp anything these days - you just have to use it and trust its properly built. Regarding Membership/Role provider its not the simplest things to implement, since there is no 1:1 mapping between the user/authentication/groups schema of C1 and asp.net membership. But by introducing a new Datatype to hold these extra values its possible http://compositec1contrib.codeplex.com/SourceControl/changeset/view/4b4032c697d3#Core%2fWeb%2fSecurity%2fCompositeC1MembershipProvider.cs – Pauli Østerø Oct 30 '11 at 16:21
  • Yes agreed it is unfeasible to understand everything about large systems. On a side note: the last link you gave (which was produced as a result of my request many moons ago (6-12 months) I don't think I ever worked out how to actually use the MembershipProvider i.e. where do I add/replace the default LoginProviderFacade. This comes back to my original question really about how it works or how do we replace/override plugins within the C1 architecture (should I raise a new Question for this?) AND (EDIT) Thanks a hell of a lot for this - I just need a kick start to get my head rnd core concepts – Paul Sullivan Oct 30 '11 at 17:29
  • sounds to me that you either don't know exactly what you want, or you have explained yourself poorly. If you wan't users to login *INTO* C1 with a Membershipprovider you already have from some 3rd party vendor, its a whole other thing you need to make. Creating a C1 membership provider is to get users OUT of C1..., not to let use other systems be the authenticator when logging into the C1 console. – Pauli Østerø Oct 30 '11 at 17:43
  • @Paul add a feature request for more API doc on our CodePlex site, suggest areas. I'm sure the request would quickly gain the votes to make it top prio and the thread would allow for discussion/suggestions. – mawtex Nov 05 '11 at 20:41

1 Answers1

40

From request to rendered page

The concrete path a request takes depends on the version of C1 you're using, since it was changed to use Routing in version 2.1.2. So lets see

  • < 2.1.2

Composite.Core.WebClient.Renderings.RequestInterceptorHttpModule will intercept all incoming requests and figure out if the requested path correspond to a valid C1 page. If it does, the url will be rewritten to the C1 page handler ~/Rendererings/Page.aspx

  • 2.1.1

Composite.Core.Routing.Routes.Register() adds a C1 page route (Composite.Core.Routing.Pages.C1PageRoute) to the Routes-collection that looks at the incoming path, figures out if its a valid C1 page. If it is, it returns an instance of ~/Rendererings/Page.aspx ready to be executed.

Okay, so now we have an instance of a IHttpHandler ready to make up the page to be returned to the client. The actual code for the IHttpHandler is easy to see since its located in ~/Renderers/Page.aspx.cs.

  • OnPreInit

Here we're figuring out which Page Id and which language that was requested and looking at whether we're in preview mode or not, which datascope etc.

  • OnInit

Now we're fetching the content from each Content Placeholder of our page, and excuting its functions it may contain. Its done by calling Composite.Core.WebClient.Renderings.Page.PageRenderer.Render passing the current page and our placeholders. Internally it will call the method ExecuteFunctions which will run through the content and recursively resolve C1 function elements (<f:function />), execute them and replace the element with the functions output. This will be done until there are no more function elements in the content in case functions them selves output other functions.

Now the whole content is wrapped in a Asp.Net WebForms control, and inserted into our WebForms page. Since C1 functions can return WebForms controls like UserControl etc., this is necessary for them to work correctly and trigger the Event Lifecycle of WebForms.

And, that's basically it. Rendering of a requested page is very simple and very extendable. For instance is there an extension that enables the usage of MasterPages which simply hooks into this rendering flow very elegantly. And because we're using Routing to map which handler to use, its also possible to forget about ~/Rendering/Page.aspx and just return a MvcHandler if your a Mvc fanatic.

API

Now, when it comes to the more core API's there are many, depending on what you want to do. But you can be pretty sure, no matter what there is the necessary ones to get the job done.

At the deep end we have the Data Layer which most other API's and facades are centered around. This means you can do most things working with the raw data, instead of going through facades all the time. This is possible since most configuration of C1 is done by using its own data layer to store configuration.

The Composite C1 core group have yet to validate/refactor and document all the API's in the system and hence operate with the concept of 'a public API' and what can become an API when the demand is there. The latter is a pretty darn stable API, but without guarantees.

The public API documentation is online at http://api.composite.net/

Functions

Functions is a fundamental part of C1 and is a technique to abstract logic from execution. Basically everything that either performs a action or returns some data/string/values can be candidates for functions. At the lowest level a function is a .Net class implementing the IFunction interface, but luckily there are many easier ways to work with it. Out of the box C1 supports functions defined as XSLT templates, C# methods or Sql. There are also community support for writing functions using Razor or having ASP.Net UserControls (.ascx files) to be functions.

Since all functions are registered in C1 during system startup, we use the Composite.Functions.FunctionFacade to execute whatever function we know the name of. Use the GetFunction to get a reference to a function, and then Execute to execute it and get a return value. Functions can take parameters which are passed as real .Net objects when executing a function. There is also full support for calling functions with Xml markup using the <f:function /> element, meaning that editors, designers, template makers etc. easily can access a wealth of functionality without having to know how to write .Net code.

Read more about functions here http://users.composite.net/C1/Functions.aspx and how to use ie Razor to make functions here http://docs.composite.net/C1/ASP-NET/Razor-Functions.aspx

Globalization and Localization

C1 has full multi-language support in the core. Composite.Core.Localization.LocalizationFacade is used for managing the installed locales in the system; querying, adding and removing. Locales can be whatever CultureInfo object is known by your system.

Composite.Core.ResourceSystem.StringResourceSystemFacade is used for getting strings at runtime that matches the CultureInfo your request is running in. Use this, instead of hardcoding strings on your pages or in your templates.

Read more about Localization here http://docs.composite.net/C1/HTML/C1-Localization.aspx

Global events

Composite.C1Console.Events.GlobalEventSystemFacade is important to know if you need to keep track on when the system is shutting down so you can make last-minute changes. Since C1 is highly multithreaded its easy to write extensions and modules for C1 that are multithreaded as well, taking advantage of multi core systems and parallelization and therefor its also crucial to shut down ones threads in a proper manner. The GlobalEventSystemFacade helps you do that.

Startup events If you write plug-ins these can have a custom factory. Other code can use the ApplicationStartupAttribute attribute to get called by the Composite C1 core when the web app start up.

Data events You can subscribe to data add, edit and delete events (pre and post) using the static methods on Composite.Data.DataEvents<T>. To attach to these events when the system start up, use the ApplicationStartupAttribute attribute.

Data

Composite.Core.Threading.ThreadDataManager is important if your accessing the Data Layer outside of a corresponding C1 Page request. This could be a custom handler that just has to feed all newest news as a Rss feed, or your maybe writing a console application. In these cases, always remember to wrap your code that accesses the data like this

using(Composite.Core.Threading.ThreadDataManager.EnsureInitialize())
{
  // Code that works with C1 data layer goes here
}

For accessing and manipulating data its recommended NOT to use the DataFacade class, but wrap all code that gets or updates or deletes or adds data like this

using(var data = new DataConnection())
{
   // Do things with data
}

IO

When working with files and directories its important to use the C1 equivalent classes Composite.Core.IO.C1File and Composite.Core.IO.C1Directory to .Net's File and Directory. This is due to the nature where C1 can be hosted on Azure, where you might not have access to the filesystem in the same way as you have on a normal Windows Server. By using the C1's File and Directory wrappers you can be sure that code you write will be able to run on Azure as well.

C1 Console

The console is a whole subject on itself and has many many API's.

You can create your own trees using Composite.C1Console.Trees.TreeFacade or Composite.C1Console.Elements.ElementFacade and implementing a Composite.C1Console.Elements.Plugins.ElementProvider.IElementProvider.

You can use the Composite.C1Console.Events.ConsoleMessageQueueFacade to send messages from the server to the client to make it do things like open a message box, refreshing a tree, set focus on a specific element, open a new tab etc. etc.

Composite.C1Console.Workflow.WorkflowFacade is used for getting instances of specific workflows and interacting with them. Workflows is a very fundamental part of C1 and is the way multi-step operations are defined and executed. This makes it possible to save state of operation so ie. a 10 step wizard is persisted even if the server restarts or anything else unexpected happens. Workflows are build using Windows Workflow Foundation, so are you familiar with this, you should be feeling at home

There is also a wealth of javascript facades and methods you can hook into when writing extensions to the Console. Much more than i could ever cover here so i will refrain myself from even getting started on that subject here.

composite.config

A fundamental part of C1 is providers, almost everything is made up of providers, even much of the core functionality. Everything in the console from Perspectives to Trees and elements and actions are feeded into C1 with providers. All the standard functions, the datalayer and all the widgets for use with the Function Call editor is feeded into C1 with providers. All the localisation strings for use with the Resources, users and permissions, url formatters etc. is all providers.

  • Composite.Data.Plugins.DataProviderConfiguration

Here all providers that can respond to the methods on DataFacade, Get, Update, Delete, Add etc. are registered. Every provider informs the system which interfaces it can interact with and C1 makes sure to route all requests for specific interfaces to their respective dataproviders.

  • Composite.C1Console.Elements.Plugins.ElementProviderConfiguration

Here we're defining the perspectives and the trees inside the Console. All the standard perspectives you see when you start the Console the first time are configured here, no magic or black box involved.

  • Composite.C1Console.Elements.Plugins.ElementActionProviderConfiguration

Action providers are able to add new menuitems to all elements in the system, based on their EntityToken. This is very powerful when you want to add new functionality to existing content like versioning, extranet security, custom cut/paste and the list goes on.

  • Composite.C1Console.Security.Plugins.LoginProviderConfiguration

A LoginProvider is what the C1 console will use to authenticate a user and let you log in or not. Unfortunately this isn't very open but with some reflection you should be all set.

  • Composite.Functions.Plugins.FunctionProviderConfiguration

Composite C1 will use all the registered FunctionProviders to populate its internal list of functions on system startup.

  • Composite.Functions.Plugins.WidgetFunctionProviderConfiguration

WidgetProviders are used in things like the Function Call Editor or in Forms Markup to render custom UI for selecting data.

  • Composite.Functions.Plugins.XslExtensionsProviderConfiguration

Custom extensions for use in XSLT templates are registered here

And then we have a few sections for pure configuration, like caching or what to to parallelize but its not as interesting as the providers.

Defining and using sections

Sections in composite.config, and other related .config files are completely standard .Net configuration and obeys the rules thereof. That means that to be able to use a custom element, like ie. Composite.Functions.Plugins.WidgetFunctionProviderConfiguration it has to be defined as a section. A section has a name and refers to a type that would inherit from System.Configuration.ConfigurationSection. Composite uses the Microsoft Enterprise Libraries for handling most of these common things like configuration and logging and validation and therefor all Composites sections inherit from Microsoft.Practices.EnterpriseLibrary.Common.Configuration.SerializableConfigurationSection. Now, this type just has to have properties for all the elements we want to be able to define in the .config-file, and .Net will automatically make sure to wire things up for us.

If you want to access configuration for a particular section you would call Composite.Core.Configuration.ConfigurationServices.ConfigurationSource.GetSection(".. section name") and cast it to your specific type and your good to go.

Adding extra properties to already defined sections

Normally .Net would complain if you write elements or attributes in the .config files that aren't recognized by the type responsible for the section or for the element. This makes it hard to write a truly flexible module-system where external authors can add specific configuration options to their providers, and therefor we have the notion of a Assembler. Its a ConfigurationElement class with a Microsoft.Practices.EnterpriseLibrary.Common.Configuration.ObjectBuilder.AssemblerAttribute attribute assigned to it that in turns takes a Microsoft.Practices.EnterpriseLibrary.Common.Configuration.ObjectBuilder.IAssembler interface as argument that is responsible for getting these custom attributes and values from the element in the .config file and emit usable object from it. This way .Net won't complain about an invalid .config file, since we inject a ConfigurationElement object that has properties for all our custom attributes, and we can get hold of them when reading the configuration through the IAssembler

Slides

Some overview slides can be found on these lins

Inspiration and examples

The C1Contrib project on GitHub is a very good introduction how to interact with the different parts of C1. Its a collection of small packages, that can be used as it is, or for inspiration. There are packages that manipulates with dynamic types to enable interface inheritance. Other packages uses the javascript api in the console, while others show how to make Function Providers, Trees and hook commands unto existing elements. There is even examples of how to manipulate with the Soap webservice communication going on between client and server so you can make it do things the way you want it. And the list goes on.

Pauli Østerø
  • 6,878
  • 2
  • 31
  • 48
  • Thanks Pauli - that's shed a fair amount of light on the top level architecture but I'm more interested in what the various XXXFacade classes are responsible for. Obviously DataFacade provides access to the Data API but what are the others doing i.e. GlobalInitializerFacade deals with start up. If you could shed light it would help - it seems to me that these facades are the real access to the underlying C1 core...? – Paul Sullivan Oct 30 '11 at 10:59
  • Updated my answer with a list of important API's and facades. – Pauli Østerø Oct 30 '11 at 14:34
  • 2
    Updated answer with slides that might be of help – Pauli Østerø Oct 31 '11 at 14:47
  • Nice Pauli. Where are these from? I've also been debugging the client side js and see the admin system is driven by soap (WSDL) + asmx in the admin side which has given me a bit of a better understanding. I'm just trying to get my head round how the system bootstraps the modules defined in Composite.config (i.e. how/can I add my own modules and the pre-requisites for doing so) – Paul Sullivan Oct 31 '11 at 16:00
  • 1
    iIs just some slides from a technical presentation that was a bit too technical for the website. Updating the answer with some hints about composite.config. – Pauli Østerø Oct 31 '11 at 19:35
  • Once again Pauli many thanks. I've been debugging the GlobalInitializer and FileConfigurationSource classes this morning and seeing how the object creation occurs (ObjectBuilder -> Activator in MS.EntLib.Common) and how/why the resourcelocker class is being interacted with. I'll plod on getting a flavour of how these facades,registry and factory classes are initing (Don't need anymore documentation on this Pauli) and then I will start looking at the FunctionProviders :) – Paul Sullivan Nov 01 '11 at 13:32
  • ah, yes... creation of object and initialization from the .config-files is done all by the Microsoft Enterprise Libraries, nothing particular C1 specific there. Defining sections is pure .Net framework concepts - i thought you where aware of these things? Sections: http://msdn.microsoft.com/en-us/library/0hyxd0xc.aspx and http://msdn.microsoft.com/en-us/library/system.configuration.configurationsection.aspx, ConfigSections in EntLib: http://msdn.microsoft.com/en-us/library/microsoft.practices.enterpriselibrary.common.configuration.serializableconfigurationsection(v=PandP.50).aspx – Pauli Østerø Nov 01 '11 at 15:41