using System; using System.Collections.Generic; using System.Diagnostics; using System.IO; using System.Reflection; using System.Xml; using System.Xml.Serialization; using Misuzilla.Applications.TwitterIrcGateway.AddIns; namespace Misuzilla.Applications.TwitterIrcGateway { /// /// アドインを管理する機能を提供します。 /// public class AddInManager : MarshalByRefObject { private static List _addInTypes; private static List _configurationTypes; private static XmlSerializer _xmlSerializer; private List _addIns; private Session _session; private Server _server; private AppDomain _addInDomain; /// /// サーバインスタンスへのプロキシを取得・設定します。 /// /// /// このプロキシは接続されているイベントを管理して、アドインが破棄されるときにすべてのイベントを解除できるようにします。 /// 特別な理由がない限り、を利用してください。 /// protected EventManagedProxy SessionProxy { get; set; } /// /// セッション情報インスタンスへのプロキシを取得・設定します。 /// /// /// このプロキシは接続されているイベントを管理して、アドインが破棄されるときにすべてのイベントを解除できるようにします。 /// 特別な理由がない限り、を利用してください。 /// protected EventManagedProxy ServerProxy { get; set; } /// /// 読み込まれているアドインのコレクションを取得します /// public ICollection AddIns { get { return _addIns.AsReadOnly(); } } /// /// アドインの型のコレクションを取得します /// public ICollection AddInTypes { get { return _addInTypes.AsReadOnly(); } } /// /// クラスのインスタンスを初期化します。 /// /// サーバのインスタンス /// 接続中のセッション情報のインスタンス public AddInManager(Server server, Session session) { SessionProxy = new EventManagedProxy(session); ServerProxy = new EventManagedProxy(server); _server = ServerProxy.GetTransparentProxy() as Server; _session = SessionProxy.GetTransparentProxy() as Session; } static AddInManager() { // これがないと AddIns 内での参照が死ぬ AppDomain.CurrentDomain.AssemblyResolve += (object sender, ResolveEventArgs args) => { foreach (Assembly asm in AppDomain.CurrentDomain.GetAssemblies()) { if (asm.FullName == args.Name) return asm; } return null; }; // Load AddIn/Configuration Types _addInTypes = new List(); _configurationTypes = new List(); LoadAddInFromAssembly(Assembly.GetExecutingAssembly()); String addinsBase = Path.Combine(Path.GetDirectoryName(Assembly.GetExecutingAssembly().Location), "AddIns"); if (Directory.Exists(addinsBase)) { foreach (String fileName in Directory.GetFiles(addinsBase, "*.dll")) { // 無視する if (String.Compare(Path.GetFileName(fileName), "Misuzilla.Applications.TwitterIrcGateway.AddIns.DLRIntegration.dll", true) == 0) continue; try { Assembly asm = Assembly.LoadFile(fileName); LoadAddInFromAssembly(asm); } catch (Exception e) { TraceLogger.Server.Error(e.ToString()); } } } // XMLのシリアライザの中で名前がかぶらないようにする XmlAttributeOverrides xmlAttrOverrides = new XmlAttributeOverrides(); foreach (var configType in _configurationTypes) { XmlAttributes xmlAttributes = new XmlAttributes(); xmlAttributes.XmlType = new XmlTypeAttribute(configType.FullName); xmlAttrOverrides.Add(configType, xmlAttributes); } _xmlSerializer = new XmlSerializer(typeof(Object), xmlAttrOverrides, _configurationTypes.ToArray(), null, null); } //public static AddInManager CreateInstanceWithAppDomain(Server server, Session session) //{ // AppDomain addInDomain = AppDomain.CreateDomain("AddInDomain-" + session.GetHashCode()); // AddInManager addInManager = addInDomain.CreateInstanceAndUnwrap(typeof(AddInManager).Assembly.FullName, typeof(AddInManager).FullName, false, // BindingFlags.Public | BindingFlags.CreateInstance | BindingFlags.Instance, null, new object[] {server, session}, null, null, null) as AddInManager; // addInDomain.UnhandledException += (sender, e) => { // Trace.WriteLine(e.ExceptionObject.ToString()); // }; // return addInManager; //} /// /// アドインを読み込みます。 /// public void Load() { _addIns = new List(); Initialize(); } /// /// アドインを初期化します。 /// public void Initialize() { foreach (Type addInType in _addInTypes) { if (_session.Config.DisabledAddInsList.Contains(addInType.FullName)) { _session.Logger.Information("AddIn[Disabled]: {0}", addInType.FullName); continue; } _session.Logger.Information("AddIn: {0}", addInType.FullName); _addIns.Add(Activator.CreateInstance(addInType) as IAddIn); } foreach (IAddIn addIn in _addIns) { try { addIn.Initialize(_server, _session); } catch (Exception e) { _session.Logger.Error(e.ToString()); } } } /// /// 読み込まれているアドインを破棄します。 /// public void Uninitialize() { foreach (IAddIn addIn in _addIns) { try { addIn.Uninitialize(); } catch (Exception e) { _session.Logger.Error("Exception at Uninitialize(Ignore): {0}\n{1}", addIn.GetType(), e.Message); } } SessionProxy.RemoveAllEvents(); ServerProxy.RemoveAllEvents(); _addIns = new List(); } /// /// 指定した型を設定ファイルから読み込みます /// /// 設定の型 /// 設定のインスタンス public Object GetConfig(Type configType) { String fileName = configType.FullName + ".xml"; String path = Path.Combine(Path.Combine(_session.UserConfigDirectory, "AddIns"), fileName); if (File.Exists(path)) { _session.Logger.Information("Load Configuration: {0}", path); try { using (FileStream fs = new FileStream(path, FileMode.Open)) { try { lock (_xmlSerializer) { Object retVal = _xmlSerializer.Deserialize(fs); if (retVal != null) return retVal; } } catch (XmlException xe) { _session.Logger.Error(xe.Message); } catch (InvalidOperationException ioe) { _session.Logger.Error(ioe.Message); } } } catch (IOException ie) { _session.Logger.Error(ie.Message); throw; } } return null; } /// /// 指定した型を設定ファイルから読み込みます /// /// 指定した設定型のインスタンス public T GetConfig() where T : class, IConfiguration, new() { T retVal = GetConfig(typeof(T)) as T; return retVal ?? new T(); } /// /// オブジェクトを設定ファイルに書き込みます /// /// 保存する設定のインスタンス public void SaveConfig(IConfiguration o) { Type configType = o.GetType(); if (!_configurationTypes.Contains(configType)) throw new ArgumentException("指定されたオブジェクトは設定ファイルとして登録されていない型です。"); String fileName = configType.FullName + ".xml"; String path = Path.Combine(Path.Combine(_session.UserConfigDirectory, "AddIns"), fileName); lock (o) { _session.Logger.Information("Save Configuration: {0}", path); try { String dir = Path.GetDirectoryName(path); Directory.CreateDirectory(dir); using (FileStream fs = new FileStream(path, FileMode.Create)) { try { lock (_xmlSerializer) { _xmlSerializer.Serialize(fs, o); } } catch (XmlException xe) { _session.Logger.Error(xe.Message); throw; } catch (InvalidOperationException ioe) { _session.Logger.Error(ioe.Message); throw; } } } catch (IOException ie) { _session.Logger.Error(ie.Message); throw; } } } /// /// 指定した型の設定を初期化します。 /// /// 初期化対象の設定型 public void ResetConfig() where T : class, IConfiguration, new() { T newObj = new T(); SaveConfig(newObj); } /// /// 指定した型のアドインのインスタンスを取得します /// /// 取得したいアドインの型 /// アドインのインスタンス public IAddIn GetAddIn(Type t) { foreach (var addIn in _addIns) { if (addIn.GetType() == t) return addIn; } return null; } /// /// 指定した型のアドインのインスタンスを取得します /// /// 取得したいアドインの型 /// アドインのインスタンス public T GetAddIn() where T : class, IAddIn { return GetAddIn(typeof(T)) as T; } /// /// アドインを初期化して実行し直します。ファイルからの読み込みは行われません。 /// public void RestartAddIns() { Uninitialize(); Initialize(); _session.OnAddInsLoadCompleted(); } #region Helper Methods private static void LoadAddInFromAssembly(Assembly asm) { lock (_addInTypes) { lock (_configurationTypes) { Type addinType = typeof(IAddIn); Type configurationType = typeof(IConfiguration); foreach (Type t in asm.GetTypes()) { if (addinType.IsAssignableFrom(t) && !t.IsAbstract && t.IsClass) { // IAddIn _addInTypes.Add(t); } else if (configurationType.IsAssignableFrom(t) && !t.IsAbstract && t.IsClass) { // IConfiguration _configurationTypes.Add(t); } } } } } #endregion } }