Fixing an issue with output parameters that are of type IntPtr
[castle.git] / Facilities / DynamicLoader / Castle.Facilities.DynamicLoader / DynamicLoaderFacility.cs
blob1aa7af77971d505c3408e3d0b719784dd242bf65
1 // Copyright 2004-2008 Castle Project - http://www.castleproject.org/
2 //
3 // Licensed under the Apache License, Version 2.0 (the "License");
4 // you may not use this file except in compliance with the License.
5 // You may obtain a copy of the License at
6 //
7 // http://www.apache.org/licenses/LICENSE-2.0
8 //
9 // Unless required by applicable law or agreed to in writing, software
10 // distributed under the License is distributed on an "AS IS" BASIS,
11 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12 // See the License for the specific language governing permissions and
13 // limitations under the License.
15 namespace Castle.Facilities.DynamicLoader
17 using System;
18 using System.Collections;
19 using System.Collections.Generic;
20 using System.IO;
21 using System.Reflection;
22 using System.Security.Policy;
23 using System.Web;
24 using Castle.Core.Configuration;
25 using Castle.Core.Logging;
26 using Castle.MicroKernel;
27 using Castle.MicroKernel.Facilities;
29 /// <summary>
30 /// DynamicLoader facility.
31 /// </summary>
32 public class DynamicLoaderFacility : MarshalByRefObject, IFacility, IDisposable
34 private readonly DynamicLoaderRegistry registry = new DynamicLoaderRegistry();
36 private ILogger log = NullLogger.Instance;
38 private bool isWeb;
40 private IKernel kernel;
41 private IConfiguration facilityConfig;
43 /// <summary>
44 /// Initializes the facility.
45 /// </summary>
46 public void Init(IKernel kernel, IConfiguration facilityConfig)
48 this.kernel = kernel;
49 this.facilityConfig = facilityConfig;
51 this.Init();
54 /// <summary>
55 /// Terminates the facility.
56 /// </summary>
57 public void Terminate()
59 Dispose(true);
61 kernel = null;
64 /// <summary>
65 /// Releases unmanaged resources and performs other cleanup operations before the
66 /// <see cref="DynamicLoaderFacility"/> is reclaimed by garbage collection.
67 /// </summary>
68 ~DynamicLoaderFacility()
70 Dispose(false);
73 /// <summary>
74 /// Inits this instance.
75 /// </summary>
76 protected void Init()
78 if (kernel.HasComponent(typeof(ILoggerFactory)))
79 log = ((ILoggerFactory) kernel.Resolve(typeof(ILoggerFactory), new Hashtable())).Create(GetType());
81 log.Info("DynamicLoader is being initialized");
83 kernel.ComponentModelBuilder.AddContributor(new DynamicLoaderInspector(registry));
85 isWeb = IsTrue(facilityConfig.Attributes["isWeb"]);
87 foreach(IConfiguration cfg in facilityConfig.Children)
89 switch(cfg.Name)
91 case "domain":
92 CreateAppDomain(cfg);
93 break;
94 default:
95 throw new FacilityException("Unrecognized configuration node: " + cfg.Name);
100 private AppDomain CreateAppDomain(IConfiguration domainNode)
102 IConfiguration cfg = domainNode.Children["config"];
104 string domainId = domainNode.Attributes["id"];
106 AppDomainSetup currentSetup = AppDomain.CurrentDomain.SetupInformation;
107 AppDomainSetup setup = new AppDomainSetup();
109 setup.ApplicationName = domainNode.Attributes["applicationName"];
110 setup.ApplicationBase = NormalizeDirectoryPath(GetChildNodeValue(cfg, "applicationBase"));
111 setup.ConfigurationFile = GetChildNodeValue(cfg, "configurationFile");
113 setup.PrivateBinPath = GetChildNodeValue(cfg, "privateBinPath");
114 if (!IsEmpty(setup.PrivateBinPath))
116 setup.PrivateBinPathProbe = Boolean.TrueString;
119 setup.ShadowCopyFiles = domainNode.Attributes["shadowCopyFiles"];
121 if (IsTrue(setup.ShadowCopyFiles))
123 setup.ShadowCopyDirectories = null;
125 setup.CachePath = cfg.Attributes["cachePath"];
126 if (IsEmpty(setup.CachePath))
128 // fix for ASP.NET:
129 // http://weblogs.asp.net/hernandl/archive/2004/10/28/appdomainshcopy.aspx
130 setup.CachePath = currentSetup.CachePath;
134 log.Info("Creating AppDomain '{0}'", setup.ApplicationName);
136 Evidence evidence = AppDomain.CurrentDomain.Evidence;
138 AppDomain appDomain = AppDomain.CreateDomain(setup.ApplicationName, evidence, setup);
140 log.Debug(" BaseDir: " + appDomain.BaseDirectory);
141 log.Debug(" RelativeSearchPath: " + appDomain.RelativeSearchPath);
142 log.Debug(" ShadowCopyDirectories: " + appDomain.SetupInformation.ShadowCopyDirectories);
143 log.Debug(" CachePath: " + appDomain.SetupInformation.CachePath);
144 log.Debug(" PrivateBinPath: " + appDomain.SetupInformation.PrivateBinPath);
146 RemoteLoader l = this.CreateRemoteLoader(appDomain);
148 // register the loader
149 registry.RegisterLoader(domainId, l);
151 this.InitializeBatchRegistration(l, domainNode.Children["batchRegistration"]);
153 log.Debug("Adding as child kernel");
154 kernel.AddChildKernel(l.Kernel);
156 log.Info("Domain '{0}' created successfully.", appDomain.FriendlyName);
158 return appDomain;
161 private RemoteLoader CreateRemoteLoader(AppDomain appDomain)
163 string remoteLoaderAsmName = typeof(RemoteLoader).Assembly.FullName;
164 string remoteLoaderClsName = typeof(RemoteLoader).FullName;
168 appDomain.DoCallBack(
169 delegate
171 Assembly.Load("Castle.Core");
172 Assembly.Load("Castle.MicroKernel");
173 Assembly.Load("Castle.Facilities.DynamicLoader");
176 catch(Exception ex)
178 log.Fatal("Error while loading required assemblies", ex);
179 throw new FacilityException("Failed to load RemoteLoader required assemblies", ex);
184 // creates the RemoteLoader
185 log.Debug("Creating the RemoteLoader");
186 return (RemoteLoader) appDomain.CreateInstanceAndUnwrap(remoteLoaderAsmName, remoteLoaderClsName);
188 catch(Exception ex)
190 log.Fatal("Error while creating the RemoteLoader", ex);
191 throw new FacilityException("Failed to create the RemoteLoader", ex);
195 /// <summary>
196 /// Initializes the batch registration.
197 /// </summary>
198 /// <param name="loader">The loader.</param>
199 /// <param name="batchRegistrationNode">The batch registration node.</param>
200 protected virtual void InitializeBatchRegistration(RemoteLoader loader, IConfiguration batchRegistrationNode)
202 if (batchRegistrationNode == null)
203 return;
205 foreach(IConfiguration comp in batchRegistrationNode.Children)
207 switch(comp.Name)
209 case "components":
210 InitializeBatchComponents(loader, comp);
211 break;
212 default:
213 throw new FacilityException("Unrecognized configuration node: " + comp.Name);
218 /// <summary>
219 /// Register each batch component.
220 /// </summary>
221 /// <param name="loader">The <see cref="RemoteLoader"/> instance in which to register</param>
222 /// <param name="componentsNode">The component configuration node</param>
223 /// <remarks>
224 /// <example>
225 /// An example of a valid configuration node:
226 /// <code>
227 /// &lt;component id="componentid.*"&gt;
228 /// &lt;providesService service="Company.Project.IService, Company.Project" /&gt;
229 /// &lt;/component&gt;
230 /// </code>
231 /// </example>
232 /// </remarks>
233 private void InitializeBatchComponents(RemoteLoader loader, IConfiguration componentsNode)
235 if (componentsNode == null)
236 return;
238 string componentIdMask = componentsNode.Attributes["id"];
239 List<Type> servicesProvided = new List<Type>();
241 foreach(IConfiguration cond in componentsNode.Children)
243 switch(cond.Name)
245 case "providesService":
246 servicesProvided.Add(Type.GetType(cond.Attributes["service"]));
247 break;
248 default:
249 throw new FacilityException("Unrecognized configuration node: " + cond.Name);
253 loader.RegisterByServiceProvided(componentIdMask, servicesProvided.ToArray());
256 #region Configuration utility methods
258 private string GetChildNodeValue(IConfiguration cfg, string nodeName)
260 if (cfg == null)
261 return null;
262 IConfiguration node = cfg.Children[nodeName];
263 if (node == null)
264 return null;
265 return node.Value;
268 /// <summary>
269 /// Gets the config attribute.
270 /// </summary>
271 /// <param name="cfg">The CFG.</param>
272 /// <param name="attribute">The attribute.</param>
273 /// <param name="defaultValue">The default value.</param>
274 /// <param name="defaultValueArguments">The default value arguments.</param>
275 /// <returns></returns>
276 protected string GetConfigAttribute(IConfiguration cfg, string attribute, string defaultValue,
277 params object[] defaultValueArguments)
279 string value = cfg.Attributes[attribute];
280 if (value == null || value.Length == 0)
281 value = String.Format(defaultValue, defaultValueArguments);
283 return value;
286 private bool IsEmpty(string value)
288 return value == null || value.Length == 0;
291 private bool IsTrue(string value)
293 return String.Compare(value, "true", true) == 0;
296 #endregion
298 #region Filesystem directory handling
300 /// <summary>
301 /// Normalizes a directory path. It includes resolving parent (<c>..</c>) paths
302 /// and the <c>~</c> prefix, which maps to the root of the current application.
303 /// </summary>
304 /// <param name="path">The directory path</param>
305 /// <returns>The normalized directory path</returns>
306 /// <seealso cref="GetCurrentAppRootDirectory"/>
307 protected virtual string NormalizeDirectoryPath(string path)
309 if (path.StartsWith("~"))
310 path = GetCurrentAppRootDirectory() + path.Substring(1);
311 return new DirectoryInfo(path).FullName;
314 /// <summary>
315 /// Gets the root directory of the current application.
316 /// For web applications, it is obtained from <see cref="HttpServerUtility.MapPath"/>.
317 /// For other applications, <see cref="AppDomain.BaseDirectory"/> is used.
318 /// </summary>
319 protected virtual string GetCurrentAppRootDirectory()
321 if (isWeb)
322 return HttpContext.Current.Server.MapPath("~");
324 return AppDomain.CurrentDomain.BaseDirectory;
327 #endregion
329 /// <summary>
330 /// <see cref="IDisposable"/> implementation. Releases all <see cref="RemoteLoader"/>s
331 /// and <see cref="AppDomain"/>s.
332 /// </summary>
333 public void Dispose()
335 Dispose(true);
337 GC.SuppressFinalize(this);
340 /// <summary>
341 /// Releases unmanaged and - optionally - managed resources
342 /// </summary>
343 /// <param name="disposing"><c>true</c> to release both managed and unmanaged resources; <c>false</c> to release only unmanaged resources.</param>
344 protected virtual void Dispose(bool disposing)
346 if (disposing)
347 registry.Dispose();