Jump to: navigation, search

Difference between revisions of "HowTo:Provide system definitions with a custom plugin"


m
Line 14: Line 14:
  
 
To understand the code, knowledge about the {{UBIK}} ER-model is required.   
 
To understand the code, knowledge about the {{UBIK}} ER-model is required.   
 +
 +
== Instructions ==
  
 
<!-- DO NOT REMOVE THIS -->{{Template:HowTo/Begin}}<!-- DO NOT REMOVE THIS -->
 
<!-- DO NOT REMOVE THIS -->{{Template:HowTo/Begin}}<!-- DO NOT REMOVE THIS -->

Revision as of 15:10, 25 November 2024


This tutorial explains how to provide any kind of preconfigured UBIK® data (system definitions) using a custom plugin.

General concept

When rolling out a UBIK® customizing into a (productive) environment, there is always the challenge regarding the transportation of the data model and basic data. The sustainable solution is to deliver versioned packages of system definitions in plugins, because that allows for easy deployment and upgrade, and for the direct application of state-of-the-art software development strategies for plugin development.

UBIK® plugins are detected when an Environment is initialized, and the user is prompted with a database upgrade if necessary. The technical maintenance of the database is automatic and allows for arbitrary adaptations. The plugin developer can decide whether to make meta definitions immutable or further customizable by the user. Custom scripts can be executed everytime the Environment is connected or just during an upgrade.

Prerequisites

To understand the code, knowledge about the UBIK® ER-model is required.

Instructions

[edit]

Big Picture

The Plugin hosts a SystemDefinitionProvider, which in turn gives access to a set of versioned SystemDefinitions. The latter can contain any kind of UBIK® data like MetaClasses, MetaProperties, instances of any type and scripts to execute when the package is installed or initialized.

Systemdefinitions2.drawio.png

Create a new plugin

The plugin class is the central access point for your definitions. It must implement the IUbikModule interface for the system definitions to be applied.

Plugin template code
namespace UBIK.MyPlugin
{
    [Export(typeof(UBIK.Injection.IUbikPlugin))]
    [ExportMetadata("ID", "e9395ebd-4e41-40a3-9ac6-01bce6fb7e68")]
    [ExportMetadata("Type", typeof(MyPlugin))]
    [ExportMetadata("Name", "MyPlugin")]
    [ExportMetadata("Description", "My custom plugin")]
    [ExportMetadata("Version", 1)]
    [ExportMetadata("Company", "Augmensys GmbH")]
    [ExportMetadata("MinimumKernelVersion", "4.0.0.0")]
    public class MyPlugin: IUbikModule
    {
        public static Guid ID
        {
            get
            {
                return Guid.Parse("e9395ebd-4e41-40a3-9ac6-01bce6fb7e68");
            }
        }

        public UBIKEnvironment Environment { get; private set; }

        public ISystemDefinitionProvider SystemDefinitionsProvider
        {
            get
            {
                return MySystemDefinitionProvider.Instance;
            }
        }

        public virtual void Initialize(UBIKEnvironment environment)
        {
            this.Environment = environment;
        }

        public virtual void Terminate()
        {
            this.Environment = null;
        }
       
        public virtual bool Initialized()
        {
            return this.Environment != null;
        }

        public List<ModuleInfo> DependencyModules
        {
            get;
        } = new List<ModuleInfo>();
    }
}

Create a new SystemDefinitionProvider

The SystemDefinitionProvider has a list of all supported system definitions packages. The base implementation uses the version information from the packages to handle the upgrading.

SystemDefinitionsProvider template code
namespace UBIK.MyPlugin
{
    public class MySystemDefinitionProvider : SystemDefinitionProviderBase
    {
        public override Guid ModuleID => MyPlugin.ID;

        private static MySystemDefinitionProvider instance = null;

        public static MySystemDefinitionProvider Instance
        {
            get
            {
                return instance ?? (instance = new MySystemDefinitionProvider());
            }
        }

