Fixing an issue with output parameters that are of type IntPtr
[castle.git] / Facilities / DynamicLoader / Castle.Facilities.DynamicLoader / RemoteLoader.cs
blob95f9167ef33054823eced17149f29a931e0b69c1
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.Diagnostics;
19 using System.IO;
20 using System.Reflection;
21 using System.Runtime.Serialization;
22 using System.Text.RegularExpressions;
24 using Castle.MicroKernel;
25 using Castle.MicroKernel.ComponentActivator;
26 using Castle.Core;
28 /// <summary>
29 /// Loads components on an isolated <see cref="AppDomain"/>.
30 /// </summary>
31 public class RemoteLoader : MarshalByRefObject, IDisposable
33 private static readonly Regex rxComponentIdMask = new Regex("([*]|$)", RegexOptions.Compiled);
35 private readonly IKernel kernel;
36 private readonly object syncRegister = new object();
38 private int seqComponent;
39 private string componentIdMask;
40 private AppDomain appDomain;
42 /// <summary>
43 /// Creates a new <see cref="RemoteLoader"/>. This constructor should not be called
44 /// directly in the code, but via <see cref="System.AppDomain.CreateInstance(string,string)"/>.
45 /// </summary>
46 public RemoteLoader()
48 this.appDomain = AppDomain.CurrentDomain;
50 // forces the loading of every library on the AppDomain directory
51 this.LoadAllAssemblies();
53 this.kernel = new DefaultKernel();
56 /// <summary>
57 /// Searches for implementations of the given services in the current <see cref="AppDomain"/>
58 /// and add as components. Used by <see cref="DynamicLoaderFacility.InitializeBatchRegistration"/>.
59 /// </summary>
60 /// <param name="componentIdMask">The component id mask. Any <c>*</c> (asterisk) character will be replaced by a sequential number, starting by 1 (one).</param>
61 /// <param name="services">The services in which to test</param>
62 public void RegisterByServiceProvided(string componentIdMask, params Type[] services)
64 lock (syncRegister)
66 this.componentIdMask = componentIdMask;
67 this.seqComponent = 0;
69 foreach (Assembly asm in AppDomain.CurrentDomain.GetAssemblies())
71 Debug.WriteLine("Assembly: " + asm);
72 foreach (Type t in asm.GetExportedTypes())
74 foreach (Type serviceType in services)
76 if (!IsValidServiceImplementation(serviceType, t))
77 continue;
79 string newId = GenerateComponentId();
80 Debug.WriteLine(String.Format("Adding component: '{0}': {1} ({2})", newId, t, serviceType));
81 kernel.AddComponent(newId, serviceType, t);
88 /// <summary>
89 /// Loads all assemblies in the current <see cref="AppDomain"/>.
90 /// </summary>
91 private void LoadAllAssemblies()
93 DirectoryInfo baseDir = new DirectoryInfo(AppDomain.CurrentDomain.BaseDirectory);
94 foreach (FileInfo file in baseDir.GetFiles("*.dll"))
95 Assembly.Load(Path.GetFileNameWithoutExtension(file.Name));
98 /// <summary>
99 /// The <see cref="IKernel"/> in which the components are registered.
100 /// </summary>
101 public IKernel Kernel
103 get { return kernel; }
106 /// <summary>
107 /// The <see cref="AppDomain"/>.
108 /// </summary>
109 public AppDomain AppDomain
111 get { return appDomain; }
114 /// <summary>
115 /// Disposes the <see cref="Kernel"/>.
116 /// </summary>
117 public void Dispose()
119 kernel.Dispose();
122 /// <summary>
123 /// Checks whether a type <paramref name="t"/> is a valid implementation of a
124 /// given service <paramref name="serviceType"/>.
125 /// </summary>
126 /// <param name="serviceType">The service type</param>
127 /// <param name="t">The component type</param>
128 /// <returns>
129 /// <c>true</c> if <paramref name="t"/> is a valid implementation of the
130 /// service specified by <paramref name="serviceType"/>, <c>false</c> otherwise.
131 /// </returns>
132 private bool IsValidServiceImplementation(Type serviceType, Type t)
134 return
135 !t.IsAbstract &&
136 serviceType.IsAssignableFrom(t) &&
137 t != serviceType;
140 /// <summary>
141 /// Generates an unique component id, given the <see cref="componentIdMask"/>.
142 /// </summary>
143 /// <returns>The unique component id</returns>
144 private string GenerateComponentId()
146 if (!rxComponentIdMask.IsMatch(componentIdMask))
148 if (!kernel.HasComponent(componentIdMask))
149 return componentIdMask;
150 else
151 componentIdMask += ".*";
154 string newId;
157 newId = rxComponentIdMask.Replace(componentIdMask, (++seqComponent).ToString(), 1);
158 } while (kernel.HasComponent(newId));
159 return newId;
162 /// <summary>
163 /// Creates a component on an isolated <see cref="AppDomain"/>.
164 /// </summary>
165 public object CreateRemoteInstance(ComponentModel model, CreationContext context, object[] arguments, Type[] signature)
167 object instance;
169 Type implType = model.Implementation;
171 if (model.Interceptors.HasInterceptors)
175 instance = Kernel.ProxyFactory.Create(Kernel, null, model, arguments);
177 catch (Exception ex)
179 throw new ComponentActivatorException("RemoteLoader: could not proxy " + model.Implementation.FullName, ex);
182 else
186 ConstructorInfo cinfo = implType.GetConstructor(
187 BindingFlags.Public | BindingFlags.Instance, null, signature, null);
189 instance = FormatterServices.GetUninitializedObject(implType);
191 cinfo.Invoke(instance, arguments);
193 catch (Exception ex)
195 throw new ComponentActivatorException("RemoteLoader: could not instantiate " + model.Implementation.FullName, ex);
199 return instance;
202 /// <summary>
203 /// Overrides <see cref="MarshalByRefObject.InitializeLifetimeService"/>,
204 /// so no lease is returned and the object is kept in memory
205 /// as long as the host application domain is running.
206 /// </summary>
207 public override object InitializeLifetimeService()
209 return null;