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
.Diagnostics
;
20 using System
.Reflection
;
21 using System
.Runtime
.Serialization
;
22 using System
.Text
.RegularExpressions
;
24 using Castle
.MicroKernel
;
25 using Castle
.MicroKernel
.ComponentActivator
;
29 /// Loads components on an isolated <see cref="AppDomain"/>.
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
;
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)"/>.
48 this.appDomain
= AppDomain
.CurrentDomain
;
50 // forces the loading of every library on the AppDomain directory
51 this.LoadAllAssemblies();
53 this.kernel
= new DefaultKernel();
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"/>.
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
)
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
))
79 string newId
= GenerateComponentId();
80 Debug
.WriteLine(String
.Format("Adding component: '{0}': {1} ({2})", newId
, t
, serviceType
));
81 kernel
.AddComponent(newId
, serviceType
, t
);
89 /// Loads all assemblies in the current <see cref="AppDomain"/>.
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
));
99 /// The <see cref="IKernel"/> in which the components are registered.
101 public IKernel Kernel
103 get { return kernel; }
107 /// The <see cref="AppDomain"/>.
109 public AppDomain AppDomain
111 get { return appDomain; }
115 /// Disposes the <see cref="Kernel"/>.
117 public void Dispose()
123 /// Checks whether a type <paramref name="t"/> is a valid implementation of a
124 /// given service <paramref name="serviceType"/>.
126 /// <param name="serviceType">The service type</param>
127 /// <param name="t">The component type</param>
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.
132 private bool IsValidServiceImplementation(Type serviceType
, Type t
)
136 serviceType
.IsAssignableFrom(t
) &&
141 /// Generates an unique component id, given the <see cref="componentIdMask"/>.
143 /// <returns>The unique component id</returns>
144 private string GenerateComponentId()
146 if (!rxComponentIdMask
.IsMatch(componentIdMask
))
148 if (!kernel
.HasComponent(componentIdMask
))
149 return componentIdMask
;
151 componentIdMask
+= ".*";
157 newId
= rxComponentIdMask
.Replace(componentIdMask
, (++seqComponent
).ToString(), 1);
158 } while (kernel
.HasComponent(newId
));
163 /// Creates a component on an isolated <see cref="AppDomain"/>.
165 public object CreateRemoteInstance(ComponentModel model
, CreationContext context
, object[] arguments
, Type
[] signature
)
169 Type implType
= model
.Implementation
;
171 if (model
.Interceptors
.HasInterceptors
)
175 instance
= Kernel
.ProxyFactory
.Create(Kernel
, null, model
, arguments
);
179 throw new ComponentActivatorException("RemoteLoader: could not proxy " + model
.Implementation
.FullName
, ex
);
186 ConstructorInfo cinfo
= implType
.GetConstructor(
187 BindingFlags
.Public
| BindingFlags
.Instance
, null, signature
, null);
189 instance
= FormatterServices
.GetUninitializedObject(implType
);
191 cinfo
.Invoke(instance
, arguments
);
195 throw new ComponentActivatorException("RemoteLoader: could not instantiate " + model
.Implementation
.FullName
, ex
);
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.
207 public override object InitializeLifetimeService()