        private MySystemDefinitionProvider() :
            base(
                new MySystemDefinitions_V100(),
                new MySystemDefinitions_V110())
        { }
    }
}

Create a new SystemDefinitions package

The SystemDefinitions contain any data you may want to deliver with your plugin.

SystemDefinitions package template code
namespace UBIK.MyPlugin
{
    public class MySystemDefinitions_V100 : SystemDefinitionsBase
    {
        #region Constants

        public static readonly Version VERSION = new Version(1, 0, 0, 0);

        #endregion Constants

        #region Version

        public override Version Version
        {
            get
            {
                return VERSION;
            }
        }

        #endregion Version

        #region System Classifications

        public override UBIKClassList<MetaClass> DefineSystemClassifications(UBIKEnvironment environment)
        {
            return new UBIKClassList<MetaClass>();
        }

        #endregion System Classifications

        #region System Meta Classes

        public override UBIKClassList<MetaClass> DefineSystemMetaClasses(UBIKEnvironment environment)
        {
            UBIKClassList<MetaClass> metaClasses = new UBIKClassList<MetaClass>();
            return metaClasses;
        }

        #endregion System Meta Classes

        #region System Meta Properties

        public override UBIKClassList<MetaProperty> DefineSystemMetaProperties(UBIKEnvironment environment)
        {
            UBIKClassList<MetaProperty> result = new UBIKClassList<MetaProperty>();
            return result;
        }

        #endregion System Meta Properties

        #region System Selective Lists

        public override UBIKClassList<SelectiveList> DefineSystemSelectiveLists(UBIKEnvironment environment)
        {
            UBIKClassList<SelectiveList> result = new UBIKClassList<SelectiveList>();
            return result;
        }

        #endregion System Selective Lists

        #region Module Definition
       
        public override void UpdateSystemContent(UBIKEnvironment environment)
        {
            UpdateModuleDefinition(environment);
        }

        protected void UpdateModuleDefinition(UBIKEnvironment environment)
        {
            MetaClass moduleMc = environment.GetSystemMetaClass(SystemObjects.MODULE);
            BaseClass module = moduleMc.GetOrCreateInstance(MyPlugin.ID);
            module.Name = "MyPlugin";
            module.Namespace = "MyPlugin";
            if (environment.LanguageManager.TryGetLanguageIndex("de", out int idxDe))
            {
                module.SetInternationalDescription(idxDe, "UBIK Plugin Vorlage. (C) Augmensys GmbH");
            }
            if (environment.LanguageManager.TryGetLanguageIndex("en", out int idxEn))
            {
                module.SetInternationalDescription(idxEn, "UBIK template plugin. (C) Augmensys GmbH");
            }
            module.Save();
        }
       
        #endregion Module Definition
    }
}

Add a new basic MetaClass

Here's how you can add a new MetaClass (and MetaProperty) to be shipped with your plugin.

SystemDesignMetaClass template code
namespace UBIK.MyPlugin
{
    public class MyBaseMetaClass : SystemDesignMetaClass
    {
        public static readonly Guid UID = Guid.Parse("30ed406b-a5f1-498b-ad34-2db1bd786b13");
   
        private const string NAME = "MY_BASECLASS";
        private const string NAMESPACE = "System.MyPlugin";
        private const string DESCRIPTION = "My Baseclass";

        public MyBaseMetaClass(UBIKEnvironment environment)
        {
            InitUidAndEnvironment(UID, environment);
            InitStrings(NAME, NAMESPACE, DESCRIPTION);

            SetModuleId(MyPlugin.ID);

            DefineMetaProperties(new List<Guid>() {
                MyMetaProperties.PROPERTY_UID_MYPROPERTY
            });
        }

        public override MetaClass Inheritance
        {
            get
            {
                return this.Environment.GetSystemMetaClass(SystemObjects.NAMEDBASECLASS);
            }
            set
            {
                // do nothing
            }
        }

