Difference between revisions of "HowTo:Provide system definitions with a custom plugin"
m |
|||
Line 1: | Line 1: | ||
This tutorial explains how to provide any kind of preconfigured {{UBIK}} data (system definitions) using a custom plugin. | 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. | ||
<!-- 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:09, 25 November 2024
This tutorial explains how to provide any kind of preconfigured UBIK® data (system definitions) using a custom plugin.
- General concept
- Prerequisites
- Big Picture
- Create a new plugin
- Create a new SystemDefinitionProvider
- Create a new SystemDefinitions package
- Add a new basic MetaClass
- Add a new special MetaClass
- Add new arbitrary instances
- Provide a version upgrade
- Create a new MetaProxy
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.
Big Picture
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.
{
[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.
{
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.
{
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.
{
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
}
}
}
}
{
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;
}
}
}
{
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.
{
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);
}
}
}
{
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.
}
}
{
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.
{
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.
{
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.
{
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
}
}
}
}
{
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;
}
}
}
{
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
// ...
}
}