resolved SPRNET-701

This commit is contained in:
eeichinger
2008-11-06 03:45:28 +00:00
parent eb7012c835
commit 7cac177276
16 changed files with 315 additions and 47 deletions

View File

@@ -23,8 +23,10 @@
using System;
using System.Collections;
using System.Collections.Specialized;
using System.Reflection;
using System.Xml;
using System.Xml.Schema;
using Spring.Collections;
using Spring.Core;
using Spring.Core.IO;
using Spring.Core.TypeResolution;
@@ -76,6 +78,7 @@ namespace Spring.Objects.Factory.Xml
#region Fields
private readonly static IDictionary parsers;
private readonly static IDictionary wellknownNamespaceParserTypeNames;
#if !NET_2_0
private readonly static XmlSchemaCollection schemas;
@@ -97,18 +100,54 @@ namespace Spring.Objects.Factory.Xml
schemas = new XmlSchemaSet();
schemas.XmlResolver = new XmlResourceUrlResolver();
#endif
wellknownNamespaceParserTypeNames = new CaseInsensitiveHashtable();
wellknownNamespaceParserTypeNames["http://www.springframework.net/tx"] = "Spring.Transaction.Config.TxNamespaceParser, Spring.Data";
wellknownNamespaceParserTypeNames["http://www.springframework.net/aop"] = "Spring.Aop.Config.AopNamespaceParser, Spring.Aop";
wellknownNamespaceParserTypeNames["http://www.springframework.net/db"] = "Spring.Data.Config.DatabaseNamespaceParser, Spring.Data";
wellknownNamespaceParserTypeNames["http://www.springframework.net/remoting"] = "Spring.Remoting.Config.RemotingNamespaceParser, Spring.Services";
wellknownNamespaceParserTypeNames["http://www.springframework.net/nms"] = "Spring.Messaging.Nms.Config.NmsNamespaceParser, Spring.Messaging.Nms";
wellknownNamespaceParserTypeNames["http://www.springframework.net/validation"] = "Spring.Validation.Config.ValidationNamespaceParser, Spring.Core";
Reset();
}
/// <summary>
/// Reset the list of registered parsers to "factory"-setting
/// </summary>
/// <remarks>use for unit tests only</remarks>
public static void Reset()
{
//TODO - externalize default list of parsers.
RegisterParser(new ObjectsNamespaceParser());
//This is done simple as a means to avoid cyclic dependencies with Factory.Xml
//which implementations of parsers typically use.
RegisterParser(ObjectUtils.InstantiateType(typeof(NamespaceParserRegistry).Assembly,
"Spring.Validation.Config.ValidationNamespaceParser") as INamespaceParser);
// register custom config parsers
ConfigurationUtils.GetSection(ConfigParsersSectionName);
}
ConfigurationUtils.GetSection(ConfigParsersSectionName);
}
/// <summary>
/// Registers the <see cref="INamespaceParser"/> type for wellknown namespaces
/// </summary>
/// <returns><c>true</c> if the parser could be registered, <c>false</c> otherwise</returns>
internal static bool RegisterWellknownNamespaceParserType(string namespaceUri)
{
if (parsers[namespaceUri] != null) return true;
if (wellknownNamespaceParserTypeNames.Contains(namespaceUri))
{
string parserTypeName = (string) wellknownNamespaceParserTypeNames[namespaceUri];
// assume, that all Spring.XXX assemblies have same version + public key
// get the ", Version=x.x.x.x, Culture=neutral, PublicKeyToken=65e474d141e25e07" part of Spring.Core and append it
string name = typeof(NamespaceParserRegistry).Assembly.GetName().Name;
string fullname = typeof(NamespaceParserRegistry).Assembly.GetName().FullName;
string versionCulturePublicKey = fullname.Substring(name.Length);
parserTypeName = parserTypeName + versionCulturePublicKey;
Type parserType = Type.GetType(parserTypeName, true);
RegisterParser(parserType);
return true;
}
return false;
}
/// <summary>
/// Constructs a "assembly://..." qualified schemaLocation url using the given type
/// to obtain the assembly name.
@@ -134,7 +173,16 @@ namespace Spring.Objects.Factory.Xml
/// </returns>
public static INamespaceParser GetParser(string namespaceURI)
{
return (INamespaceParser) parsers[namespaceURI];
INamespaceParser parser = (INamespaceParser) parsers[namespaceURI];
if (parser == null)
{
bool ok = RegisterWellknownNamespaceParserType(namespaceURI);
if (ok)
{
parser = (INamespaceParser) parsers[namespaceURI];
}
}
return parser;
}
/// <summary>