        public override MetaClassTypes ClassType
        {
            get
            {
                return MetaClassTypes.Inherited;
            }
            set
            {
                // do nothing
            }
        }
    }
}
SystemDesignMetaProperty template code
namespace UBIK.MyPlugin
{
    public class MyMetaProperties
    {
        public const string NS_MY_PLUGIN = "System.MyPlugin;
       
        public static readonly Guid PROPERTY_UID_MYPROPERTY = Guid.Parse("
d90b50e0-17d9-40ac-b89d-18f4a01cd5cb");
        public const string PROPERTY_NAME_MYPROPERTY = "
LK_LOCATION";
       
        public static UBIKClassList<MetaProperty> DefineSystemMetaProperties(UBIKEnvironment environment)
        {
            MetaProperty myProperty = SystemDesignMetaProperty.CreateStringSystemDesignMetaProperty
                (environment, PROPERTY_NAME_MYPROPERTY, NS_MY_PLUGIN, PROPERTY_UID_MYPROPERTY, multiLang: false, orderID: 0, stringLen: 0);
            myProperty.Description = "
MyProperty";
           
            UBIKClassList<MetaProperty> metaProperties = new UBIKClassList<MetaProperty>() {
                myProperty
            };
           
            return metaProperties;
        }
    }
}
SystemDefinitions adaptations
namespace UBIK.MyPlugin
{
    public class MySystemDefinitions_V100 : SystemDefinitionsBase
    {
        // ...

        #region System Meta Classes

        public override UBIKClassList<MetaClass> DefineSystemMetaClasses(UBIKEnvironment environment)
        {
            UBIKClassList<MetaClass> metaClasses = new UBIKClassList<MetaClass>();
           
            MyBaseMetaClass myBaseClass = new MyBaseMetaClass(environment);
            metaClasses.Add(myBaseClass);
           
            return metaClasses;
        }

        #endregion System Meta Classes

        #region System Meta Properties

        public override UBIKClassList<MetaProperty> DefineSystemMetaProperties(UBIKEnvironment environment)
        {
            UBIKClassList<MetaProperty> result = new UBIKClassList<MetaProperty>();
            result.AddRangeUnique(MyMetaProperties.DefineSystemMetaProperties(environment));
            return result;
        }

        #endregion System Meta Properties

        // ...
    }
}

Add a new special MetaClass

In some cases you need to defined something more elaborate, e.g., a MetaClass with custom code deriving from another MetaClass you defined.

SystemDesignMetaClass template code
namespace UBIK.MyPlugin
{
    public class MySpecialMetaClass : SystemDesignMetaClass
    {
        public static readonly Guid UID = Guid.Parse("0925ad66-7199-4e70-871c-cdd799f8d8e7");
   
        private const string NAME = "MY_SEPCIALCLASS";
        private const string NAMESPACE = "System.MyPlugin";
        private const string DESCRIPTION = "My special class";
       
        private MyBaseMetaClass parent = null;

        public MySpecialMetaClass(UBIKEnvironment environment, MyBaseMetaClass parent)
        {
            InitUidAndEnvironment(UID, environment);
            InitStrings(NAME, NAMESPACE, DESCRIPTION);

            SetModuleId(MyPlugin.ID);
           
            this.parent = parent;

            DefineMetaProperties(new List<Guid>() {
                // ...
            });
        }

        public override MetaClass Inheritance
        {
            get
            {
                return this.parent;
            }
            set
            {
                // do nothing
            }
        }

        public override MetaClassTypes ClassType
        {
            get
            {
                return MetaClassTypes.DEFINED_IN_PLUGIN;
            }
            set
            {
                // do nothing
            }
        }
       
        public override Type GetRuntimeType()
        {
            return typeof(MySpecialType);
        }

