1 // Copyright 2004-2008 Castle Project - http://www.castleproject.org/
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
7 // http://www.apache.org/licenses/LICENSE-2.0
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
18 using System
.Collections
;
19 using System
.Collections
.Generic
;
21 using System
.Reflection
;
22 using System
.Security
.Policy
;
24 using Castle
.Core
.Configuration
;
25 using Castle
.Core
.Logging
;
26 using Castle
.MicroKernel
;
27 using Castle
.MicroKernel
.Facilities
;
30 /// DynamicLoader facility.
32 public class DynamicLoaderFacility
: MarshalByRefObject
, IFacility
, IDisposable
34 private readonly DynamicLoaderRegistry registry
= new DynamicLoaderRegistry();
36 private ILogger log
= NullLogger
.Instance
;
40 private IKernel kernel
;
41 private IConfiguration facilityConfig
;
44 /// Initializes the facility.
46 public void Init(IKernel kernel
, IConfiguration facilityConfig
)
49 this.facilityConfig
= facilityConfig
;
55 /// Terminates the facility.
57 public void Terminate()
65 /// Releases unmanaged resources and performs other cleanup operations before the
66 /// <see cref="DynamicLoaderFacility"/> is reclaimed by garbage collection.
68 ~
DynamicLoaderFacility()
74 /// Inits this instance.
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
)
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
))
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
);
161 private RemoteLoader
CreateRemoteLoader(AppDomain appDomain
)
163 string remoteLoaderAsmName
= typeof(RemoteLoader
).Assembly
.FullName
;
164 string remoteLoaderClsName
= typeof(RemoteLoader
).FullName
;
168 appDomain
.DoCallBack(
171 Assembly
.Load("Castle.Core");
172 Assembly
.Load("Castle.MicroKernel");
173 Assembly
.Load("Castle.Facilities.DynamicLoader");
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
);
190 log
.Fatal("Error while creating the RemoteLoader", ex
);
191 throw new FacilityException("Failed to create the RemoteLoader", ex
);
196 /// Initializes the batch registration.
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)
205 foreach(IConfiguration comp
in batchRegistrationNode
.Children
)
210 InitializeBatchComponents(loader
, comp
);
213 throw new FacilityException("Unrecognized configuration node: " + comp
.Name
);
219 /// Register each batch component.
221 /// <param name="loader">The <see cref="RemoteLoader"/> instance in which to register</param>
222 /// <param name="componentsNode">The component configuration node</param>
225 /// An example of a valid configuration node:
227 /// <component id="componentid.*">
228 /// <providesService service="Company.Project.IService, Company.Project" />
229 /// </component>
233 private void InitializeBatchComponents(RemoteLoader loader
, IConfiguration componentsNode
)
235 if (componentsNode
== null)
238 string componentIdMask
= componentsNode
.Attributes
["id"];
239 List
<Type
> servicesProvided
= new List
<Type
>();
241 foreach(IConfiguration cond
in componentsNode
.Children
)
245 case "providesService":
246 servicesProvided
.Add(Type
.GetType(cond
.Attributes
["service"]));
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
)
262 IConfiguration node
= cfg
.Children
[nodeName
];
269 /// Gets the config attribute.
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
);
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;
298 #region Filesystem directory handling
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.
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
;
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.
319 protected virtual string GetCurrentAppRootDirectory()
322 return HttpContext
.Current
.Server
.MapPath("~");
324 return AppDomain
.CurrentDomain
.BaseDirectory
;
330 /// <see cref="IDisposable"/> implementation. Releases all <see cref="RemoteLoader"/>s
331 /// and <see cref="AppDomain"/>s.
333 public void Dispose()
337 GC
.SuppressFinalize(this);
341 /// Releases unmanaged and - optionally - managed resources
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
)