// Copyright 2004-2008 Castle Project - http://www.castleproject.org/ // // 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. using System.Collections.Generic; namespace Castle.DynamicProxy { using System; using System.Collections; using System.IO; using System.Reflection; using System.Reflection.Emit; using System.Resources; using System.Threading; using Castle.DynamicProxy.Generators; using System.Runtime.Serialization.Formatters.Binary; /// /// Summary description for ModuleScope. /// public class ModuleScope { /// /// The default file name used when the assembly is saved using . /// public static readonly String DEFAULT_FILE_NAME = "CastleDynProxy2.dll"; /// /// The default assembly (simple) name used for the assemblies generated by a instance. /// public static readonly String DEFAULT_ASSEMBLY_NAME = "DynamicProxyGenAssembly2"; private ModuleBuilder moduleBuilderWithStrongName = null; private ModuleBuilder moduleBuilder = null; // The names to use for the generated assemblies and the paths (including the names) of their manifest modules private readonly string strongAssemblyName; private readonly string weakAssemblyName; private readonly string strongModulePath; private readonly string weakModulePath; // Keeps track of generated types private readonly Hashtable typeCache = Hashtable.Synchronized(new Hashtable()); // Users of ModuleScope should use this lock when accessing the cache private readonly ReaderWriterLock readerWriterLock = new ReaderWriterLock(); // Used to lock the module builder creation private readonly object _lockobj = new object(); // Specified whether the generated assemblies are intended to be saved private readonly bool savePhysicalAssembly; /// /// Initializes a new instance of the class; assemblies created by this instance will not be saved. /// public ModuleScope() : this(false) { } /// /// Initializes a new instance of the class, allowing to specify whether the assemblies generated by this instance /// should be saved. /// /// If set to true saves the generated module. public ModuleScope(bool savePhysicalAssembly) : this(savePhysicalAssembly, DEFAULT_ASSEMBLY_NAME, DEFAULT_FILE_NAME, DEFAULT_ASSEMBLY_NAME, DEFAULT_FILE_NAME) { } /// /// Initializes a new instance of the class, allowing to specify whether the assemblies generated by this instance /// should be saved and what simple names are to be assigned to them. /// /// If set to true saves the generated module. /// The simple name of the strong-named assembly generated by this . /// The path and file name of the manifest module of the strong-named assembly generated by this . /// The simple name of the weak-named assembly generated by this . /// The path and file name of the manifest module of the weak-named assembly generated by this . public ModuleScope(bool savePhysicalAssembly, string strongAssemblyName, string strongModulePath, string weakAssemblyName, string weakModulePath) { this.savePhysicalAssembly = savePhysicalAssembly; this.strongAssemblyName = strongAssemblyName; this.strongModulePath = strongModulePath; this.weakAssemblyName = weakAssemblyName; this.weakModulePath = weakModulePath; } /// /// Users of this should use this lock when accessing the cache. /// public ReaderWriterLock RWLock { get { return readerWriterLock; } } /// /// Returns a type from this scope's type cache, or null if the key cannot be found. /// /// The key to be looked up in the cache. /// The type from this scope's type cache matching the key, or null if the key cannot be found public Type GetFromCache(CacheKey key) { // no lock needed, typeCache is synchronized return (Type) typeCache[key]; } /// /// Registers a type in this scope's type cache. /// /// The key to be associated with the type. /// The type to be stored in the cache. public void RegisterInCache(CacheKey key, Type type) { // no lock needed, typeCache is synchronized typeCache[key] = type; } /// /// Gets the key pair used to sign the strong-named assembly generated by this . /// /// public static byte[] GetKeyPair() { byte[] keyPair; using (Stream stream = Assembly.GetExecutingAssembly().GetManifestResourceStream("Castle.DynamicProxy.DynProxy.snk")) { if (stream == null) throw new MissingManifestResourceException( "Should have a Castle.DynamicProxy.DynProxy.snk as an embedded resource, so Dynamic Proxy could sign generated assembly"); int length = (int) stream.Length; keyPair = new byte[length]; stream.Read(keyPair, 0, length); } return keyPair; } /// /// Gets the strong-named module generated by this scope, or if none has yet been generated. /// /// The strong-named module generated by this scope, or if none has yet been generated. public ModuleBuilder StrongNamedModule { get { lock (_lockobj) { return moduleBuilderWithStrongName; } } } /// /// Gets the file name of the strongly named module generated by this scope. /// /// The file name of the strongly named module generated by this scope. public string StrongNamedModuleName { get { return Path.GetFileName(strongModulePath); } } /// /// Gets the directory where the strongly named module generated by this scope will be saved, or if the current directory /// is used. /// /// The directory where the strongly named module generated by this scope will be saved when is called /// (if this scope was created to save modules). public string StrongNamedModuleDirectory { get { string directory = Path.GetDirectoryName(strongModulePath); if (directory == "") return null; else return directory; } } /// /// Gets the weak-named module generated by this scope, or if none has yet been generated. /// /// The weak-named module generated by this scope, or if none has yet been generated. public ModuleBuilder WeakNamedModule { get { lock (_lockobj) { return moduleBuilder; } } } /// /// Gets the file name of the weakly named module generated by this scope. /// /// The file name of the weakly named module generated by this scope. public string WeakNamedModuleName { get { return Path.GetFileName(weakModulePath); } } /// /// Gets the directory where the weakly named module generated by this scope will be saved, or if the current directory /// is used. /// /// The directory where the weakly named module generated by this scope will be saved when is called /// (if this scope was created to save modules). public string WeakNamedModuleDirectory { get { string directory = Path.GetDirectoryName(weakModulePath); if (directory == "") return null; else return directory; } } /// /// Gets the specified module generated by this scope, creating a new one if none has yet been generated. /// /// If set to true, a strong-named module is returned; otherwise, a weak-named module is returned. /// A strong-named or weak-named module generated by this scope, as specified by the parameter. public ModuleBuilder ObtainDynamicModule(bool isStrongNamed) { lock (_lockobj) { if (isStrongNamed) return ObtainDynamicModuleWithStrongName(); else return ObtainDynamicModuleWithWeakName(); } } /// /// Gets the strong-named module generated by this scope, creating a new one if none has yet been generated. /// /// A strong-named module generated by this scope. public ModuleBuilder ObtainDynamicModuleWithStrongName() { lock (_lockobj) { if (moduleBuilderWithStrongName == null) { moduleBuilderWithStrongName = CreateModule(true); } return moduleBuilderWithStrongName; } } /// /// Gets the weak-named module generated by this scope, creating a new one if none has yet been generated. /// /// A weak-named module generated by this scope. public ModuleBuilder ObtainDynamicModuleWithWeakName() { lock (_lockobj) { if (moduleBuilder == null) { moduleBuilder = CreateModule(false); } return moduleBuilder; } } private ModuleBuilder CreateModule(bool signStrongName) { AssemblyName assemblyName = GetAssemblyName(signStrongName); string moduleName = signStrongName ? StrongNamedModuleName : WeakNamedModuleName; string moduleDirectory = signStrongName ? StrongNamedModuleDirectory : WeakNamedModuleDirectory; if (savePhysicalAssembly) { AssemblyBuilder assemblyBuilder = AppDomain.CurrentDomain.DefineDynamicAssembly( assemblyName, AssemblyBuilderAccess.RunAndSave, moduleDirectory); return assemblyBuilder.DefineDynamicModule(moduleName, moduleName, true); } else { AssemblyBuilder assemblyBuilder = AppDomain.CurrentDomain.DefineDynamicAssembly( assemblyName, AssemblyBuilderAccess.Run); return assemblyBuilder.DefineDynamicModule(moduleName, true); } } private AssemblyName GetAssemblyName(bool signStrongName) { AssemblyName assemblyName = new AssemblyName(); assemblyName.Name = signStrongName ? strongAssemblyName : weakAssemblyName; if (signStrongName) { byte[] keyPairStream = GetKeyPair(); if (keyPairStream != null) { assemblyName.KeyPair = new StrongNameKeyPair(keyPairStream); } } return assemblyName; } /// /// Saves the generated assembly with the name and directory information given when this instance was created (or with /// the and current directory if none was given). /// /// /// /// This method stores the generated assembly in the directory passed as part of the module information specified when this instance was /// constructed (if any, else the current directory is used). If both a strong-named and a weak-named assembly /// have been generated, it will throw an exception; in this case, use the overload. /// /// /// If this was created without indicating that the assembly should be saved, this method does nothing. /// /// Both a strong-named and a weak-named assembly have been generated. /// The path of the generated assembly file, or null if no file has been generated. public string SaveAssembly() { if (!savePhysicalAssembly) return null; if (StrongNamedModule != null && WeakNamedModule != null) throw new InvalidOperationException("Both a strong-named and a weak-named assembly have been generated."); else if (StrongNamedModule != null) return SaveAssembly(true); else if (WeakNamedModule != null) return SaveAssembly(false); else return null; } /// /// Saves the specified generated assembly with the name and directory information given when this instance was created /// (or with the and current directory if none was given). /// /// True if the generated assembly with a strong name should be saved (see ); /// false if the generated assembly without a strong name should be saved (see . /// /// /// This method stores the specified generated assembly in the directory passed as part of the module information specified when this instance was /// constructed (if any, else the current directory is used). /// /// /// If this was created without indicating that the assembly should be saved, this method does nothing. /// /// /// No assembly has been generated that matches the parameter. /// /// The path of the generated assembly file, or null if no file has been generated. public string SaveAssembly(bool strongNamed) { if (!savePhysicalAssembly) return null; AssemblyBuilder assemblyBuilder; string assemblyFileName; string assemblyFilePath; if (strongNamed) { if (StrongNamedModule == null) throw new InvalidOperationException("No strong-named assembly has been generated."); else { assemblyBuilder = (AssemblyBuilder) StrongNamedModule.Assembly; assemblyFileName = StrongNamedModuleName; assemblyFilePath = StrongNamedModule.FullyQualifiedName; } } else { if (WeakNamedModule == null) throw new InvalidOperationException("No weak-named assembly has been generated."); else { assemblyBuilder = (AssemblyBuilder) WeakNamedModule.Assembly; assemblyFileName = WeakNamedModuleName; assemblyFilePath = WeakNamedModule.FullyQualifiedName; } } if (File.Exists(assemblyFilePath)) File.Delete(assemblyFilePath); AddCacheMappings(assemblyBuilder); assemblyBuilder.Save(assemblyFileName); return assemblyFilePath; } private void AddCacheMappings(AssemblyBuilder builder) { Dictionary mappings; lock (typeCache.SyncRoot) { mappings = new Dictionary(); foreach (DictionaryEntry cacheEntry in typeCache) mappings.Add((CacheKey) cacheEntry.Key, ((Type) cacheEntry.Value).FullName); } CacheMappingsAttribute.ApplyTo(builder, mappings); } /// /// Loads the generated types from the given assembly into this 's cache. /// /// The assembly to load types from. This assembly must have been saved via or /// , or it must have the manually applied. /// /// This method can be used to load previously generated and persisted proxy types from disk into this scope's type cache, eg. in order /// to avoid the performance hit associated with proxy generation. /// public void LoadAssemblyIntoCache(Assembly assembly) { if (assembly == null) throw new ArgumentNullException("assembly"); CacheMappingsAttribute[] cacheMappings = (CacheMappingsAttribute[]) assembly.GetCustomAttributes(typeof (CacheMappingsAttribute), false); if (cacheMappings.Length == 0) { string message = string.Format( "The given assembly '{0}' does not contain any cache information for generated types.", assembly.FullName); throw new ArgumentException(message, "assembly"); } lock (typeCache.SyncRoot) { foreach (KeyValuePair mapping in cacheMappings[0].GetDeserializedMappings()) { Type loadedType = assembly.GetType(mapping.Value); if (loadedType != null) RegisterInCache(mapping.Key, loadedType); } } } } }