View File

@@ -53,6 +53,42 @@ namespace Spring.Objects.Factory.Xml
/// <author>Rick Evans (.NET)</author>
public class XmlObjectDefinitionReader : AbstractObjectDefinitionReader
{
#region Utility Classes
/// <summary>
/// For retrying the parse process
/// </summary>
private class RetryParseException : Exception
{
public RetryParseException()
{}
}
#if !NET_2_0
private class ValidationEventHandlerWrapper
{
private XmlReader _sender;
private XmlObjectDefinitionReader _owner;
public XmlReader Reader
{
set { _sender = value; }
}
public ValidationEventHandlerWrapper(XmlObjectDefinitionReader owner)
{
_owner = owner;
}
public void HandleValidation(object sender, ValidationEventArgs args)
{
_owner.HandleValidation(_sender,args);
}
}
#endif
#endregion
#region Fields
[NonSerialized]
@@ -208,29 +244,27 @@ namespace Spring.Objects.Factory.Xml
{
try
{
XmlReader reader;
if (SystemUtils.MonoRuntime)
// create local copy of data
byte[] xmlData = IOUtils.ToByteArray( stream );
XmlDocument doc;
// loop until no unregistered, wellknown namespaces left
while(true)
{
reader = XmlUtils.CreateReader(stream);
XmlReader reader = null;
try
{
MemoryStream xmlDataStream = new MemoryStream(xmlData);
reader = CreateValidatingReader(xmlDataStream);
doc = new ConfigXmlDocument();
doc.Load(reader);
break;
}
catch(RetryParseException)
{
if (reader != null) reader.Close();
}
}
else
{
reader = XmlUtils.CreateValidatingReader(stream, Resolver, NamespaceParserRegistry.GetSchemas(),
new ValidationEventHandler(HandleValidation));
}
#region Instrumentation
if (log.IsDebugEnabled)
{
log.Debug("Using the following XmlReader implementation : " + reader.GetType());
}
#endregion
XmlDocument doc = new ConfigXmlDocument();
doc.Load(reader);
return RegisterObjectDefinitions(doc, resource);
}
catch (XmlException ex)
@@ -253,6 +287,37 @@ namespace Spring.Objects.Factory.Xml
{
throw new ObjectDefinitionStoreException("Unexpected exception parsing XML document from " + resource.Description + "Inner exception message= " + ex.Message, ex);
}
}
private XmlReader CreateValidatingReader(MemoryStream stream)
{
XmlReader reader;
if (SystemUtils.MonoRuntime)
{
reader = XmlUtils.CreateReader(stream);
}
else
{
#if !NET_2_0
// only because 1.0/1.1 don't pass the sender into the handler callback...
ValidationEventHandlerWrapper validationEventHandlerWrapper = new ValidationEventHandlerWrapper(this);
reader = XmlUtils.CreateValidatingReader(stream, Resolver, NamespaceParserRegistry.GetSchemas(),
new ValidationEventHandler(validationEventHandlerWrapper.HandleValidation));
validationEventHandlerWrapper.Reader = reader;
#else
reader = XmlUtils.CreateValidatingReader(stream, Resolver, NamespaceParserRegistry.GetSchemas(), HandleValidation);
#endif
}
#region Instrumentation
if (log.IsDebugEnabled)
{
log.Debug("Using the following XmlReader implementation : " + reader.GetType());
}
return reader;
#endregion
}
/// <summary>
@@ -265,6 +330,20 @@ namespace Spring.Objects.Factory.Xml
if (args.Severity == XmlSeverityType.Error)
{
XmlSchemaException ex = args.Exception;
XmlReader xmlReader = (XmlReader) sender;
if (!NamespaceParserRegistry.GetSchemas().Contains(xmlReader.NamespaceURI)
#if NET_2_0
&& ex is XmlSchemaValidationException
#endif
)
{
// try wellknown parsers
bool registered = NamespaceParserRegistry.RegisterWellknownNamespaceParserType(xmlReader.NamespaceURI);
if (registered)
{
throw new RetryParseException();
}
}
#if !NET_2_0
// ignore validation errors for well-known 'xml' namespace. This seems to be a bug in net 1.0 + 1.1
if (ex.Message.IndexOf("http://www.w3.org/XML/1998/namespace:") > -1)

View File

@@ -2392,6 +2392,11 @@
SubType = "Code"
BuildAction = "Compile"
/>
<File
RelPath = "Util\IoUtils.cs"
SubType = "Code"
BuildAction = "Compile"
/>
<File
RelPath = "Util\ITextPosition.cs"
SubType = "Code"

View File

@@ -1003,6 +1003,7 @@
<Compile Include="Util\ConfigXmlDocument.cs" />
<Compile Include="Util\ConfigXmlElement.cs" />
<Compile Include="Util\FatalReflectionException.cs" />
<Compile Include="Util\IoUtils.cs" />
<Compile Include="Util\ITextPosition.cs" />
<Compile Include="Util\ObjectUtils.cs" />
<Compile Include="Util\ReflectionException.cs" />

View File

@@ -0,0 +1,55 @@
using System;
using System.IO;
namespace Spring.Util
{
/// <summary>
/// Utility methods for IO handling
/// </summary>
internal sealed class IOUtils
{
private IOUtils()
{
throw new InvalidOperationException("instantiation not supported");
}
/// <summary>
/// Copies one stream into another.
/// (Don't forget to call <see cref="Stream.Flush"/> on the destination stream!)
/// </summary>
/// <remarks>
/// Does not close the input stream!
/// </remarks>
public static void CopyStream( Stream src, Stream dest )
{
int bufferSize = 2048;
byte[] buffer = new byte[bufferSize];
int bytesRead = src.Read(buffer, 0, bufferSize);
while( bytesRead == bufferSize )
{
dest.Write( buffer, 0, bytesRead );
bytesRead = src.Read( buffer, 0, bufferSize );
}
if (bytesRead > 0)
{
dest.Write( buffer, 0, bytesRead );
}
}
/// <summary>
/// Reads a stream into a byte array.
/// </summary>
/// <remarks>
/// Does not close the input stream!
/// </remarks>
public static byte[] ToByteArray( Stream src )
{
MemoryStream stm = new MemoryStream();
CopyStream( src, stm );
stm.Flush();
return stm.ToArray();
}
}
}

View File

@@ -44,8 +44,9 @@ namespace Spring.Aop.Config
[SetUp]
public void Setup()
{
NamespaceParserRegistry.RegisterParser(typeof(AopNamespaceParser));
{
// IS WELLKNOWN NOW
//NamespaceParserRegistry.RegisterParser(typeof(AopNamespaceParser));
//ctx = new XmlApplicationContext( "assembly://Spring.Aop.Tests/Spring.Aop.Config/AopNamespaceParserTests.xml");
ctx = new XmlApplicationContext(ReadOnlyXmlTestResource.GetFilePath("AopNamespaceParserTests.xml", this.GetType()));
}

View File

@@ -64,7 +64,7 @@ namespace Spring.Objects.Factory.Xml
[Test]
public void CanLoadSchemaImportingOtherSchemaByRelativePath()
{
string schemaLocation = GetAssemblyResource( this.GetType(), "NamespaceParserRegistryTests_TestSchema.xsd" );
string schemaLocation = TestResourceLoader.GetAssemblyResourceUri( this.GetType(), "NamespaceParserRegistryTests_TestSchema.xsd" );
NamespaceParserRegistry.RegisterParser(new TestNamespaceParser(), "http://www.example.com/brief", schemaLocation);
XmlReader vr = XmlUtils.CreateValidatingReader( new StringResource(
@"<?xml version='1.0' encoding='UTF-8' ?>
@@ -74,9 +74,5 @@ namespace Spring.Objects.Factory.Xml
newDoc.Load(vr);
}
#endif
private string GetAssemblyResource( Type hint, string name)
{
return "assembly://" + hint.Assembly.FullName.Split(',')[0].Trim() + "/" + hint.Namespace + "/" + name;
}
}
}

View File

@@ -24,7 +24,9 @@ using System;
using System.Xml;
using NUnit.Framework;
using Spring.Core.IO;
using Spring.Objects.Factory.Config;
using Spring.Objects.Factory.Support;
using Spring.Validation;
#endregion
@@ -65,6 +67,49 @@ namespace Spring.Objects.Factory.Xml
reader.LoadObjectDefinitions(new ReadOnlyXmlTestResource("/dev/null"));
}
[Test]
public void AutoRegistersWellknownNamespaceParser()
{
try
{
NamespaceParserRegistry.Reset();
DefaultListableObjectFactory of = new DefaultListableObjectFactory();
XmlObjectDefinitionReader reader = new XmlObjectDefinitionReader(of);
reader.LoadObjectDefinitions(new StringResource(
@"<?xml version='1.0' encoding='UTF-8' ?>
<objects xmlns='http://www.springframework.net'
xmlns:v='http://www.springframework.net/validation'>
<v:required id='tripValidator' test='true' />
</objects>
"));
Assert.AreEqual(typeof(RequiredValidator), of.GetObject("tripValidator").GetType());
}
finally
{
NamespaceParserRegistry.Reset();
}
}
[Test]
[ExpectedException(typeof(ObjectDefinitionStoreException))]
public void ThrowsOnUnknownNamespaceUri()
{
NamespaceParserRegistry.Reset();
DefaultListableObjectFactory of = new DefaultListableObjectFactory();
XmlObjectDefinitionReader reader = new XmlObjectDefinitionReader(of);
reader.LoadObjectDefinitions(new StringResource(
@"<?xml version='1.0' encoding='UTF-8' ?>
<objects xmlns='http://www.springframework.net'
xmlns:x='http://www.springframework.net/XXXX'>
<x:group id='tripValidator' />
</objects>
"));
Assert.Fail();
}
[Test]
public void WhitespaceValuesArePreservedForValueAttribute()
{
@@ -105,6 +150,7 @@ namespace Spring.Objects.Factory.Xml
Assert.AreEqual(string.Empty, ((TestObject) of.GetObject("test4")).Name);
}
[Test]
public void WhitespaceValuesArePreservedForValueElementWhenSpaceIsSetToPreserve()
{

View File

@@ -0,0 +1,22 @@
<xsd:schema targetNamespace="http://www.example.com/brief"
xmlns:brief="http://www.example.com/brief"
xmlns:xsd="http://www.w3.org/2001/XMLSchema"
elementFormDefault="qualified"
attributeFormDefault="unqualified">
<xsd:attributeGroup name="CommonAttributes">
<xsd:attribute name="class" type="xsd:NMTOKEN" use="optional"/>
<xsd:attribute name="id" type="xsd:ID" use="required"/>
</xsd:attributeGroup>
<xsd:element name="brief">
<xsd:complexType>
<xsd:sequence>
<xsd:element name="kopf" type="xsd:string"/>
<xsd:element name="inhalt" type="xsd:string"/>
</xsd:sequence>
<xsd:attributeGroup ref="CommonAttributes"/>
</xsd:complexType>
</xsd:element>
</xsd:schema>

View File

@@ -76,7 +76,8 @@ namespace Spring
protected void ProcessAssembly (Assembly a) {
foreach (Type t in a.GetTypes ()) {
if (IsCheckedType (t)) {
if ( (t.IsPublic||t.IsNestedPublic)
&& IsCheckedType (t)) {
CheckStandardsCompliance (a, t);
}
}

View File

@@ -1,6 +1,7 @@
using System;
using System.IO;
using System.Net;
using System.Reflection;
using System.Web;
using System.Xml;
using NUnit.Framework;
@@ -99,5 +100,15 @@ namespace Spring
Assert.IsNotNull(stm, "Resource '{0}' in assembly '{1}' not found", resname, contextType.Assembly.FullName);
return stm;
}
/// <summary>
/// returns an "assembly://" uri for the specified manifest resource, scoped by the namespace of the specified type.
/// ("assembly://hint.assemblyname_without_version/hint.Namespace/name")
/// </summary>
/// <see cref="Assembly.GetManifestResourceStream(Type,string)"/>
public static string GetAssemblyResourceUri( Type hint, string name)
{
return "assembly://" + hint.Assembly.FullName.Split(',')[0].Trim() + "/" + hint.Namespace + "/" + name;
}
}
}

View File

@@ -46,8 +46,9 @@ namespace Spring.Data
//BasicConfigurator.Configure();
Console.WriteLine("Hello");
LogManager.Adapter = new ConsoleOutLoggerFactoryAdapter(new NameValueCollection());
NamespaceParserRegistry.RegisterParser(typeof(TxNamespaceParser));
NamespaceParserRegistry.RegisterParser(typeof(AopNamespaceParser));
//WELLKNOWN: NamespaceParserRegistry.RegisterParser(typeof(TxNamespaceParser));
//WELLKNOWN: NamespaceParserRegistry.RegisterParser(typeof(AopNamespaceParser));
string ctxName = "DTCAppContext.xml"; // for .NET 2.0
//string ctxName = "DTC1.1AppContext.xml"; // for .NET 1.1
ctx =

View File

@@ -56,9 +56,10 @@ namespace Spring.Data
[SetUp]
public void RollbackTestSetup()
{
NamespaceParserRegistry.RegisterParser(typeof (DatabaseNamespaceParser));
NamespaceParserRegistry.RegisterParser(typeof (TxNamespaceParser));
NamespaceParserRegistry.RegisterParser(typeof (AopNamespaceParser));
// WELLKNOWN
// NamespaceParserRegistry.RegisterParser(typeof (DatabaseNamespaceParser));
// NamespaceParserRegistry.RegisterParser(typeof (TxNamespaceParser));
// NamespaceParserRegistry.RegisterParser(typeof (AopNamespaceParser));
IApplicationContext ctx =
new XmlApplicationContext(
"assembly://Spring.Data.Integration.Tests/Spring.Data/TestTxIsolationLevel.xml");

View File

@@ -45,8 +45,8 @@ namespace Spring.Transaction.Config
[SetUp]
public void SetUp()
{
NamespaceParserRegistry.RegisterParser(typeof(TxNamespaceParser));
NamespaceParserRegistry.RegisterParser(typeof(AopNamespaceParser));
//WELLKNOWN: NamespaceParserRegistry.RegisterParser(typeof(TxNamespaceParser));
//WELLKNOWN: NamespaceParserRegistry.RegisterParser(typeof(AopNamespaceParser));
ctx = new XmlApplicationContext("assembly://Spring.Data.Tests/Spring.Transaction.Config/TxNamespaceParserTests.xml");
}

View File

@@ -58,7 +58,8 @@ namespace Spring.Messaging.Nms.Config
[SetUp]
public void Setup()
{
NamespaceParserRegistry.RegisterParser(typeof(NmsNamespaceParser));
// WELLKNOWN
//NamespaceParserRegistry.RegisterParser(typeof(NmsNamespaceParser));
ctx = new XmlApplicationContext(ReadOnlyXmlTestResource.GetFilePath("NmsNamespaceHandlerTests.xml", GetType()));
mocks = new MockRepository();
}

View File

@@ -321,7 +321,7 @@
/>
<File
RelPath = "Web\Support\ControlInterceptionTests.cs"
SubType = "Code"
SubType = "ASPXCodeBehind"
BuildAction = "Compile"
/>
<File