        public override Type GetSystemRuntimeType()
        {
            return typeof(MySpecialType);
        }
    }
}
Special runtime type template code
namespace UBIK.MyPlugin
{
    public class MySpecialType : ContentClass
    {
        // This implementation will be used to instantiate respective instances in UBIK during runtime.
        // Here you can override anything that can be overridden in UBIK.Studio's code editor, and more.
        // Additional code may be added, too.
    }
}
SystemDefinitions adaptations
namespace UBIK.MyPlugin
{
    public class MySystemDefinitions_V100 : SystemDefinitionsBase
    {
        // ...

        #region System Meta Classes
       
        protected MyBaseMetaClass MyBaseMetaClass { get; set; }
        protected MySpecialMetaClass MySpecialMetaClass { get; set; }

        public override UBIKClassList<MetaClass> DefineSystemMetaClasses(UBIKEnvironment environment)
        {
            UBIKClassList<MetaClass> metaClasses = new UBIKClassList<MetaClass>();
           
            MyBaseMetaClass = new MyBaseMetaClass(environment);
            metaClasses.Add(MyBaseMetaClass);
           
            MySpecialMetaClass = new MySpecialMetaClass(environment, MyBaseMetaClass);
            metaClasses.Add(MySpecialMetaClass);
           
            return metaClasses;
        }

        #endregion System Meta Classes

        // ...
    }
}

Add new arbitrary instances

In some cases you may want to deliver instances of an arbitrary type the user can change to their liking.

SystemDefinitions adaptations
namespace UBIK.MyPlugin
{
    public class MySystemDefinitions_V100 : SystemDefinitionsBase
    {
        public static readonly Guid DEFAULT_INSTANCE_UID = Guid.Parse("a71c2c7e-a7b7-4462-b1c8-61ea9cba0dcd");
       
        // ...

        #region System Content

        public override void UpdateSystemContent(UBIKEnvironment environment)
        {
            MySpecialType defaultInstance = MySpecialMetaClass.GetOrCreateInstance(DEFAULT_INSTANCE_UID) as MySpecialType;
            if (defaultInstance.IsNewInstance)
            {
                defaultInstance.Namespace = "MyPlugin.Default";
                defaultInstance.Name = "MySpecialInstance";
                defaultInstance.Description = "Default instance";
                defaultInstance.Save();
            }
        }

        #endregion System Content

        // ...
    }
}

Provide a version upgrade

If you released system definitions previously, which are already used productively, how do you deliver changes? The solution is to not change the older version, but create a newer version instead. We always want to stay downward compatible.

SystemDefinitions adaptations
namespace UBIK.MyPlugin
{
    public class MySystemDefinitions_V110 : MySystemDefinitions_V100
    {
        #region Constants

        public static readonly Version VERSION = new Version(1, 1, 0, 0);

        #endregion Constants

        #region Version

        public override Version Version
        {
            get
            {
                return VERSION;
            }
        }

        #endregion Version
       
        // ...

        #region System Meta Classes
       
        // Refactor MySystemDefinitions_V100 so the "MyBaseMetaClass" is defined in a separate method, so we can override it!
        public override MyBaseMetaClass DefineMyBaseMetaClass(UBIKEnvironment environment)
        {
            MyBaseMetaClass mc = new MyBaseMetaClass_V110(environment);
            // MyBaseMetaClass_V110 should derive from the original MyBaseMetaClass, adapting it to the new version
            return mc;
        }

        #endregion System Meta Classes

        // ...
    }
}

Create a new MetaProxy

MetaProxies are managed very similarly to MetaClasses, but the MetaProxyProperties are slightly more complex because they require additional information like the UIDs for the import and export versions of itself, the target property in UBIK®, etc.

SystemDesignMetaProxy template code
namespace UBIK.MyPlugin
{
    public class MyProxyMetaClass : SystemDesignMetaProxy
    {
        public static Guid UID = GuidUtility.MY_PROXY;
        private const string NAME = "MYPROXY";
        private const string NAMESPACE = "System.MyPlugin";
        private const string DESCRIPTION = "My meta proxy";

