// Copyright 2003-2004 The Apache Software Foundation // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. namespace Apache.Avalon.Container { using System; using System.Reflection; using System.Collections; using EventHandlerList = System.ComponentModel.EventHandlerList; using Apache.Avalon.Framework; using Apache.Avalon.Container.Configuration; using Apache.Avalon.Container.Extension; using Apache.Avalon.Container.Lookup; using Apache.Avalon.Container.Services; using Apache.Avalon.Container.Handler; using Apache.Avalon.Container.Factory; using Apache.Avalon.Container.Logger; using Apache.Avalon.Meta; /// /// Basic handler to listen to Container events. /// public delegate void LifecycleHandler(LifecycleEventArgs e); /// /// Summary description for LifecycleManager. /// public sealed class LifecycleManager : ILogEnabled, IInitializable, IDisposable, IConfigurable, ILookupEnabled, IContextualizable { private static readonly object BeforeCreationEvent = new object(); private static readonly object AfterCreationEvent = new object(); private static readonly object BeforeReleaseEvent = new object(); private static readonly object AfterReleaseEvent = new object(); private static readonly object DependenciesEvent = new object(); private EventHandlerList m_events; private IComponentFactoryManager m_factoryManager; private DefaultContainer m_container; private ILogger m_logger; private IContext m_context; private ILoggerManager m_loggerManager; private ArrayList m_loadedModules; private WeakMap m_knowReferences; public LifecycleManager() { m_loadedModules = new ArrayList(); m_knowReferences = new WeakMap(); } public void PrepareComponent(ComponentEntry entry) { m_logger.Debug("Preparing component '{0}'", entry.ComponentType.FullName); IComponentFactory factory = m_factoryManager.GetFactory(entry); DelegateHandler handler = new DelegateHandler(factory, entry); entry.Handler = handler.GetProxy(this); } internal void BeforeGetInstance(DelegateHandler handler) { m_logger.Debug("BeforeGetInstance '{0}'", handler.ComponentEntry.ComponentType.FullName); OnBeforeCreation(handler.ComponentEntry); } internal void AfterGetInstance(DelegateHandler handler, object instance) { if (m_logger.IsDebugEnabled && instance != null) { m_logger.Debug("AfterGetInstance '{0}'", instance.GetType().FullName); } ComponentEntry entry = handler.ComponentEntry; OnAfterCreation(ref instance, entry); if (ShallSetUpComponent(instance, entry)) { if (m_logger.IsDebugEnabled) { m_logger.Debug("Setting up component '{0}'", instance.GetType().FullName); } if (instance is ILogEnabled) { ContainerUtil.EnableLogging(instance, m_loggerManager[entry.LoggerName]); } if (instance is IContextualizable) { ContainerUtil.Contextualize(instance, m_context); } BlindLookupManager lookupManager = new BlindLookupManager(m_container); if (entry.Dependencies.Length != 0) { SetupComponentDependencies(instance, entry, lookupManager); } if (instance is ILookupEnabled) { ContainerUtil.Service(instance, lookupManager); } ContainerUtil.Configure(instance, entry.Configuration); ContainerUtil.Initialize(instance); ContainerUtil.Start(instance); } } internal void BeforePutInstance(DelegateHandler handler, object instance) { if (m_logger.IsDebugEnabled && instance != null) { m_logger.Debug("BeforePutInstance '{0}'", instance.GetType().FullName); } OnBeforeRelease(instance, handler.ComponentEntry); } internal void AfterPutInstance(DelegateHandler handler, object instance) { if (m_logger.IsDebugEnabled && instance != null) { m_logger.Debug("AfterPutInstance '{0}'", instance.GetType().FullName); } ComponentEntry entry = handler.ComponentEntry; OnAfterRelease(instance, entry); if (ShallSetUpComponent(instance, entry)) { ContainerUtil.Shutdown(instance); } } #region Events public event LifecycleHandler BeforeCreation { add { Events.AddHandler(BeforeCreationEvent, value); } remove { Events.RemoveHandler(BeforeCreationEvent, value); } } public event LifecycleHandler AfterCreation { add { Events.AddHandler(AfterCreationEvent, value); } remove { Events.RemoveHandler(AfterCreationEvent, value); } } public event LifecycleHandler BeforeRelease { add { Events.AddHandler(BeforeReleaseEvent, value); } remove { Events.RemoveHandler(BeforeReleaseEvent, value); } } public event LifecycleHandler AfterRelease { add { Events.AddHandler(AfterReleaseEvent, value); } remove { Events.RemoveHandler(AfterReleaseEvent, value); } } public event LifecycleHandler SetupDependencies { add { Events.AddHandler(DependenciesEvent, value); } remove { Events.RemoveHandler(DependenciesEvent, value); } } private EventHandlerList Events { get { if (m_events == null) { m_events = new EventHandlerList(); } return m_events; } } private void OnBeforeCreation(ComponentEntry entry) { if (Events[BeforeCreationEvent] != null) { System.Delegate del = Events[BeforeCreationEvent]; del.Method.Invoke(del.Target, new object[] {new LifecycleEventArgs(null, entry)}); } } private void OnAfterCreation(ref object instance, ComponentEntry entry) { if (Events[AfterCreationEvent] != null) { System.Delegate del = Events[AfterCreationEvent]; LifecycleEventArgs args = new LifecycleEventArgs(instance, entry); del.Method.Invoke(del.Target, new object[] {args}); if (args.Component != instance) { if (m_logger.IsDebugEnabled && instance != null) { m_logger.Debug( "OnAfterCreation - Extension changed component instance " + "from '{0}' to '{1}'", instance.GetType().FullName, args.Component.GetType().FullName); } instance = args.Component; } } } private void OnBeforeRelease(object instance, ComponentEntry entry) { if (Events[BeforeReleaseEvent] != null) { System.Delegate del = Events[BeforeReleaseEvent]; del.Method.Invoke(del.Target, new object[] {new LifecycleEventArgs(instance, entry)}); } } private void OnAfterRelease(object instance, ComponentEntry entry) { if (Events[AfterReleaseEvent] != null) { System.Delegate del = Events[AfterReleaseEvent]; del.Method.Invoke(del.Target, new object[] {new LifecycleEventArgs(instance, entry)}); } } private void OnSetupDependencies(object instance, ComponentEntry entry, String dependencyRole, String dependencyKey, ILookupManager manager) { if (Events[DependenciesEvent] != null) { System.Delegate del = Events[DependenciesEvent]; del.Method.Invoke(del.Target, new object[] {new LifecycleEventArgs(instance, entry, dependencyRole, dependencyKey, manager)}); } } #endregion #region IInitializable Members public void Initialize() { } #endregion #region ILogEnabled Members public void EnableLogging(ILogger logger) { m_logger = logger; } #endregion #region IConfigurable Members public void Configure(IConfiguration config) { ConfigurationCollection addNodes = config.GetChildren("add"); ArrayList loadedTypes = new ArrayList(); foreach(IConfiguration addNode in addNodes) { String typeName = (String) addNode.Attributes["type"]; // String name = addNode.Attributes["name"]; try { Type type = Type.GetType(typeName, true, true); loadedTypes.Add(type); } catch(ArgumentNullException) { throw new ConfigurationException( String.Format("Invalid type '{0}'.", typeName)); } catch(TargetInvocationException inner) { throw new ConfigurationException( String.Format("Error loading type '{0}'.", typeName), inner); } } foreach(Type type in loadedTypes) { IExtensionModule module = (IExtensionModule) Activator.CreateInstance(type); module.Init(this); m_loadedModules.Add(module); } } #endregion #region IDisposable Members public void Dispose() { foreach(object module in m_loadedModules) { ContainerUtil.Shutdown(module); } m_loadedModules.Clear(); ContainerUtil.Shutdown(m_logger); m_logger = null; ContainerUtil.Shutdown(m_factoryManager); m_factoryManager = null; } #endregion #region ILookupEnabled Members public void EnableLookups(ILookupManager manager) { m_factoryManager = (IComponentFactoryManager) manager[ typeof(IComponentFactoryManager).FullName ]; m_container = (DefaultContainer) manager[ "Container" ]; m_loggerManager = (ILoggerManager) manager[ typeof(ILoggerManager).FullName ]; } #endregion #region IContextualizable Members public void Contextualize(IContext context) { this.m_context = context; } #endregion private void SetupComponentDependencies(object instance, ComponentEntry entry, BlindLookupManager lookupManager) { if (m_logger.IsDebugEnabled && instance != null) { m_logger.Debug("SetupComponentDependencies '{0}'", instance.GetType().FullName); } // TODO: There are a lot of to-dos here. As we are focusing // the simplest case, they should not be an issue. foreach(DependencyDescriptor dependency in entry.Dependencies) { String depRole = dependency.Service.Typename; ComponentEntry depEntry = m_container.Components[depRole]; if (depEntry != null && entry != depEntry) { if (m_logger.IsDebugEnabled) { m_logger.Debug(" Dependency: '{0}'", depEntry.ComponentType.FullName); } Type dependencyType = Type.GetType( dependency.Service.Typename, true, true ); lookupManager.Add(dependency.Key, dependencyType); OnSetupDependencies(instance, entry, depRole, dependency.Key, lookupManager); } } } private bool ShallSetUpComponent(object instance, ComponentEntry entry) { // Here, LifestyleManager must know something about lifestyles. // This is too fragile. A better way is the handler/factory tell // us someway if the component needs set up or not if (entry.Lifestyle == Lifestyle.Transient) { return true; } // TODO: Ensure thread safety if (!m_knowReferences.IsAlreadyInitialized(entry, instance)) { // This should be done at the end of initialization process... m_knowReferences.Add(entry, instance); return true; } return false; } } /// /// /// public sealed class LifecycleEventArgs : EventArgs { private object m_component; private ComponentEntry m_entry; private String m_dependencyRole; private String m_dependencyKey; private ILookupManager m_lookupManager; internal LifecycleEventArgs(object component, ComponentEntry entry) { m_component = component; m_entry = entry; } internal LifecycleEventArgs(object component, ComponentEntry entry, String dependencyRole, String dependencyKey, ILookupManager lookupManager) : this(component, entry) { m_dependencyRole = dependencyRole; m_dependencyKey = dependencyKey; m_lookupManager = lookupManager; } public ILookupManager LookupManager { get { return m_lookupManager; } } public object Component { get { return m_component; } set { m_component = value; } } public object ComponentType { get { return m_entry.ComponentType; } } public Lifestyle Lifestyle { get { return m_entry.Lifestyle; } } public String DependencyRole { get { return m_dependencyRole; } } public String DependencyKey { get { return m_dependencyKey; } } } internal class WeakMap { private Hashtable m_components; public WeakMap() { m_components = new Hashtable(); } public bool IsAlreadyInitialized(ComponentEntry entry, object instance) { lock(this) { WeakItemList item = m_components[entry.ComponentType] as WeakItemList; if (item != null) { return item.HasInstance(instance); } } return false; } public void Add(ComponentEntry entry, object instance) { lock(this) { WeakItemList item = m_components[entry.ComponentType] as WeakItemList; if (item == null) { item = new WeakItemList(); m_components[entry.ComponentType] = item; } item.Add(instance); } } internal class WeakItemList { private ArrayList m_items; public WeakItemList() { m_items = new ArrayList(); } public void Add(object instance) { m_items.Add(new WeakReference(instance)); } public bool HasInstance(object instance) { foreach(WeakReference weakRef in m_items) { if (!weakRef.IsAlive) { m_items.Remove(weakRef); } if (weakRef.IsAlive && weakRef.Target == instance) { return true; } } return true; } } } }