<commit_before>﻿using System;
using System.Collections.Generic;
using System.ComponentModel.Composition.Hosting;
using System.IO;
using System.Linq;
using System.Reflection;
using System.Threading;
using System.Threading.Tasks;
using Topshelf;
using Totem.Reflection;
using Totem.Runtime.Map;

namespace Totem.Runtime.Hosting
{
	/// <summary>
	/// The Topshelf service control hosting the Totem runtime
	/// </summary>
	internal sealed class RuntimeService : ServiceControl, ITaggable, IRuntimeService
	{
		private readonly Assembly _programAssembly;
		private HostControl _hostControl;
		private CompositionContainer _container;
		private CancellationTokenSource _cancellationTokenSource;
		private IDisposable _instance;

		internal RuntimeService(Assembly programAssembly)
		{
			_programAssembly = programAssembly;

			Tags = new Tags();

			SetCurrentDirectoryToProgram();
		}

		Tags ITaggable.Tags => Tags;
		private Tags Tags { get; set; }
		private IClock Cloc => Notion.Traits.Clock.Get(this);
		private ILog Log => Notion.Traits.Log.Get(this);
		private RuntimeMap Runtime => Notion.Traits.Runtime.Get(this);

		private void SetCurrentDirectoryToProgram()
		{
			// Run the service where installed, instead of a system folder

			Directory.SetCurrentDirectory(_programAssembly.GetDirectoryName());
		}

		//
		// Start
		//

		internal static IRuntimeService Instance;

		public bool Start(HostControl hostControl)
		{
			_hostControl = hostControl;

			Instance = this;

			Log.Info("[runtime] Starting service");

			OpenScope();

			LoadInstance();

			Log.Info("[runtime] Service started");

			return true;
		}

		private void OpenScope()
		{
			_container = new CompositionContainer(Runtime.Catalog, CompositionOptions.DisableSilentRejection);

			_cancellationTokenSource = new CancellationTokenSource();
		}

		private void LoadInstance()
		{
			_instance = _container
				.GetExportedValue<CompositionRoot>()
				.Connect(_cancellationTokenSource.Token);
		}
<commit_msg>Provide details of composition errors<commit_after>﻿using System;
using System.Collections.Generic;
using System.ComponentModel.Composition;
using System.ComponentModel.Composition.Hosting;
using System.ComponentModel.Composition.Primitives;
using System.IO;
using System.Linq;
using System.Reflection;
using System.Threading;
using System.Threading.Tasks;
using Topshelf;
using Totem.Reflection;
using Totem.Runtime.Map;

namespace Totem.Runtime.Hosting
{
	/// <summary>
	/// The Topshelf service control hosting the Totem runtime
	/// </summary>
	internal sealed class RuntimeService : ServiceControl, ITaggable, IRuntimeService
	{
		private readonly Assembly _programAssembly;
		private HostControl _hostControl;
		private CompositionContainer _container;
		private CancellationTokenSource _cancellationTokenSource;
		private IDisposable _instance;

		internal RuntimeService(Assembly programAssembly)
		{
			_programAssembly = programAssembly;

			Tags = new Tags();

			SetCurrentDirectoryToProgram();
		}

		Tags ITaggable.Tags => Tags;
		private Tags Tags { get; set; }
		private IClock Cloc => Notion.Traits.Clock.Get(this);
		private ILog Log => Notion.Traits.Log.Get(this);
		private RuntimeMap Runtime => Notion.Traits.Runtime.Get(this);

		private void SetCurrentDirectoryToProgram()
		{
			// Run the service where installed, instead of a system folder

			Directory.SetCurrentDirectory(_programAssembly.GetDirectoryName());
		}

		//
		// Start
		//

		internal static IRuntimeService Instance;

		public bool Start(HostControl hostControl)
		{
			_hostControl = hostControl;

			Instance = this;

			Log.Info("[runtime] Starting service");

			OpenScope();

			LoadInstance();

			Log.Info("[runtime] Service started");

			return true;
		}

		private void OpenScope()
		{
			_container = new CompositionContainer(Runtime.Catalog, CompositionOptions.DisableSilentRejection);

			_cancellationTokenSource = new CancellationTokenSource();
		}

		private void LoadInstance()
		{
      try
      {
        _instance = _container
          .GetExportedValue<CompositionRoot>()
          .Connect(_cancellationTokenSource.Token);
      }
      catch(Exception error)
      {
        throw new Exception("Failed to compose the runtime service", GetRootException(error));
      }
		}

    private Exception GetRootException(Exception error)
    {
      // http://haacked.com/archive/2014/12/09/unwrap-mef-exception/

      var compositionError = error as CompositionException;

      if(compositionError == null)
      {
        return error;
      }

      var unwrapped = compositionError;

      while(unwrapped != null)
      {
        var firstError = unwrapped.Errors.FirstOrDefault();

        if(firstError == null)
        {
          break;
        }

        var currentError = firstError.Exception;

        if(currentError == null)
        {
          break;
        }

        var composablePartError = currentError as ComposablePartException;

        if(composablePartError != null && composablePartError.InnerException != null)
        {
          var innerCompositionError = composablePartError.InnerException as CompositionException;

          if(innerCompositionError == null)
          {
            return currentError.InnerException ?? error;
          }

          currentError = innerCompositionError;
        }

        unwrapped = currentError as CompositionException;
      }

      return error;
    }

{'fraction_non_alphanumeric': 0.08729281767955802, 'fraction_numerical': 0.0014732965009208103, 'mean_word_length': 3.5183028286189684, 'pattern_counts': {'":': 0, '<': 9, '<?xml version=': 0, '>': 17, 'https://': 0, 'lorem ipsum': 0, 'www.': 0, 'xml': 0}, 'pii_count': 0, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 0, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'dir': 'git-commits-cleaned', 'id': '2550174', 'n_tokens_mistral': 1628, 'n_tokens_neox': 1472, 'n_words': 358}