        public MyProxyMetaClass(UBIKEnvironment environment)
        {
            InitUidAndEnvironment(UID, environment);
            InitStrings(NAME, NAMESPACE, DESCRIPTION);

            SetModuleId(MyPlugin.ID);

            List<Guid> metaProperties = new List<Guid>();
            // regular meta properties          
            DefineMetaProperties(metaProperties);

            List<MetaProxyPropertyInfo> metaProxyPropertyInfos = new List<MetaProxyPropertyInfo>();
            metaProxyPropertyInfos.Add(new MetaProxyPropertyInfo(environment, MyProxyMetaProperties.PROXY_PROPERTY_UID_MYPROPERTY, true, -1, ProxyUpdateModes.BiDirectional, MyMetaProperties.PROPERTY_UID_MYPROPERTY, "My property"));
           
            DefineMetaProxyProperties(metaProxyPropertyInfos);
        }
       
        public override MetaClass MetaClass
        {
            get
            {
                return this.Environment.GetSystemMetaClass(SystemObjects.METAPROXY);
            }
        }

        public override MetaClass Inheritance
        {
            get
            {
                return this.Environment.GetSystemMetaClass(SystemObjects.PROXY_ROOT);
            }
            set
            {
                // do nothing
            }
        }

        public override MetaClassTypes ClassType
        {
            get
            {
                return MetaClassTypes.Proxy;
            }
            set
            {
                // do nothing
            }
        }
    }
}
SystemDesignMetaProxyProperty template code
namespace UBIK.MyPlugin
{
    public class MyMetaProxyProperties
    {
        public const string NS_MY_PLUGIN = "System.MyPlugin";
        public const int STR_LEN_MED = 255;
       
        public static UBIKClassList<MetaProperty> DefineSystemMetaProperties(UBIKEnvironment environment)
        {
            UBIKClassList<MetaProperty> metaProperties = new UBIKClassList<MetaProperty>();
           
            SystemDesignMetaProxyProperty myMetaProxyProperty = new SystemDesignMetaProxyProperty(environment,
                GuidUtility.MPP_MYPROPERTY, // assuming the ids are documented in a class GuidUtility
                MyMetaProperties.PROPERTY_NAME_MYPROPERTY,
                NAMESPACE,
                PropertyTypes.String,
                GuidUtility.MPP_MYPROPERTY_VIRTUAL_PROPERTIES[0],
                GuidUtility.MPP_MYPROPERTY_VIRTUAL_PROPERTIES[1]);
            myMetaProxyProperty.MetaAttribute = environment.GetSystemMetaClass(SystemObjects.PROXYATTRIBUTE);            
            myMetaProxyProperty.SetStringLen(STR_LEN_MED);
            proxyProperties.Add(myMetaProxyProperty);
           
            return metaProperties;
        }
    }
}
SystemDefinitions adaptations
namespace UBIK.MyPlugin
{
    public class MySystemDefinitions_V100 : SystemDefinitionsBase
    {
        // ...

        #region System Meta Classes

        public override UBIKClassList<MetaClass> DefineSystemMetaClasses(UBIKEnvironment environment)
        {
            UBIKClassList<MetaClass> metaClasses = new UBIKClassList<MetaClass>();
           
            MyProxyMetaClass myBaseClass = new MyProxyMetaClass(environment);
            metaClasses.Add(myBaseClass);
           
            return metaClasses;
        }

        #endregion System Meta Classes

        #region System Meta Properties

        public override UBIKClassList<MetaProperty> DefineSystemMetaProperties(UBIKEnvironment environment)
        {
            UBIKClassList<MetaProperty> result = new UBIKClassList<MetaProperty>();
            result.AddRangeUnique(MyMetaProxyProperties.DefineSystemMetaProperties(environment));
            return result;
        }

        #endregion System Meta Properties

        // ...
    }
}


See also