From 81e8541c60b9ee7e0c70fb421afb1bec039705e0 Mon Sep 17 00:00:00 2001 From: wdroste Date: Sat, 6 Sep 2008 15:42:38 +0000 Subject: [PATCH 001/342] Initial import --- .../ActiveDirectoryConfiguration.cs | 143 + .../ActiveDirectoryConnector.cs | 1192 ++++++ .../ActiveDirectoryConnector.csproj | 121 + .../ActiveDirectoryFilterTranslator.cs | 471 +++ .../ActiveDirectoryUtils.cs | 595 +++ ActiveDirectoryConnector/AssemblyInfo.cs | 71 + .../CustomAttributeHandlers.cs | 981 +++++ .../Messages.en.Designer.cs | 0 ActiveDirectoryConnector/Messages.en.resx | 123 + ActiveDirectoryConnector/Messages.es-ES.resx | 123 + ActiveDirectoryConnector/Messages.es.resx | 123 + ActiveDirectoryConnector/Messages.resx | 135 + .../PasswordChangeHandler.cs | 152 + .../TerminalServicesUtils.cs | 321 ++ .../UserAccountControl.cs | 128 + .../ActiveDirectoryConnectorTest.cs | 2123 +++++++++++ .../ActiveDirectoryConnectorTests.csproj | 159 + .../Properties/AssemblyInfo.cs | 75 + ActiveDirectoryConnectorTests/TestParams.resx | 147 + ActiveDirectoryConnectorTests/project.xml | 19 + BooScriptExecutorFactory/AssemblyInfo.cs | 70 + .../BooScriptExecutorFactory.cs | 98 + .../BooScriptExecutorFactory.csproj | 104 + .../lib/Boo.Lang.Compiler.dll | Bin 0 -> 729088 bytes .../lib/Boo.Lang.Interpreter.dll | Bin 0 -> 86016 bytes .../lib/Boo.Lang.Parser.dll | Bin 0 -> 421888 bytes .../lib/Boo.Lang.Useful.dll | Bin 0 -> 81920 bytes BooScriptExecutorFactory/lib/Boo.Lang.dll | Bin 0 -> 110592 bytes Common/AssemblyInfo.cs | 70 + Common/Assertions.cs | 68 + Common/CollectionUtil.cs | 755 ++++ Common/Common.csproj | 108 + Common/DateTimeUtil.cs | 61 + Common/FrameworkInternalBridge.cs | 71 + Common/IOUtil.cs | 64 + Common/Locale.cs | 227 ++ Common/Pair.cs | 90 + Common/Pooling.cs | 206 ++ Common/Proxy.cs | 284 ++ Common/ReflectionUtil.cs | 166 + Common/Script.cs | 184 + Common/Security.cs | 559 +++ Common/StringUtil.cs | 109 + Common/TraceUtil.cs | 68 + Common/XmlUtil.cs | 209 ++ Connector Gateway.sln | 166 + Console/AssemblyInfo.cs | 70 + Console/Console.csproj | 100 + Console/MainForm.Designer.cs | 47 + Console/MainForm.cs | 64 + Console/Program.cs | 62 + Framework/Api.cs | 535 +++ Framework/ApiOperations.cs | 323 ++ Framework/AssemblyInfo.cs | 70 + Framework/Common.cs | 289 ++ Framework/CommonExceptions.cs | 251 ++ Framework/CommonObjects.cs | 3278 +++++++++++++++++ Framework/CommonObjectsFilter.cs | 1331 +++++++ Framework/CommonSerializer.cs | 305 ++ Framework/Framework.csproj | 109 + Framework/Spi.cs | 222 ++ Framework/SpiOperations.cs | 357 ++ Framework/Test.cs | 304 ++ FrameworkInternal/Api.cs | 856 +++++ FrameworkInternal/ApiLocal.cs | 1030 ++++++ FrameworkInternal/ApiLocalOperations.cs | 1365 +++++++ FrameworkInternal/ApiRemote.cs | 396 ++ FrameworkInternal/ApiRemoteMessages.cs | 244 ++ FrameworkInternal/AssemblyInfo.cs | 70 + FrameworkInternal/FrameworkInternal.csproj | 112 + FrameworkInternal/Resources.resx | 461 +++ FrameworkInternal/Security.cs | 141 + FrameworkInternal/Serializer.cs | 2294 ++++++++++++ FrameworkInternal/SerializerBinary.cs | 792 ++++ FrameworkInternal/SerializerXml.cs | 811 ++++ FrameworkInternal/Server.cs | 797 ++++ FrameworkInternal/Test.cs | 118 + FrameworkTests/AssemblyInfo.cs | 70 + FrameworkTests/CollectionUtilTests.cs | 107 + FrameworkTests/ConnectorInfoManagerTests.cs | 590 +++ FrameworkTests/FilterTranslatorTests.cs | 626 ++++ FrameworkTests/FrameworkTests.csproj | 167 + FrameworkTests/GuardedStringTests.cs | 117 + FrameworkTests/LocaleTests.cs | 227 ++ FrameworkTests/ObjectNormalizerFacadeTests.cs | 251 ++ FrameworkTests/ObjectPoolTests.cs | 269 ++ FrameworkTests/ObjectSerializationTests.cs | 1082 ++++++ FrameworkTests/ProxyTests.cs | 117 + FrameworkTests/ScriptTests.cs | 79 + FrameworkTests/TestHelperTests.cs | 89 + FrameworkTests/TestUtil.cs | 70 + FrameworkTests/UpdateImplTests.cs | 327 ++ FrameworkTests/app.config | 24 + FrameworkTests/project.xml | 5 + Service/AssemblyInfo.cs | 70 + Service/Program.cs | 238 ++ Service/ProjectInstaller.cs | 73 + Service/Service.cs | 194 + Service/Service.csproj | 118 + Service/app.config | 32 + ServiceInstall/ExtBuild.proj | 78 + ServiceInstall/File.bottom | 6 + ServiceInstall/File.top | 21 + ServiceInstall/ServiceInstall.wixproj | 72 + ServiceInstall/Setup.wxs | 50 + ServiceInstall/license.rtf | 30 + ShellScriptExecutorFactory/AssemblyInfo.cs | 70 + .../ShellScriptExecutorFactory.cs | 145 + .../ShellScriptExecutorFactory.csproj | 89 + TestBundleV1/AssemblyInfo.cs | 74 + TestBundleV1/Messages.es-ES.resx | 126 + TestBundleV1/Messages.es.resx | 126 + TestBundleV1/Messages.resx | 126 + TestBundleV1/TestBundleV1.csproj | 94 + TestBundleV1/TestConnector.cs | 221 ++ TestBundleV2/AssemblyInfo.cs | 72 + TestBundleV2/TestBundleV2.csproj | 91 + TestBundleV2/TestConnector.cs | 78 + server.pfx | Bin 0 -> 1704 bytes 119 files changed, 34654 insertions(+) create mode 100644 ActiveDirectoryConnector/ActiveDirectoryConfiguration.cs create mode 100644 ActiveDirectoryConnector/ActiveDirectoryConnector.cs create mode 100644 ActiveDirectoryConnector/ActiveDirectoryConnector.csproj create mode 100644 ActiveDirectoryConnector/ActiveDirectoryFilterTranslator.cs create mode 100644 ActiveDirectoryConnector/ActiveDirectoryUtils.cs create mode 100644 ActiveDirectoryConnector/AssemblyInfo.cs create mode 100644 ActiveDirectoryConnector/CustomAttributeHandlers.cs create mode 100644 ActiveDirectoryConnector/Messages.en.Designer.cs create mode 100644 ActiveDirectoryConnector/Messages.en.resx create mode 100644 ActiveDirectoryConnector/Messages.es-ES.resx create mode 100644 ActiveDirectoryConnector/Messages.es.resx create mode 100644 ActiveDirectoryConnector/Messages.resx create mode 100644 ActiveDirectoryConnector/PasswordChangeHandler.cs create mode 100644 ActiveDirectoryConnector/TerminalServicesUtils.cs create mode 100644 ActiveDirectoryConnector/UserAccountControl.cs create mode 100644 ActiveDirectoryConnectorTests/ActiveDirectoryConnectorTest.cs create mode 100644 ActiveDirectoryConnectorTests/ActiveDirectoryConnectorTests.csproj create mode 100644 ActiveDirectoryConnectorTests/Properties/AssemblyInfo.cs create mode 100644 ActiveDirectoryConnectorTests/TestParams.resx create mode 100644 ActiveDirectoryConnectorTests/project.xml create mode 100644 BooScriptExecutorFactory/AssemblyInfo.cs create mode 100644 BooScriptExecutorFactory/BooScriptExecutorFactory.cs create mode 100644 BooScriptExecutorFactory/BooScriptExecutorFactory.csproj create mode 100644 BooScriptExecutorFactory/lib/Boo.Lang.Compiler.dll create mode 100644 BooScriptExecutorFactory/lib/Boo.Lang.Interpreter.dll create mode 100644 BooScriptExecutorFactory/lib/Boo.Lang.Parser.dll create mode 100644 BooScriptExecutorFactory/lib/Boo.Lang.Useful.dll create mode 100644 BooScriptExecutorFactory/lib/Boo.Lang.dll create mode 100644 Common/AssemblyInfo.cs create mode 100644 Common/Assertions.cs create mode 100644 Common/CollectionUtil.cs create mode 100644 Common/Common.csproj create mode 100644 Common/DateTimeUtil.cs create mode 100644 Common/FrameworkInternalBridge.cs create mode 100644 Common/IOUtil.cs create mode 100644 Common/Locale.cs create mode 100644 Common/Pair.cs create mode 100644 Common/Pooling.cs create mode 100644 Common/Proxy.cs create mode 100644 Common/ReflectionUtil.cs create mode 100644 Common/Script.cs create mode 100644 Common/Security.cs create mode 100644 Common/StringUtil.cs create mode 100644 Common/TraceUtil.cs create mode 100644 Common/XmlUtil.cs create mode 100644 Connector Gateway.sln create mode 100644 Console/AssemblyInfo.cs create mode 100644 Console/Console.csproj create mode 100644 Console/MainForm.Designer.cs create mode 100644 Console/MainForm.cs create mode 100644 Console/Program.cs create mode 100644 Framework/Api.cs create mode 100644 Framework/ApiOperations.cs create mode 100644 Framework/AssemblyInfo.cs create mode 100644 Framework/Common.cs create mode 100644 Framework/CommonExceptions.cs create mode 100644 Framework/CommonObjects.cs create mode 100644 Framework/CommonObjectsFilter.cs create mode 100644 Framework/CommonSerializer.cs create mode 100644 Framework/Framework.csproj create mode 100644 Framework/Spi.cs create mode 100644 Framework/SpiOperations.cs create mode 100644 Framework/Test.cs create mode 100644 FrameworkInternal/Api.cs create mode 100644 FrameworkInternal/ApiLocal.cs create mode 100644 FrameworkInternal/ApiLocalOperations.cs create mode 100644 FrameworkInternal/ApiRemote.cs create mode 100644 FrameworkInternal/ApiRemoteMessages.cs create mode 100644 FrameworkInternal/AssemblyInfo.cs create mode 100644 FrameworkInternal/FrameworkInternal.csproj create mode 100644 FrameworkInternal/Resources.resx create mode 100644 FrameworkInternal/Security.cs create mode 100644 FrameworkInternal/Serializer.cs create mode 100644 FrameworkInternal/SerializerBinary.cs create mode 100644 FrameworkInternal/SerializerXml.cs create mode 100644 FrameworkInternal/Server.cs create mode 100644 FrameworkInternal/Test.cs create mode 100644 FrameworkTests/AssemblyInfo.cs create mode 100644 FrameworkTests/CollectionUtilTests.cs create mode 100644 FrameworkTests/ConnectorInfoManagerTests.cs create mode 100644 FrameworkTests/FilterTranslatorTests.cs create mode 100644 FrameworkTests/FrameworkTests.csproj create mode 100644 FrameworkTests/GuardedStringTests.cs create mode 100644 FrameworkTests/LocaleTests.cs create mode 100644 FrameworkTests/ObjectNormalizerFacadeTests.cs create mode 100644 FrameworkTests/ObjectPoolTests.cs create mode 100644 FrameworkTests/ObjectSerializationTests.cs create mode 100644 FrameworkTests/ProxyTests.cs create mode 100644 FrameworkTests/ScriptTests.cs create mode 100644 FrameworkTests/TestHelperTests.cs create mode 100644 FrameworkTests/TestUtil.cs create mode 100644 FrameworkTests/UpdateImplTests.cs create mode 100644 FrameworkTests/app.config create mode 100644 FrameworkTests/project.xml create mode 100644 Service/AssemblyInfo.cs create mode 100644 Service/Program.cs create mode 100644 Service/ProjectInstaller.cs create mode 100644 Service/Service.cs create mode 100644 Service/Service.csproj create mode 100644 Service/app.config create mode 100644 ServiceInstall/ExtBuild.proj create mode 100644 ServiceInstall/File.bottom create mode 100644 ServiceInstall/File.top create mode 100644 ServiceInstall/ServiceInstall.wixproj create mode 100644 ServiceInstall/Setup.wxs create mode 100644 ServiceInstall/license.rtf create mode 100644 ShellScriptExecutorFactory/AssemblyInfo.cs create mode 100644 ShellScriptExecutorFactory/ShellScriptExecutorFactory.cs create mode 100644 ShellScriptExecutorFactory/ShellScriptExecutorFactory.csproj create mode 100644 TestBundleV1/AssemblyInfo.cs create mode 100644 TestBundleV1/Messages.es-ES.resx create mode 100644 TestBundleV1/Messages.es.resx create mode 100644 TestBundleV1/Messages.resx create mode 100644 TestBundleV1/TestBundleV1.csproj create mode 100644 TestBundleV1/TestConnector.cs create mode 100644 TestBundleV2/AssemblyInfo.cs create mode 100644 TestBundleV2/TestBundleV2.csproj create mode 100644 TestBundleV2/TestConnector.cs create mode 100644 server.pfx diff --git a/ActiveDirectoryConnector/ActiveDirectoryConfiguration.cs b/ActiveDirectoryConnector/ActiveDirectoryConfiguration.cs new file mode 100644 index 00000000..fe4b079e --- /dev/null +++ b/ActiveDirectoryConnector/ActiveDirectoryConfiguration.cs @@ -0,0 +1,143 @@ +/* + * Copyright 2008 Sun Microsystems, Inc. All rights reserved. + * + * U.S. Government Rights - Commercial software. Government users + * are subject to the Sun Microsystems, Inc. standard license agreement + * and applicable provisions of the FAR and its supplements. + * + * Use is subject to license terms. + * + * This distribution may include materials developed by third parties. + * Sun, Sun Microsystems, the Sun logo, Java and Project Identity + * Connectors are trademarks or registered trademarks of Sun + * Microsystems, Inc. or its subsidiaries in the U.S. and other + * countries. + * + * UNIX is a registered trademark in the U.S. and other countries, + * exclusively licensed through X/Open Company, Ltd. + * + * ----------- + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER. + * + * Copyright 2008 Sun Microsystems, Inc. All rights reserved. + * + * The contents of this file are subject to the terms of the Common Development + * and Distribution License(CDDL) (the License). You may not use this file + * except in compliance with the License. + * + * You can obtain a copy of the License at + * http://identityconnectors.dev.java.net/CDDLv1.0.html + * See the License for the specific language governing permissions and + * limitations under the License. + * + * When distributing the Covered Code, include this CDDL Header Notice in each + * file and include the License file at identityconnectors/legal/license.txt. + * If applicable, add the following below this CDDL Header, with the fields + * enclosed by brackets [] replaced by your own identifying information: + * "Portions Copyrighted [year] [name of copyright owner]" + * ----------- + */ +using System; +using System.Collections.Generic; +using System.Text; +using System.Diagnostics; +using Org.IdentityConnectors.Framework.Spi; +using Org.IdentityConnectors.Framework.Common.Exceptions; + +namespace Org.IdentityConnectors.ActiveDirectory +{ + public class ActiveDirectoryConfiguration : Org.IdentityConnectors.Framework.Spi.AbstractConfiguration + { + [ConfigurationProperty(Confidential = false, DisplayMessageKey = "display_SyncGlobalCatalogServer", HelpMessageKey = "help_SyncGlobalCatalogServer")] + public String SyncGlobalCatalogServer + { get; set; } + + [ConfigurationProperty(Confidential = false, DisplayMessageKey = "display_SyncDomainController", HelpMessageKey = "help_SyncDomainController")] + public String SyncDomainController + { get; set; } + + [ConfigurationProperty(Confidential = false, DisplayMessageKey = "display_SyncSearchContext", HelpMessageKey = "help_SyncSearchContext")] + public String SyncSearchContext + { get; set; } + + [ConfigurationProperty(Confidential = false, DisplayMessageKey = "display_domainName", HelpMessageKey = "help_domainName")] + public String DomainName + { get; set; } + + [ConfigurationProperty(Confidential = false, DisplayMessageKey = "display.DirectoryAdminName", HelpMessageKey = "help.DirectoryAdminName")] + public String DirectoryAdminName + { get; set; } + + [ConfigurationProperty(Confidential = true, DisplayMessageKey = "display.DirectoryAdminPassword", HelpMessageKey = "help.DirectoryAdminPassword")] + public String DirectoryAdminPassword + { get; set; } + + [ConfigurationProperty(Confidential = false, DisplayMessageKey = "display.ObjectClass", HelpMessageKey = "help.ObjectClass")] + public String ObjectClass + { get; set; } + + [ConfigurationProperty(Confidential = false, DisplayMessageKey = "display.UserProvidesPasswordOnChange", HelpMessageKey = "help.UserProvidesPasswordOnChange")] + public bool UserProvidesPasswordOnChange{get;set;} + + [ConfigurationProperty(Confidential = false, DisplayMessageKey = "display.CreateHomeDirectory", HelpMessageKey = "help.CreateHomeDirectory")] + public bool CreateHomeDirectory { get; set; } + + [ConfigurationProperty(Confidential = false, DisplayMessageKey = "display.SearchContext", HelpMessageKey = "help.SearchContext")] + public String SearchContext + { get; set; } + + [ConfigurationProperty(Confidential = false, DisplayMessageKey = "display.SearchChildDomains", HelpMessageKey = "help.SearchChildDomains")] + public bool SearchChildDomains {get;set;} + + [ConfigurationProperty(Confidential = false, DisplayMessageKey = "display.EncryptionType", HelpMessageKey = "help.EncryptionType")] + public String EncryptionType + { get; set; } + + [ConfigurationProperty(Confidential = false, DisplayMessageKey = "display.LDAPHostName", HelpMessageKey = "help.LDAPHostName")] + public String LDAPHostName + { get; set; } + + public ActiveDirectoryConfiguration() + { + DomainName = ""; + SearchContext = ""; + DirectoryAdminName = "cn=DirectoryAdmin"; + ObjectClass = "User"; + UserProvidesPasswordOnChange = false; + CreateHomeDirectory = false; + SearchChildDomains = false; + EncryptionType = "NONE"; + LDAPHostName = ""; + } + + public override void Validate() + { + String message = "Configuration errors: "; + Boolean foundError = false; + + // can't lookup the schema without the domain name + if (DomainName.Length == 0) + { + message += "->Domain name not supplied "; + foundError = true; + } + + if (DirectoryAdminName.Length == 0) + { + message += "->Directory administrator name not supplied "; + foundError = true; + } + + if (ObjectClass.Length == 0) + { + message += "->ObjectClass was not supplied "; + foundError = true; + } + + if (foundError) + { + throw new ConfigurationException(message); + } + } + } +} diff --git a/ActiveDirectoryConnector/ActiveDirectoryConnector.cs b/ActiveDirectoryConnector/ActiveDirectoryConnector.cs new file mode 100644 index 00000000..108b3542 --- /dev/null +++ b/ActiveDirectoryConnector/ActiveDirectoryConnector.cs @@ -0,0 +1,1192 @@ +/* + * Copyright 2008 Sun Microsystems, Inc. All rights reserved. + * + * U.S. Government Rights - Commercial software. Government users + * are subject to the Sun Microsystems, Inc. standard license agreement + * and applicable provisions of the FAR and its supplements. + * + * Use is subject to license terms. + * + * This distribution may include materials developed by third parties. + * Sun, Sun Microsystems, the Sun logo, Java and Project Identity + * Connectors are trademarks or registered trademarks of Sun + * Microsystems, Inc. or its subsidiaries in the U.S. and other + * countries. + * + * UNIX is a registered trademark in the U.S. and other countries, + * exclusively licensed through X/Open Company, Ltd. + * + * ----------- + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER. + * + * Copyright 2008 Sun Microsystems, Inc. All rights reserved. + * + * The contents of this file are subject to the terms of the Common Development + * and Distribution License(CDDL) (the License). You may not use this file + * except in compliance with the License. + * + * You can obtain a copy of the License at + * http://identityconnectors.dev.java.net/CDDLv1.0.html + * See the License for the specific language governing permissions and + * limitations under the License. + * + * When distributing the Covered Code, include this CDDL Header Notice in each + * file and include the License file at identityconnectors/legal/license.txt. + * If applicable, add the following below this CDDL Header, with the fields + * enclosed by brackets [] replaced by your own identifying information: + * "Portions Copyrighted [year] [name of copyright owner]" + * ----------- + */ +using System; +using System.Reflection; +using ActiveDs; +using System.Collections.Generic; +using System.Collections.ObjectModel; +using Org.IdentityConnectors.Common.Pooling; +using Org.IdentityConnectors.Framework.Api.Operations; +using Org.IdentityConnectors.Framework.Spi; +using Org.IdentityConnectors.Framework.Spi.Operations; +using System.Diagnostics; +using Org.IdentityConnectors.Framework.Common.Objects; +using Org.IdentityConnectors.Framework.Common.Exceptions; +using System.DirectoryServices; +using System.DirectoryServices.ActiveDirectory; +using Org.IdentityConnectors.Framework.Common; +using System.Text; +using Org.IdentityConnectors.Common.Script; + +namespace Org.IdentityConnectors.ActiveDirectory +{ + [ConnectorClass("connector_displayName", + typeof(ActiveDirectoryConfiguration), + MessageCatalogPath = "Org.IdentityConnectors.ActiveDirectory.Messages" + )] + /// + /// The Active Directory Connector + /// + public class ActiveDirectoryConnector : CreateOp, Connector, SchemaOp, DeleteOp, + SearchOp, TestOp, AdvancedUpdateOp, ScriptOnResourceOp, SyncOp, + AuthenticateOp, AttributeNormalizer + { + // This is the list of attributes returned by default if no attributes are + // requested in the options field of ExecuteQuery + public readonly static ICollection AccountAttributesReturnedByDefault = + new HashSet(StringComparer.CurrentCultureIgnoreCase) { +// "userPassword", + "sAMAccountName", + "givenName", + "sn", + "displayName", + "mail", + "telephoneNumber", + "employeeId", + "division", + "mobile", + "middleName", + "description", + "department", + "manager", + "title", + "initials", + "co", + "company", + "facsimileTelephoneNumber", + "homePhone", + "streetAddress", + "l", + "st", + "postalCode", + "TerminalServicesInitialProgram", + "TerminalServicesWorkDirectory", + "AllowLogon", + "MaxConnectionTime", + "MaxDisconnectionTime", + "MaxIdleTime", + "ConnectClientDrivesAtLogon", + "ConnectClientPrintersAtLogon", + "DefaultToMainPrinter", + "BrokenConnectionAction", + "ReconnectionAction", + "EnableRemoteControl", + "TerminalServicesProfilePath", + "TerminalServicesHomeDirectory", + "TerminalServicesHomeDrive", + "uSNChanged", + "ad_container", + "otherHomePhone", + "distinguishedName", + "objectClass", + "homeDirectory", + }; + + public static IDictionary> AttributesReturnedByDefault = null; + + + // special attribute names + public static readonly string ATT_CONTAINER = "ad_container"; + public static readonly string ATT_USER_PASSWORD = "userPassword"; + public static readonly string ATT_CN = "cn"; + public static readonly string ATT_OU = "ou"; + public static readonly string ATT_OBJECT_GUID = "objectGuid"; + public static readonly string ATT_IS_DELETED = "isDeleted"; + public static readonly string ATT_USN_CHANGED = "uSNChanged"; + public static readonly string ATT_DISTINGUISHED_NAME = "distinguishedName"; + public static readonly string ATT_SAMACCOUNT_NAME = "sAMAccountName"; + public static readonly string ATT_MEMBER = "member"; + public static readonly string ATT_MEMBEROF = "memberOf"; + public static readonly string ATT_HOME_DIRECTORY = "homeDirectory"; + public static readonly string ATT_OBJECT_SID = "objectSid"; + + public static readonly string OBJECTCLASS_OU = "organizationalUnit"; + public static readonly ObjectClass ouObjectClass = new ObjectClass(OBJECTCLASS_OU); + + ActiveDirectoryConfiguration _configuration = null; + ActiveDirectoryUtils _utils = null; + private static Schema _schema = null; + private static object _schema_lock = new object(); + private static object _defaultAttributes_lock = new object(); + + public ActiveDirectoryConnector() + { + // if no one has initialized the default attributes, + // initialize them + if (AttributesReturnedByDefault == null) + { + // lock the default attibutes lock object + lock (_defaultAttributes_lock) + { + // make sure no one populated this while we + // were waiting for the lock + if (AttributesReturnedByDefault == null) + { + // populate default attributes + AttributesReturnedByDefault = new Dictionary>(); + AttributesReturnedByDefault.Add(ObjectClass.ACCOUNT, AccountAttributesReturnedByDefault); + } + } + } + } + + #region CreateOp Members + // implementation of CreateSpiOp + public Uid Create(ObjectClass oclass, + ICollection attributes, OperationOptions options) + { + Uid uid = null; + + // I had lots of problems here. Here are the things + // that seemed to make everything work: + // - Create the object with the minimum data and commit it, + // then update the object with the rest of the info. + // - After updating an object and committing, be sure to + // do a refresh cache before continuing to use it. If + // not, it seems like multi-value attributes get hosed. + // - Group membership cannot be change by memberOf, but must + // be changed by changing the members property of the group + + Trace.TraceInformation("Create method"); + if (_configuration == null) + { + throw new ConfigurationException("Connector has not been configured"); + } + Name nameAttribute = ConnectorAttributeUtil.GetNameFromAttributes(attributes); + if (nameAttribute == null) + { + throw new ConnectorException("The name operational attribute cannot be null"); + } + + String ldapContainerPath = ActiveDirectoryUtils.GetLDAPPath(_configuration.LDAPHostName, + ActiveDirectoryUtils.GetParentDn(nameAttribute.GetNameValue())); + String ldapEntryPath = ActiveDirectoryUtils.GetLDAPPath(_configuration.LDAPHostName, + nameAttribute.GetNameValue()); + + try + { + if (!DirectoryEntry.Exists(ldapContainerPath)) + { + throw new ConnectorException("Container does not exist"); + } + + // Get the correct container, and put the new user in it + DirectoryEntry containerDe = new DirectoryEntry(ldapContainerPath, + _configuration.DirectoryAdminName, _configuration.DirectoryAdminPassword); + DirectoryEntry newDe = containerDe.Children.Add( + ActiveDirectoryUtils.GetRelativeName(nameAttribute), + ActiveDirectoryUtils.GetADObjectClass(oclass)); + + newDe.CommitChanges(); + _utils.UpdateADObject(oclass, newDe, attributes, + UpdateType.REPLACE, _configuration); + Object guidValue = newDe.Properties["objectGUID"].Value; + if (guidValue != null) + { + // format the uid in the special way required for searching + String guidString = + ActiveDirectoryUtils.ConvertUIDBytesToGUIDString( + (Byte[])guidValue); + + Trace.TraceInformation("Created object with uid {0}", guidString); + uid = new Uid(guidString); + } + else + { + Trace.TraceError("Unable to find uid attribute for newly created object"); + } + + + } + catch (DirectoryServicesCOMException exception) + { + // have to make sure the new thing gets deleted in + // the case of error + Console.WriteLine("caught exception:" + exception); + Trace.TraceError(exception.Message); + throw; + } + + return uid; + } + + #endregion + + #region Connector Members + + // implementation of Connector + public void Init(Configuration configuration) + { + Trace.TraceInformation("Init method"); + _configuration = (ActiveDirectoryConfiguration)configuration; + _utils = new ActiveDirectoryUtils(_configuration); + } + + #endregion + + #region IDisposable Members + + public void Dispose() + { + } + + #endregion + + #region SchemaOp Members + // implementation of SchemaSpiOp + public Schema Schema() + { + Trace.TraceInformation("Schema method"); + + if (_schema != null) + { + return _schema; + } + + lock (_schema_lock) + { + // could have blocked on the lock while someone else built the + // schema, so check one more time before building + if (_schema == null) + { + String serverName = _configuration.LDAPHostName; + Forest forest = null; + + if ((serverName == null) || (serverName.Length == 0)) + { + // get the active directory schema + DirectoryContext context = new DirectoryContext( + DirectoryContextType.Domain, + _configuration.DomainName, + _configuration.DirectoryAdminName, + _configuration.DirectoryAdminPassword); + DomainController dc = DomainController.FindOne(context); + forest = dc.Forest; + } + else + { + DirectoryContext context = new DirectoryContext( + DirectoryContextType.DirectoryServer, + _configuration.LDAPHostName, + _configuration.DirectoryAdminName, + _configuration.DirectoryAdminPassword); + forest = Forest.GetForest(context); + } + + ActiveDirectorySchema ADSchema = forest.Schema; + + + // get the user attribute infos and operations + ICollection userAttributeInfos = + GetUserAttributeInfos(ADSchema); + ICollection userOperations = GetUserOperations(); + ObjectClassInfoBuilder ociBuilder = new ObjectClassInfoBuilder(); + ociBuilder.ObjectType = ObjectClass.ACCOUNT_NAME; + ociBuilder.AddAllAttributeInfo(userAttributeInfos); + ObjectClassInfo userInfo = ociBuilder.Build(); + + // get the group attribute infos and operations + ICollection groupAttributeInfos = + GetGroupAttributeInfos(ADSchema); + ociBuilder = new ObjectClassInfoBuilder(); + ociBuilder.ObjectType = ObjectClass.GROUP_NAME; + ociBuilder.AddAllAttributeInfo(groupAttributeInfos); + ICollection groupOperations = GetGroupOperations(); + ObjectClassInfo groupInfo = ociBuilder.Build(); + + // get the organizationalUnit attribute infos and operations + ICollection ouAttributeInfos = + GetOuAttributeInfos(ADSchema); + ociBuilder = new ObjectClassInfoBuilder(); + ociBuilder.ObjectType = OBJECTCLASS_OU; + ociBuilder.AddAllAttributeInfo(ouAttributeInfos); + ICollection ouOperations = GetOuOperations(); + ObjectClassInfo ouInfo = ociBuilder.Build(); + + SchemaBuilder schemaBuilder = new SchemaBuilder(typeof(ActiveDirectoryConnector)); + schemaBuilder.DefineObjectClass(userInfo); + schemaBuilder.DefineObjectClass(groupInfo); + schemaBuilder.DefineObjectClass(ouInfo); + _schema = schemaBuilder.Build(); + } + } + + return _schema; + } + + public ICollection GetUserAttributeInfos( + ActiveDirectorySchema ADSchema) + { + ICollection attributeInfos = new Collection(); + // put in operational attributes + attributeInfos.Add(OperationalAttributeInfos.ENABLE); + /* + attributeInfos.Add(OperationalAttributeInfos.ENABLE_DATE); + attributeInfos.Add(OperationalAttributeInfos.DISABLE_DATE); + attributeInfos.Add(OperationalAttributeInfos.LOCK_OUT); + */ + attributeInfos.Add(OperationalAttributeInfos.CURRENT_PASSWORD); + // dont think I need this + // attributeInfos.Add(OperationalAttributeInfos.RESET_PASSWORD); + attributeInfos.Add(PredefinedAttributeInfos.GROUPS); + attributeInfos.Add(OperationalAttributeInfos.PASSWORD); + + ICollection attributesToIgnore = new List(); + attributesToIgnore.Add("CN"); + attributesToIgnore.Add(ATT_USER_PASSWORD); + + // get everything else from the schema + PopulateSchemaFromAD(_configuration.ObjectClass, ADSchema, attributeInfos, + attributesToIgnore, ObjectClass.ACCOUNT); + + // add in the uid + attributeInfos.Add(GetConnectorAttributeInfo(Uid.NAME, + typeof(string), false, true, true, false, ObjectClass.ACCOUNT)); + + // now add in container ... + attributeInfos.Add(GetConnectorAttributeInfo(ATT_CONTAINER, + typeof(string), true, true, false, false, ObjectClass.ACCOUNT)); + + // add in the userPassword + // attributeInfos.Add(GetConnectorAttributeInfo(ATT_USER_PASSWORD, + // typeof(string), false, true, true, false, ObjectClass.ACCOUNT)); + + // add in terminal services attributes + attributeInfos.Add(GetConnectorAttributeInfo( + TerminalServicesUtils.TS_INITIAL_PROGRAM, typeof(string), + false, true, false, false, ObjectClass.ACCOUNT)); + attributeInfos.Add(GetConnectorAttributeInfo( + TerminalServicesUtils.TS_INITIAL_PROGRAM_DIR, typeof(string), + false, true, false, false, ObjectClass.ACCOUNT)); + attributeInfos.Add(GetConnectorAttributeInfo( + TerminalServicesUtils.TS_ALLOW_LOGON, typeof(int), + false, true, false, false, ObjectClass.ACCOUNT)); + attributeInfos.Add(GetConnectorAttributeInfo( + TerminalServicesUtils.TS_MAX_CONNECTION_TIME, typeof(int), + false, true, false, false, ObjectClass.ACCOUNT)); + attributeInfos.Add(GetConnectorAttributeInfo( + TerminalServicesUtils.TS_MAX_DISCONNECTION_TIME, typeof(int), + false, true, false, false, ObjectClass.ACCOUNT)); + attributeInfos.Add(GetConnectorAttributeInfo( + TerminalServicesUtils.TS_MAX_IDLE_TIME, typeof(int), + false, true, false, false, ObjectClass.ACCOUNT)); + attributeInfos.Add(GetConnectorAttributeInfo( + TerminalServicesUtils.TS_CONNECT_CLIENT_DRIVES_AT_LOGON, typeof(int), + false, true, false, false, ObjectClass.ACCOUNT)); + attributeInfos.Add(GetConnectorAttributeInfo( + TerminalServicesUtils.TS_CONNECT_CLIENT_PRINTERS_AT_LOGON, typeof(int), + false, true, false, false, ObjectClass.ACCOUNT)); + attributeInfos.Add(GetConnectorAttributeInfo( + TerminalServicesUtils.TS_DEFAULT_TO_MAIN_PRINTER, typeof(int), + false, true, false, false, ObjectClass.ACCOUNT)); + attributeInfos.Add(GetConnectorAttributeInfo( + TerminalServicesUtils.TS_BROKEN_CONNECTION_ACTION, typeof(int), + false, true, false, false, ObjectClass.ACCOUNT)); + attributeInfos.Add(GetConnectorAttributeInfo( + TerminalServicesUtils.TS_RECONNECTION_ACTION, typeof(int), + false, true, false, false, ObjectClass.ACCOUNT)); + attributeInfos.Add(GetConnectorAttributeInfo( + TerminalServicesUtils.TS_ENABLE_REMOTE_CONTROL, typeof(int), + false, true, false, false, ObjectClass.ACCOUNT)); + attributeInfos.Add(GetConnectorAttributeInfo( + TerminalServicesUtils.TS_PROFILE_PATH, typeof(string), + false, true, false, false, ObjectClass.ACCOUNT)); + attributeInfos.Add(GetConnectorAttributeInfo( + TerminalServicesUtils.TS_HOME_DIRECTORY, typeof(string), + false, true, false, false, ObjectClass.ACCOUNT)); + attributeInfos.Add(GetConnectorAttributeInfo( + TerminalServicesUtils.TS_HOME_DRIVE, typeof(string), + false, true, false, false, ObjectClass.ACCOUNT)); + + return attributeInfos; + } + + public ICollection GetGroupAttributeInfos( + ActiveDirectorySchema ADSchema) + { + ICollection attributeInfos = new Collection(); + // put in operational attributes + //attributeInfos.Add(OperationalAttributeInfos.EXPIRE_PASSWORD); + + // add in the uid + attributeInfos.Add(GetConnectorAttributeInfo(Uid.NAME, + typeof(string), false, true, true, false, ObjectClass.GROUP)); + + // now add in container ... + attributeInfos.Add(GetConnectorAttributeInfo(ATT_CONTAINER, + typeof(string), true, true, false, false, ObjectClass.GROUP)); + + attributeInfos.Add(PredefinedAttributeInfos.ACCOUNTS); + + // get everything else from the schema + PopulateSchemaFromAD("Group", ADSchema, attributeInfos, null, ObjectClass.GROUP); + return attributeInfos; + } + + public ICollection GetOuAttributeInfos( + ActiveDirectorySchema ADSchema) + { + ICollection attributeInfos = new Collection(); + + // add in the uid + attributeInfos.Add(GetConnectorAttributeInfo(Uid.NAME, + typeof(string), false, true, true, false, ouObjectClass)); + + // add in container ... + attributeInfos.Add(GetConnectorAttributeInfo(ATT_CONTAINER, + typeof(string), true, true, false, false, ouObjectClass)); + + // add in name ... + attributeInfos.Add(GetConnectorAttributeInfo(Name.NAME, + typeof(string), true, true, true, false, ouObjectClass)); + + return attributeInfos; + } + + protected void PopulateSchemaFromAD(String className, + ActiveDirectorySchema ADSchema, + ICollection attributeInfos, + ICollection attributesToIgnore, ObjectClass oclass) + { + if(attributesToIgnore == null) { + attributesToIgnore = new List(); + } + ActiveDirectorySchemaClass schemaClass = ADSchema.FindClass(className); + AddPropertyCollectionToSchema(schemaClass.MandatoryProperties, + attributeInfos, false, oclass); + + AddPropertyCollectionToSchema(schemaClass.OptionalProperties, + attributeInfos, false, oclass); + } + + protected void AddPropertyCollectionToSchema( + ActiveDirectorySchemaPropertyCollection schemaProperties, + ICollection attributeInfos, + Boolean required, ObjectClass oclass) + { + foreach (ActiveDirectorySchemaProperty schemaProperty in + schemaProperties) + { + DirectoryEntry sde = schemaProperty.GetDirectoryEntry(); + PropertyValueCollection systemOnlyPvc = sde.Properties["systemOnly"]; + Boolean writable = true; + if (systemOnlyPvc != null) + { + Object value = systemOnlyPvc.Value; + if ((value != null) && (value.Equals(true))) + { + writable = false; + } + } + + String syntax = schemaProperty.Syntax.ToString(); + syntax = syntax.ToUpper(); + Type connectorType = typeof(string); + + // if this gets larger, break it out + // into a special method. + if ("BOOLEAN".Equals(syntax, StringComparison.CurrentCultureIgnoreCase)) + { + connectorType = typeof(bool); + } + else if ("INTEGER".Equals(syntax, StringComparison.CurrentCultureIgnoreCase)) + { + connectorType = typeof(int); + } + else if ("INT64".Equals( + syntax, StringComparison.CurrentCultureIgnoreCase)) + { + connectorType = typeof(long); + } + + attributeInfos.Add(GetConnectorAttributeInfo(schemaProperty.Name, + connectorType, writable, true, required, + schemaProperty.IsSingleValued ? false : true, oclass)); + +/* + Console.WriteLine("***->" + schemaProperty.Name + "<-***"); + foreach (String pName in sde.Properties.PropertyNames) + { + Console.WriteLine("***->" + pName + " = " + sde.Properties[pName].Value); + } +*/ + } + } + + public ICollection GetUserOperations() + { + ICollection operations = new Collection(); + operations.Add(typeof(CreateApiOp)); + operations.Add(typeof(DeleteApiOp)); + operations.Add(typeof(UpdateApiOp)); + operations.Add(typeof(SearchApiOp)); + operations.Add(typeof(ScriptOnResourceApiOp)); + operations.Add(typeof(SyncApiOp)); + + return operations; + } + + public ICollection GetGroupOperations() + { + ICollection operations = new Collection(); + operations.Add(typeof(CreateApiOp)); + return operations; + } + + public ICollection GetOuOperations() + { + ICollection operations = new Collection(); + operations.Add(typeof(SearchApiOp)); + return operations; + } + + private ConnectorAttributeInfo GetConnectorAttributeInfo(string name, + Type type, bool writable, bool readable, bool required, + bool multivalue, ObjectClass oclass) + { + ConnectorAttributeInfoBuilder builder = new ConnectorAttributeInfoBuilder(); + builder.Name = name; + builder.ValueType = type; + builder.Writeable = writable; + builder.Readable = readable; + builder.Required = required; + builder.MultiValue = multivalue; + + // if there is a set of attributes to return by default + // for this object class use it. If not, just use the + // the builder's default value + if(AttributesReturnedByDefault.Keys.Contains(oclass)) { + if (AttributesReturnedByDefault[oclass].Contains(name)) + { + builder.ReturnedByDefault = true; + } + else + { + builder.ReturnedByDefault = false; + } + } + + return builder.Build(); + } + + #endregion + + #region SearchOp Members + + // implementation of SearchSpiOp + public Org.IdentityConnectors.Framework.Common.Objects.Filters.FilterTranslator CreateFilterTranslator(ObjectClass oclass, OperationOptions options) + { + return new ActiveDirectoryFilterTranslator(); + } + + // implementation of SearchSpiOp + public void ExecuteQuery(ObjectClass oclass, string query, + ResultsHandler handler, OperationOptions options) + { + try + { + bool useGC = false; + if (_configuration.SearchChildDomains) + { + useGC = true; + } + + ExecuteQuery(oclass, query, handler, options, + false, null, _configuration.LDAPHostName, useGC, _configuration.SearchContext); + } + catch (Exception e) + { + Trace.TraceError(String.Format("Caught Exception: {0}", e)); + throw; + } + } + + // this is used by the ExecuteQuere method of SearchSpiOp, and + // by the SyncSpiOp + private void ExecuteQuery(ObjectClass oclass, string query, + ResultsHandler handler, OperationOptions options, bool includeDeleted, + SortOption sortOption, string serverName, bool useGlobalCatalog, string searchRoot) + { + StringBuilder fullQueryBuilder = new StringBuilder(); + if (query == null) + { + fullQueryBuilder.Append("(objectclass="); + fullQueryBuilder.Append(ActiveDirectoryUtils.GetADObjectClass(oclass)); + fullQueryBuilder.Append(")"); + } + else + { + fullQueryBuilder.Append("(&(objectclass="); + fullQueryBuilder.Append(ActiveDirectoryUtils.GetADObjectClass(oclass)); + fullQueryBuilder.Append(")"); + fullQueryBuilder.Append(query); + fullQueryBuilder.Append(")"); + } + query = fullQueryBuilder.ToString(); + + if (query == null) + { + Trace.TraceInformation("query is null"); + } + else + { + Trace.TraceInformation("Setting search string to \'{0}\'", query); + } + + string path; + + if (useGlobalCatalog) + { + path = ActiveDirectoryUtils.GetGCPath(serverName, searchRoot); + } + else + { + path = ActiveDirectoryUtils.GetLDAPPath(serverName, searchRoot); + } + + DirectoryEntry searchRootEntry = new DirectoryEntry(path, + _configuration.DirectoryAdminName, _configuration.DirectoryAdminPassword); + DirectorySearcher searcher = new DirectorySearcher(searchRootEntry, query); + searcher.PageSize = 1000; + if (includeDeleted) + { + searcher.Tombstone = true; + } + + if (sortOption != null) + { + searcher.Sort = sortOption; + } + + SearchResultCollection resultSet = searcher.FindAll(); + ICollection attributesToReturn = null; + if (resultSet.Count > 0) + { + attributesToReturn = GetAttributesToReturn(oclass, options); + } + + foreach (SearchResult result in resultSet) + { + Trace.TraceInformation("Found object {0}", result.Path); + ConnectorObjectBuilder builder = new ConnectorObjectBuilder(); + + bool isDeleted = false; + if(result.Properties.Contains(ATT_IS_DELETED)) { + ResultPropertyValueCollection pvc = result.Properties[ATT_IS_DELETED]; + if(pvc.Count > 0) { + isDeleted = (bool)pvc[0]; + } + } + + if (isDeleted.Equals(false)) + { + // if we were using the global catalog (gc), we have to + // now retrieve the object from a domain controller (dc) + // because the gc may not have have all of the attributes, + // depending on which attributes are replicated to the gc. + SearchResult savedGcResult = null; + SearchResult savedDcResult = result; + if (useGlobalCatalog) + { + savedGcResult = result; + + String dcSearchRootPath = ActiveDirectoryUtils.GetLDAPPath( + _configuration.LDAPHostName, searchRoot); + + DirectoryEntry dcSearchRoot = new DirectoryEntry(dcSearchRootPath, + _configuration.DirectoryAdminName, _configuration.DirectoryAdminPassword); + + string dcSearchQuery = String.Format("(" + ATT_DISTINGUISHED_NAME + "={0})", + ActiveDirectoryUtils.GetDnFromPath(savedGcResult.Path)); + DirectorySearcher dcSearrcher = + new DirectorySearcher(dcSearchRoot, dcSearchQuery); + savedDcResult = dcSearrcher.FindOne(); + if (savedDcResult == null) + { + // in this case, we found it in the gc, but not in the + // domain controller. We cant return a result. The could + // be a case where the account was deleted, but the global + // catalog doesn't know it yet + Trace.TraceWarning(String.Format("Found result in global catalog " + + "for ''{0}'', but could not retrieve the entry from the domain " + + "controller", savedGcResult.Path)); + continue; + } + } + + foreach (string attributeName in attributesToReturn) + { + // if we are using the global catalog, we had to get the + // dc's version of the directory entry, but for usnchanged, + // we need the gc version of it + if (useGlobalCatalog && attributeName.Equals(ATT_USN_CHANGED, + StringComparison.CurrentCultureIgnoreCase)) + { + AddAttributeIfNotNull(builder, + _utils.GetConnectorAttributeFromADEntry( + oclass, attributeName, savedGcResult)); + } + else + { + AddAttributeIfNotNull(builder, + _utils.GetConnectorAttributeFromADEntry( + oclass, attributeName, savedDcResult)); + + } + } + } + else + { + // get uid + AddAttributeIfNotNull(builder, + _utils.GetConnectorAttributeFromADEntry( + oclass, Uid.NAME, result)); + + // get uid + AddAttributeIfNotNull(builder, + _utils.GetConnectorAttributeFromADEntry( + oclass, Name.NAME, result)); + + // get usnchanged + AddAttributeIfNotNull(builder, + _utils.GetConnectorAttributeFromADEntry( + oclass, ATT_USN_CHANGED, result)); + + // add isDeleted + builder.AddAttribute(ATT_IS_DELETED, true); + } + + String msg = String.Format("Returning ''{0}''", + (result.Path != null) ? result.Path : ""); + Trace.TraceInformation(msg); + handler(builder.Build()); + } + } + + private ICollection GetAttributesToReturn(ObjectClass oclass, OperationOptions options) + { + ICollection attributeNames = new HashSet(); + + if ((options.AttributesToGet != null) && (options.AttributesToGet.Length > 0)) + { + foreach (string name in options.AttributesToGet) + { + attributeNames.Add(name); + } + // now add in operational attributes ... they are always returned + ObjectClassInfo ocInfo = Schema().FindObjectClassInfo(oclass.GetObjectClassValue()); + foreach (ConnectorAttributeInfo info in ocInfo.ConnectorAttributeInfos) + { + Trace.TraceInformation(String.Format( + "Adding {0} to list of returned attributes", info.Name)); + if ((info.IsReturnedByDefault) && (ConnectorAttributeUtil.IsSpecial(info))) + { + if (!attributeNames.Contains(info.Name)) + { + attributeNames.Add(info.Name); + } + } + } + } + else + { + ObjectClassInfo ocInfo = Schema().FindObjectClassInfo(oclass.GetObjectClassValue()); + foreach (ConnectorAttributeInfo info in ocInfo.ConnectorAttributeInfos) + { + Trace.TraceInformation(String.Format( + "Adding {0} to list of returned attributes", info.Name)); + if (info.IsReturnedByDefault) + { + attributeNames.Add(info.Name); + } + } + } + + // Uid is always returned + attributeNames.Add(Uid.NAME); + return attributeNames; + } + + private void AddAttributeIfNotNull(ConnectorObjectBuilder builder, + ConnectorAttribute attribute) + { + if (attribute != null) + { + builder.AddAttribute(attribute); + } + } + + #endregion + + #region TestOp Members + + public void Test() + { + _configuration.Validate(); + } + + #endregion + + #region AdvancedUpdateOp Members + + // implementation of AdvancedUpdateSpiOp + public Uid Update(UpdateType type, ObjectClass oclass, + ICollection attributes, OperationOptions options) + { + Uid updatedUid = null; + + Trace.TraceInformation("Update method"); + if (_configuration == null) + { + throw new ConfigurationException("Connector has not been configured"); + } + + updatedUid = ConnectorAttributeUtil.GetUidAttribute(attributes); + if (updatedUid == null) + { + throw new ConnectorException("Uid was not present in attributes to be modified"); + } + + DirectoryEntry updateEntry = + ActiveDirectoryUtils.GetDirectoryEntryFromUid(_configuration.LDAPHostName, updatedUid, + _configuration.DirectoryAdminName, _configuration.DirectoryAdminPassword); + + _utils.UpdateADObject(oclass, updateEntry, + attributes, type, _configuration); + + try + { + Object guidValue = updateEntry.Properties["objectGUID"].Value; + if (guidValue != null) + { + // format the uid in the special way required for searching + String searchGuid = + ActiveDirectoryUtils.ConvertUIDBytesToGUIDString( + (Byte[])guidValue); + + Trace.TraceInformation("Created user with uid {0}", searchGuid); + updatedUid = new Uid(searchGuid); + } + else + { + Trace.TraceError("Unable to find uid attribute for newly created object"); + } + } + catch (DirectoryServicesCOMException exception) + { + // have to make sure the new thing gets deleted in + // the case of error + Console.WriteLine("caught exception:" + exception); + return null; + } + return updatedUid; + } + + #endregion + + #region DeleteOp Members + + // implementation of DeleteSpiOp + public void Delete(ObjectClass objClass, Uid uid, OperationOptions options) + { + DirectoryEntry de = null; + + de = ActiveDirectoryUtils.GetDirectoryEntryFromUid(_configuration.LDAPHostName, uid, + _configuration.DirectoryAdminName, _configuration.DirectoryAdminPassword); + + if (objClass.Equals(ObjectClass.ACCOUNT)) + { + // if it's a user account, get the parent's child list + // and remove this entry + DirectoryEntry parent = de.Parent; + parent.Children.Remove(de); + } + else if (objClass.Equals(ObjectClass.GROUP)) + { + // if it's a group (container), delete this + // entry and all it's children + de.DeleteTree(); + } + } + + #endregion + + + #region ScriptOnResourceOp Members + + public object RunScriptOnResource(ScriptContext request, OperationOptions options) + { + IDictionary arguments = new Dictionary(request.ScriptArguments); + // per Will D. batch scripts need special parameters set, but other scripts + // don't. He doesn't feel that this can be changed at present, so setting + // the parameters here. + + // Cant find a constant for the string to represent the shell script executor, + // replace embedded string constant if one turns up. + if (request.ScriptLanguage.Equals("Shell", StringComparison.CurrentCultureIgnoreCase)) + { + if (options.RunAsUser != null) + { + arguments.Add("USERNAME", options.RunAsUser); + arguments.Add("PASSWORD", + ActiveDirectoryUtils.GetSecureString(options.RunWithPassword)); + } + } + + + ScriptExecutorFactory factory = ScriptExecutorFactory.NewInstance(request.ScriptLanguage); + ScriptExecutor executor = factory.NewScriptExecutor(new Assembly[0],request.ScriptText, true); + return executor.Execute(arguments); + } + + #endregion + + #region SyncOp Members + + // implementation of SyncSpiOp + public class SyncResults + { + SyncResultsHandler _syncResultsHandler; + string _serverName; + bool _useGlobalCatalog; + + public SyncResults(SyncResultsHandler syncResultsHandler, + bool useGlobalCatalog, string serverName) { + _syncResultsHandler = syncResultsHandler; + _serverName = serverName; + _useGlobalCatalog = false; + } + + public bool SyncHandler(ConnectorObject obj) + { + SyncDeltaBuilder builder = new SyncDeltaBuilder(); + + // for some reason, this cannot contain a uid, + // if it does, the builder throws an exception + ICollection attributes = obj.GetAttributes(); + foreach (ConnectorAttribute attribute in attributes) + { + if (!Uid.NAME.Equals(attribute.Name, StringComparison.CurrentCultureIgnoreCase)) + { + builder.AddAttribute(attribute); + } + } + + ConnectorAttribute tokenAttr = + ConnectorAttributeUtil.Find(ATT_USN_CHANGED, obj.GetAttributes()); + if(tokenAttr == null) { + string msg = String.Format("Attribute {0} is not present " + + "in connector object. Cannot proceed with synchronization", ATT_USN_CHANGED); + Trace.TraceError(msg); + throw new ConnectorException(msg); + } + long tokenUsnValue = (long)ConnectorAttributeUtil.GetSingleValue(tokenAttr); + string tokenServerName = _serverName; + + SyncToken token = new SyncToken(String.Format("{0}|{1}|{2}", + tokenUsnValue, _useGlobalCatalog, tokenServerName)); + builder.Token = token; + + bool? isDeleted = false; + ConnectorAttribute isDeletedAttr = + ConnectorAttributeUtil.Find(ATT_IS_DELETED, obj.GetAttributes()); + if (isDeletedAttr != null) + { + isDeleted = (bool?)ConnectorAttributeUtil.GetSingleValue(isDeletedAttr); + } + + if ((isDeleted != null) && (isDeleted.Equals(true))) + { + builder.DeltaType = SyncDeltaType.DELETE; + } + else + { + builder.DeltaType = SyncDeltaType.UPDATE; + } + + builder.Uid = obj.Uid; + _syncResultsHandler(builder.Build()); + return true; + } + } + + public void Sync(ObjectClass objClass, SyncToken token, + SyncResultsHandler handler, OperationOptions options) + { + string query = null; + string serverName = null; + bool useGlobalCatalog = false; + + if (_configuration.SearchChildDomains) + { + serverName = _configuration.SyncGlobalCatalogServer; + useGlobalCatalog = true; + } + else + { + serverName = _configuration.SyncDomainController; + } + + if ((serverName == null) || (serverName.Length == 0)) + { + Trace.TraceWarning("No server was configured for synchronization, so picking one. You should configure a server for best performance."); + // we have to know which server we are working against, + // so find one. + if (useGlobalCatalog) + { + DirectoryContext context = new DirectoryContext( + DirectoryContextType.Forest, _configuration.DomainName, + _configuration.DirectoryAdminName, + _configuration.DirectoryAdminPassword); + GlobalCatalog gc = GlobalCatalog.FindOne(context); + _configuration.SyncGlobalCatalogServer = gc.ToString(); + serverName = _configuration.SyncGlobalCatalogServer; + } + else + { + DirectoryContext context = new DirectoryContext( + DirectoryContextType.Domain, _configuration.DomainName, + _configuration.DirectoryAdminName, + _configuration.DirectoryAdminPassword); + DomainController controller = DomainController.FindOne(context); + _configuration.SyncDomainController = controller.ToString(); + serverName = _configuration.SyncDomainController; + } + } + + + + // if the token is not null, we may be able to start from + // the usn contained there + if (token != null) + { + string[] tokenParts = ((string)(token.Value)).Split('|'); + bool tokenUseGlobalCatalog = bool.Parse(tokenParts[1]); + string tokenServerName = tokenParts[2]; + + // If the token server is the same as the configured server, + // use the token value (usn) to limit the query. The token is + // server specific though, so we cant use the usn if it didn't come + // from this server. + // If no server is configured, just try to use what we used last time. + if (tokenServerName != null) + { + if ((serverName == null) || + (tokenServerName.Equals(serverName)) || (serverName.Length == 0)) + { + // add usnchanged >= token to search ... active directory doesn't support + // > just >=, so add one, and then search for >= + long nextUsn = long.Parse(tokenParts[0]); + nextUsn++; + query = string.Format("({0}>={1})", ATT_USN_CHANGED, nextUsn); + } + } + } + + OperationOptionsBuilder builder = new OperationOptionsBuilder(); + SyncResults syncResults = new SyncResults(handler, useGlobalCatalog, serverName); + + ExecuteQuery(objClass, query, syncResults.SyncHandler, builder.Build(), + true, new SortOption(ATT_USN_CHANGED, SortDirection.Ascending), + serverName, useGlobalCatalog, _configuration.SyncSearchContext); + } + + #endregion + + #region AuthenticateOp Members + + public void Authenticate(string username, + Org.IdentityConnectors.Common.Security.GuardedString password, + OperationOptions options) + { + PasswordChangeHandler handler = new PasswordChangeHandler(); + String ldapEntryPath = ActiveDirectoryUtils.GetLDAPPath(_configuration.LDAPHostName, + username); + + DirectoryEntry directoryEntry = new DirectoryEntry(ldapEntryPath, + _configuration.DirectoryAdminName, _configuration.DirectoryAdminPassword); + + handler.Authenticate(directoryEntry, username, password); + } + + #endregion + + #region AttributeNormalizer Members + + public ConnectorAttribute NormalizeAttribute(ObjectClass oclass, ConnectorAttribute attribute) + { + // if this gets big, use deleagates, but for now, just + // handle individual attirbutes; + if (attribute is Uid) + { + StringBuilder normalizedUidValue = new StringBuilder(); + String uidValue = ((Uid)attribute).GetUidValue(); + // convert to upper case + if (uidValue != null) + { + uidValue = uidValue.ToUpper(); + + // now remove spaces + foreach(Char nextChar in uidValue) { + if(!nextChar.Equals(" ")) { + normalizedUidValue.Append(nextChar); + } + } + + return new Uid(normalizedUidValue.ToString()); + } + else + { + return attribute; + } + } + else if (attribute is Name) + { + String nameValue = ((Name)attribute).GetNameValue(); + return ConnectorAttributeBuilder.Build(attribute.Name, + ActiveDirectoryUtils.NormalizeLdapString(nameValue)); + } + + return attribute; + } + + #endregion + } +} diff --git a/ActiveDirectoryConnector/ActiveDirectoryConnector.csproj b/ActiveDirectoryConnector/ActiveDirectoryConnector.csproj new file mode 100644 index 00000000..6565cfaf --- /dev/null +++ b/ActiveDirectoryConnector/ActiveDirectoryConnector.csproj @@ -0,0 +1,121 @@ + + + + {BDF495CA-0FCD-4E51-A871-D467CDE3B43E} + Debug + AnyCPU + Library + Org.IdentityConnectors.ActiveDirectory + ActiveDirectory.Connector + v3.5 + + + bin\Debug\ + True + Full + False + True + DEBUG;TRACE + + + bin\Release\ + False + None + True + False + TRACE + + + + + + 3.5 + + + + + + 3.5 + + + + + + Code + + + + + + + + + + + + {F140E8DA-52B4-4159-992A-9DA10EA8EEFB} + Common + + + {8B24461B-456A-4032-89A1-CD418F7B5B62} + Framework + + + {97d25db0-0363-11cf-abc4-02608c9e7553} + 1 + 0 + 0 + tlbimp + False + + + {97d25db0-0363-11cf-abc4-02608c9e7553} + 1 + 0 + 0 + tlbimp + False + + + + + Designer + + + \ No newline at end of file diff --git a/ActiveDirectoryConnector/ActiveDirectoryFilterTranslator.cs b/ActiveDirectoryConnector/ActiveDirectoryFilterTranslator.cs new file mode 100644 index 00000000..f93cc20c --- /dev/null +++ b/ActiveDirectoryConnector/ActiveDirectoryFilterTranslator.cs @@ -0,0 +1,471 @@ +/* + * Copyright 2008 Sun Microsystems, Inc. All rights reserved. + * + * U.S. Government Rights - Commercial software. Government users + * are subject to the Sun Microsystems, Inc. standard license agreement + * and applicable provisions of the FAR and its supplements. + * + * Use is subject to license terms. + * + * This distribution may include materials developed by third parties. + * Sun, Sun Microsystems, the Sun logo, Java and Project Identity + * Connectors are trademarks or registered trademarks of Sun + * Microsystems, Inc. or its subsidiaries in the U.S. and other + * countries. + * + * UNIX is a registered trademark in the U.S. and other countries, + * exclusively licensed through X/Open Company, Ltd. + * + * ----------- + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER. + * + * Copyright 2008 Sun Microsystems, Inc. All rights reserved. + * + * The contents of this file are subject to the terms of the Common Development + * and Distribution License(CDDL) (the License). You may not use this file + * except in compliance with the License. + * + * You can obtain a copy of the License at + * http://identityconnectors.dev.java.net/CDDLv1.0.html + * See the License for the specific language governing permissions and + * limitations under the License. + * + * When distributing the Covered Code, include this CDDL Header Notice in each + * file and include the License file at identityconnectors/legal/license.txt. + * If applicable, add the following below this CDDL Header, with the fields + * enclosed by brackets [] replaced by your own identifying information: + * "Portions Copyrighted [year] [name of copyright owner]" + * ----------- + */ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using Org.IdentityConnectors.Framework.Common.Objects.Filters; +using Org.IdentityConnectors.Framework.Common.Objects; +using Org.IdentityConnectors.Common; +using System.Diagnostics; +using Org.IdentityConnectors.Framework.Common.Exceptions; + +namespace Org.IdentityConnectors.ActiveDirectory +{ + /// + /// This was taken from the LDAP filter translator (java) and ported to + /// C#. There are a few changes, but not many ... that will change over + /// time of course. + /// + class ActiveDirectoryFilterTranslator : AbstractFilterTranslator + { + protected override String CreateAndExpression(String leftExpression, + String rightExpression) { + StringBuilder builder = new StringBuilder(); + builder.Append("(&"); + builder.Append(leftExpression); + builder.Append(rightExpression); + builder.Append(')'); + return builder.ToString(); + } + + protected override String CreateOrExpression(String leftExpression, + String rightExpression) { + StringBuilder builder = new StringBuilder(); + builder.Append("(|"); + builder.Append(leftExpression); + builder.Append(rightExpression); + builder.Append(')'); + return builder.ToString(); + } + + protected override String CreateContainsExpression(ContainsFilter filter, + Boolean not) { + String[] attrNames = GetLdapNamesForAttribute(filter.GetAttribute()); + if (attrNames == null) { + return null; + } + + StringBuilder builder = new StringBuilder(); + if (not) { + builder.Append("!("); + } + if (attrNames.Length == 1) { + builder.Append('('); + builder.Append(attrNames[0]); + builder.Append("=*"); + int len = builder.Length; + GetLdapFilterValue(builder, attrNames[0], filter.GetValue()); + // Build (attr=*) rather than (attr=**) for zero-length values. + if (builder.Length != len) { + builder.Append('*'); + } + builder.Append(')'); + } else { + builder.Append("(|"); + foreach (String attrName in attrNames) { + builder.Append('('); + builder.Append(attrName); + builder.Append("=*"); + int len = builder.Length; + GetLdapFilterValue(builder, attrName, filter.GetValue()); + // Build (attr=*) rather than (attr=**) for zero-length values. + if (builder.Length != len) { + builder.Append('*'); + } + builder.Append(')'); + } + builder.Append(')'); + } + if (not) { + builder.Append(')'); + } + return builder.ToString(); + } + + protected override String CreateStartsWithExpression(StartsWithFilter filter, + Boolean not) { + String[] attrNames = GetLdapNamesForAttribute(filter.GetAttribute()); + if (attrNames == null) { + return null; + } + + StringBuilder builder = new StringBuilder(); + if (not) { + builder.Append("!("); + } + if (attrNames.Length == 1) { + builder.Append('('); + builder.Append(attrNames[0]); + builder.Append('='); + GetLdapFilterValue(builder, attrNames[0], filter.GetValue()); + builder.Append("*)"); + } else { + builder.Append("(|"); + foreach (String attrName in attrNames) { + builder.Append('('); + builder.Append(attrName); + builder.Append('='); + GetLdapFilterValue(builder, attrName, filter.GetValue()); + builder.Append("*)"); + } + builder.Append(')'); + } + if (not) { + builder.Append(')'); + } + return builder.ToString(); + } + + protected override String CreateEndsWithExpression(EndsWithFilter filter, + Boolean not) { + String[] attrNames = GetLdapNamesForAttribute(filter.GetAttribute()); + if (attrNames == null) { + return null; + } + + StringBuilder builder = new StringBuilder(); + if (not) { + builder.Append("!("); + } + if (attrNames.Length == 1) { + builder.Append('('); + builder.Append(attrNames[0]); + builder.Append("=*"); + GetLdapFilterValue(builder, attrNames[0], filter.GetValue()); + builder.Append(')'); + } else { + builder.Append("(|"); + foreach (String attrName in attrNames) { + builder.Append('('); + builder.Append(attrName); + builder.Append("=*"); + GetLdapFilterValue(builder, attrName, filter.GetValue()); + builder.Append(')'); + } + builder.Append(')'); + } + if (not) { + builder.Append(')'); + } + return builder.ToString(); + } + + protected override String CreateEqualsExpression(EqualsFilter filter, Boolean not) { + // The LDAP equality filter matches any one attribute value, + // whereas the connector EqualsFilter matches an attribute and + // its values exactly. + if (not) { + return null; + } + + ConnectorAttribute attr = filter.GetAttribute(); + // if there is only one thing to search on, and it's + // a uid we need to convert the uid to something we + // can search on. NOTE: only handling the case where + // we are doing an equality search, and only one item + // is in the equality search ... It's all that makes + // sense for uid. + if (attr is Uid) + { + String searchGuid = GetUidSearchString(((Uid)attr).GetUidValue()); + attr = new Uid(searchGuid); + } + + + String[] attrNames = GetLdapNamesForAttribute(attr); + if (attrNames == null) { + return null; + } + + StringBuilder builder = new StringBuilder(); + + if (attr.Value == null) { + return null; + } + if (attr.Value.Count == 1) { + BuildEqualityFilter(builder, attrNames, + attr.Value[0]); + } else { + builder.Append("(&"); + foreach (Object value in attr.Value) { + BuildEqualityFilter(builder, attrNames, value); + } + builder.Append(')'); + } + + return builder.ToString(); + } + + protected override String CreateGreaterThanExpression(GreaterThanFilter filter, + Boolean not) { + // Note that (!(a > X)) is only the same as (a <= X) if every object + // has a value of a. + if (not) { + return null; + } + + String[] attrNames = GetLdapNamesForAttribute(filter.GetAttribute()); + if (attrNames == null) { + return null; + } + + StringBuilder builder = new StringBuilder(); + BuildGreaterOrEqualFilter(builder, attrNames, filter.GetValue()); + return builder.ToString(); + } + + protected override String CreateGreaterThanOrEqualExpression( + GreaterThanOrEqualFilter filter, Boolean not) { + String[] attrNames = GetLdapNamesForAttribute(filter.GetAttribute()); + if (attrNames == null) { + return null; + } + + StringBuilder builder = new StringBuilder(); + if (not) { + builder.Append("!("); + } + BuildGreaterOrEqualFilter(builder, attrNames, filter.GetValue()); + if (not) { + builder.Append(')'); + } + return builder.ToString(); + } + + protected override String CreateLessThanExpression(LessThanFilter filter, + Boolean not) { + // Note that (!(a < X)) is only the same as (a >= X) if every object + // has a value of a. + if (not) { + return null; + } + + String[] attrNames = GetLdapNamesForAttribute(filter.GetAttribute()); + if (attrNames == null) { + return null; + } + + StringBuilder builder = new StringBuilder(); + BuildLessOrEqualFilter(builder, attrNames, filter.GetValue()); + return builder.ToString(); + } + + protected override String CreateLessThanOrEqualExpression( + LessThanOrEqualFilter filter, Boolean not) { + String[] attrNames = GetLdapNamesForAttribute(filter.GetAttribute()); + if (attrNames == null) { + return null; + } + + StringBuilder builder = new StringBuilder(); + if (not) { + builder.Append("!("); + } + BuildLessOrEqualFilter(builder, attrNames, filter.GetValue()); + if (not) { + builder.Append(')'); + } + return builder.ToString(); + } + + /** + * Get the string representation of an attribute value suitable for + * embedding in an LDAP search filter (RFC 2254 / RFC 4515). + * + * @param builder A string builder on to which a suitably escaped attribute + * value will be appended. + * + * @param value The attribute value to be embedded. + */ + static void GetLdapFilterValue(StringBuilder builder, + String AttributeName, Object value) { + // at this point, this can probably go away + // it was here to properyly escape queries, but + // it doesn't seem that they need escaping. + if (value == null) + { + return; + } + else + { + builder.Append(value); + } + } + + /** + * Get the LDAP name or names for a given connector attribute used in a + * search filter. + * + * @param attr The connector attribute used in a search filter. + * + * @return The name or names of the corresponding LDAP attribute. + * Returns null if the attribute cannot be specified in an LDAP + * filter. + */ + String[] GetLdapNamesForAttribute(ConnectorAttribute attr) { + // Special processing for certain connector attributes. + String[] attrNames = null; + if (attr is Uid) { + /* + attrNames = new String[] { + configCache.getConfiguration().getUuidAttribute() }; + */ + attrNames = new String[] { "objectGUID" }; + } else if (attr is Name) { + /* + attrNames = configCache.getNamingAttributes(); + */ + attrNames = new String [] { "distinguishedName" }; + } else if (attr.Is(OperationalAttributes.PASSWORD_NAME)) { + /* + attrNames = new String[] { + configCache.getConfiguration().getPasswordAttribute() + }; + */ + attrNames = new String[] { "userPassword" }; + } else if (ConnectorAttributeUtil.IsSpecial(attr)) { + return null; + } else { + attrNames = new String[] { attr.Name }; + } + + return attrNames; + } + + static void BuildEqualityFilter(StringBuilder builder, + String[] attrNames, + Object attrValue) { + if (attrNames.Length == 1) { + builder.Append('('); + builder.Append(attrNames[0]); + builder.Append('='); + GetLdapFilterValue(builder, attrNames[0], attrValue); + builder.Append(')'); + } else { + builder.Append("(|"); + foreach (String attrName in attrNames) { + builder.Append('('); + builder.Append(attrName); + builder.Append('='); + GetLdapFilterValue(builder, attrName, attrValue); + builder.Append(')'); + } + builder.Append(')'); + } + } + + static void BuildGreaterOrEqualFilter(StringBuilder builder, + String[] attrNames, + Object attrValue) { + if (attrNames.Length == 1) { + builder.Append('('); + builder.Append(attrNames[0]); + builder.Append(">="); + GetLdapFilterValue(builder, attrNames[0], attrValue); + builder.Append(')'); + } else { + builder.Append("(|"); + foreach (String attrName in attrNames) { + builder.Append('('); + builder.Append(attrName); + builder.Append(">="); + GetLdapFilterValue(builder, attrName, attrValue); + builder.Append(')'); + } + builder.Append(')'); + } + } + + static void BuildLessOrEqualFilter(StringBuilder builder, + String[] attrNames, + Object attrValue) { + if (attrNames.Length == 1) { + builder.Append('('); + builder.Append(attrNames[0]); + builder.Append("<="); + GetLdapFilterValue(builder, attrNames[0], attrValue); + builder.Append(')'); + } else { + builder.Append("(|"); + foreach (String attrName in attrNames) { + builder.Append('('); + builder.Append(attrName); + builder.Append("<="); + GetLdapFilterValue(builder, attrName, attrValue); + builder.Append(')'); + } + builder.Append(')'); + } + } + + // This is called to fix up UID values which are in the + // format , but need to be in the + // format \\xx\\xx\\xx... + static internal string GetUidSearchString(string uidString) + { + // be tolerant of whitespace between '<' and "GUID", + // and between "GUID" and '=' and between '=' and + // start of guidstring, and between start of guidstring + // and '>' + string uidSearchString = ""; + string[] uidStringParts = uidString.Split('='); + if ((uidStringParts.Length != 2) || (uidStringParts[1] == null)) + { + throw new ConnectorException("Uid is not in the expected format"); + } + uidSearchString = uidStringParts[1].Trim(); + + // take off the final '>' + uidSearchString = uidSearchString.Substring(0, uidSearchString.IndexOf('>')); + + // now put the '\' characters in + string escapedSearchString = ""; + for(int position = 0;position < uidSearchString.Length;position++) { + if(position % 2 == 0) { + escapedSearchString += "\\"; + } + escapedSearchString += uidSearchString[position]; + } + + return escapedSearchString; + } + } +} diff --git a/ActiveDirectoryConnector/ActiveDirectoryUtils.cs b/ActiveDirectoryConnector/ActiveDirectoryUtils.cs new file mode 100644 index 00000000..e07d7202 --- /dev/null +++ b/ActiveDirectoryConnector/ActiveDirectoryUtils.cs @@ -0,0 +1,595 @@ +/* + * Copyright 2008 Sun Microsystems, Inc. All rights reserved. + * + * U.S. Government Rights - Commercial software. Government users + * are subject to the Sun Microsystems, Inc. standard license agreement + * and applicable provisions of the FAR and its supplements. + * + * Use is subject to license terms. + * + * This distribution may include materials developed by third parties. + * Sun, Sun Microsystems, the Sun logo, Java and Project Identity + * Connectors are trademarks or registered trademarks of Sun + * Microsystems, Inc. or its subsidiaries in the U.S. and other + * countries. + * + * UNIX is a registered trademark in the U.S. and other countries, + * exclusively licensed through X/Open Company, Ltd. + * + * ----------- + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER. + * + * Copyright 2008 Sun Microsystems, Inc. All rights reserved. + * + * The contents of this file are subject to the terms of the Common Development + * and Distribution License(CDDL) (the License). You may not use this file + * except in compliance with the License. + * + * You can obtain a copy of the License at + * http://identityconnectors.dev.java.net/CDDLv1.0.html + * See the License for the specific language governing permissions and + * limitations under the License. + * + * When distributing the Covered Code, include this CDDL Header Notice in each + * file and include the License file at identityconnectors/legal/license.txt. + * If applicable, add the following below this CDDL Header, with the fields + * enclosed by brackets [] replaced by your own identifying information: + * "Portions Copyrighted [year] [name of copyright owner]" + * ----------- + */ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using Org.IdentityConnectors.Framework.Common.Objects; +using System.DirectoryServices; +using Org.IdentityConnectors.Framework.Common.Exceptions; +using System.Diagnostics; +using Org.IdentityConnectors.Framework.Common; +using Org.IdentityConnectors.Framework.Spi.Operations; +using System.Security; +using ActiveDs; +using Org.IdentityConnectors.Common.Security; + +namespace Org.IdentityConnectors.ActiveDirectory +{ + /// + /// Collection of Active directory utilities. Some are static methods, + /// other require configuration, so they are instance methods. + /// + public class ActiveDirectoryUtils + { + ActiveDirectoryConfiguration _configuration = null; + private CustomAttributeHandlers _customHandlers = null; + + /// + /// Constructor + /// + /// + /// Configuration object for the connector. + /// + public ActiveDirectoryUtils(ActiveDirectoryConfiguration configuration) + { + _configuration = configuration; + _customHandlers = new CustomAttributeHandlers(_configuration); + } + + /// + /// Converts a guid in byte array form to a string suitable + /// for ldap search. + /// + /// + /// + internal static String ConvertUIDBytesToSearchString(Byte[] guidBytes) + { + String searchGuid = ""; + + for (int i = 0; i < guidBytes.Length; i++) + { + searchGuid += String.Format("\\{0:X2}", guidBytes[i]); + } + + return searchGuid; + } + + /// + /// Converts a guid in byte array form to a string with the format + /// >GUID = xxxxxxxxxxxxxxxxxxxxxxxxxxxxx< where the x's represent + /// uppercase hexadecimal digits + /// + /// + /// + internal static String ConvertUIDBytesToGUIDString(Byte[] guidBytes) + { + String guidString = " + /// Returns an ldap path in the form of: + /// LDAP://servernameIfSpecified/path + /// + /// Servername can be null + /// Path should not be null + /// + internal static String GetLDAPPath(string serverName, string path) + { + return GetFullPath("LDAP", serverName, path); + } + + /// + /// Returns a path string in the format: + /// GC://servernameIfSpecified/path + /// + /// Servername is optional + /// Path should be specified + /// + internal static String GetGCPath(string serverName, string path) + { + return GetFullPath("GC", serverName, path); + } + + /// + /// Returns a path string in the format: + /// provider://servernameIfSpecified/path + /// + /// provider (such as ldap or gc) + /// servername - optional + /// path to resource + /// + internal static String GetFullPath(string provider, string serverName, string path) + { + IADsPathname pathName = getADSPathname(provider, serverName, path); + return pathName.Retrieve((int)ADS_FORMAT_ENUM.ADS_FORMAT_X500); + } + + /// + /// uses iadspathname to create paths in a standard way + /// + /// + /// + /// + /// + internal static IADsPathname getADSPathname(string provider, string serverName, string path) + { + IADsPathname pathName = new PathnameClass(); + if ((provider != null) && (provider.Length != 0)) + { + pathName.Set(provider, (int)ADS_SETTYPE_ENUM.ADS_SETTYPE_PROVIDER); + } + + if ((serverName != null) && (serverName.Length != 0)) + { + pathName.Set(serverName, (int)ADS_SETTYPE_ENUM.ADS_SETTYPE_SERVER); + } + + if ((path != null) && (path.Length != 0)) + { + // must supply a path + pathName.Set(path, (int)ADS_SETTYPE_ENUM.ADS_SETTYPE_DN); + } + return pathName; + } + + /// + /// Gets the dn of the parent object of the object specified by childDn + /// + /// distinguished name of an object to retrieve the parent of + /// distinguished name of the parent of 'childDn' or null + internal static string GetParentDn(string childDn) + { + IADsPathname pathName = getADSPathname(null, null, childDn); + return pathName.Retrieve((int)ADS_FORMAT_ENUM.ADS_FORMAT_X500_PARENT); + } + + /// + /// Updates an AD object (also called by create after object is created) + /// + /// + /// + /// + /// + /// + internal void UpdateADObject(ObjectClass oclass, + DirectoryEntry directoryEntry, ICollection attributes, + UpdateType type, ActiveDirectoryConfiguration config) + { + if(oclass.Equals(ObjectClass.ACCOUNT)) + { + // translate attribute passed in + foreach (ConnectorAttribute attribute in attributes) + { + // Temporary + // Trace.TraceInformation(String.Format("Setting attribute {0} to {1}", + // attribute.Name, attribute.Value)); + AddConnectorAttributeToADProperties(oclass, + directoryEntry, attribute, type); + // Uncommenting the next line is very helpful in + // finding mysterious errors. + // directoryEntry.CommitChanges(); + } + + directoryEntry.CommitChanges(); + + // now do the password change. This is handled separately, because + // it might be a user changing his own password, or it might be an + // administrative change. + + GuardedString gsNewPassword = ConnectorAttributeUtil.GetPasswordValue(attributes); + if (gsNewPassword != null) + { + GuardedString gsCurrentPassword = ConnectorAttributeUtil.GetCurrentPasswordValue(attributes); + PasswordChangeHandler changeHandler = new PasswordChangeHandler(); + if (gsCurrentPassword == null) + { + // just a normal password change + changeHandler.changePassword(directoryEntry, gsNewPassword); + } + else + { + changeHandler.changePassword(directoryEntry, + gsCurrentPassword, gsNewPassword); + } + + directoryEntry.CommitChanges(); + } + + HandleNameChange(type, directoryEntry, attributes); + HandleContainerChange(type, directoryEntry, attributes, config); + } + else if (oclass.Equals(ObjectClass.GROUP)) + { + // translate attribute passed in + foreach (ConnectorAttribute attribute in attributes) + { + // Temporary + // Trace.TraceInformation(String.Format("Setting attribute {0} to {1}", + // attribute.Name, attribute.Value)); + AddConnectorAttributeToADProperties(oclass, + directoryEntry, attribute, type); + // Uncommenting the next line is very helpful in + // finding mysterious errors. + // directoryEntry.CommitChanges(); + } + + directoryEntry.CommitChanges(); + HandleNameChange(type, directoryEntry, attributes); + HandleContainerChange(type, directoryEntry, attributes, config); + } + else + { + throw new ConnectorException( + String.Format("Invalid object class: {0}", oclass.GetObjectClassValue())); + } + } + + internal ConnectorAttribute GetConnectorAttributeFromADEntry(ObjectClass oclass, + String attributeName, SearchResult searchResult) + { + // Boolean translated = false; + if (searchResult == null) + { + throw new ConnectorException("Could not add connector attribute to search result"); + } + + return _customHandlers.GetCaFromDe(oclass, + attributeName, searchResult); + + } + + internal void AddConnectorAttributeToADProperties(ObjectClass oclass, + DirectoryEntry directoryEntry, ConnectorAttribute attribute, + UpdateType type) + { + // Boolean translated = false; + if (directoryEntry == null) + { + throw new ConnectorException("Could not add connector attribute to directory entry"); + } + + _customHandlers.UpdateDeFromCa(oclass, type, + directoryEntry, attribute); + + } + + /* + /// + /// creates and returns a connector attribute or null. the attribute + /// has the name 'name' and the values associated with 'name' in the + /// directory entry + /// + /// + /// + /// + private static ConnectorAttribute CreateConnectorAttribute(String name, + PropertyValueCollection pvc) + { + ConnectorAttributeBuilder attributeBuilder = new ConnectorAttributeBuilder(); + + if (name == null) + { + return null; + } + + attributeBuilder.Name = name; + + if (pvc == null) + { + attributeBuilder.AddValue(null); + } + else + { + for (int i = 0; i < pvc.Count; i++) + { + Object valueObject = pvc[i]; + if ((pvc[i] == null) || + (FrameworkUtil.IsSupportedAttributeType(valueObject.GetType()))) + { + attributeBuilder.AddValue(pvc[i]); + } + else + { + Trace.TraceWarning( + "Unsupported attribute type ... calling ToString (Name: \'{0}\'({1}) Type: \'{2}\' String Value: \'{3}\'", + name, i, pvc[i].GetType(), pvc[i].ToString()); + attributeBuilder.AddValue(pvc[i].ToString()); + } + } + } + + return attributeBuilder.Build(); + } + + private static void AddConnectorAttributeToADProperties_general( + PropertyCollection properties, + ConnectorAttribute attribute, UpdateType type) + { + // null out the values if we are deleting + // or replacing attributes. + if (type.Equals(UpdateType.DELETE) || + type.Equals(UpdateType.REPLACE)) + { + properties[attribute.Name].Value = null; + } + + // if we are updating or adding, put the + // new values in. + if (type.Equals(UpdateType.ADD) || + type.Equals(UpdateType.REPLACE)) + { + foreach (Object valueObject in attribute.Value) + { + properties[attribute.Name].Add(valueObject); + } + } + } + */ + + /// + /// Gets a single value from a propertyvaluecollection + /// for a particular property name. Its an error if the + /// property contains multiple values. + /// + /// + /// + internal static Object GetSingleValue(PropertyValueCollection pvc) + { + if((pvc == null) || (pvc.Count == 0)) + { + return null; + } + + if (pvc.Count > 1) + { + String msg = String.Format("Expecting single value, but found multiple values for attribute {0}", + pvc.PropertyName); + throw new ConnectorException(msg); + } + + return pvc[0]; + } + + /// + /// Finds a DirectoryEntry by it's uid + /// + /// + /// + /// + /// + /// + internal static DirectoryEntry GetDirectoryEntryFromUid(String serverName, + Uid uid, string adminUserName, string adminPassword) + { + DirectoryEntry foundDirectoryEntry = new DirectoryEntry( + ActiveDirectoryUtils.GetLDAPPath(serverName, uid.GetUidValue()), + adminUserName, adminPassword); + string dn = (string)foundDirectoryEntry.Properties["distinguishedName"][0]; + foundDirectoryEntry = new DirectoryEntry( + ActiveDirectoryUtils.GetLDAPPath(serverName, dn), + adminUserName, adminPassword); + return foundDirectoryEntry; + } + + /// + /// Returns the AD ObjectClass associated with a particular + /// Connector ObjectClass + /// + /// + /// + internal static String GetADObjectClass(ObjectClass oclass) + { + + if (oclass.Equals(ObjectClass.ACCOUNT)) + { + return "User"; + } + else if (oclass.Equals(ObjectClass.GROUP)) + { + return "Group"; + } + else if ("ORGANIZATIONALUNIT".Equals(oclass.GetObjectClassValue(), StringComparison.CurrentCultureIgnoreCase)) + { + return "organizationalUnit"; + } + else + { + String msg = String.Format("ObjectClass \'{0}\' is not valid for this connector", + oclass.GetObjectClassValue()); + throw new ConnectorException(msg); + } + } + + /// + /// Puts an ldap string into a normalilzed format + /// + /// + /// + public static String NormalizeLdapString(String ldapString) + { + StringBuilder normalPath = new StringBuilder(); + String[] parts = ldapString.Split(','); + for (int i = 0; i < parts.Length; i++) + { + normalPath.Append(parts[i].Trim().ToUpper()); + // append a comma after each part (except the last one) + if (i < (parts.Length - 1)) + { + normalPath.Append(","); + } + } + return normalPath.ToString(); + } + + public static String GetRelativeName(Name name) + { + return GetNameAsCN(name.GetNameValue()); + } + + /// + /// Returns the leaf value of a distinguished name + /// + /// + /// + internal static String GetNameAsCN(String nameValue) + { + IADsPathname pathName = getADSPathname(null, null, nameValue); + return pathName.Retrieve((int)ADS_FORMAT_ENUM.ADS_FORMAT_LEAF); + } + + /// + /// This does not work ... for now, don't handle container changes + /// + /// + /// + /// + /// + private static void HandleContainerChange(UpdateType type, + DirectoryEntry directoryEntry, ICollection attributes, + ActiveDirectoryConfiguration config) + { + // this return means that te connector attribute is ignored for + // the purpose of moving an object to a different container + return; + + // this code seems right, but doesnt work. The DirectoryEntry.Move() + // method always throws an Exception with the text 'unspecified error' + + ConnectorAttribute containerAttribute = + ConnectorAttributeUtil.Find(ActiveDirectoryConnector.ATT_CONTAINER, attributes); + if (containerAttribute != null) + { + // this only make sense for replace. you can't + // add a name or delete a name + if (type.Equals(UpdateType.REPLACE)) + { + DirectoryEntry parent = directoryEntry.Parent; + String oldContainer = null; + if (parent != null) + { + PropertyValueCollection parentDNValues = + parent.Properties[ActiveDirectoryConnector.ATT_DISTINGUISHED_NAME]; + if ((parentDNValues.Count == 1) && (parentDNValues[0] is String)) + { + oldContainer = (String)parentDNValues[0]; + } + else + { + String msg = String.Format("Unable to retrieve the distinguished name for {0}.", + parent.Path); + throw new ConnectorException(msg); + } + } + + String newContainer = ConnectorAttributeUtil.GetStringValue(containerAttribute); + + if (newContainer != null) + { + try + { + if (!NormalizeLdapString(oldContainer).Equals( + NormalizeLdapString(newContainer))) + { + DirectoryEntry newContainerDe = new DirectoryEntry(newContainer, + config.DirectoryAdminName, config.DirectoryAdminPassword); + directoryEntry.MoveTo(newContainerDe); + } + } + catch (Exception e) + { + throw e; + } + } + } + } + } + + private static void HandleNameChange(UpdateType type, + DirectoryEntry directoryEntry, + ICollection attributes) + { + Name nameAttribute = ConnectorAttributeUtil.GetNameFromAttributes(attributes); + if (nameAttribute != null) + { + // this only make sense for replace. you can't + // add a name or delete a name + if (type.Equals(UpdateType.REPLACE)) + { + String oldName = directoryEntry.Name; + String newName = GetRelativeName(nameAttribute); + if (!NormalizeLdapString(oldName).Equals(NormalizeLdapString(newName))) + { + directoryEntry.Rename(newName); + } + } + } + } + + public static SecureString GetSecureString(String stringToSecure) + { + SecureString secure = new SecureString(); + + foreach (char nextChar in stringToSecure) + { + secure.AppendChar(nextChar); + } + + return secure; + } + + internal static string GetDnFromPath(string fullPath) + { + IADsPathname pathName = new PathnameClass(); + pathName.Set(fullPath, (int)ADS_SETTYPE_ENUM.ADS_SETTYPE_FULL); + return pathName.Retrieve((int)ADS_FORMAT_ENUM.ADS_FORMAT_X500_DN); + } + + } + +} diff --git a/ActiveDirectoryConnector/AssemblyInfo.cs b/ActiveDirectoryConnector/AssemblyInfo.cs new file mode 100644 index 00000000..fa480ab5 --- /dev/null +++ b/ActiveDirectoryConnector/AssemblyInfo.cs @@ -0,0 +1,71 @@ +/* + * Copyright 2008 Sun Microsystems, Inc. All rights reserved. + * + * U.S. Government Rights - Commercial software. Government users + * are subject to the Sun Microsystems, Inc. standard license agreement + * and applicable provisions of the FAR and its supplements. + * + * Use is subject to license terms. + * + * This distribution may include materials developed by third parties. + * Sun, Sun Microsystems, the Sun logo, Java and Project Identity + * Connectors are trademarks or registered trademarks of Sun + * Microsystems, Inc. or its subsidiaries in the U.S. and other + * countries. + * + * UNIX is a registered trademark in the U.S. and other countries, + * exclusively licensed through X/Open Company, Ltd. + * + * ----------- + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER. + * + * Copyright 2008 Sun Microsystems, Inc. All rights reserved. + * + * The contents of this file are subject to the terms of the Common Development + * and Distribution License(CDDL) (the License). You may not use this file + * except in compliance with the License. + * + * You can obtain a copy of the License at + * http://identityconnectors.dev.java.net/CDDLv1.0.html + * See the License for the specific language governing permissions and + * limitations under the License. + * + * When distributing the Covered Code, include this CDDL Header Notice in each + * file and include the License file at identityconnectors/legal/license.txt. + * If applicable, add the following below this CDDL Header, with the fields + * enclosed by brackets [] replaced by your own identifying information: + * "Portions Copyrighted [year] [name of copyright owner]" + * ----------- + */ +#region Using directives + +using System; +using System.Reflection; +using System.Resources; +using System.Runtime.InteropServices; + +#endregion + +// General Information about an assembly is controlled through the following +// set of attributes. Change these attribute values to modify the information +// associated with an assembly. +[assembly: AssemblyTitle("ActiveDirectoryConnector")] +[assembly: AssemblyDescription("")] +[assembly: AssemblyConfiguration("")] +[assembly: AssemblyCompany("")] +[assembly: AssemblyProduct("ActiveDirectoryConnector")] +[assembly: AssemblyCopyright("")] +[assembly: AssemblyTrademark("")] +[assembly: AssemblyCulture("")] + +// This sets the default COM visibility of types in the assembly to invisible. +// If you need to expose a type to COM, use [ComVisible(true)] on that type. +[assembly: ComVisible(false)] + +// The assembly version has following format : +// +// Major.Minor.Build.Revision +// +// You can specify all the values or you can use the default the Revision and +// Build Numbers by using the '*' as shown below: +[assembly: AssemblyVersion("1.0.0.0")] diff --git a/ActiveDirectoryConnector/CustomAttributeHandlers.cs b/ActiveDirectoryConnector/CustomAttributeHandlers.cs new file mode 100644 index 00000000..414dddf3 --- /dev/null +++ b/ActiveDirectoryConnector/CustomAttributeHandlers.cs @@ -0,0 +1,981 @@ +/* + * Copyright 2008 Sun Microsystems, Inc. All rights reserved. + * + * U.S. Government Rights - Commercial software. Government users + * are subject to the Sun Microsystems, Inc. standard license agreement + * and applicable provisions of the FAR and its supplements. + * + * Use is subject to license terms. + * + * This distribution may include materials developed by third parties. + * Sun, Sun Microsystems, the Sun logo, Java and Project Identity + * Connectors are trademarks or registered trademarks of Sun + * Microsystems, Inc. or its subsidiaries in the U.S. and other + * countries. + * + * UNIX is a registered trademark in the U.S. and other countries, + * exclusively licensed through X/Open Company, Ltd. + * + * ----------- + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER. + * + * Copyright 2008 Sun Microsystems, Inc. All rights reserved. + * + * The contents of this file are subject to the terms of the Common Development + * and Distribution License(CDDL) (the License). You may not use this file + * except in compliance with the License. + * + * You can obtain a copy of the License at + * http://identityconnectors.dev.java.net/CDDLv1.0.html + * See the License for the specific language governing permissions and + * limitations under the License. + * + * When distributing the Covered Code, include this CDDL Header Notice in each + * file and include the License file at identityconnectors/legal/license.txt. + * If applicable, add the following below this CDDL Header, with the fields + * enclosed by brackets [] replaced by your own identifying information: + * "Portions Copyrighted [year] [name of copyright owner]" + * ----------- + */ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Security; +using Org.IdentityConnectors.Framework.Common.Objects; +using Org.IdentityConnectors.Framework.Common; +using Org.IdentityConnectors.Framework.Spi.Operations; +using System.DirectoryServices; +using Org.IdentityConnectors.Framework.Common.Exceptions; +using Org.IdentityConnectors.Common.Security; +using System.Diagnostics; +using ActiveDs; +using System.IO; +using System.Security.AccessControl; +using System.Security.Principal; + +namespace Org.IdentityConnectors.ActiveDirectory +{ + /// + /// This class will encapsulate all changes from AD attributes + /// to Connector attributes and from Connector attributes to + /// AD attributes. If attributes are more complex and can't be + /// handled here, add them to the appropriate ignore list, and handle + /// them in the AD Connector (or elsewhere). + /// + /// If it is a connector attribute that has the same name as the + /// ad attribute, and the value requires no translation, it will + /// be handled by the generic handler. If not, add a delegate for + /// either AD->Connector or for Connector->AD (or both). + /// + internal class CustomAttributeHandlers + { + // names from active directory attributes to ignore during + // generic translation + IList IgnoreADAttributeNames_account = new List(); + IList IgnoreADAttributeNames_group = new List(); + + // names from connector attributes to ignore during + // generic translation + IList IgnoreConnectorAttributeNames_account = new List(); + IList IgnoreConnectorAttributeNames_group = new List(); + + // method to update a directory entry from a connector attribute + Dictionary + UpdateDeFromCaDelegates = new Dictionary(StringComparer.CurrentCultureIgnoreCase); + + // method to get a connector attribute from a directory entry + Dictionary + GetCaFromDeDelegates = new Dictionary(StringComparer.CurrentCultureIgnoreCase); + + ActiveDirectoryConfiguration _configuration = null; + + internal CustomAttributeHandlers(ActiveDirectoryConfiguration configuration) { + // save the configuration + _configuration = configuration; + + // Connector attributes names to ignore for accounts + IgnoreConnectorAttributeNames_account.Add(Name.NAME); + IgnoreConnectorAttributeNames_account.Add(ActiveDirectoryConnector.ATT_CONTAINER); + IgnoreConnectorAttributeNames_account.Add(Uid.NAME); + IgnoreConnectorAttributeNames_account.Add(OperationalAttributes.PASSWORD_NAME); + IgnoreConnectorAttributeNames_account.Add(OperationalAttributes.CURRENT_PASSWORD_NAME); + + // Connector attributes names to ignore for groups + IgnoreConnectorAttributeNames_group.Add(Name.NAME); + IgnoreConnectorAttributeNames_group.Add(ActiveDirectoryConnector.ATT_CONTAINER); + IgnoreConnectorAttributeNames_group.Add(Uid.NAME); + + // methods to update a directory entry from a connectorattribute + UpdateDeFromCaDelegates.Add(PredefinedAttributes.ACCOUNTS_NAME, + UpdateDeFromCa_OpAtt_Accounts); + UpdateDeFromCaDelegates.Add(PredefinedAttributes.GROUPS_NAME, + UpdateDeFromCa_OpAtt_Groups); + UpdateDeFromCaDelegates.Add(ActiveDirectoryConnector.ATT_HOME_DIRECTORY, + UpdateDeFromCa_Att_HomeDirectory); + UpdateDeFromCaDelegates.Add(OperationalAttributes.ENABLE_NAME, + UpdateDeFromCa_OpAtt_Enable); + // supporting class not implemented in the framework + /* + UpdateDeFromCaDelegates.Add(OperationalAttributes.ENABLE_DATE_NAME, + UpdateDeFromCa_OpAtt_EnableDate); + UpdateDeFromCaDelegates.Add(OperationalAttributes.DISABLE_DATE_NAME, + UpdateDeFromCa_OpAtt_DisableDate); + */ + UpdateDeFromCaDelegates.Add(TerminalServicesUtils.TS_ALLOW_LOGON, + UpdateDeFromCa_Att_TSAllowLogon); + UpdateDeFromCaDelegates.Add(TerminalServicesUtils.TS_INITIAL_PROGRAM, + UpdateDeFromCa_Att_TSInitialProgram); + UpdateDeFromCaDelegates.Add(TerminalServicesUtils.TS_INITIAL_PROGRAM_DIR, + UpdateDeFromCa_Att_TSInitialProgramDir); + UpdateDeFromCaDelegates.Add(TerminalServicesUtils.TS_MAX_CONNECTION_TIME, + UpdateDeFromCa_Att_TSMaxConnectionTime); + UpdateDeFromCaDelegates.Add(TerminalServicesUtils.TS_MAX_DISCONNECTION_TIME, + UpdateDeFromCa_Att_TSMaxDisconnectionTime); + UpdateDeFromCaDelegates.Add(TerminalServicesUtils.TS_MAX_IDLE_TIME, + UpdateDeFromCa_Att_TSMaxIdleTime); + UpdateDeFromCaDelegates.Add(TerminalServicesUtils.TS_CONNECT_CLIENT_DRIVES_AT_LOGON, + UpdateDeFromCa_Att_TSConnectClientDrivesAtLogon); + UpdateDeFromCaDelegates.Add(TerminalServicesUtils.TS_CONNECT_CLIENT_PRINTERS_AT_LOGON, + UpdateDeFromCa_Att_TSConnectClientPrintersAtLogon); + UpdateDeFromCaDelegates.Add(TerminalServicesUtils.TS_DEFAULT_TO_MAIN_PRINTER, + UpdateDeFromCa_Att_TSDefaultToMainPrinter); + UpdateDeFromCaDelegates.Add(TerminalServicesUtils.TS_BROKEN_CONNECTION_ACTION, + UpdateDeFromCa_Att_TSBrokenConnectionAction); + UpdateDeFromCaDelegates.Add(TerminalServicesUtils.TS_RECONNECTION_ACTION, + UpdateDeFromCa_Att_TSReconnectionAction); + UpdateDeFromCaDelegates.Add(TerminalServicesUtils.TS_ENABLE_REMOTE_CONTROL, + UpdateDeFromCa_Att_TSEnableRemoteControl); + UpdateDeFromCaDelegates.Add(TerminalServicesUtils.TS_PROFILE_PATH, + UpdateDeFromCa_Att_TSProfilePath); + UpdateDeFromCaDelegates.Add(TerminalServicesUtils.TS_HOME_DIRECTORY, + UpdateDeFromCa_Att_TSHomeDirectory); + UpdateDeFromCaDelegates.Add(TerminalServicesUtils.TS_HOME_DRIVE, + UpdateDeFromCa_Att_TSHomeDrive); + + // methods to create a connector attribute from a directory entry + GetCaFromDeDelegates.Add(Name.NAME, GetCaFromDe_OpAtt_Name); + GetCaFromDeDelegates.Add(Uid.NAME, GetCaFromDe_OpAtt_Uid); + GetCaFromDeDelegates.Add(ActiveDirectoryConnector.ATT_CONTAINER, + GetCaFromDe_Att_Container); + GetCaFromDeDelegates.Add(PredefinedAttributes.ACCOUNTS_NAME, + GetCaFromDe_OpAtt_Accounts); + GetCaFromDeDelegates.Add(PredefinedAttributes.GROUPS_NAME, + GetCaFromDe_OpAtt_Groups); + GetCaFromDeDelegates.Add(OperationalAttributes.ENABLE_NAME, + GetCaFromDe_OpAtt_Enabled); + // supporting class not implemented in the framework + /* + GetCaFromDeDelegates.Add(OperationalAttributes.ENABLE_DATE_NAME, + GetCaFromDe_OpAtt_EnableDate); + GetCaFromDeDelegates.Add(OperationalAttributes.DISABLE_DATE_NAME, + GetCaFromDe_OpAtt_DisableDate); + */ + GetCaFromDeDelegates.Add(TerminalServicesUtils.TS_INITIAL_PROGRAM, + GetCaFromDe_Att_TSInitialProgram); + GetCaFromDeDelegates.Add(TerminalServicesUtils.TS_INITIAL_PROGRAM_DIR, + GetCaFromDe_Att_TSInitalProgramDir); + GetCaFromDeDelegates.Add(TerminalServicesUtils.TS_ALLOW_LOGON, + GetCaFromDe_Att_TSAllowLogon); + GetCaFromDeDelegates.Add(TerminalServicesUtils.TS_MAX_CONNECTION_TIME, + GetCaFromDe_Att_TSMaxConnectionTime); + GetCaFromDeDelegates.Add(TerminalServicesUtils.TS_MAX_DISCONNECTION_TIME, + GetCaFromDe_Att_TSMaxDisconnectionTime); + GetCaFromDeDelegates.Add(TerminalServicesUtils.TS_MAX_IDLE_TIME, + GetCaFromDe_Att_TSMaxIdleTime); + GetCaFromDeDelegates.Add(TerminalServicesUtils.TS_CONNECT_CLIENT_DRIVES_AT_LOGON, + GetCaFromDe_Att_TSConnectClientDrivesAtLogon); + GetCaFromDeDelegates.Add(TerminalServicesUtils.TS_CONNECT_CLIENT_PRINTERS_AT_LOGON, + GetCaFromDe_Att_TSConnectClientPrintersAtLogon); + GetCaFromDeDelegates.Add(TerminalServicesUtils.TS_DEFAULT_TO_MAIN_PRINTER, + GetCaFromDe_Att_TSDefaultToMainPrinter); + GetCaFromDeDelegates.Add(TerminalServicesUtils.TS_BROKEN_CONNECTION_ACTION, + GetCaFromDe_Att_TSBrokenConnectionAction); + GetCaFromDeDelegates.Add(TerminalServicesUtils.TS_RECONNECTION_ACTION, + GetCaFromDe_Att_TSReconnectionAction); + GetCaFromDeDelegates.Add(TerminalServicesUtils.TS_ENABLE_REMOTE_CONTROL, + GetCaFromDe_Att_TSEnableRemoteControl); + GetCaFromDeDelegates.Add(TerminalServicesUtils.TS_PROFILE_PATH, + GetCaFromDe_Att_TSProfilePath); + GetCaFromDeDelegates.Add(TerminalServicesUtils.TS_HOME_DIRECTORY, + GetCaFromDe_Att_TSHomeDirectory); + GetCaFromDeDelegates.Add(TerminalServicesUtils.TS_HOME_DRIVE, + GetCaFromDe_Att_TSHomeDrive); + } + + internal void UpdateDeFromCa(ObjectClass oclass, + UpdateType type, DirectoryEntry directoryEntry, + ConnectorAttribute attribute) { + + // if this gets big, replace with dictionary key by object class + IList ignoreList = null; + if(oclass.Equals(ObjectClass.ACCOUNT)) { + ignoreList = IgnoreConnectorAttributeNames_account; + } + else if (oclass.Equals(ObjectClass.GROUP)) + { + ignoreList = IgnoreConnectorAttributeNames_group; + } + + // if it's an ignored attribute, we're done + if ((ignoreList != null) && + (ignoreList.Contains(attribute.Name, + StringComparer.CurrentCultureIgnoreCase))) { + return; + } + + if (UpdateDeFromCaDelegates.ContainsKey(attribute.Name)) + { + // if it's an attribute with a special handler, + // call the handler + UpdateDeFromCa_delegate handler = + UpdateDeFromCaDelegates[attribute.Name]; + handler(oclass, type, directoryEntry, attribute); + } + else + { + // if none of the above, call the generic handler + UpdateDeFromCa_Att_Generic(oclass, type, directoryEntry, attribute); + } + } + + + internal ConnectorAttribute GetCaFromDe(ObjectClass oclass, + string attributeName, SearchResult searchResult) + { + ConnectorAttribute attribute = null; + + if (GetCaFromDeDelegates.ContainsKey(attributeName)) + { + // if it's an attribute with a special handler, + // call the handler + GetCaFromDe_delegate handler = GetCaFromDeDelegates[attributeName]; + attribute = handler(oclass, attributeName, searchResult); + } + else + { + // if none of the above, call the generic handler + attribute = GetCaFromDe_Att_Generic(oclass, attributeName, searchResult); + } + + return attribute; + } + + internal delegate void UpdateDeFromCa_delegate(ObjectClass oclass, + UpdateType type, DirectoryEntry directoryEntry, + ConnectorAttribute attribute); + + internal delegate ConnectorAttribute GetCaFromDe_delegate(ObjectClass oclass, + string attributeName, SearchResult searchResult); + + public void GetAddsAndDeletes(ICollectionvaluesToAdd, ICollectionvaluesToRemove, + PropertyValueCollection oldValues, ICollectionnewValues, UpdateType type) { + if (UpdateType.ADD.Equals(type)) + { + // add all groups + foreach (Object value in newValues) + { + valuesToAdd.Add(value); + } + } + else if (UpdateType.REPLACE.Equals(type)) + { + // look through existing values, and remove them + // if they are not in the newValues + if (oldValues != null) + { + foreach (Object value in oldValues) + { + if (!newValues.Contains(value)) + { + valuesToRemove.Add(value); + } + } + } + + // look through the values passed in and + // add them if they are not existing values + foreach (Object value in newValues) + { + if ((oldValues == null) || (!oldValues.Contains(value))) + { + valuesToAdd.Add(value); + } + } + } + else if (UpdateType.DELETE.Equals(type)) + { + foreach (Object value in newValues) + { + valuesToRemove.Add(value); + } + } + } + + #region UpdateDeFromCa handlers + + + + internal void UpdateDeFromCa_OpAtt_Groups(ObjectClass oclass, + UpdateType type, DirectoryEntry directoryEntry, + ConnectorAttribute attribute) + { + if (oclass.Equals(ObjectClass.ACCOUNT)) + { + // in this case, AD will not allow groups to be added + // to a user. To simulate this, lookup each added group + // and add this user to the group + ICollection newValues = attribute.Value; + PropertyValueCollection oldValues = null; + if(directoryEntry.Properties.Contains(ActiveDirectoryConnector.ATT_MEMBEROF)) { + PropertyValueCollection pvc = directoryEntry.Properties[ActiveDirectoryConnector.ATT_MEMBEROF]; + } + + ICollection groupsToAdd = new HashSet(); + ICollection groupsToRemove = new HashSet(); + + GetAddsAndDeletes(groupsToAdd, groupsToRemove, oldValues, newValues, type); + + foreach (Object obj in groupsToRemove) + { + // lookup the group and remove this user from group if it's a + // valid group. + String groupPath = ActiveDirectoryUtils.GetLDAPPath( + _configuration.LDAPHostName, (String)obj); + DirectoryEntry groupDe = new DirectoryEntry(groupPath, + _configuration.DirectoryAdminName, _configuration.DirectoryAdminPassword); + String distinguishedName = ActiveDirectoryUtils.GetDnFromPath(directoryEntry.Path); + groupDe.Properties[ActiveDirectoryConnector.ATT_MEMBER].Remove(distinguishedName); + groupDe.CommitChanges(); + } + + foreach (Object obj in groupsToAdd) + { + // lookup the group and add this user to group if it's a + // valid group. + String groupPath = ActiveDirectoryUtils.GetLDAPPath( + _configuration.LDAPHostName, (String)obj); + DirectoryEntry groupDe = new DirectoryEntry(groupPath, + _configuration.DirectoryAdminName, _configuration.DirectoryAdminPassword); + String distinguishedName = ActiveDirectoryUtils.GetDnFromPath(directoryEntry.Path); + groupDe.Properties[ActiveDirectoryConnector.ATT_MEMBER].Add(distinguishedName); + groupDe.CommitChanges(); + } + } + else + { + throw new ConnectorException( + String.Format("''{0}'' is an invalid attribute for object class ''{1}''", + PredefinedAttributeInfos.GROUPS, oclass.GetObjectClassValue())); + } + } + + internal void UpdateDeFromCa_OpAtt_Accounts(ObjectClass oclass, + UpdateType type, DirectoryEntry directoryEntry, + ConnectorAttribute attribute) + { + if (ObjectClass.GROUP.Equals(oclass)) + { + // create an 'attribute' with the real name, and then call the + // generic version + ConnectorAttribute newAttribute = ConnectorAttributeBuilder.Build( + ActiveDirectoryConnector.ATT_MEMBER, attribute.Value); + UpdateDeFromCa_Att_Generic(oclass, type, directoryEntry, newAttribute); + } + else + { + throw new ConnectorException( + String.Format("'{0}' is an invalid attribute for object class '{1}'", + PredefinedAttributeInfos.ACCOUNTS, oclass.GetObjectClassValue())); + } + } + + internal void UpdateDeFromCa_Att_HomeDirectory(ObjectClass oclass, + UpdateType type, DirectoryEntry directoryEntry, + ConnectorAttribute attribute) + { + String homeDir = ConnectorAttributeUtil.GetStringValue(attribute); + + if (homeDir != null) + { + if (type == UpdateType.REPLACE) + { + // first set the attribute + UpdateDeFromCa_Att_Generic(oclass, type, directoryEntry, attribute); + + // now create attribute if needed/possible + if (_configuration.CreateHomeDirectory) + { + // from old code ... should start with '\\' and have at least one + // '\' later that's not the end of the string + // i.e + // \\somemachine\someshare\somedirectory + // \\somemachine\someshare\somedirectory\someotherdirectory + // but not + // \\somemachine\someshare\ + // \\somemachine\someshare + // just ignore if it's not correct + String directoryName = ConnectorAttributeUtil.GetStringValue(attribute); + if (directoryName.StartsWith("\\\\")) + { + int secondPathSepIndex = directoryName.IndexOf('\\', 2); + if ((secondPathSepIndex > 2) && (directoryName.Length > secondPathSepIndex + 1)) + { + // name passes, so create directory + + // create security object + DirectorySecurity dirSecurity = new DirectorySecurity(); + PropertyValueCollection pvc = + directoryEntry.Properties[ActiveDirectoryConnector.ATT_OBJECT_SID]; + // there should always be exactly one sid + SecurityIdentifier sid = new SecurityIdentifier((byte[])pvc[0], 0); + // dirSecurity.SetOwner(sid); + InheritanceFlags iFlags = InheritanceFlags.ContainerInherit; + dirSecurity.AddAccessRule( + new FileSystemAccessRule(sid, FileSystemRights.FullControl, + InheritanceFlags.ContainerInherit | InheritanceFlags.ObjectInherit, + PropagationFlags.None, AccessControlType.Allow) + ); + Directory.CreateDirectory(directoryName, dirSecurity); + } + } + } + } + else + { + throw new ConnectorException("Only updatetype of replace is supported for home directory"); + } + } + } + + internal void UpdateDeFromCa_OpAtt_Enable(ObjectClass oclass, + UpdateType type, DirectoryEntry directoryEntry, + ConnectorAttribute attribute) { + + // set the proper flag in the userAccountControl bitfield + PropertyValueCollection uacPvc = + directoryEntry.Properties[UserAccountControl.UAC_ATTRIBUTE_NAME]; + + UserAccountControl.Set(uacPvc, + UserAccountControl.ACCOUNTDISABLE, + // attribute is enable, but the flag is for + // disable, so send the opposite + !ConnectorAttributeUtil.GetBooleanValue(attribute)); + } + +// supporting class not implemented in the framework +/* + internal void UpdateDeFromCa_OpAtt_EnableDate(ObjectClass oclass, + UpdateType type, DirectoryEntry directoryEntry, + ConnectorAttribute attribute) + { + } + + internal void UpdateDeFromCa_OpAtt_DisableDate(ObjectClass oclass, + UpdateType type, DirectoryEntry directoryEntry, + ConnectorAttribute attribute) + { + / * + long? utcMilliDate = ConnectorAttributeUtil.GetLongValue(attribute); + if(utcMilliDate == null) { + return; + } + + DateTime disableDate = DateTime.FromFileTimeUtc((long)utcMilliDate); + + LargeInteger disableTicks = new LargeIntegerClass(); + disableTicks.HighPart = (int)((disableDate.Ticks >> 32) & 0xFFFFFFFF); + disableTicks.LowPart = (int)(disableDate.Ticks & 0xFFFFFFFF); + + PropertyValueCollection pvc = directoryEntry.Properties["accountExpires"]; + if ((pvc == null) || (pvc.Count == 0)) + { + // if nothing there, add the value + pvc.Add(disableTicks); + } + else + { + // set the value + pvc[0] = disableTicks; + } + * / + } +*/ + internal void UpdateDeFromCa_Att_TSAllowLogon(ObjectClass oclass, + UpdateType type, DirectoryEntry directoryEntry, + ConnectorAttribute attribute) + { + TerminalServicesUtils.SetAllowLogon(type, directoryEntry, + ConnectorAttributeUtil.GetIntegerValue(attribute)); + } + + internal void UpdateDeFromCa_Att_TSInitialProgram(ObjectClass oclass, + UpdateType type, DirectoryEntry directoryEntry, + ConnectorAttribute attribute) + { + TerminalServicesUtils.SetInitialProgram(type, directoryEntry, + ConnectorAttributeUtil.GetStringValue(attribute)); + } + + internal void UpdateDeFromCa_Att_TSInitialProgramDir(ObjectClass oclass, + UpdateType type, DirectoryEntry directoryEntry, + ConnectorAttribute attribute) + { + TerminalServicesUtils.SetInitialProgramDir(type, directoryEntry, + ConnectorAttributeUtil.GetStringValue(attribute)); + } + + internal void UpdateDeFromCa_Att_TSMaxConnectionTime(ObjectClass oclass, + UpdateType type, DirectoryEntry directoryEntry, + ConnectorAttribute attribute) + { + TerminalServicesUtils.SetMaxConnectionTime(type, directoryEntry, + ConnectorAttributeUtil.GetIntegerValue(attribute)); + } + + internal void UpdateDeFromCa_Att_TSMaxDisconnectionTime(ObjectClass oclass, + UpdateType type, DirectoryEntry directoryEntry, + ConnectorAttribute attribute) + { + TerminalServicesUtils.SetMaxDisconnectionTime(type, directoryEntry, + ConnectorAttributeUtil.GetIntegerValue(attribute)); + } + + internal void UpdateDeFromCa_Att_TSMaxIdleTime(ObjectClass oclass, + UpdateType type, DirectoryEntry directoryEntry, + ConnectorAttribute attribute) + { + TerminalServicesUtils.SetMaxIdleTime(type, directoryEntry, + ConnectorAttributeUtil.GetIntegerValue(attribute)); + } + + internal void UpdateDeFromCa_Att_TSConnectClientDrivesAtLogon(ObjectClass oclass, + UpdateType type, DirectoryEntry directoryEntry, + ConnectorAttribute attribute) + { + TerminalServicesUtils.SetConnectClientDrivesAtLogon(type, directoryEntry, + ConnectorAttributeUtil.GetIntegerValue(attribute)); + } + + internal void UpdateDeFromCa_Att_TSConnectClientPrintersAtLogon(ObjectClass oclass, + UpdateType type, DirectoryEntry directoryEntry, + ConnectorAttribute attribute) + { + TerminalServicesUtils.SetConnectClientPrintersAtLogon(type, directoryEntry, + ConnectorAttributeUtil.GetIntegerValue(attribute)); + } + + internal void UpdateDeFromCa_Att_TSDefaultToMainPrinter(ObjectClass oclass, + UpdateType type, DirectoryEntry directoryEntry, + ConnectorAttribute attribute) + { + TerminalServicesUtils.SetDefaultToMainPrinter(type, directoryEntry, + ConnectorAttributeUtil.GetIntegerValue(attribute)); + } + + internal void UpdateDeFromCa_Att_TSBrokenConnectionAction(ObjectClass oclass, + UpdateType type, DirectoryEntry directoryEntry, + ConnectorAttribute attribute) + { + TerminalServicesUtils.SetBrokenConnectionAction(type, directoryEntry, + ConnectorAttributeUtil.GetIntegerValue(attribute)); + } + + internal void UpdateDeFromCa_Att_TSReconnectionAction(ObjectClass oclass, + UpdateType type, DirectoryEntry directoryEntry, + ConnectorAttribute attribute) + { + TerminalServicesUtils.SetReconnectionAction(type, directoryEntry, + ConnectorAttributeUtil.GetIntegerValue(attribute)); + } + + internal void UpdateDeFromCa_Att_TSEnableRemoteControl(ObjectClass oclass, + UpdateType type, DirectoryEntry directoryEntry, + ConnectorAttribute attribute) + { + TerminalServicesUtils.SetEnableRemoteControl(type, directoryEntry, + ConnectorAttributeUtil.GetIntegerValue(attribute)); + } + + internal void UpdateDeFromCa_Att_TSProfilePath(ObjectClass oclass, + UpdateType type, DirectoryEntry directoryEntry, + ConnectorAttribute attribute) + { + TerminalServicesUtils.SetProfilePath(type, directoryEntry, + ConnectorAttributeUtil.GetStringValue(attribute)); + } + + internal void UpdateDeFromCa_Att_TSHomeDirectory(ObjectClass oclass, + UpdateType type, DirectoryEntry directoryEntry, + ConnectorAttribute attribute) + { + TerminalServicesUtils.SetHomeDirectory(type, directoryEntry, + ConnectorAttributeUtil.GetStringValue(attribute)); + } + + internal void UpdateDeFromCa_Att_TSHomeDrive(ObjectClass oclass, + UpdateType type, DirectoryEntry directoryEntry, + ConnectorAttribute attribute) + { + TerminalServicesUtils.SetHomeDrive(type, directoryEntry, + ConnectorAttributeUtil.GetStringValue(attribute)); + } + + internal void UpdateDeFromCa_Att_Generic(ObjectClass oclass, + UpdateType type, DirectoryEntry directoryEntry, + ConnectorAttribute attribute) + { + + // null out the values if we are replacing attributes. + if (type.Equals(UpdateType.REPLACE)) + { + directoryEntry.Properties[attribute.Name].Value = null; + } + + // if we are updating or adding, put the + // new values in. + if (type.Equals(UpdateType.ADD) || + type.Equals(UpdateType.REPLACE)) + { + foreach (Object valueObject in attribute.Value) + { + directoryEntry.Properties[attribute.Name].Add(valueObject); + } + } + else if (type.Equals(UpdateType.DELETE)) + { + // if deleting, find the values, + // and remove them if they exist + if (directoryEntry.Properties.Contains(attribute.Name)) + { + PropertyValueCollection pvc = directoryEntry.Properties[attribute.Name]; + + foreach (Object valueObject in attribute.Value) + { + if (pvc.Contains(valueObject)) + { + pvc.Remove(valueObject); + } + } + } + + } + } + + #endregion + + #region GetCaFromDe Handlers + private ConnectorAttribute GetCaFromDe_Att_Generic( + ObjectClass oclass, string attributeName, SearchResult searchResult) + { + ConnectorAttributeBuilder attributeBuilder = new ConnectorAttributeBuilder(); + + if (attributeName == null) + { + return null; + } + + attributeBuilder.Name = attributeName; + + ResultPropertyValueCollection pvc = null; + if (searchResult.Properties.Contains(attributeName)) + { + pvc = searchResult.Properties[attributeName]; + } + + if (pvc == null) + { + return null; + } + else + { + for (int i = 0; i < pvc.Count; i++) + { + Object valueObject = pvc[i]; + if ((pvc[i] == null) || + (FrameworkUtil.IsSupportedAttributeType(valueObject.GetType()))) + { + attributeBuilder.AddValue(pvc[i]); + } + else + { + Trace.TraceWarning( + "Unsupported attribute type ... calling ToString (Name: \'{0}\'({1}) Type: \'{2}\' String Value: \'{3}\'", + attributeName, i, pvc[i].GetType(), pvc[i].ToString()); + attributeBuilder.AddValue(pvc[i].ToString()); + } + } + } + + return attributeBuilder.Build(); + } + + private ConnectorAttribute GetCaFromDe_OpAtt_Name( + ObjectClass oclass, string attributeName, SearchResult searchResult) + { + ICollection value = new List(); + ResultPropertyValueCollection pvc = null; + + pvc = searchResult.Properties[ActiveDirectoryConnector.ATT_DISTINGUISHED_NAME]; + if ((pvc != null) && (pvc.Count == 1)) + { + value.Add(pvc[0]); + } + else if (pvc.Count > 1) + { + throw new ConnectorException("There should be exactly one value for the name attribute"); + } + + return ConnectorAttributeBuilder.Build(Name.NAME, value); + } + + private ConnectorAttribute GetCaFromDe_OpAtt_Uid( + ObjectClass oclass, string attributeName, SearchResult searchResult) + { + ICollection value = new List(); + + // uid is objectGuid + ResultPropertyValueCollection pvc = + searchResult.Properties[ActiveDirectoryConnector.ATT_OBJECT_GUID]; + + if ((pvc.Count == 1) && (pvc[0] is Byte[])) + { + value.Add(ActiveDirectoryUtils.ConvertUIDBytesToGUIDString((Byte[])pvc[0])); + } + else if (pvc.Count > 1) + { + throw new ConnectorException("There should be only one UID, but multiple values were specified"); + } + + return ConnectorAttributeBuilder.Build(Uid.NAME, value); + } + + private ConnectorAttribute GetCaFromDe_Att_Container( + ObjectClass oclass, string attributeName, SearchResult searchResult) + { + if (searchResult == null) + { + return null; + } + + DirectoryEntry parentDe = searchResult.GetDirectoryEntry().Parent; + String container = ""; + if (parentDe != null) + { + container = ActiveDirectoryUtils.GetDnFromPath(parentDe.Path); + } + + return ConnectorAttributeBuilder.Build( + ActiveDirectoryConnector.ATT_CONTAINER, container); + } + + private ConnectorAttribute GetCaFromDe_OpAtt_Groups( + ObjectClass oclass, string attributeName, SearchResult searchResult) + { + ConnectorAttribute realAttribute = GetCaFromDe_Att_Generic( + oclass, ActiveDirectoryConnector.ATT_MEMBEROF, searchResult); + if (realAttribute == null) + { + return null; + } + else + { + return ConnectorAttributeBuilder.Build(PredefinedAttributes.GROUPS_NAME, + realAttribute.Value); + } + } + + private ConnectorAttribute GetCaFromDe_OpAtt_Accounts( + ObjectClass oclass, string attributeName, SearchResult searchResult) + { + ConnectorAttribute realAttribute = GetCaFromDe_Att_Generic( + oclass, ActiveDirectoryConnector.ATT_MEMBER, searchResult); + if (realAttribute == null) + { + return null; + } + else + { + return ConnectorAttributeBuilder.Build(PredefinedAttributes.ACCOUNTS_NAME, + realAttribute.Value); + } + } + + private ConnectorAttribute GetCaFromDe_OpAtt_Enabled( + ObjectClass oclass, string attributeName, SearchResult searchResult) + { + if (searchResult == null) + { + return null; + } + + bool disabled = UserAccountControl.IsSet( + searchResult.GetDirectoryEntry().Properties[UserAccountControl.UAC_ATTRIBUTE_NAME], + UserAccountControl.ACCOUNTDISABLE); + + return ConnectorAttributeBuilder.BuildEnabled(!disabled); + } + +// supporting class not implemented in the framework +/* + private ConnectorAttribute GetCaFromDe_OpAtt_EnableDate( + ObjectClass oclass, string attributeName, SearchResult searchResult) + { + return null; + } + + private ConnectorAttribute GetCaFromDe_OpAtt_DisableDate( + ObjectClass oclass, string attributeName, SearchResult searchResult) + { + / * + if (searchResult == null) + { + return null; + } + + ResultPropertyValueCollection rpvc = + searchResult.Properties["accountExpires"]; + if(rpvc.Count == 0) { + return null; + } + + long ticks = (long)rpvc[0]; + if ((ticks < DateTime.MinValue.Ticks) || (ticks > DateTime.MaxValue.Ticks)) + { + return null; + } + + DateTime disableDate = new DateTime(ticks); + + return ConnectorAttributeBuilder.BuildDisableDate(disableDate); + * / + return null; + } + */ + + private ConnectorAttribute GetCaFromDe_Att_TSInitialProgram( + ObjectClass oclass, string attributeName, SearchResult searchResult) + { + return ReturnConnectorAttribute(TerminalServicesUtils.TS_INITIAL_PROGRAM, + TerminalServicesUtils.GetInitialProgram(searchResult)); + } + + private ConnectorAttribute GetCaFromDe_Att_TSInitalProgramDir( + ObjectClass oclass, string attributeName, SearchResult searchResult) + { + return ReturnConnectorAttribute(TerminalServicesUtils.TS_INITIAL_PROGRAM_DIR, + TerminalServicesUtils.GetInitialProgramDir(searchResult)); + } + + + private ConnectorAttribute GetCaFromDe_Att_TSAllowLogon( + ObjectClass oclass, string attributeName, SearchResult searchResult) + { + return ReturnConnectorAttribute(TerminalServicesUtils.TS_ALLOW_LOGON, + TerminalServicesUtils.GetAllowLogon(searchResult)); + } + + private ConnectorAttribute GetCaFromDe_Att_TSMaxConnectionTime( + ObjectClass oclass, string attributeName, SearchResult searchResult) + { + return ReturnConnectorAttribute(TerminalServicesUtils.TS_MAX_CONNECTION_TIME, + TerminalServicesUtils.GetMaxConnectionTime(searchResult)); + } + + private ConnectorAttribute GetCaFromDe_Att_TSMaxDisconnectionTime( + ObjectClass oclass, string attributeName, SearchResult searchResult) + { + return ReturnConnectorAttribute(TerminalServicesUtils.TS_MAX_DISCONNECTION_TIME, + TerminalServicesUtils.GetMaxDisconnectionTime(searchResult)); + } + + private ConnectorAttribute GetCaFromDe_Att_TSMaxIdleTime( + ObjectClass oclass, string attributeName, SearchResult searchResult) + { + return ReturnConnectorAttribute(TerminalServicesUtils.TS_MAX_IDLE_TIME, + TerminalServicesUtils.GetMaxIdleTime(searchResult)); + } + + private ConnectorAttribute GetCaFromDe_Att_TSConnectClientDrivesAtLogon( + ObjectClass oclass, string attributeName, SearchResult searchResult) + { + return ReturnConnectorAttribute(TerminalServicesUtils.TS_CONNECT_CLIENT_DRIVES_AT_LOGON, + TerminalServicesUtils.GetConnectClientDrivesAtLogon(searchResult)); + } + + private ConnectorAttribute GetCaFromDe_Att_TSConnectClientPrintersAtLogon( + ObjectClass oclass, string attributeName, SearchResult searchResult) + { + return ReturnConnectorAttribute( + TerminalServicesUtils.TS_CONNECT_CLIENT_PRINTERS_AT_LOGON, + TerminalServicesUtils.GetConnectClientPrintersAtLogon(searchResult)); + } + + private ConnectorAttribute GetCaFromDe_Att_TSDefaultToMainPrinter( + ObjectClass oclass, string attributeName, SearchResult searchResult) + { + return ReturnConnectorAttribute( + TerminalServicesUtils.TS_DEFAULT_TO_MAIN_PRINTER, + TerminalServicesUtils.GetDefaultToMainPrinter(searchResult)); + } + + private ConnectorAttribute GetCaFromDe_Att_TSBrokenConnectionAction( + ObjectClass oclass, string attributeName, SearchResult searchResult) + { + return ReturnConnectorAttribute( + TerminalServicesUtils.TS_BROKEN_CONNECTION_ACTION, + TerminalServicesUtils.GetBrokenConnectionAction(searchResult)); + } + + private ConnectorAttribute GetCaFromDe_Att_TSReconnectionAction( + ObjectClass oclass, string attributeName, SearchResult searchResult) + { + return ReturnConnectorAttribute(TerminalServicesUtils.TS_RECONNECTION_ACTION, + TerminalServicesUtils.GetReconnectionAction(searchResult)); + } + + private ConnectorAttribute GetCaFromDe_Att_TSEnableRemoteControl( + ObjectClass oclass, string attributeName, SearchResult searchResult) + { + return ReturnConnectorAttribute(TerminalServicesUtils.TS_ENABLE_REMOTE_CONTROL, + TerminalServicesUtils.GetEnableRemoteControl(searchResult)); + } + + private ConnectorAttribute GetCaFromDe_Att_TSProfilePath( + ObjectClass oclass, string attributeName, SearchResult searchResult) + { + return ReturnConnectorAttribute(TerminalServicesUtils.TS_PROFILE_PATH, + TerminalServicesUtils.GetProfilePath(searchResult)); + } + private ConnectorAttribute GetCaFromDe_Att_TSHomeDirectory( + ObjectClass oclass, string attributeName, SearchResult searchResult) + { + return ReturnConnectorAttribute(TerminalServicesUtils.TS_HOME_DIRECTORY, + TerminalServicesUtils.GetHomeDirectory(searchResult)); + } + private ConnectorAttribute GetCaFromDe_Att_TSHomeDrive( + ObjectClass oclass, string attributeName, SearchResult searchResult) + { + return ReturnConnectorAttribute(TerminalServicesUtils.TS_HOME_DRIVE, + TerminalServicesUtils.GetHomeDrive(searchResult)); + } + + #endregion + + internal ConnectorAttribute ReturnConnectorAttribute + (string name, T value) { + ConnectorAttribute newAttribute = null; + + if (value != null) + { + newAttribute = ConnectorAttributeBuilder.Build( + name, value); + } + return newAttribute; + } + + } + +} diff --git a/ActiveDirectoryConnector/Messages.en.Designer.cs b/ActiveDirectoryConnector/Messages.en.Designer.cs new file mode 100644 index 00000000..e69de29b diff --git a/ActiveDirectoryConnector/Messages.en.resx b/ActiveDirectoryConnector/Messages.en.resx new file mode 100644 index 00000000..aacdfbf3 --- /dev/null +++ b/ActiveDirectoryConnector/Messages.en.resx @@ -0,0 +1,123 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + text/microsoft-resx + + + 2.0 + + + System.Resources.ResXResourceReader, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + System.Resources.ResXResourceWriter, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + ActiveDirectory Connector + + \ No newline at end of file diff --git a/ActiveDirectoryConnector/Messages.es-ES.resx b/ActiveDirectoryConnector/Messages.es-ES.resx new file mode 100644 index 00000000..03886a03 --- /dev/null +++ b/ActiveDirectoryConnector/Messages.es-ES.resx @@ -0,0 +1,123 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + text/microsoft-resx + + + 2.0 + + + System.Resources.ResXResourceReader, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + System.Resources.ResXResourceWriter, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + Active Direcory Connector + + \ No newline at end of file diff --git a/ActiveDirectoryConnector/Messages.es.resx b/ActiveDirectoryConnector/Messages.es.resx new file mode 100644 index 00000000..aacdfbf3 --- /dev/null +++ b/ActiveDirectoryConnector/Messages.es.resx @@ -0,0 +1,123 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + text/microsoft-resx + + + 2.0 + + + System.Resources.ResXResourceReader, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + System.Resources.ResXResourceWriter, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + ActiveDirectory Connector + + \ No newline at end of file diff --git a/ActiveDirectoryConnector/Messages.resx b/ActiveDirectoryConnector/Messages.resx new file mode 100644 index 00000000..975d8b60 --- /dev/null +++ b/ActiveDirectoryConnector/Messages.resx @@ -0,0 +1,135 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + text/microsoft-resx + + + 2.0 + + + System.Resources.ResXResourceReader, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + System.Resources.ResXResourceWriter, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + Active Directory Connector + + + Active Directory Hostname + + + The hostname of the global catalog (used for sync) + + + Hostname of the Active Directory Domain Controller + + + Help texxt for display_SyncGlobalCatalogServer + + \ No newline at end of file diff --git a/ActiveDirectoryConnector/PasswordChangeHandler.cs b/ActiveDirectoryConnector/PasswordChangeHandler.cs new file mode 100644 index 00000000..2e6ef3c7 --- /dev/null +++ b/ActiveDirectoryConnector/PasswordChangeHandler.cs @@ -0,0 +1,152 @@ +/* + * Copyright 2008 Sun Microsystems, Inc. All rights reserved. + * + * U.S. Government Rights - Commercial software. Government users + * are subject to the Sun Microsystems, Inc. standard license agreement + * and applicable provisions of the FAR and its supplements. + * + * Use is subject to license terms. + * + * This distribution may include materials developed by third parties. + * Sun, Sun Microsystems, the Sun logo, Java and Project Identity + * Connectors are trademarks or registered trademarks of Sun + * Microsystems, Inc. or its subsidiaries in the U.S. and other + * countries. + * + * UNIX is a registered trademark in the U.S. and other countries, + * exclusively licensed through X/Open Company, Ltd. + * + * ----------- + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER. + * + * Copyright 2008 Sun Microsystems, Inc. All rights reserved. + * + * The contents of this file are subject to the terms of the Common Development + * and Distribution License(CDDL) (the License). You may not use this file + * except in compliance with the License. + * + * You can obtain a copy of the License at + * http://identityconnectors.dev.java.net/CDDLv1.0.html + * See the License for the specific language governing permissions and + * limitations under the License. + * + * When distributing the Covered Code, include this CDDL Header Notice in each + * file and include the License file at identityconnectors/legal/license.txt. + * If applicable, add the following below this CDDL Header, with the fields + * enclosed by brackets [] replaced by your own identifying information: + * "Portions Copyrighted [year] [name of copyright owner]" + * ----------- + */ + +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.DirectoryServices; +using Org.IdentityConnectors.Common.Security; +using ActiveDs; + +namespace Org.IdentityConnectors.ActiveDirectory +{ + /** + * This class will decrypt passwords, and handle + * authentication and password changes (both + * administrative and user) + */ + internal class PasswordChangeHandler + { + String _currentPassword; + String _newPassword; + + internal PasswordChangeHandler() + { + } + + /// + /// sets the _currentPassword variable + /// + /// + internal void setCurrentPassword(UnmanagedArray clearChars) + { + _currentPassword = ""; + + // build up the string from the unmanaged array + for (int i = 0; i < clearChars.Length; i++) + { + _currentPassword += clearChars[i]; + } + } + + /// + /// Sets the _newPassword variable + /// + /// + internal void setNewPassword(UnmanagedArray clearChars) + { + _newPassword = ""; + + // build up the string from the unmanaged array + for (int i = 0; i < clearChars.Length; i++) + { + _newPassword += clearChars[i]; + } + } + + /// + /// Does an administrative password change. The Directory + /// entry must be created with username and password of + /// a user with permission to change the password + /// + /// + /// + internal void changePassword(DirectoryEntry directoryEntry, + GuardedString gsNewPassword) + { + // decrypt and save the new password + gsNewPassword.Access(setNewPassword); + + // get the native com object as an IADsUser, and set the + // password + IADsUser user = (IADsUser)directoryEntry.NativeObject; + user.SetPassword(_newPassword); + } + + /// + /// Does a user password change. Must supply the currentpassword + /// and the new password + /// + /// + /// + /// + internal void changePassword(DirectoryEntry directoryEntry, + GuardedString gsCurrentPassword, GuardedString gsNewPassword) + { + // decrypt and save the old nad new passwords + gsNewPassword.Access(setNewPassword); + gsCurrentPassword.Access(setCurrentPassword); + + // get the native com object as an IADsUser, and change the + // password + IADsUser user = (IADsUser)directoryEntry.NativeObject; + user.ChangePassword(_currentPassword, _newPassword); + } + + /// + /// Authenticates the user + /// + /// + /// + /// + internal void Authenticate(DirectoryEntry directoryEntry, string username, + Org.IdentityConnectors.Common.Security.GuardedString password) + { + password.Access(setCurrentPassword); + String sAMAccountName = (String)directoryEntry.Properties[ActiveDirectoryConnector.ATT_SAMACCOUNT_NAME][0]; + + DirectoryEntry userDe = new DirectoryEntry(directoryEntry.Path, + sAMAccountName, _currentPassword); + userDe.RefreshCache(); + + } + } +} diff --git a/ActiveDirectoryConnector/TerminalServicesUtils.cs b/ActiveDirectoryConnector/TerminalServicesUtils.cs new file mode 100644 index 00000000..53f326cb --- /dev/null +++ b/ActiveDirectoryConnector/TerminalServicesUtils.cs @@ -0,0 +1,321 @@ +/* + * Copyright 2008 Sun Microsystems, Inc. All rights reserved. + * + * U.S. Government Rights - Commercial software. Government users + * are subject to the Sun Microsystems, Inc. standard license agreement + * and applicable provisions of the FAR and its supplements. + * + * Use is subject to license terms. + * + * This distribution may include materials developed by third parties. + * Sun, Sun Microsystems, the Sun logo, Java and Project Identity + * Connectors are trademarks or registered trademarks of Sun + * Microsystems, Inc. or its subsidiaries in the U.S. and other + * countries. + * + * UNIX is a registered trademark in the U.S. and other countries, + * exclusively licensed through X/Open Company, Ltd. + * + * ----------- + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER. + * + * Copyright 2008 Sun Microsystems, Inc. All rights reserved. + * + * The contents of this file are subject to the terms of the Common Development + * and Distribution License(CDDL) (the License). You may not use this file + * except in compliance with the License. + * + * You can obtain a copy of the License at + * http://identityconnectors.dev.java.net/CDDLv1.0.html + * See the License for the specific language governing permissions and + * limitations under the License. + * + * When distributing the Covered Code, include this CDDL Header Notice in each + * file and include the License file at identityconnectors/legal/license.txt. + * If applicable, add the following below this CDDL Header, with the fields + * enclosed by brackets [] replaced by your own identifying information: + * "Portions Copyrighted [year] [name of copyright owner]" + * ----------- + */ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.DirectoryServices; +using Org.IdentityConnectors.Framework.Spi.Operations; +using Org.IdentityConnectors.Framework.Common.Exceptions; + +namespace Org.IdentityConnectors.ActiveDirectory +{ + /** + * This class will handle setting all of the terminal services attributes + */ + public class TerminalServicesUtils + { + // used to be 'Terminal Services Initial Program' + public static string TS_INITIAL_PROGRAM = "TerminalServicesInitialProgram"; + + // used to be 'Terminal Services Initial Program Directory' + public static string TS_INITIAL_PROGRAM_DIR = "TerminalServicesWorkDirectory"; + + // used to be 'Terminal Services Inherit Initial Program' + + // used to be 'Terminal Services Allow Logon' - defaults to false, so testing true + public static string TS_ALLOW_LOGON = "AllowLogon"; + + // used to be 'Terminal Services Active Session Timeout' + public static string TS_MAX_CONNECTION_TIME = "MaxConnectionTime"; + + // used to be 'Terminal Services Disconnected Session Timeout' + public static string TS_MAX_DISCONNECTION_TIME = "MaxDisconnectionTime"; + + // used to be 'Terminal Services Idle Timeout' + public static string TS_MAX_IDLE_TIME = "MaxIdleTime"; + + // used to be 'Terminal Services Connect Client Drives At Logon' + public static string TS_CONNECT_CLIENT_DRIVES_AT_LOGON = "ConnectClientDrivesAtLogon"; + + // used to be 'Terminal Services Connect Client Printers At Logon' + public static string TS_CONNECT_CLIENT_PRINTERS_AT_LOGON = "ConnectClientPrintersAtLogon"; + + // used to be 'Terminal Services Default To Main Client Printer' + public static string TS_DEFAULT_TO_MAIN_PRINTER = "DefaultToMainPrinter"; + + // used to be 'Terminal Services End Session On Timeout Or Broken Connection' + public static string TS_BROKEN_CONNECTION_ACTION = "BrokenConnectionAction"; + + // used to be 'Terminal Services Allow Reconnect From Originating Client Only' + public static string TS_RECONNECTION_ACTION = "ReconnectionAction"; + + // used to be 'Terminal Services Callback Settings' + + // used to be 'Terminal Services Callback Phone Number' + + // used to be 'Terminal Services Remote Control Settings' + public static string TS_ENABLE_REMOTE_CONTROL = "EnableRemoteControl"; + + // used to be 'Terminal Services User Profile' + public static string TS_PROFILE_PATH = "TerminalServicesProfilePath"; + + // used to be 'Terminal Services Local Home Directory' + public static string TS_HOME_DIRECTORY = "TerminalServicesHomeDirectory"; + + // used to be 'Terminal Services Home Directory Drive' + public static string TS_HOME_DRIVE = "TerminalServicesHomeDrive"; + + private static T GetValue(SearchResult searchResult, string name, T defaultValue) + { + // get the directory entry + DirectoryEntry directoryEntry = searchResult.GetDirectoryEntry(); + + // get 'name' from the directory entry, and return it if it exists + object result = directoryEntry.InvokeGet(name); + if (result != null) + { + T value = (T)result; + return value; + } + + // if the name didn't exist, return 'defaultValue' + return defaultValue; + } + + internal static void SetValue(UpdateType type, + DirectoryEntry directoryEntry, string name, T value) + { + if (!type.Equals(UpdateType.REPLACE)) + { + // Only allow replace on single value attributes, + // and for now, all terminal services are single value + ThrowInvalidUpdateType(name); + } + + if (value == null) + { + // just ignore a null + return; + } + + // invoke set on 'name' with 'value' + directoryEntry.InvokeSet(name, value); + } + + private static void ThrowInvalidUpdateType(string attributeName) + { + // throws an exception that says invalid update type + string msg = string.Format("The update type specified is invalid for the terminal services attribute ''{0}''", + attributeName); + throw new ConnectorException(msg); + } + + internal static string GetInitialProgram(SearchResult searchResult) + { + return GetValue(searchResult, TS_INITIAL_PROGRAM, null); + } + + internal static void SetInitialProgram(UpdateType type, DirectoryEntry directoryEntry, + string initialProgram) + { + SetValue(type, directoryEntry, TS_INITIAL_PROGRAM, initialProgram); + } + + internal static string GetInitialProgramDir(SearchResult searchResult) + { + return GetValue(searchResult, TS_INITIAL_PROGRAM_DIR, null); + } + + internal static void SetInitialProgramDir(UpdateType type, + DirectoryEntry directoryEntry, string initialProgramDir) + { + SetValue(type, directoryEntry, TS_INITIAL_PROGRAM_DIR, initialProgramDir); + } + + internal static int? GetAllowLogon(SearchResult searchResult) + { + return GetValue(searchResult, TS_ALLOW_LOGON, null); + } + + internal static void SetAllowLogon(UpdateType type, DirectoryEntry directoryEntry, + int? isAllowed) + { + SetValue(type, directoryEntry, TS_ALLOW_LOGON, isAllowed); + } + + internal static int? GetMaxConnectionTime(SearchResult searchResult) + { + return GetValue(searchResult, TS_MAX_CONNECTION_TIME, null); + } + + internal static void SetMaxConnectionTime(UpdateType type, DirectoryEntry directoryEntry, + int? maxConnectionTime) + { + SetValue(type, directoryEntry, TS_MAX_CONNECTION_TIME, maxConnectionTime); + } + + internal static int? GetMaxDisconnectionTime(SearchResult searchResult) + { + return GetValue(searchResult, TS_MAX_DISCONNECTION_TIME, null); + } + + internal static void SetMaxDisconnectionTime(UpdateType type, + DirectoryEntry directoryEntry, int? maxDisconnectionTime) + { + SetValue(type, directoryEntry, TS_MAX_DISCONNECTION_TIME, maxDisconnectionTime); + } + + internal static int? GetMaxIdleTime(SearchResult searchResult) + { + return GetValue(searchResult, TS_MAX_IDLE_TIME, null); + } + + internal static void SetMaxIdleTime(UpdateType type, DirectoryEntry directoryEntry, + int? maxIdleTime) + { + SetValue(type, directoryEntry, TS_MAX_IDLE_TIME, maxIdleTime); + } + + internal static int? GetConnectClientDrivesAtLogon(SearchResult searchResult) + { + return GetValue(searchResult, TS_CONNECT_CLIENT_DRIVES_AT_LOGON, null); + } + + internal static void SetConnectClientDrivesAtLogon(UpdateType type, + DirectoryEntry directoryEntry, int? connectClientDrivesAtLogon) + { + SetValue(type, directoryEntry, TS_CONNECT_CLIENT_DRIVES_AT_LOGON, + connectClientDrivesAtLogon); + } + + internal static int? GetConnectClientPrintersAtLogon(SearchResult searchResult) + { + return GetValue(searchResult, TS_CONNECT_CLIENT_PRINTERS_AT_LOGON, null); + } + + internal static void SetConnectClientPrintersAtLogon(UpdateType type, + DirectoryEntry directoryEntry, int? connectClientPrintersAtLogon) + { + SetValue(type, directoryEntry, TS_CONNECT_CLIENT_PRINTERS_AT_LOGON, + connectClientPrintersAtLogon); + } + + internal static int? GetDefaultToMainPrinter(SearchResult searchResult) + { + return GetValue(searchResult, TS_DEFAULT_TO_MAIN_PRINTER, null); + } + + internal static void SetDefaultToMainPrinter(UpdateType type, + DirectoryEntry directoryEntry, int? defaultToMainPrinter) + { + SetValue(type, directoryEntry, TS_DEFAULT_TO_MAIN_PRINTER, + defaultToMainPrinter); + } + + internal static int? GetBrokenConnectionAction(SearchResult searchResult) + { + return GetValue(searchResult, TS_BROKEN_CONNECTION_ACTION, null); + } + + internal static void SetBrokenConnectionAction(UpdateType type, + DirectoryEntry directoryEntry, int? brokenConnectionAction) + { + SetValue(type, directoryEntry, TS_BROKEN_CONNECTION_ACTION, + brokenConnectionAction); + } + + internal static int? GetReconnectionAction(SearchResult searchResult) + { + return GetValue(searchResult, TS_RECONNECTION_ACTION, null); + } + + internal static void SetReconnectionAction(UpdateType type, + DirectoryEntry directoryEntry, int? reconnectionAction) + { + SetValue(type, directoryEntry, TS_RECONNECTION_ACTION, reconnectionAction); + } + + internal static int? GetEnableRemoteControl(SearchResult searchResult) + { + return GetValue(searchResult, TS_ENABLE_REMOTE_CONTROL, null); + } + + internal static void SetEnableRemoteControl(UpdateType type, + DirectoryEntry directoryEntry, int? enableRemoteControl) + { + SetValue(type, directoryEntry, TS_ENABLE_REMOTE_CONTROL, enableRemoteControl); + } + + internal static string GetProfilePath(SearchResult searchResult) + { + return GetValue(searchResult, TS_PROFILE_PATH, null); + } + + internal static void SetProfilePath(UpdateType type, + DirectoryEntry directoryEntry, string profilePath) + { + SetValue(type, directoryEntry, TS_PROFILE_PATH, profilePath); + } + + internal static string GetHomeDirectory(SearchResult searchResult) + { + return GetValue(searchResult, TS_HOME_DIRECTORY, null); + } + + internal static void SetHomeDirectory(UpdateType type, + DirectoryEntry directoryEntry, string homeDirectory) + { + SetValue(type, directoryEntry, TS_HOME_DIRECTORY, homeDirectory); + } + + internal static string GetHomeDrive(SearchResult searchResult) + { + return GetValue(searchResult, TS_HOME_DRIVE, null); + } + + internal static void SetHomeDrive(UpdateType type, + DirectoryEntry directoryEntry, string homeDrive) + { + SetValue(type, directoryEntry, TS_HOME_DRIVE, homeDrive); + } + + } +} diff --git a/ActiveDirectoryConnector/UserAccountControl.cs b/ActiveDirectoryConnector/UserAccountControl.cs new file mode 100644 index 00000000..5750433f --- /dev/null +++ b/ActiveDirectoryConnector/UserAccountControl.cs @@ -0,0 +1,128 @@ +/* + * Copyright 2008 Sun Microsystems, Inc. All rights reserved. + * + * U.S. Government Rights - Commercial software. Government users + * are subject to the Sun Microsystems, Inc. standard license agreement + * and applicable provisions of the FAR and its supplements. + * + * Use is subject to license terms. + * + * This distribution may include materials developed by third parties. + * Sun, Sun Microsystems, the Sun logo, Java and Project Identity + * Connectors are trademarks or registered trademarks of Sun + * Microsystems, Inc. or its subsidiaries in the U.S. and other + * countries. + * + * UNIX is a registered trademark in the U.S. and other countries, + * exclusively licensed through X/Open Company, Ltd. + * + * ----------- + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER. + * + * Copyright 2008 Sun Microsystems, Inc. All rights reserved. + * + * The contents of this file are subject to the terms of the Common Development + * and Distribution License(CDDL) (the License). You may not use this file + * except in compliance with the License. + * + * You can obtain a copy of the License at + * http://identityconnectors.dev.java.net/CDDLv1.0.html + * See the License for the specific language governing permissions and + * limitations under the License. + * + * When distributing the Covered Code, include this CDDL Header Notice in each + * file and include the License file at identityconnectors/legal/license.txt. + * If applicable, add the following below this CDDL Header, with the fields + * enclosed by brackets [] replaced by your own identifying information: + * "Portions Copyrighted [year] [name of copyright owner]" + * ----------- + */ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.DirectoryServices; +using Org.IdentityConnectors.Framework.Common.Exceptions; + +namespace Org.IdentityConnectors.ActiveDirectory +{ + internal class UserAccountControl + { + public static string UAC_ATTRIBUTE_NAME = "userAccountControl"; + + // values taken from - http://support.microsoft.com/kb/305144 + static public int SCRIPT = 0x0001; + static public int ACCOUNTDISABLE = 0x0002; + static public int HOMEDIR_REQUIRED = 0x0008; + static public int LOCKOUT = 0x0010; + static public int PASSWD_NOTREQD = 0x0020; + + // Note You cannot assign this permission by directly modifying + // the UserAccountControl attribute. For information about how + // to set the permission programmatically, see the "Property + // flag descriptions" section. + static public int PASSWD_CANT_CHANGE = 0x0040; + + static public int ENCRYPTED_TEXT_PWD_ALLOWED = 0x0080; + static public int TEMP_DUPLICATE_ACCOUNT = 0x0100; + static public int NORMAL_ACCOUNT = 0x0200; + static public int INTERDOMAIN_TRUST_ACCOUNT = 0x0800; + static public int WORKSTATION_TRUST_ACCOUNT = 0x1000; + static public int SERVER_TRUST_ACCOUNT = 0x2000; + static public int DONT_EXPIRE_PASSWORD = 0x10000; + static public int MNS_LOGON_ACCOUNT = 0x20000; + static public int SMARTCARD_REQUIRED = 0x40000; + static public int TRUSTED_FOR_DELEGATION = 0x80000; + static public int NOT_DELEGATED = 0x100000; + static public int USE_DES_KEY_ONLY = 0x200000; + static public int DONT_REQ_PREAUTH = 0x400000; + public static int PASSWORD_EXPIRED = 0x800000; + public static int TRUSTED_TO_AUTH_FOR_DELEGATION = 0x1000000; + + // get the uac value from the property value collection + private static int GetUAC(PropertyValueCollection pvc) + { + // default schema says it's an integer, so it better be one + if ((pvc != null) && (pvc.Count == 1) && (pvc[0] is int)) + { + return (int)pvc[0]; + } + else + { + throw new ConnectorException("Active Directory attribute '" + + UAC_ATTRIBUTE_NAME + "' was expected to be a single integer, but was not"); + } + } + + // sets the uac value in a propertyvaluecollection + private static void SetUAC(PropertyValueCollection pvc, int value) + { + // set the value + pvc[0] = value; + } + + // generically set a value in the uac to the value of 'isSet' + internal static void Set(PropertyValueCollection pvc, int flag, bool? isSet) + { + int uac = GetUAC(pvc); + // boolean false (null is same as false) + if ((isSet == null) || (isSet.Value.Equals(false))) + { + int clearMask = 0xFFFF ^ flag; + uac &= clearMask; + } + else + { + uac |= flag; + } + SetUAC(pvc, uac); + } + + // chec to see if a particular value of the uac is set + internal static bool IsSet(PropertyValueCollection pvc, int flag) + { + int uac = GetUAC(pvc); + return ((uac & flag) != 0); + } + } +} diff --git a/ActiveDirectoryConnectorTests/ActiveDirectoryConnectorTest.cs b/ActiveDirectoryConnectorTests/ActiveDirectoryConnectorTest.cs new file mode 100644 index 00000000..4bab9221 --- /dev/null +++ b/ActiveDirectoryConnectorTests/ActiveDirectoryConnectorTest.cs @@ -0,0 +1,2123 @@ +/* + * Copyright 2008 Sun Microsystems, Inc. All rights reserved. + * + * U.S. Government Rights - Commercial software. Government users + * are subject to the Sun Microsystems, Inc. standard license agreement + * and applicable provisions of the FAR and its supplements. + * + * Use is subject to license terms. + * + * This distribution may include materials developed by third parties. + * Sun, Sun Microsystems, the Sun logo, Java and Project Identity + * Connectors are trademarks or registered trademarks of Sun + * Microsystems, Inc. or its subsidiaries in the U.S. and other + * countries. + * + * UNIX is a registered trademark in the U.S. and other countries, + * exclusively licensed through X/Open Company, Ltd. + * + * ----------- + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER. + * + * Copyright 2008 Sun Microsystems, Inc. All rights reserved. + * + * The contents of this file are subject to the terms of the Common Development + * and Distribution License(CDDL) (the License). You may not use this file + * except in compliance with the License. + * + * You can obtain a copy of the License at + * http://identityconnectors.dev.java.net/CDDLv1.0.html + * See the License for the specific language governing permissions and + * limitations under the License. + * + * When distributing the Covered Code, include this CDDL Header Notice in each + * file and include the License file at identityconnectors/legal/license.txt. + * If applicable, add the following below this CDDL Header, with the fields + * enclosed by brackets [] replaced by your own identifying information: + * "Portions Copyrighted [year] [name of copyright owner]" + * ----------- + */ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using NUnit.Framework; +using Org.IdentityConnectors.Framework; +using Org.IdentityConnectors.Framework.Spi; +using Org.IdentityConnectors.Framework.Spi.Operations; +using Org.IdentityConnectors.Framework.Common.Objects; +using Org.IdentityConnectors.Framework.Common.Objects.Filters; +using Org.IdentityConnectors.Framework.Test; +using Org.IdentityConnectors.Common.Security; +using System.Diagnostics; +using System.Resources; +using System.IO; +using System.Security.AccessControl; +using System.Security.Principal; + +namespace Org.IdentityConnectors.ActiveDirectory +{ + [TestFixture] + public class ActiveDirectoryConnectorTest + { + public static readonly string CONFIG_PROPERTY_USER = "config_user"; + public static readonly string CONFIG_PROPERTY_PASSWORD = "config_password"; + public static readonly string CONFIG_PROPERTY_HOST = "config_host"; + public static readonly string CONFIG_PROPERTY_PORT = "config_port"; + public static readonly string CONFIG_PROPERTY_CONTAINER = "config_container"; + public static readonly string CONFIG_PROPERTY_SCRIPT_USER_LOCAL = "config_script_user_local"; + public static readonly string CONFIG_PROPERTY_SCRIPT_PASSWORD_LOCAL = "config_script_password_local"; + public static readonly string CONFIG_PROPERTY_SCRIPT_USER_DOMAIN = "config_script_user_domain"; + public static readonly string CONFIG_PROPERTY_SCRIPT_PASSWORD_DOMAIN = "config_script_password_domain"; + public static readonly string CONFIG_PROPERTY_LDAPHOSTNAME = "config_ldap_hostname"; + public static readonly string CONFIG_PROPERTY_SEARCH_CONTEXT = "config_search_context"; + public static readonly string CONFIG_PROPERTY_SYNC_SEARCH_CONTEXT = "config_sync_search_context"; + public static readonly string CONFIG_PROPERTY_DOMAIN_NAME = "config_domain_name"; + public static readonly string CONFIG_PROPERTY_SYNC_DOMAIN_CONTROLLER = "config_sync_domain_controller"; + public static readonly string CONFIG_PROPERTY_GC_DOMAIN_CONTROLLER = "config_sync_gc_domain_controller"; + public static readonly string TEST_PARAM_SHARED_HOME_FOLDER = "test_param_shared_home_folder"; + + [Test] + public void TestConfiguration() { + ActiveDirectoryConfiguration config = new ActiveDirectoryConfiguration(); + String directoryAdminName = GetProperty(CONFIG_PROPERTY_USER); + config.DirectoryAdminName = directoryAdminName; + Assert.AreEqual(directoryAdminName, config.DirectoryAdminName); + } + + [Test] + public void TestSchema() + { + ActiveDirectoryConnector connector = new ActiveDirectoryConnector(); + connector.Init(GetConfiguration()); + Schema schema = connector.Schema(); + Boolean foundOptionalAttributes = false; + Boolean foundOperationalAttributes = false; + + // just a very high level check of things. Should have 3 ObjectClassInfos, + // (group, account, and organizationalUnit) and nothing contained in them + // should be null. Group and account should have some operational + // attributes + Assert.AreEqual(3, schema.ObjectClassInfo.Count); + foreach(ObjectClassInfo ocInfo in schema.ObjectClassInfo) { + Assert.IsNotNull(ocInfo); + Assert.That((ocInfo.ObjectType == ObjectClass.ACCOUNT.GetObjectClassValue()) + || (ocInfo.ObjectType == ObjectClass.GROUP.GetObjectClassValue()) + || (ocInfo.ObjectType == ActiveDirectoryConnector.OBJECTCLASS_OU)); + Console.WriteLine("****** " + ocInfo.ObjectType); + + // skip this for organizational unit ... it doesnt really have this + if (ocInfo.ObjectType.Equals(ActiveDirectoryConnector.ouObjectClass)) + { + continue; + } + + foreach (ConnectorAttributeInfo caInfo in ocInfo.ConnectorAttributeInfos) + { + Assert.IsNotNull(caInfo); + Console.WriteLine("{0} {1} {2} {3}", caInfo.Name, + caInfo.IsWritable ? "writable" : "", + caInfo.IsRequired ? "required" : "", + caInfo.IsMultiValue ? "multivalue" : ""); + if(ConnectorAttributeUtil.IsSpecial(caInfo)) { + foundOperationalAttributes = true; + } else { + if (!caInfo.IsRequired) + { + foundOptionalAttributes = true; + } + } + } + Assert.That(foundOperationalAttributes && foundOptionalAttributes); + } + } + + // test proper behavior of each supported operation + // and test proper reporting of unsuppoorted operations + [Test] + public void TestBasics_Account() + { + //Initialize Connector + ActiveDirectoryConnector connector = new ActiveDirectoryConnector(); + connector.Init(GetConfiguration()); + + // create user + ICollection createAttributes = GetNormalAttributes_Account(); + Uid createUid = CreateAndVerifyObject(connector, + ObjectClass.ACCOUNT, createAttributes); + + // update the user - replace + ICollection updateReplaceAttrs = + new List(); + Name oldName = ConnectorAttributeUtil.GetNameFromAttributes(createAttributes); + String newName = ActiveDirectoryUtils.GetRelativeName(oldName); + newName = newName.Trim() + "_new, " + GetProperty(CONFIG_PROPERTY_CONTAINER); + updateReplaceAttrs.Add(ConnectorAttributeBuilder.Build( + Name.NAME, newName)); + updateReplaceAttrs.Add(ConnectorAttributeBuilder.Build( + "sn", "newsn")); + Uid updateReplaceUid = UpdateReplaceAndVerifyObject(connector, + ObjectClass.ACCOUNT, createUid, updateReplaceAttrs); + + // update the user - add + ICollection updateAddAttrs = + new List(); + updateAddAttrs.Add(ConnectorAttributeBuilder.Build("otherHomePhone", "123.456.7890", "098.765.4321")); + Uid updateAddUid = UpdateAddAndVerifyUser(connector, + ObjectClass.ACCOUNT, createUid, updateAddAttrs, null); + + // update the user - delete + + // delete user + DeleteAndVerifyObject(connector, ObjectClass.ACCOUNT, createUid, true, true); + } + + // test proper behaviour of each supported operation + // and test proper reporting of unsuppoorted operations + [Test] + public void TestBasics_Group() + { + //Initialize Connector + ActiveDirectoryConnector connector = new ActiveDirectoryConnector(); + connector.Init(GetConfiguration()); + + // create group + ICollection createAttributes = GetNormalAttributes_Group(); + createAttributes.Add(ConnectorAttributeBuilder.Build(PredefinedAttributes.ACCOUNTS_NAME, + CreateGroupMember(connector))); + + // create object + Uid createUid = connector.Create(ObjectClass.GROUP, createAttributes, null); + Assert.IsNotNull(createUid); + + // find new object ... have to add groups to list of things to return + OperationOptionsBuilder optionsBuilder = new OperationOptionsBuilder(); + ICollection attributesToGet = GetDefaultAttributesToGet(ObjectClass.GROUP); + attributesToGet.Add(PredefinedAttributes.ACCOUNTS_NAME); + optionsBuilder.AttributesToGet = attributesToGet.ToArray(); + + ConnectorObject newObject = GetConnectorObjectFromUid(connector, + ObjectClass.GROUP, createUid, optionsBuilder.Build()); + VerifyObject(createAttributes, newObject); + // update the group - replace + ICollection updateReplaceAttrs = + new List(); + Name oldName = ConnectorAttributeUtil.GetNameFromAttributes(createAttributes); + String newName = ActiveDirectoryUtils.GetRelativeName(oldName); + newName = newName.Trim() + "_new, " + GetProperty(CONFIG_PROPERTY_CONTAINER); + + updateReplaceAttrs.Add(createUid); + updateReplaceAttrs.Add(ConnectorAttributeBuilder.Build( + Name.NAME, newName)); + updateReplaceAttrs.Add(ConnectorAttributeBuilder.Build( + "description", "New description")); + Uid updateReplaceUid = UpdateReplaceAndVerifyObject(connector, + ObjectClass.GROUP, createUid, updateReplaceAttrs); + + // update the group - add + ICollection updateAddAttrs = + new List(); + updateAddAttrs.Add(ConnectorAttributeBuilder.Build(PredefinedAttributes.ACCOUNTS_NAME, + CreateGroupMember(connector), CreateGroupMember(connector))); + + Uid updateAddUid = UpdateAddAndVerifyUser(connector, + ObjectClass.GROUP, createUid, updateAddAttrs, optionsBuilder.Build()); + + // delete user + DeleteAndVerifyObject(connector, ObjectClass.GROUP, createUid, true, true); + } + + [Test] + public void TestCreate_Account() + { + //Initialize Connector + ActiveDirectoryConnector connector = new ActiveDirectoryConnector(); + connector.Init(GetConfiguration()); + Uid createUid = null; + try + { + ICollection createAttributes = GetNormalAttributes_Account(); + createUid = CreateAndVerifyObject(connector, ObjectClass.ACCOUNT, createAttributes); + } + finally + { + if (createUid != null) + { + //remove the one we created + DeleteAndVerifyObject(connector, ObjectClass.ACCOUNT, + createUid, false, true); + } + } + } + + [Test] + public void TestCreate_Group() + { + //Initialize Connector + ActiveDirectoryConnector connector = new ActiveDirectoryConnector(); + connector.Init(GetConfiguration()); + Uid createUid = null; + try + { + ICollection createAttributes = GetNormalAttributes_Group(); + createUid = CreateAndVerifyObject(connector, ObjectClass.GROUP, createAttributes); + } + finally + { + if (createUid != null) + { + //remove the one we created + DeleteAndVerifyObject(connector, ObjectClass.ACCOUNT, + createUid, false, true); + } + } + } + + [Test] + public void TestCreateWithHomeDirectory_Account() + { + //Initialize Connector + ActiveDirectoryConnector connector = new ActiveDirectoryConnector(); + ActiveDirectoryConfiguration config = (ActiveDirectoryConfiguration)GetConfiguration(); + config.CreateHomeDirectory = true; + connector.Init(config); + Uid userUid = null; + try + { + // get the normal attributes + ICollection createAttributes = GetNormalAttributes_Account(); + + // read the homedir path, and append the samaccountname + StringBuilder homeDirPathBuilder = new StringBuilder(GetProperty(TEST_PARAM_SHARED_HOME_FOLDER)); + if (!homeDirPathBuilder.ToString().EndsWith("\\")) + { + homeDirPathBuilder.Append('\\'); + } + ConnectorAttribute samAccountNameAttr = ConnectorAttributeUtil.Find( + ActiveDirectoryConnector.ATT_SAMACCOUNT_NAME, createAttributes); + homeDirPathBuilder.Append(ConnectorAttributeUtil.GetStringValue(samAccountNameAttr)); + + // if it exists, delete it + String homeDir = homeDirPathBuilder.ToString(); + if (Directory.Exists(homeDir)) + { + Directory.Delete(homeDir); + } + Assert.IsFalse(Directory.Exists(homeDir)); + + // add homeDirectory to the attributes, and create user + createAttributes.Add(ConnectorAttributeBuilder.Build("homeDirectory", homeDir)); + userUid = CreateAndVerifyObject(connector, + ObjectClass.ACCOUNT, createAttributes); + + // now directory should exist + Assert.IsTrue(Directory.Exists(homeDir)); + + // get sid to check permissions + OperationOptionsBuilder optionsBuilder = new OperationOptionsBuilder(); + ICollection attributesToGet = GetDefaultAttributesToGet(ObjectClass.ACCOUNT); + attributesToGet.Add(ActiveDirectoryConnector.ATT_OBJECT_SID); + optionsBuilder.AttributesToGet = attributesToGet.ToArray(); + + ConnectorObject newUser = GetConnectorObjectFromUid(connector, + ObjectClass.ACCOUNT, userUid, optionsBuilder.Build()); + ConnectorAttribute sidAttr = + newUser.GetAttributeByName(ActiveDirectoryConnector.ATT_OBJECT_SID); + Byte[] sidBytes = (Byte[])ConnectorAttributeUtil.GetSingleValue(sidAttr); + SecurityIdentifier newUserSid = new SecurityIdentifier(sidBytes, 0); + + // check permissions + DirectoryInfo dirInfo = new DirectoryInfo(homeDir); + DirectorySecurity dirSec = dirInfo.GetAccessControl(); + AuthorizationRuleCollection rules = dirSec.GetAccessRules(true, true, typeof(SecurityIdentifier)); + bool foundCorrectRule = false; + foreach (AuthorizationRule rule in rules) + { + if (rule is FileSystemAccessRule) + { + FileSystemAccessRule fsaRule = (FileSystemAccessRule)rule; + if (fsaRule.IdentityReference.Equals(newUserSid)) + { + if ((fsaRule.AccessControlType.Equals(AccessControlType.Allow)) && + (fsaRule.FileSystemRights.Equals(FileSystemRights.FullControl)) && + (fsaRule.InheritanceFlags.Equals(InheritanceFlags.ContainerInherit | InheritanceFlags.ObjectInherit)) && + (fsaRule.IsInherited.Equals(false))) + { + foundCorrectRule = true; + } + + } + } + } + + // remove the directory (before assertion may fail) + Directory.Delete(homeDir); + + // check that we found the proper permission record + Assert.IsTrue(foundCorrectRule); + } + finally + { + if (userUid != null) + { + DeleteAndVerifyObject(connector, ObjectClass.ACCOUNT, userUid, false, false); + } + } + } + + [Test] // tests that if create home directory is set to false, no directory is created + public void TestCreateWithHomeDirectoryNoCreateConfig_Account() + { + //Initialize Connector + ActiveDirectoryConnector connector = new ActiveDirectoryConnector(); + ActiveDirectoryConfiguration config = (ActiveDirectoryConfiguration)GetConfiguration(); + config.CreateHomeDirectory = false; + connector.Init(config); + Uid userUid = null; + try + { + // get the normal attributes + ICollection createAttributes = GetNormalAttributes_Account(); + + // read the homedir path, and append the samaccountname + StringBuilder homeDirPathBuilder = new StringBuilder(GetProperty(TEST_PARAM_SHARED_HOME_FOLDER)); + if (!homeDirPathBuilder.ToString().EndsWith("\\")) + { + homeDirPathBuilder.Append('\\'); + } + ConnectorAttribute samAccountNameAttr = ConnectorAttributeUtil.Find( + ActiveDirectoryConnector.ATT_SAMACCOUNT_NAME, createAttributes); + homeDirPathBuilder.Append(ConnectorAttributeUtil.GetStringValue(samAccountNameAttr)); + + // if it exists, delete it + String homeDir = homeDirPathBuilder.ToString(); + if (Directory.Exists(homeDir)) + { + Directory.Delete(homeDir); + } + Assert.IsFalse(Directory.Exists(homeDir)); + + // add homeDirectory to the attributes, and create user + createAttributes.Add(ConnectorAttributeBuilder.Build("homeDirectory", homeDir)); + userUid = CreateAndVerifyObject(connector, + ObjectClass.ACCOUNT, createAttributes); + + // now directory should not exist + // (createhomedirectory was set to false in the configuration) + Assert.IsFalse(Directory.Exists(homeDir)); + } + finally + { + if (userUid != null) + { + DeleteAndVerifyObject(connector, ObjectClass.ACCOUNT, userUid, false, false); + } + } + } + + [Test] + public void TestSearchNoFilter_Account() + { + ActiveDirectoryConnector connector = new ActiveDirectoryConnector(); + connector.Init(GetConfiguration()); + ICollection createdUids = new HashSet(); + + try + { + int numCreated = 0; + for (numCreated = 0; numCreated < 5; numCreated++) + { + createdUids.Add(CreateAndVerifyObject(connector, + ObjectClass.ACCOUNT, GetNormalAttributes_Account())); + } + + ICollection results = TestHelpers.SearchToList(connector, + ObjectClass.ACCOUNT, null); + + // not sure how many should be found ... it should find everything + // it's hard to say how many that is, but it should at least find the + // number we created + Assert.GreaterOrEqual(results.Count, numCreated); + + // check that they are all of the proper objectclass + foreach (ConnectorObject co in results) + { + ConnectorAttribute objectClassAttr = + co.GetAttributeByName("objectClass"); + Boolean foundCorrectObjectClass = false; + foreach (Object o in objectClassAttr.Value) + { + if ((o is String) && (o != null)) + { + String stringValue = (String)o; + if (stringValue.ToUpper().Trim().Equals("USER")) + { + foundCorrectObjectClass = true; + } + } + } + Assert.IsTrue(foundCorrectObjectClass); + } + } + finally + { + foreach (Uid uid in createdUids) + { + if (uid != null) + { + DeleteAndVerifyObject(connector, ObjectClass.ACCOUNT, uid, false, true); + } + } + } + } + + [Test] + public void TestSearchNoFilter_Group() + { + ActiveDirectoryConnector connector = new ActiveDirectoryConnector(); + connector.Init(GetConfiguration()); + ICollection createdUids = new HashSet(); + + try + { + int numCreated = 0; + for (numCreated = 0; numCreated < 5; numCreated++) + { + createdUids.Add(CreateAndVerifyObject(connector, + ObjectClass.GROUP, GetNormalAttributes_Group())); + } + + ICollection results = TestHelpers.SearchToList(connector, + ObjectClass.GROUP, null); + + // not sure how many should be found ... it should find everything + // it's hard to say how many that is, but it should at least find the + // number we created + Assert.GreaterOrEqual(results.Count, numCreated); + + // check that they are all of the proper objectclass + foreach (ConnectorObject co in results) + { + ConnectorAttribute objectClassAttr = + co.GetAttributeByName("objectClass"); + Boolean foundCorrectObjectClass = false; + foreach (Object o in objectClassAttr.Value) + { + if ((o is String) && (o != null)) + { + String stringValue = (String)o; + if (stringValue.ToUpper().Trim().Equals("GROUP")) + { + foundCorrectObjectClass = true; + } + } + } + Assert.IsTrue(foundCorrectObjectClass); + } + } + finally + { + foreach (Uid uid in createdUids) + { + if (uid != null) + { + DeleteAndVerifyObject(connector, ObjectClass.GROUP, uid, false, true); + } + } + } + } + + [Test] + public void TestSearchByName_account() + { + ActiveDirectoryConnector connector = new ActiveDirectoryConnector(); + connector.Init(GetConfiguration()); + + Uid createUid = null; + + try + { + createUid = CreateAndVerifyObject(connector, ObjectClass.ACCOUNT, + GetNormalAttributes_Account()); + + // find out what the name was + ConnectorObject newObject = GetConnectorObjectFromUid(connector, + ObjectClass.ACCOUNT, createUid); + Name nameAttr = newObject.Name; + Assert.IsNotNull(nameAttr); + + //search normally + ICollection results = TestHelpers.SearchToList(connector, + ObjectClass.ACCOUNT, FilterBuilder.EqualTo(nameAttr)); + + // there really should only be one + Assert.AreEqual(results.Count, 1); + + // and it must have the value we were searching for + String createName = ActiveDirectoryUtils.NormalizeLdapString(nameAttr.GetNameValue()); + String foundName = ActiveDirectoryUtils.NormalizeLdapString(results.ElementAt(0).Name.GetNameValue()); + Assert.AreEqual(createName, foundName); + + //search in uppercase + ConnectorAttribute nameUpper = ConnectorAttributeBuilder.Build( + nameAttr.Name, nameAttr.GetNameValue().ToUpper()); + results = TestHelpers.SearchToList(connector, + ObjectClass.ACCOUNT, FilterBuilder.EqualTo(nameUpper)); + + // there really should only be one + Assert.AreEqual(results.Count, 1); + + // and it must have the value we were searching for + createName = ActiveDirectoryUtils.NormalizeLdapString(nameAttr.GetNameValue()); + foundName = ActiveDirectoryUtils.NormalizeLdapString(results.ElementAt(0).Name.GetNameValue()); + Assert.AreEqual(createName, foundName); + + //search in lowercase + ConnectorAttribute nameLower = ConnectorAttributeBuilder.Build( + nameAttr.Name, nameAttr.GetNameValue().ToLower()); + results = TestHelpers.SearchToList(connector, + ObjectClass.ACCOUNT, FilterBuilder.EqualTo(nameLower)); + + // there really should only be one + Assert.AreEqual(results.Count, 1); + + // and it must have the value we were searching for + createName = ActiveDirectoryUtils.NormalizeLdapString(nameAttr.GetNameValue()); + foundName = ActiveDirectoryUtils.NormalizeLdapString(results.ElementAt(0).Name.GetNameValue()); + Assert.AreEqual(createName, foundName); + + } + finally + { + if (createUid != null) + { + //remove the one we created + DeleteAndVerifyObject(connector, ObjectClass.ACCOUNT, + createUid, false, true); + } + } + } + + [Test] + public void TestSearchByName_group() + { + ActiveDirectoryConnector connector = new ActiveDirectoryConnector(); + connector.Init(GetConfiguration()); + + Uid createUid = null; + + try + { + createUid = CreateAndVerifyObject(connector, ObjectClass.GROUP, + GetNormalAttributes_Group()); + + // find out what the name was + ConnectorObject newObject = GetConnectorObjectFromUid(connector, + ObjectClass.GROUP, createUid); + Name nameAttr = newObject.Name; + Assert.IsNotNull(nameAttr); + + //search normally + ICollection results = TestHelpers.SearchToList(connector, + ObjectClass.GROUP, FilterBuilder.EqualTo(nameAttr)); + + // there really should only be one + Assert.AreEqual(results.Count, 1); + + // and it must have the value we were searching for + String createName = ActiveDirectoryUtils.NormalizeLdapString(nameAttr.GetNameValue()); + String foundName = ActiveDirectoryUtils.NormalizeLdapString(results.ElementAt(0).Name.GetNameValue()); + Assert.AreEqual(createName, foundName); + + //search in uppercase + ConnectorAttribute nameUpper = ConnectorAttributeBuilder.Build( + nameAttr.Name, nameAttr.GetNameValue().ToUpper()); + results = TestHelpers.SearchToList(connector, + ObjectClass.GROUP, FilterBuilder.EqualTo(nameUpper)); + + // there really should only be one + Assert.AreEqual(results.Count, 1); + + // and it must have the value we were searching for + createName = ActiveDirectoryUtils.NormalizeLdapString(nameAttr.GetNameValue()); + foundName = ActiveDirectoryUtils.NormalizeLdapString(results.ElementAt(0).Name.GetNameValue()); + Assert.AreEqual(createName, foundName); + + //search in lowercase + ConnectorAttribute nameLower = ConnectorAttributeBuilder.Build( + nameAttr.Name, nameAttr.GetNameValue().ToLower()); + results = TestHelpers.SearchToList(connector, + ObjectClass.GROUP, FilterBuilder.EqualTo(nameLower)); + + // there really should only be one + Assert.AreEqual(results.Count, 1); + + // and it must have the value we were searching for + createName = ActiveDirectoryUtils.NormalizeLdapString(nameAttr.GetNameValue()); + foundName = ActiveDirectoryUtils.NormalizeLdapString(results.ElementAt(0).Name.GetNameValue()); + Assert.AreEqual(createName, foundName); + + } + finally + { + if (createUid != null) + { + //remove the one we created + DeleteAndVerifyObject(connector, ObjectClass.GROUP, + createUid, false, true); + } + } + } + + [Test] + public void TestSearchByCNWithWildcard_account() + { + ActiveDirectoryConnector connector = new ActiveDirectoryConnector(); + connector.Init(GetConfiguration()); + + Uid uid1 = null; + Uid uid2 = null; + + try + { + // create a couple things to find + uid1 = CreateAndVerifyObject(connector, ObjectClass.ACCOUNT, + GetNormalAttributes_Account()); + uid2 = CreateAndVerifyObject(connector, ObjectClass.ACCOUNT, + GetNormalAttributes_Account()); + + ICollection results = TestHelpers.SearchToList(connector, + ObjectClass.ACCOUNT, FilterBuilder.EqualTo( + ConnectorAttributeBuilder.Build("CN", "nunit*"))); + + // there should be at least the two we just created + Assert.GreaterOrEqual(results.Count, 2); + foreach (ConnectorObject co in results) + { + // and it must have the value we were searching for + String foundName = ActiveDirectoryUtils.NormalizeLdapString( + co.Name.GetNameValue()); + Assert.That(foundName.ToUpper().StartsWith("CN=NUNIT")); + } + } + finally + { + if (uid1 != null) + { + //remove the one we created + DeleteAndVerifyObject(connector, ObjectClass.ACCOUNT, + uid1, false, true); + } + if (uid2 != null) + { + //remove the one we created + DeleteAndVerifyObject(connector, ObjectClass.ACCOUNT, + uid2, false, true); + } + } + } + + [Test] + public void TestSearchByRegularAttribute_account() + { + ActiveDirectoryConnector connector = new ActiveDirectoryConnector(); + connector.Init(GetConfiguration()); + + Uid createUid = null; + + try + { + ConnectorAttribute createSnAttr = + ConnectorAttributeBuilder.Build("sn", "nunitSearch"); + ICollection attributes = GetNormalAttributes_Account(); + attributes.Remove(ConnectorAttributeUtil.Find("sn", attributes)); + attributes.Add(createSnAttr); + createUid = CreateAndVerifyObject(connector, ObjectClass.ACCOUNT, + attributes); + + ICollection results = TestHelpers.SearchToList(connector, + ObjectClass.ACCOUNT, FilterBuilder.EqualTo(createSnAttr)); + + // there should be at least the newly created one + Assert.GreaterOrEqual(results.Count, 1); + + // and it must have the value we were searching for + Boolean foundCreated = false; + foreach (ConnectorObject resultObject in results) + { + ConnectorAttribute foundSnAttr = + resultObject.GetAttributeByName("sn"); + Assert.AreEqual(createSnAttr, foundSnAttr); + + // keep track of if we've found the one we created + if (createUid.Equals(resultObject.Uid)) + { + // cant have it twice + Assert.IsFalse(foundCreated); + foundCreated = true; + } + } + // be certain we saw the one we created + Assert.IsTrue(foundCreated); + } + finally + { + if (createUid != null) + { + //remove the one we created + DeleteAndVerifyObject(connector, ObjectClass.ACCOUNT, + createUid, false, true); + } + } + } + + [Test] + public void TestSearchByRegularAttributeWithWildcard_account() + { + ActiveDirectoryConnector connector = new ActiveDirectoryConnector(); + connector.Init(GetConfiguration()); + + ICollection uids = new HashSet(); + + try + { + Random random = new Random(); + Int32 randomNumber = random.Next(1000000); + + String snPrefix = "nunitWCTest"; + + for (int i = 0; i < 10; i++) + { + ICollection attributes = + GetNormalAttributes_Account(); + attributes.Remove(ConnectorAttributeUtil.Find("sn", attributes)); + attributes.Add(ConnectorAttributeBuilder.Build("sn", + snPrefix + random.Next())); + Uid tempUid = CreateAndVerifyObject(connector, ObjectClass.ACCOUNT, + attributes); + Assert.IsNotNull(tempUid); + uids.Add(tempUid); + } + + ICollection results = TestHelpers.SearchToList(connector, + ObjectClass.ACCOUNT, FilterBuilder.StartsWith( + ConnectorAttributeBuilder.Build("sn", snPrefix))); + + // there should be at least the newly created one + Assert.GreaterOrEqual(results.Count, 1); + + // make a duplicate list + ICollection uidsToValidate = new HashSet(uids); + + // and it must have the value we were searching for + foreach (ConnectorObject resultObject in results) + { + ConnectorAttribute foundSnAttr = + resultObject.GetAttributeByName("sn"); + String snValue = ConnectorAttributeUtil.GetStringValue(foundSnAttr); + Assert.That(snValue.StartsWith(snPrefix)); + uidsToValidate.Remove(resultObject.Uid); + if (uidsToValidate.Count == 0) + { + break; + } + } + Assert.AreEqual(0, uidsToValidate.Count); + } + finally + { + foreach (Uid createdUid in uids) + { + //remove the one we created + DeleteAndVerifyObject(connector, ObjectClass.ACCOUNT, + createdUid, false, true); + } + } + } + + // test proper behavior of create with ALL attributes specified + [Test] + public void TestCreateWithAllAttributes_Account() + { + //Initialize Connector + ActiveDirectoryConnector connector = new ActiveDirectoryConnector(); + connector.Init(GetConfiguration()); + Uid createUid = null; + + try + { + // create user + ICollection createAttributes = GetAllAttributes_Account(); + createUid = CreateAndVerifyObject(connector, + ObjectClass.ACCOUNT, createAttributes); + } + finally + { + if (createUid != null) + { + //remove the one we created + DeleteAndVerifyObject(connector, ObjectClass.ACCOUNT, + createUid, false, true); + } + } + } + + // test proper behavior of create with ALL attributes specified + [Test] + public void Test_OpAtt_Enabled_Account() + { + //Initialize Connector + ActiveDirectoryConnector connector = new ActiveDirectoryConnector(); + connector.Init(GetConfiguration()); + Uid createUid = null; + + try + { + // create user + ICollection createAttributes = GetNormalAttributes_Account(); + createAttributes.Add(ConnectorAttributeBuilder.BuildEnabled(true)); + createUid = CreateAndVerifyObject(connector, + ObjectClass.ACCOUNT, createAttributes); + + ICollection updateReplaceAttributes = + new HashSet(); + updateReplaceAttributes.Add(ConnectorAttributeBuilder.BuildEnabled(false)); + UpdateReplaceAndVerifyObject(connector, ObjectClass.ACCOUNT, + createUid, updateReplaceAttributes); + } + finally + { + if (createUid != null) + { + //remove the one we created + DeleteAndVerifyObject(connector, ObjectClass.ACCOUNT, + createUid, false, true); + } + } + } + + // Test scripting + [Test] + public void TestScriptOnResource() + { + //Initialize Connector + ActiveDirectoryConnector connector = new ActiveDirectoryConnector(); + connector.Init(GetConfiguration()); + + try + { + RunScript(connector, "", ""); + RunScript(connector, GetProperty(CONFIG_PROPERTY_SCRIPT_USER_LOCAL), + GetProperty(CONFIG_PROPERTY_SCRIPT_PASSWORD_LOCAL)); + RunScript(connector, GetProperty(CONFIG_PROPERTY_SCRIPT_USER_DOMAIN), + GetProperty(CONFIG_PROPERTY_SCRIPT_PASSWORD_DOMAIN)); + + + // try with invalid credentials + bool scriptFailed = false; + try + { + RunScript(connector, GetProperty(CONFIG_PROPERTY_USER), "bogus"); + } + catch (Exception e) + { + scriptFailed = true; + } + Assert.That(scriptFailed); + } + finally + { + } + } + + [Test] + public void TestAddGroup_Account() + { + //Initialize Connector + ActiveDirectoryConnector connector = new ActiveDirectoryConnector(); + connector.Init(GetConfiguration()); + Uid groupUid = null; + Uid userUid = null; + try + { + userUid = CreateAndVerifyObject(connector, + ObjectClass.ACCOUNT, GetNormalAttributes_Account()); + Filter userUidFilter = FilterBuilder.EqualTo(userUid); + IList foundUserObjects = + TestHelpers.SearchToList(connector, ObjectClass.ACCOUNT, userUidFilter); + Assert.AreEqual(1, foundUserObjects.Count); + + groupUid = CreateAndVerifyObject(connector, + ObjectClass.GROUP, GetNormalAttributes_Group()); + Filter groupUidFilter = FilterBuilder.EqualTo(groupUid); + IList foundGroupObjects = + TestHelpers.SearchToList(connector, ObjectClass.GROUP, groupUidFilter); + Assert.AreEqual(1, foundGroupObjects.Count); + String groupName = foundGroupObjects[0].Name.GetNameValue(); + + ICollection modifiedAttrs = new HashSet(); + modifiedAttrs.Add(ConnectorAttributeBuilder.Build(PredefinedAttributes.GROUPS_NAME, groupName)); + OperationOptionsBuilder optionsBuilder = new OperationOptionsBuilder(); + ICollection attributesToGet = GetDefaultAttributesToGet(ObjectClass.ACCOUNT); + attributesToGet.Add(PredefinedAttributes.GROUPS_NAME); + optionsBuilder.AttributesToGet = attributesToGet.ToArray(); + UpdateAddAndVerifyUser(connector, ObjectClass.ACCOUNT, + userUid, modifiedAttrs, optionsBuilder.Build()); + + } finally { + DeleteAndVerifyObject(connector, ObjectClass.ACCOUNT, userUid, false, false); + DeleteAndVerifyObject(connector, ObjectClass.GROUP, groupUid, false, false); + } + } + + [Test] + public void TestRemoveAttributeValue() + { + ActiveDirectoryConnector connector = new ActiveDirectoryConnector(); + connector.Init(GetConfiguration()); + + Uid createUid = null; + + try + { + Random random = new Random(); + Int32 randomNumber = random.Next(1000000); + ICollection attributes = new HashSet(); + + attributes.Add(ConnectorAttributeBuilder.Build( + "ad_container", GetProperty(CONFIG_PROPERTY_CONTAINER))); + attributes.Add(ConnectorAttributeBuilder.Build( + "userPassword", "secret")); + attributes.Add(ConnectorAttributeBuilder.Build( + "sAMAccountName", "nunit" + randomNumber)); + attributes.Add(ConnectorAttributeBuilder.Build( + "givenName", "nunit")); + attributes.Add(ConnectorAttributeBuilder.Build( + "sn", "TestUser" + randomNumber)); + attributes.Add(ConnectorAttributeBuilder.Build( + "displayName", "nunit test user " + randomNumber)); + attributes.Add(ConnectorAttributeBuilder.Build( + Name.NAME, "cn=nunit" + randomNumber + "," + + GetProperty(CONFIG_PROPERTY_CONTAINER))); + attributes.Add(ConnectorAttributeBuilder.Build( + "mail", "nunitUser" + randomNumber + "@some.com")); + attributes.Add(ConnectorAttributeBuilder.Build( + "otherHomePhone", "512.555.1212", "512.123.4567")); + + createUid = CreateAndVerifyObject(connector, ObjectClass.ACCOUNT, + attributes); + + ICollection modifyAttributes = new HashSet(); + modifyAttributes.Add(createUid); + modifyAttributes.Add(ConnectorAttributeBuilder.Build("otherHomePhone", "512.555.1212")); + + connector.Update(UpdateType.DELETE, ObjectClass.ACCOUNT, modifyAttributes, null); + + Filter uidFilter = FilterBuilder.EqualTo(createUid); + IList objects = TestHelpers.SearchToList(connector, ObjectClass.ACCOUNT, uidFilter); + Assert.AreEqual(1, objects.Count); + + ConnectorAttribute otherHomePhoneAttr = ConnectorAttributeUtil.Find( + "otherHomePhone", objects[0].GetAttributes()); + + Assert.AreEqual(1, otherHomePhoneAttr.Value.Count); + Assert.AreEqual("512.123.4567", ConnectorAttributeUtil.GetSingleValue(otherHomePhoneAttr)); + } + finally + { + if (createUid != null) + { + //remove the one we created + DeleteAndVerifyObject(connector, ObjectClass.ACCOUNT, + createUid, false, true); + } + } + } + + + [Ignore] + [Test] + public void TestContainerChange_account() + { + ActiveDirectoryConnector connector = new ActiveDirectoryConnector(); + connector.Init(GetConfiguration()); + + Uid createGroupUid = null; + Uid createUserUid = null; + + try { + // create container for this test + ICollection groupAttributes = GetNormalAttributes_Group(); + createGroupUid = CreateAndVerifyObject(connector, + ObjectClass.GROUP, groupAttributes); + ICollection groupResults = TestHelpers.SearchToList( + connector, ObjectClass.GROUP, FilterBuilder.EqualTo(createGroupUid)); + Assert.AreEqual(1, groupResults.Count); + Assert.AreEqual(createGroupUid, groupResults.ElementAt(0).Uid); + ConnectorAttribute groupDnAttr = + groupResults.ElementAt(0).GetAttributeByName("distinguishedName"); + Assert.IsNotNull(groupDnAttr); + String groupPath = ConnectorAttributeUtil.GetStringValue(groupDnAttr); + + // create user + createUserUid = CreateAndVerifyObject(connector, + ObjectClass.ACCOUNT, GetNormalAttributes_Account()); + + //now change user container to the newly created one + ICollection updateAttrs = new HashSet(); + updateAttrs.Add(ConnectorAttributeBuilder.Build("ad_container", groupPath)); + updateAttrs.Add(createUserUid); + + connector.Update(UpdateType.REPLACE, ObjectClass.ACCOUNT, updateAttrs, null); + + ICollection results = TestHelpers.SearchToList( + connector, ObjectClass.ACCOUNT, FilterBuilder.EqualTo(createUserUid)); + Assert.AreEqual(1, results.Count); + Assert.AreEqual(createUserUid, results.ElementAt(0).Uid); + ConnectorAttribute foundContainerAttr = results.ElementAt(0).GetAttributeByName("ad_container"); + Assert.IsNotNull(foundContainerAttr); + + String lhs = ActiveDirectoryUtils.NormalizeLdapString(groupPath); + String rhs = ActiveDirectoryUtils.NormalizeLdapString(ConnectorAttributeUtil.GetStringValue(foundContainerAttr)); + Assert.AreEqual(lhs, rhs); + } + finally + { + if (createUserUid != null) + { + //remove the one we created + DeleteAndVerifyObject(connector, ObjectClass.ACCOUNT, + createUserUid, false, true); + } + + if (createGroupUid != null) + { + //remove the one we created + DeleteAndVerifyObject(connector, ObjectClass.GROUP, + createGroupUid, false, true); + } + } + } + + [Ignore] + [Test] + public void TestContainerChange_group() + { + ActiveDirectoryConnector connector = new ActiveDirectoryConnector(); + connector.Init(GetConfiguration()); + + // create user + Uid createUid = CreateAndVerifyObject(connector, + ObjectClass.ACCOUNT, GetNormalAttributes_Account()); + + // create container for this test + + //now change user container to the newly created one + + throw new NotImplementedException(); + } + + [Ignore] + [Test] + public void TestEnableDate() + { + //Initialize Connector + ActiveDirectoryConnector connector = new ActiveDirectoryConnector(); + connector.Init(GetConfiguration()); + Uid createUid = null; + + try + { + // create user + ICollection createAttributes = GetNormalAttributes_Account(); + createAttributes.Add(ConnectorAttributeBuilder.BuildEnabled(true)); + createAttributes.Add(ConnectorAttributeBuilder.BuildEnableDate(new DateTime(2000, 01, 01))); + createUid = CreateAndVerifyObject(connector, + ObjectClass.ACCOUNT, createAttributes); + + ICollection updateReplaceAttributes = + new HashSet(); + updateReplaceAttributes.Add(ConnectorAttributeBuilder.BuildEnabled(false)); + UpdateReplaceAndVerifyObject(connector, ObjectClass.ACCOUNT, + createUid, updateReplaceAttributes); + } + finally + { + if (createUid != null) + { + //remove the one we created + DeleteAndVerifyObject(connector, ObjectClass.ACCOUNT, + createUid, false, true); + } + } + } + + [Ignore] + [Test] + public void TestDisableDate() + { + //Initialize Connector + ActiveDirectoryConnector connector = new ActiveDirectoryConnector(); + connector.Init(GetConfiguration()); + Uid createUid = null; + + try + { + // create user + ICollection createAttributes = GetNormalAttributes_Account(); + createAttributes.Add(ConnectorAttributeBuilder.BuildEnabled(true)); + + // disable tommorrow + DateTime disableDate = DateTime.Now.AddHours(24); + + createAttributes.Add(ConnectorAttributeBuilder.BuildDisableDate(disableDate)); + createUid = CreateAndVerifyObject(connector, + ObjectClass.ACCOUNT, createAttributes); + + ICollection updateReplaceAttributes = + new HashSet(); + updateReplaceAttributes.Add(ConnectorAttributeBuilder.BuildEnabled(false)); + UpdateReplaceAndVerifyObject(connector, ObjectClass.ACCOUNT, + createUid, updateReplaceAttributes); + } + finally + { + if (createUid != null) + { + //remove the one we created + DeleteAndVerifyObject(connector, ObjectClass.ACCOUNT, + createUid, false, true); + } + } + } + + // test sync + [Test] + public void TestSyncGC() + { + // test with searchChildDomain (uses GC) + TestSync(true); + } + + // test sync + [Test] + public void TestSyncDC() + { + // test withouth searchChildDomains (uses DC) + TestSync(false); + } + + [Test] + public void TestUserPasswordChange() + { + //Initialize Connector + ActiveDirectoryConnector connector = new ActiveDirectoryConnector(); + connector.Init(GetConfiguration()); + Uid createUid = null; + + try + { + // create user + ICollection createAttributes = GetNormalAttributes_Account(); + // remove password, and set to something memorable + createAttributes.Remove(ConnectorAttributeUtil.Find(OperationalAttributes.PASSWORD_NAME, createAttributes)); + GuardedString gsCurrentPassword = new GuardedString(); + foreach (char c in "1Password") + { + gsCurrentPassword.AppendChar(c); + } + createAttributes.Add(ConnectorAttributeBuilder.BuildPassword(gsCurrentPassword)); + createAttributes.Add(ConnectorAttributeBuilder.BuildEnabled(true)); + createUid = CreateAndVerifyObject(connector, + ObjectClass.ACCOUNT, createAttributes); + + // make sure authenticate works here + connector.Authenticate(ConnectorAttributeUtil.GetNameFromAttributes(createAttributes).GetNameValue(), + gsCurrentPassword, null); + + ICollection updateReplaceAttributes = + new HashSet(); + GuardedString gsNewPassword = new GuardedString(); + foreach (char c in "LongPassword2MeetTheRequirements!") + { + gsNewPassword.AppendChar(c); + } + updateReplaceAttributes.Add(ConnectorAttributeBuilder.BuildCurrentPassword(gsCurrentPassword)); + updateReplaceAttributes.Add(ConnectorAttributeBuilder.BuildPassword(gsNewPassword)); + UpdateReplaceAndVerifyObject(connector, ObjectClass.ACCOUNT, + createUid, updateReplaceAttributes); + + // make sure authenticate works here + connector.Authenticate(ConnectorAttributeUtil.GetNameFromAttributes(createAttributes).GetNameValue(), + gsNewPassword, null); + + bool caughtAuthenticateFailedException = false; + try + { + // make sure authenticate doesnt work with original password + connector.Authenticate(ConnectorAttributeUtil.GetNameFromAttributes(createAttributes).GetNameValue(), + gsCurrentPassword, null); + } + catch (Exception e) + { + caughtAuthenticateFailedException = true; + } + + Assert.IsTrue(caughtAuthenticateFailedException, "Negative test case should throw an exception"); + + + // now a negative test case + GuardedString gsBogusPassword = new GuardedString(); + foreach (char c in "BogusPassword") + { + gsBogusPassword.AppendChar(c); + } + ICollection updateErrorReplaceAttributes = + new HashSet(); + updateErrorReplaceAttributes.Add(ConnectorAttributeBuilder.BuildCurrentPassword(gsBogusPassword)); + updateErrorReplaceAttributes.Add(ConnectorAttributeBuilder.BuildPassword(gsNewPassword)); + bool caughtWrongCurrentPasswordException = false; + try + { + // update should fail due to wrong current password + UpdateReplaceAndVerifyObject(connector, ObjectClass.ACCOUNT, + createUid, updateErrorReplaceAttributes); + } + catch (Exception e) + { + caughtWrongCurrentPasswordException = true; + } + + Assert.IsTrue(caughtWrongCurrentPasswordException, "Negative test case should throw an exception"); + + // make sure authenticate works here + + } + finally + { + if (createUid != null) + { + //remove the one we created + DeleteAndVerifyObject(connector, ObjectClass.ACCOUNT, + createUid, false, true); + } + } + } + + public void TestSync(bool searchChildDomains) { + //Initialize Connector + ActiveDirectoryConnector connector = new ActiveDirectoryConnector(); + ActiveDirectoryConfiguration configuration = + (ActiveDirectoryConfiguration)GetConfiguration(); + configuration.SearchChildDomains = searchChildDomains; + connector.Init(configuration); + + Uid createUid = null; + + try + { + SyncTestHelper syncHelper = new SyncTestHelper(); + + // do the first sync + connector.Sync(ObjectClass.ACCOUNT, syncHelper.Token, syncHelper.SyncHandler_Initial, null); + + syncHelper.Init(); + ICollection attributes = null; + ICollection createdUids = new List(); + + // create some users + for (int i = 0; i < 10; i++) + { + attributes = GetNormalAttributes_Account(); + createUid = CreateAndVerifyObject(connector, ObjectClass.ACCOUNT, + attributes); + syncHelper.AddModUid(createUid, attributes); + createdUids.Add(createUid); + } + + // sync, and verify + connector.Sync(ObjectClass.ACCOUNT, syncHelper.Token, syncHelper.SyncHandler_ModifiedAccounts, null); + syncHelper.CheckAllSyncsProcessed(); + + // reset everything + syncHelper.Init(); + + // modify a user, then add some users, then modify one of the added users + attributes = new List(); + attributes.Add(createdUids.First()); + attributes.Add(ConnectorAttributeBuilder.Build("sn", "replaced")); + connector.Update(UpdateType.REPLACE, ObjectClass.ACCOUNT, attributes, null); + syncHelper.AddModUid(createdUids.First(), attributes); + + for(int i = 0; i < 10; i++) + { + attributes = GetNormalAttributes_Account(); + createUid = CreateAndVerifyObject(connector, ObjectClass.ACCOUNT, + attributes); + syncHelper.AddModUid(createUid, attributes); + createdUids.Add(createUid); + } + + attributes = new List(); + attributes.Add(createdUids.Last()); + attributes.Add(ConnectorAttributeBuilder.Build("sn", "replaced")); + connector.Update(UpdateType.REPLACE, ObjectClass.ACCOUNT, attributes, null); + syncHelper.AddModUid(createdUids.Last(), attributes); + + // sync, and verify + connector.Sync(ObjectClass.ACCOUNT, syncHelper.Token, syncHelper.SyncHandler_ModifiedAccounts, null); + syncHelper.CheckAllSyncsProcessed(); + + syncHelper.Init(); + // delete the user + foreach (Uid uid in createdUids) + { + DeleteAndVerifyObject(connector, ObjectClass.ACCOUNT, uid, + false, true); + syncHelper.AddDelUid(uid); + } + // sync and verify + connector.Sync(ObjectClass.ACCOUNT, syncHelper.Token, syncHelper.SyncHandler_DeletedAccounts, null); + syncHelper.CheckAllSyncsProcessed(); + } + finally + { + } + } + + class SyncTestHelper + { + IDictionary> _mods = null; + IList _dels = null; + + public SyncToken Token { get; set; } + + public void Init() + { + _mods = new Dictionary>(); + _dels = new List(); + } + + public void AddModUid(Uid uid, ICollection attributes) + { + _mods[uid]=attributes; + } + + public void AddDelUid(Uid uid) + { + _dels.Add(uid); + } + + public bool SyncHandler_Initial(SyncDelta delta) + { + // do nothing .. just establishing the baseline + Token = delta.Token; + return true; + } + + public bool SyncHandler_ModifiedAccounts(SyncDelta delta) + { + // just ignore extra ones. they might have come in by other means + if (_mods.ContainsKey(delta.Uid)) + { + ICollection requestedAttrs = _mods[delta.Uid]; + ConnectorObjectBuilder builder = new ConnectorObjectBuilder(); + builder.ObjectClass = ObjectClass.ACCOUNT; + builder.SetUid(delta.Uid); + builder.AddAttributes(delta.Attributes); + ActiveDirectoryConnectorTest.VerifyObject(requestedAttrs, + builder.Build()); + _mods.Remove(delta.Uid); + } + return true; + } + + public bool SyncHandler_DeletedAccounts(SyncDelta delta) + { + _dels.Remove(delta.Uid); + return true; + } + + public bool SyncHandler_Mixed(SyncDelta delta) + { + return true; + } + + public void CheckAllSyncsProcessed() + { + // since the handlers remove things from + // the list as found, this method is called + // at then end of a sync, and all arrays should + // be empty ... meaning everything is accounted + // for. + Assert.AreEqual(0, _dels.Count); + Assert.AreEqual(0, _mods.Count); + } + } + + public void RunScript(ActiveDirectoryConnector connector, String user, + string password) + { + string tempFileName = Path.GetTempFileName(); + string scriptText = String.Format( + "echo %ARG0%:%ARG1%:%USERNAME%:%PASSWORD% > \"{0}\"", tempFileName); + + IDictionary arguments = new Dictionary(); + string arg0 = "argument_zero"; + string arg1 = "argument one"; + arguments.Add("ARG0", arg0); + arguments.Add("ARG1", arg1); + + OperationOptionsBuilder builder = new OperationOptionsBuilder(); + if (user.Length > 0) + { + builder.RunAsUser = user; + } + if (password.Length > 0) + { + builder.RunWithPassword = password; + } + + ScriptContext context = new ScriptContext("Shell", scriptText, arguments); + object resultObject = connector.RunScriptOnResource(context, builder.Build()); + Assert.IsNotNull(resultObject); + Assert.That(resultObject is int); + Assert.AreEqual(0, resultObject); + FileStream outputFs = new FileStream(tempFileName, FileMode.Open, FileAccess.Read); + StreamReader outputReader = new StreamReader(outputFs); + // read the first line + string output = outputReader.ReadLine(); + string[] returnedArray = output.Split(':'); + Assert.AreEqual(4, returnedArray.Length); + Assert.AreEqual(arg0, returnedArray[0]); + Assert.AreEqual(arg1, returnedArray[1]); + } + + // does a create and verify, then looks up and returns + // the new user's dn (used for adding to a group) + public String CreateGroupMember(ActiveDirectoryConnector connector) + { + Uid uidMember = CreateAndVerifyObject(connector, ObjectClass.ACCOUNT, + GetNormalAttributes_Account()); + + Filter uidFilter = FilterBuilder.EqualTo(uidMember); + ICollection foundObjects = TestHelpers.SearchToList( + connector, ObjectClass.ACCOUNT, uidFilter); + Assert.IsTrue(foundObjects.Count == 1); + String dnMember = ConnectorAttributeUtil.GetAsStringValue( + foundObjects.ElementAt(0).GetAttributeByName("distinguishedName")); + return dnMember; + } + /* + public Uid CreateAndVerifyObject(ActiveDirectoryConnector connector, + ObjectClass oclass, ICollection attributes) + { + // create object + Uid uid = connector.Create(oclass, attributes, null); + Assert.IsNotNull(uid); + + // find new object + Filter uidFilter = FilterBuilder.EqualTo(uid); + ICollection foundObjects = TestHelpers.SearchToList( + connector, oclass, uidFilter); + Assert.IsTrue(foundObjects.Count == 1); + VerifyObject(attributes, foundObjects.ElementAt(0)); + return uid; + } + */ + public Uid UpdateReplaceAndVerifyObject(ActiveDirectoryConnector connector, + ObjectClass oclass, Uid uid, ICollection attributes) + { + attributes.Add(uid); + Filter uidFilter = FilterBuilder.EqualTo(uid); + + // find the object ... can't update if it doesn't exist + ICollection currentConnectorObjects = TestHelpers.SearchToList( + connector, oclass, uidFilter); + Assert.AreEqual(1, currentConnectorObjects.Count); + + Uid updatedUid = connector.Update(UpdateType.REPLACE, oclass, + attributes, null); + + Assert.IsNotNull(updatedUid); + + ICollection updatedConnectorObjects = TestHelpers.SearchToList( + connector, oclass, uidFilter); + Assert.IsTrue(updatedConnectorObjects.Count == 1); + VerifyObject(attributes, updatedConnectorObjects.ElementAt(0)); + return updatedUid; + } + + public Uid UpdateAddAndVerifyUser(ActiveDirectoryConnector connector, + ObjectClass oclass, Uid uid, ICollection attributes, + OperationOptions searchOptions) + { + // find the existing one, and save off all attributes + Filter uidFilter = FilterBuilder.EqualTo(uid); + ICollection currentObjects = TestHelpers.SearchToList( + connector, oclass, uidFilter, searchOptions); + Assert.IsTrue(currentObjects.Count == 1); + ICollection currentAttributes = + currentObjects.ElementAt(0).GetAttributes(); + + // build a list that has the 'added' values added to the existing values + ICollection comparisonAttributes = new List(); + foreach (ConnectorAttribute updateAttribute in attributes) + { + ConnectorAttribute existingAttribute = ConnectorAttributeUtil.Find( + updateAttribute.Name, currentAttributes); + comparisonAttributes.Add(AttConcat(updateAttribute, existingAttribute)); + } + + // make sure the uid is present in the attributes + attributes.Add(uid); + // now update with ADD to add additional home phones + Uid updatedUid = connector.Update(UpdateType.ADD, oclass, + attributes, null); + + // find it back + ICollection updatedObjects = TestHelpers.SearchToList( + connector, oclass, uidFilter, searchOptions); + Assert.IsTrue(updatedObjects.Count == 1); + + VerifyObject(comparisonAttributes, updatedObjects.ElementAt(0)); + + return updatedUid; + } + + /// + /// Concatenates two attributes' values + /// + /// Must be non null + /// May be null + /// new attribute with name of ca1 and value of ca1 + ca2 + public ConnectorAttribute AttConcat(ConnectorAttribute ca1, ConnectorAttribute ca2) + { + ConnectorAttributeBuilder builder = new ConnectorAttributeBuilder(); + Assert.IsNotNull(ca1); + if (ca2 == null) + { + // if the second is null, just build up a dummy one + ca2 = ConnectorAttributeBuilder.Build(ca1.Name); + } + + Assert.AreEqual(ca1.Name, ca2.Name); + builder.Name = ca1.Name; + builder.AddValue(ca1.Value); + builder.AddValue(ca2.Value); + + return builder.Build(); + } + + public Uid UpdateDeleteAndVerifyUser(ActiveDirectoryConnector connector, + ICollection attributes) + { + throw new NotImplementedException(); + } + + public void DeleteAndVerifyObject(ActiveDirectoryConnector connector, + ObjectClass oclass, Uid uid, bool verifyExists, bool verifyDeleted) + { + Filter uidFilter = FilterBuilder.EqualTo(uid); + + if (verifyExists) + { + // verify that object currently exists + ICollection foundObjects = TestHelpers.SearchToList( + connector, oclass, uidFilter); + + // verify that it was deleted + Assert.AreEqual(1, foundObjects.Count); + } + + // delete + connector.Delete(oclass, uid, null); + if (verifyDeleted) + { + // verify that object was deleted + ICollection deletedObjects = TestHelpers.SearchToList( + connector, oclass, uidFilter); + + // verify that it was deleted + Assert.AreEqual(0, deletedObjects.Count); + } + } + + [Test] + // note that you must create at least one ou for this test to work + public void TestOuSearch() + { + ActiveDirectoryConnector connector = new ActiveDirectoryConnector(); + ActiveDirectoryConfiguration config = (ActiveDirectoryConfiguration)GetConfiguration(); + connector.Init(config); + ObjectClass OUObjectClass = new ObjectClass("organizationalUnit"); + + ICollection foundObjects = TestHelpers.SearchToList( + connector, OUObjectClass, null); + Assert.Greater(foundObjects.Count, 0); + } + + [Test] + public void TestUnmatchedCaseGUIDSearch() + { + //Initialize Connector + ActiveDirectoryConnector connector = new ActiveDirectoryConnector(); + connector.Init(GetConfiguration()); + Uid userUid = null; + try + { + // test normal case + userUid = CreateAndVerifyObject(connector, + ObjectClass.ACCOUNT, GetNormalAttributes_Account()); + Filter userUidFilter = FilterBuilder.EqualTo(userUid); + IList foundUserObjects = + TestHelpers.SearchToList(connector, ObjectClass.ACCOUNT, userUidFilter); + Assert.AreEqual(1, foundUserObjects.Count); + + // now test for searching with uppercase guid + userUidFilter = FilterBuilder.EqualTo(new Uid(userUid.GetUidValue().ToUpper())); + foundUserObjects = TestHelpers.SearchToList( + connector, ObjectClass.ACCOUNT, userUidFilter); + Assert.AreEqual(1, foundUserObjects.Count); + + // now test for searching with lowercase guid + userUidFilter = FilterBuilder.EqualTo(new Uid(userUid.GetUidValue().ToLower())); + foundUserObjects = TestHelpers.SearchToList( + connector, ObjectClass.ACCOUNT, userUidFilter); + Assert.AreEqual(1, foundUserObjects.Count); + } + finally + { + if (userUid != null) + { + DeleteAndVerifyObject(connector, ObjectClass.ACCOUNT, userUid, false, false); + } + } + } + + public Uid CreateAndVerifyObject(ActiveDirectoryConnector connector, + ObjectClass oclass, ICollection attributes) + { + // create object + Uid uid = CreateObject(connector, oclass, attributes); + VerifyObject(connector, uid, oclass, attributes); + return uid; + } + + public Uid CreateObject(ActiveDirectoryConnector connector, + ObjectClass oclass, ICollection attributes) + { + // create object + Uid uid = connector.Create(oclass, attributes, null); + Assert.IsNotNull(uid); + + return uid; + } + + public void VerifyObject(ActiveDirectoryConnector connector, Uid uid, + ObjectClass oclass, ICollection attributes) + { + // find the object + Filter uidFilter = FilterBuilder.EqualTo(uid); + ICollection foundObjects = TestHelpers.SearchToList( + connector, oclass, uidFilter); + Assert.IsTrue(foundObjects.Count == 1); + + // verify the object + VerifyObject(attributes, foundObjects.ElementAt(0)); + } + + + public Configuration GetConfiguration() + { + ActiveDirectoryConfiguration config = new ActiveDirectoryConfiguration(); + config.DomainName = GetProperty(CONFIG_PROPERTY_DOMAIN_NAME); + config.LDAPHostName = GetProperty(CONFIG_PROPERTY_LDAPHOSTNAME); + config.DirectoryAdminName = GetProperty(CONFIG_PROPERTY_USER); + config.DirectoryAdminPassword = GetProperty(CONFIG_PROPERTY_PASSWORD); + config.SearchContext = GetProperty(CONFIG_PROPERTY_SEARCH_CONTEXT); + config.SyncSearchContext = GetProperty(CONFIG_PROPERTY_SYNC_SEARCH_CONTEXT); + config.SyncDomainController = GetProperty(CONFIG_PROPERTY_SYNC_DOMAIN_CONTROLLER); + config.SyncGlobalCatalogServer = GetProperty(CONFIG_PROPERTY_GC_DOMAIN_CONTROLLER); + return config; + } + + /** + * NOTES: + * - cn and @@NAME@@ should be the same. Test if they are not + * test for proper behavior if @@name@@ is not supplied + * - test bogus attributes to like attribut named BogusAttr = hello + * or something + * - test writing to a read only attribute + * - test attributes with special values such as *, (, ), \ + */ + + public ICollection GetNormalAttributes_Account() + { + ICollection attributes = new HashSet(); + Random random = new Random(); + Int32 randomNumber = random.Next(10000); + + // the container ... is a fabricated attribute + attributes.Add(ConnectorAttributeBuilder.Build( + "ad_container", GetProperty(CONFIG_PROPERTY_CONTAINER))); + attributes.Add(ConnectorAttributeBuilder.Build( + "userPassword", "secret")); + attributes.Add(ConnectorAttributeBuilder.Build( + "sAMAccountName", "nunitUser" + randomNumber)); + attributes.Add(ConnectorAttributeBuilder.Build( + "givenName", "nunit")); + attributes.Add(ConnectorAttributeBuilder.Build( + "sn", "TestUser" + randomNumber)); + attributes.Add(ConnectorAttributeBuilder.Build( + "displayName", "nunit test user " + randomNumber)); + attributes.Add(ConnectorAttributeBuilder.Build( + Name.NAME, "cn=nunit" + randomNumber + "," + + GetProperty(CONFIG_PROPERTY_CONTAINER))); + attributes.Add(ConnectorAttributeBuilder.Build( + "mail", "nunitUser" + randomNumber + "@some.com")); + attributes.Add(ConnectorAttributeBuilder.Build( + "otherHomePhone", "512.555.1212", "512.123.4567")); + return attributes; + } + + public ICollection GetAllAttributes_Account() + { + ICollection attributes = new HashSet(); + Random random = new Random(); + Int32 randomNumber = random.Next(10000); + + // the container ... is a fabricated attribute + attributes.Add(ConnectorAttributeBuilder.Build( + "ad_container", GetProperty(CONFIG_PROPERTY_CONTAINER))); + GuardedString password = new GuardedString(); + foreach (char c in "secret") + { + password.AppendChar(c); + } + attributes.Add(ConnectorAttributeBuilder.BuildPassword( + password)); + attributes.Add(ConnectorAttributeBuilder.Build( + "sAMAccountName", "nunit" + randomNumber)); + attributes.Add(ConnectorAttributeBuilder.Build( + "givenName", "nunit")); + attributes.Add(ConnectorAttributeBuilder.Build( + "sn", "TestUser" + randomNumber)); + attributes.Add(ConnectorAttributeBuilder.Build( + "displayName", "nunit test user " + randomNumber)); + attributes.Add(ConnectorAttributeBuilder.Build( + "mail", "nunitUser" + randomNumber + "@some.com")); + attributes.Add(ConnectorAttributeBuilder.Build( + "telephoneNumber", "333-547-8453")); + // attributes.Add(ConnectorAttributeBuilder.Build( + // "msExchHomeServerName", "")); + attributes.Add(ConnectorAttributeBuilder.Build( + "employeeID", "1234567")); + attributes.Add(ConnectorAttributeBuilder.Build( + "division", "Identity Services")); + attributes.Add(ConnectorAttributeBuilder.Build( + "mobile", "554-210-8631")); + // attributes.Add(ConnectorAttributeBuilder.Build( + // "mDBOverQuotaLimit", "")); + attributes.Add(ConnectorAttributeBuilder.Build( + "middleName", "testCase")); + attributes.Add(ConnectorAttributeBuilder.Build( + "description", "This user was created as a test case for the AD Connector")); + // attributes.Add(ConnectorAttributeBuilder.Build( + // "mDBOverHardQuotaLimit", "")); + // attributes.Add(ConnectorAttributeBuilder.Build( + // "mDBUseDefaults", "")); + attributes.Add(ConnectorAttributeBuilder.Build( + "department", "Connector Affairs")); + // for manager, it looks like the manager has to exist + // attributes.Add(ConnectorAttributeBuilder.Build( + // "manager", "Some Guy")); + // attributes.Add(ConnectorAttributeBuilder.Build( + // "mDBStorageQuota", "")); + // attributes.Add(ConnectorAttributeBuilder.Build( + // "mailNickName", "")); + attributes.Add(ConnectorAttributeBuilder.Build( + "title", "Manager")); + attributes.Add(ConnectorAttributeBuilder.Build( + "initials", "XYZ")); + // attributes.Add(ConnectorAttributeBuilder.Build( + // "homeMTA", "")); + attributes.Add(ConnectorAttributeBuilder.Build( + "co", "United States")); + // attributes.Add(ConnectorAttributeBuilder.Build( + // "homeMDB", "")); + attributes.Add(ConnectorAttributeBuilder.Build( + "company", "NUnit Test Company")); + attributes.Add(ConnectorAttributeBuilder.Build( + "facsimileTelephoneNumber", "111-222-3333")); + attributes.Add(ConnectorAttributeBuilder.Build( + "homePhone", "222-333-4444")); + // attributes.Add(ConnectorAttributeBuilder.Build( + // "directoryEntryWS_PasswordExpired", "")); + attributes.Add(ConnectorAttributeBuilder.Build( + "streetAddress", "12345 Some Street")); + attributes.Add(ConnectorAttributeBuilder.Build( + "l", "Austin")); + attributes.Add(ConnectorAttributeBuilder.Build( + "st", "Texas")); + attributes.Add(ConnectorAttributeBuilder.Build( + "postalCode", "78717")); + // attributes.Add(ConnectorAttributeBuilder.Build( + // "AccountLocked", "")); + + // used to be 'Terminal Services Initial Program' + attributes.Add(ConnectorAttributeBuilder.Build( + TerminalServicesUtils.TS_INITIAL_PROGRAM, "myprog.exe")); + + // used to be 'Terminal Services Initial Program Directory' + attributes.Add(ConnectorAttributeBuilder.Build( + TerminalServicesUtils.TS_INITIAL_PROGRAM_DIR, "c:\\nunittest\\dir")); + + // unknown ... + // attributes.Add(ConnectorAttributeBuilder.Build( + // "Terminal Services Inherit Initial Program", true)); + + // used to be 'Terminal Services Allow Logon' - defaults to false, so testing true + attributes.Add(ConnectorAttributeBuilder.Build( + TerminalServicesUtils.TS_ALLOW_LOGON, 1)); + + // used to be 'Terminal Services Active Session Timeout' + attributes.Add(ConnectorAttributeBuilder.Build( + TerminalServicesUtils.TS_MAX_CONNECTION_TIME, 10000)); + + // used to be 'Terminal Services Disconnected Session Timeout' + attributes.Add(ConnectorAttributeBuilder.Build( + TerminalServicesUtils.TS_MAX_DISCONNECTION_TIME, 20000)); + + // used to be 'Terminal Services Idle Timeout' + attributes.Add(ConnectorAttributeBuilder.Build( + TerminalServicesUtils.TS_MAX_IDLE_TIME, 30000)); + + // used to be 'Terminal Services Connect Client Drives At Logon' + attributes.Add(ConnectorAttributeBuilder.Build( + TerminalServicesUtils.TS_CONNECT_CLIENT_DRIVES_AT_LOGON, 1)); + + // used to be 'Terminal Services Connect Client Printers At Logon' + attributes.Add(ConnectorAttributeBuilder.Build( + TerminalServicesUtils.TS_CONNECT_CLIENT_PRINTERS_AT_LOGON, 1)); + + // used to be 'Terminal Services Default To Main Client Printer' + attributes.Add(ConnectorAttributeBuilder.Build( + TerminalServicesUtils.TS_DEFAULT_TO_MAIN_PRINTER, 1)); + + // used to be 'Terminal Services End Session On Timeout Or Broken Connection' + attributes.Add(ConnectorAttributeBuilder.Build( + TerminalServicesUtils.TS_BROKEN_CONNECTION_ACTION, 1)); + + // used to be 'Terminal Services Allow Reconnect From Originating Client Only' + attributes.Add(ConnectorAttributeBuilder.Build( + TerminalServicesUtils.TS_RECONNECTION_ACTION, 1)); + + // attributes.Add(ConnectorAttributeBuilder.Build( + // "Terminal Services Callback Settings", "")); + + // attributes.Add(ConnectorAttributeBuilder.Build( + // "Terminal Services Callback Phone Number", "")); + + // used to be 'Terminal Services Remote Control Settings' + attributes.Add(ConnectorAttributeBuilder.Build( + TerminalServicesUtils.TS_ENABLE_REMOTE_CONTROL, 1)); + + // used to be 'Terminal Services User Profile + attributes.Add(ConnectorAttributeBuilder.Build( + TerminalServicesUtils.TS_PROFILE_PATH, "\\My Profile")); + + // used to be 'Terminal Services Local Home Directory + attributes.Add(ConnectorAttributeBuilder.Build( + TerminalServicesUtils.TS_HOME_DIRECTORY, "\\My Home Dir")); + + // used to be 'Terminal Services Home Directory Drive + attributes.Add(ConnectorAttributeBuilder.Build( + TerminalServicesUtils.TS_HOME_DRIVE, "C:")); + + // uSNChanged should be read only + // attributes.Add(ConnectorAttributeBuilder.Build( + // "uSNChanged", "")); + // objectGUID should be read only + // attributes.Add(ConnectorAttributeBuilder.Build( + // "objectGUID", "")); + + + // now set name operational attribute + attributes.Add(ConnectorAttributeBuilder.Build( + Name.NAME, "cn=nunit" + randomNumber + "," + + GetProperty(CONFIG_PROPERTY_CONTAINER))); + + + /* + // a few attributes not used in IDM + + // country code is not returned by default + attributes.Add(ConnectorAttributeBuilder.Build( + "countryCode", 23)); + + */ + return attributes; + } + + public ICollection GetNormalAttributes_Group() + { + ICollection attributes = new List(); + Random random = new Random(); + Int32 randomNumber = random.Next(10000); + + // the container ... is a fabricated attribute + attributes.Add(ConnectorAttributeBuilder.Build( + "ad_container", GetProperty(CONFIG_PROPERTY_CONTAINER))); + attributes.Add(ConnectorAttributeBuilder.Build( + "displayName", "nunit test group" + randomNumber + "," + + GetProperty(CONFIG_PROPERTY_CONTAINER))); + attributes.Add(ConnectorAttributeBuilder.Build( + "description", "Original Description" + randomNumber)); + attributes.Add(ConnectorAttributeBuilder.Build( + Name.NAME, "cn=nunitGroup" + randomNumber + "," + + GetProperty(CONFIG_PROPERTY_CONTAINER))); + return attributes; + } + + private static void VerifyObject(ICollection requestedAttributes, + ConnectorObject returnedObject) + { + + // for now, skipping values that are very difficult to + // determine equality ... or they are not returned like + // 'userPassword'. + ICollection skipAttributeNames = new List(); + skipAttributeNames.Add("USERPASSWORD"); + skipAttributeNames.Add(OperationalAttributes.PASSWORD_NAME); + skipAttributeNames.Add(OperationalAttributes.CURRENT_PASSWORD_NAME); + + ICollection ldapStringAttributes = new List(); + ldapStringAttributes.Add("AD_CONTAINER"); + ldapStringAttributes.Add("@@NAME@@"); + + // for each attribute in the connector object ... + foreach (ConnectorAttribute attribute in requestedAttributes) + { + ConnectorAttribute returnedAttribute = returnedObject.GetAttributeByName( + attribute.Name); + + if (skipAttributeNames.Contains(attribute.Name.ToUpper())) + { + Trace.TraceWarning("Skipping comparison of attribute {0}", + attribute.Name); + Trace.TraceWarning("requested values were:"); + foreach (Object requestedValueObject in attribute.Value) + { + Trace.TraceWarning(requestedValueObject.ToString()); + } + if (returnedAttribute == null) + { + Trace.TraceWarning(" no {0} attribute was returned", + attribute.Name); + } + else + { + Trace.TraceWarning("returned values were:"); + foreach (Object returnedValueObject in returnedAttribute.Value) + { + Trace.TraceWarning(returnedValueObject.ToString()); + } + } + continue; + } + + Assert.IsNotNull(returnedAttribute); + + // for each value in the attribute ... + foreach (Object requestedValueObject in attribute.Value) + { + // order of multivalue attributes is not gauranted, so check + // all values of the returned object against the value + // also attributes like the ldap 'objectclass' might return + // more values than I set ... (set User, return user, top, inetorgperson) + Boolean foundValue = false; + foreach (Object returnedValueObject in returnedAttribute.Value) + { + Object lhs = requestedValueObject; + Object rhs = returnedValueObject; + + // if its an ldap string, put it in a standard form + if (ldapStringAttributes.Contains(attribute.Name.ToUpper())) + { + Assert.That(requestedValueObject is String); + Assert.That(returnedValueObject is String); + lhs = ActiveDirectoryUtils.NormalizeLdapString((String)requestedValueObject); + rhs = ActiveDirectoryUtils.NormalizeLdapString((String)returnedValueObject); + /* + // if either of them start with a server name, take it off + // it's not important to the comparison + string []lhsParts = ((string)lhs).Split('/'); + Assert.LessOrEqual(lhsParts.Length, 2); + lhs = (lhsParts.Length) == 1 ? lhsParts[0] : lhsParts[1]; + string[] rhsParts = ((string)rhs).Split('/'); + Assert.LessOrEqual(rhsParts.Length, 2); + lhs = (rhsParts.Length) == 1 ? rhsParts[0] : rhsParts[1]; + */ + } + + if (lhs.Equals(rhs)) + { + foundValue = true; + break; + } + } + Assert.IsTrue(foundValue, + String.Format("Could not find value {0} for attribute named {1}", + requestedValueObject, attribute.Name)); + } + } + } + + // this needs to be replaced by the real one. + public string GetProperty(string propertyName) + { + string x = TestHelpers.GetProperty(propertyName, null); + return x; + } + + public ConnectorObject GetConnectorObjectFromUid( + ActiveDirectoryConnector connector, ObjectClass oclass, Uid uid) + { + return GetConnectorObjectFromUid(connector, oclass, uid, null); + } + + public ConnectorObject GetConnectorObjectFromUid( + ActiveDirectoryConnector connector, ObjectClass oclass, Uid uid, + OperationOptions options) + { + // get sid to check permissions + Filter uidFilter = FilterBuilder.EqualTo(uid); + ICollection objects = TestHelpers.SearchToList(connector, + oclass, uidFilter, options); + Assert.AreEqual(1, objects.Count); + return objects.ElementAt(0); + } + + public ICollection GetDefaultAttributesToGet(ObjectClass oclass) + { + ICollection attributesToGet = new HashSet(); + + ActiveDirectoryConnector connector = new ActiveDirectoryConnector(); + connector.Init(GetConfiguration()); + Schema schema = connector.Schema(); + ObjectClassInfo ocInfo = schema.FindObjectClassInfo(oclass.GetObjectClassValue()); + Assert.IsNotNull(ocInfo); + + foreach (ConnectorAttributeInfo caInfo in ocInfo.ConnectorAttributeInfos) + { + if (caInfo.IsReturnedByDefault) + { + attributesToGet.Add(caInfo.Name); + } + } + + return attributesToGet; + } + } +} diff --git a/ActiveDirectoryConnectorTests/ActiveDirectoryConnectorTests.csproj b/ActiveDirectoryConnectorTests/ActiveDirectoryConnectorTests.csproj new file mode 100644 index 00000000..8b378ae1 --- /dev/null +++ b/ActiveDirectoryConnectorTests/ActiveDirectoryConnectorTests.csproj @@ -0,0 +1,159 @@ + + + + Debug + AnyCPU + 9.0.21022 + 2.0 + {DB8F7DA9-11A2-4BEB-8C14-25D8229EBE7E} + Library + Properties + Sun.OpenConnectors.ActiveDirectory + ActiveDirectoryConnectorTests + v3.5 + 512 + + + + + true + full + false + bin\Debug\ + DEBUG;TRACE + prompt + 4 + + + pdbonly + true + bin\Release\ + TRACE + prompt + 4 + + + Project + + + + + + + + + + + + + + + + + + + + + + + + + 3.5 + + + + 3.5 + + + 3.5 + + + + + + + + + True + True + TestParams.resx + + + + + {BDF495CA-0FCD-4E51-A871-D467CDE3B43E} + ActiveDirectoryConnector + + + {F140E8DA-52B4-4159-992A-9DA10EA8EEFB} + Common + + + {8B24461B-456A-4032-89A1-CD418F7B5B62} + Framework + + + + + ResXFileCodeGenerator + TestParams.Designer.cs + Designer + + + + + Always + + + + + + + + copy "$(ProjectDir)\..\FrameworkInternal\bin\Debug\FrameworkInternal.dll" "$(ProjectDir)\$(OutDir)"; +copy "$(ProjectDir)\..\ShellScriptExecutorFactory\bin\Debug\Shell.ScriptExecutorFactory.dll" "$(ProjectDir)\$(OutDir)"; + + + \ No newline at end of file diff --git a/ActiveDirectoryConnectorTests/Properties/AssemblyInfo.cs b/ActiveDirectoryConnectorTests/Properties/AssemblyInfo.cs new file mode 100644 index 00000000..aee55be5 --- /dev/null +++ b/ActiveDirectoryConnectorTests/Properties/AssemblyInfo.cs @@ -0,0 +1,75 @@ +/* + * Copyright 2008 Sun Microsystems, Inc. All rights reserved. + * + * U.S. Government Rights - Commercial software. Government users + * are subject to the Sun Microsystems, Inc. standard license agreement + * and applicable provisions of the FAR and its supplements. + * + * Use is subject to license terms. + * + * This distribution may include materials developed by third parties. + * Sun, Sun Microsystems, the Sun logo, Java and Project Identity + * Connectors are trademarks or registered trademarks of Sun + * Microsystems, Inc. or its subsidiaries in the U.S. and other + * countries. + * + * UNIX is a registered trademark in the U.S. and other countries, + * exclusively licensed through X/Open Company, Ltd. + * + * ----------- + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER. + * + * Copyright 2008 Sun Microsystems, Inc. All rights reserved. + * + * The contents of this file are subject to the terms of the Common Development + * and Distribution License(CDDL) (the License). You may not use this file + * except in compliance with the License. + * + * You can obtain a copy of the License at + * http://identityconnectors.dev.java.net/CDDLv1.0.html + * See the License for the specific language governing permissions and + * limitations under the License. + * + * When distributing the Covered Code, include this CDDL Header Notice in each + * file and include the License file at identityconnectors/legal/license.txt. + * If applicable, add the following below this CDDL Header, with the fields + * enclosed by brackets [] replaced by your own identifying information: + * "Portions Copyrighted [year] [name of copyright owner]" + * ----------- + */ +using System.Reflection; +using System.Runtime.CompilerServices; +using System.Runtime.InteropServices; + +// General Information about an assembly is controlled through the following +// set of attributes. Change these attribute values to modify the information +// associated with an assembly. +[assembly: AssemblyTitle("ActiveDirectoryConnectorTests")] +[assembly: AssemblyDescription("")] +[assembly: AssemblyConfiguration("")] +[assembly: AssemblyCompany("Sun Microsystems")] +[assembly: AssemblyProduct("ActiveDirectoryConnectorTests")] +[assembly: AssemblyCopyright("Copyright Sun Microsystems 2008")] +[assembly: AssemblyTrademark("")] +[assembly: AssemblyCulture("")] + +// Setting ComVisible to false makes the types in this assembly not visible +// to COM components. If you need to access a type in this assembly from +// COM, set the ComVisible attribute to true on that type. +[assembly: ComVisible(false)] + +// The following GUID is for the ID of the typelib if this project is exposed to COM +[assembly: Guid("bdb18bb6-ec8d-4a9b-a56b-7c600534f7f5")] + +// Version information for an assembly consists of the following four values: +// +// Major Version +// Minor Version +// Build Number +// Revision +// +// You can specify all the values or you can default the Build and Revision Numbers +// by using the '*' as shown below: +// [assembly: AssemblyVersion("1.0.*")] +[assembly: AssemblyVersion("1.0.0.0")] +[assembly: AssemblyFileVersion("1.0.0.0")] diff --git a/ActiveDirectoryConnectorTests/TestParams.resx b/ActiveDirectoryConnectorTests/TestParams.resx new file mode 100644 index 00000000..735169b0 --- /dev/null +++ b/ActiveDirectoryConnectorTests/TestParams.resx @@ -0,0 +1,147 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + text/microsoft-resx + + + 2.0 + + + System.Resources.ResXResourceReader, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + System.Resources.ResXResourceWriter, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + cn=users,dc=dv207518domain2,dc=central,dc=sun,dc=com + + + localhost + + + etegrity + + + 389 + + + etegrity + + + etegrity + + + dv207518domain2\administrator + + + administrator + + + dv207518domain2\administrator + + \ No newline at end of file diff --git a/ActiveDirectoryConnectorTests/project.xml b/ActiveDirectoryConnectorTests/project.xml new file mode 100644 index 00000000..9d99efc1 --- /dev/null +++ b/ActiveDirectoryConnectorTests/project.xml @@ -0,0 +1,19 @@ + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/BooScriptExecutorFactory/AssemblyInfo.cs b/BooScriptExecutorFactory/AssemblyInfo.cs new file mode 100644 index 00000000..cfe5e33f --- /dev/null +++ b/BooScriptExecutorFactory/AssemblyInfo.cs @@ -0,0 +1,70 @@ +/* + * Copyright 2008 Sun Microsystems, Inc. All rights reserved. + * + * U.S. Government Rights - Commercial software. Government users + * are subject to the Sun Microsystems, Inc. standard license agreement + * and applicable provisions of the FAR and its supplements. + * + * Use is subject to license terms. + * + * This distribution may include materials developed by third parties. + * Sun, Sun Microsystems, the Sun logo, Java and Project Identity + * Connectors are trademarks or registered trademarks of Sun + * Microsystems, Inc. or its subsidiaries in the U.S. and other + * countries. + * + * UNIX is a registered trademark in the U.S. and other countries, + * exclusively licensed through X/Open Company, Ltd. + * + * ----------- + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER. + * + * Copyright 2008 Sun Microsystems, Inc. All rights reserved. + * + * The contents of this file are subject to the terms of the Common Development + * and Distribution License(CDDL) (the License). You may not use this file + * except in compliance with the License. + * + * You can obtain a copy of the License at + * http://identityconnectors.dev.java.net/CDDLv1.0.html + * See the License for the specific language governing permissions and + * limitations under the License. + * + * When distributing the Covered Code, include this CDDL Header Notice in each + * file and include the License file at identityconnectors/legal/license.txt. + * If applicable, add the following below this CDDL Header, with the fields + * enclosed by brackets [] replaced by your own identifying information: + * "Portions Copyrighted [year] [name of copyright owner]" + * ----------- + */ +#region Using directives + +using System; +using System.Reflection; +using System.Runtime.InteropServices; + +#endregion + +// General Information about an assembly is controlled through the following +// set of attributes. Change these attribute values to modify the information +// associated with an assembly. +[assembly: AssemblyTitle("BooScriptExecutorFactory")] +[assembly: AssemblyDescription("")] +[assembly: AssemblyConfiguration("")] +[assembly: AssemblyCompany("")] +[assembly: AssemblyProduct("BooScriptExecutorFactory")] +[assembly: AssemblyCopyright("")] +[assembly: AssemblyTrademark("")] +[assembly: AssemblyCulture("")] + +// This sets the default COM visibility of types in the assembly to invisible. +// If you need to expose a type to COM, use [ComVisible(true)] on that type. +[assembly: ComVisible(false)] + +// The assembly version has following format : +// +// Major.Minor.Build.Revision +// +// You can specify all the values or you can use the default the Revision and +// Build Numbers by using the '*' as shown below: +[assembly: AssemblyVersion("1.0.*")] diff --git a/BooScriptExecutorFactory/BooScriptExecutorFactory.cs b/BooScriptExecutorFactory/BooScriptExecutorFactory.cs new file mode 100644 index 00000000..ff6a3983 --- /dev/null +++ b/BooScriptExecutorFactory/BooScriptExecutorFactory.cs @@ -0,0 +1,98 @@ +/* + * Copyright 2008 Sun Microsystems, Inc. All rights reserved. + * + * U.S. Government Rights - Commercial software. Government users + * are subject to the Sun Microsystems, Inc. standard license agreement + * and applicable provisions of the FAR and its supplements. + * + * Use is subject to license terms. + * + * This distribution may include materials developed by third parties. + * Sun, Sun Microsystems, the Sun logo, Java and Project Identity + * Connectors are trademarks or registered trademarks of Sun + * Microsystems, Inc. or its subsidiaries in the U.S. and other + * countries. + * + * UNIX is a registered trademark in the U.S. and other countries, + * exclusively licensed through X/Open Company, Ltd. + * + * ----------- + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER. + * + * Copyright 2008 Sun Microsystems, Inc. All rights reserved. + * + * The contents of this file are subject to the terms of the Common Development + * and Distribution License(CDDL) (the License). You may not use this file + * except in compliance with the License. + * + * You can obtain a copy of the License at + * http://identityconnectors.dev.java.net/CDDLv1.0.html + * See the License for the specific language governing permissions and + * limitations under the License. + * + * When distributing the Covered Code, include this CDDL Header Notice in each + * file and include the License file at identityconnectors/legal/license.txt. + * If applicable, add the following below this CDDL Header, with the fields + * enclosed by brackets [] replaced by your own identifying information: + * "Portions Copyrighted [year] [name of copyright owner]" + * ----------- + */ +using System; +using System.Reflection; +using System.Collections; +using System.Collections.Generic; +using Boo.Lang.Interpreter; +using Boo.Lang.Compiler; + +namespace Org.IdentityConnectors.Common.Script.Boo +{ + [ScriptExecutorFactoryClass("Boo")] + public class BooScriptExecutorFactory : ScriptExecutorFactory + { + /// + /// Attempt to trigger an exception if the runtime is not present. + /// + public BooScriptExecutorFactory() { + new BooScriptExecutor(new Assembly[0], "1").Execute(null); + } + + /// + /// Creates a script executor give the Boo script. + /// + override + public ScriptExecutor NewScriptExecutor(Assembly [] referencedAssemblies, string script, bool compile) { + return new BooScriptExecutor(referencedAssemblies,script); + } + + /// + /// Processes the script. + /// + class BooScriptExecutor : ScriptExecutor { + private readonly Assembly [] _referencedAssemblies; + private readonly string _script; + private readonly InteractiveInterpreter _engine; + + public BooScriptExecutor(Assembly [] referencedAssemblies, string script) { + _referencedAssemblies = referencedAssemblies; + _script = script; + _engine = new InteractiveInterpreter(); + _engine.RememberLastValue = true; + foreach (Assembly assembly in referencedAssemblies) { + _engine.References.Add(assembly); + } + } + public object Execute(IDictionary arguments) { + // add all the globals + IDictionary args = CollectionUtil.NullAsEmpty(arguments); + foreach (KeyValuePair entry in args) { + _engine.SetValue(entry.Key, entry.Value); + } + CompilerContext context = _engine.Eval(_script); + if ( context.Errors.Count > 0 ) { + throw context.Errors[0]; + } + return _engine.LastValue; + } + } + } +} diff --git a/BooScriptExecutorFactory/BooScriptExecutorFactory.csproj b/BooScriptExecutorFactory/BooScriptExecutorFactory.csproj new file mode 100644 index 00000000..aa9e0516 --- /dev/null +++ b/BooScriptExecutorFactory/BooScriptExecutorFactory.csproj @@ -0,0 +1,104 @@ + + + + {0747C440-70E4-4E63-9F9D-03B3A010C991} + Debug + AnyCPU + Library + Org.IdentityConnectors.Common.Script.Boo + Boo.ScriptExecutorFactory + v3.5 + + + bin\Debug\ + True + Full + False + True + DEBUG;TRACE + + + bin\Release\ + False + None + True + False + TRACE + + + + + lib\Boo.Lang.dll + + + lib\Boo.Lang.Compiler.dll + + + lib\Boo.Lang.Interpreter.dll + + + lib\Boo.Lang.Parser.dll + + + lib\Boo.Lang.Useful.dll + + + + 3.5 + + + + 3.5 + + + + + + + + + {F140E8DA-52B4-4159-992A-9DA10EA8EEFB} + Common + + + + + + + \ No newline at end of file diff --git a/BooScriptExecutorFactory/lib/Boo.Lang.Compiler.dll b/BooScriptExecutorFactory/lib/Boo.Lang.Compiler.dll new file mode 100644 index 0000000000000000000000000000000000000000..7a4bc09ba3dce42a0b0abf3030063f29df749338 GIT binary patch literal 729088 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
GU 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

T_?v+9kPqlvw@YaP6tl3J0exTmi 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�FL@bc7u8lj-pJ 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 W7|1Y?VIadmhJg$N83z7uG4OBPtaM!f literal 0 HcmV?d00001 diff --git a/BooScriptExecutorFactory/lib/Boo.Lang.Interpreter.dll b/BooScriptExecutorFactory/lib/Boo.Lang.Interpreter.dll new file mode 100644 index 0000000000000000000000000000000000000000..407d01147637f06a2a95f0ad3445cabc552c6683 GIT binary patch literal 86016 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 V1`afEpn(Go9BAM`1OFlo{C^)m7oGqB literal 0 HcmV?d00001 diff --git a/BooScriptExecutorFactory/lib/Boo.Lang.Parser.dll b/BooScriptExecutorFactory/lib/Boo.Lang.Parser.dll new file mode 100644 index 0000000000000000000000000000000000000000..8bf0313d832a327a3568d7b39af0325641090f04 GIT binary patch literal 421888 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

|?;D`Z! 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 T4zxJX;y{Z7Ee`yD$$|e13;By` literal 0 HcmV?d00001 diff --git a/BooScriptExecutorFactory/lib/Boo.Lang.Useful.dll b/BooScriptExecutorFactory/lib/Boo.Lang.Useful.dll new file mode 100644 index 0000000000000000000000000000000000000000..73b494556eb679b91547b5e8e616973ae92ae8e1 GIT binary patch literal 81920 zcmeFa36xaD^*?^!(yy248EAT#m4P0hhlXL98AJyV83dG7K>-;>1wjQxpLBx)!yrV& z4HqJcU=&27pco->!x*<9u2Ed#iU~$sq8OLBU^Mvu+*{S%uZP8af8X!-e9!qG`<%I5 zcd1*qZrxg5RlT0kCtgDq5!v|r@kgQu@y(wGfd>bRz%JrjI{o=*eOkskEt@UsVg_Q1~`_}K$L zd*Eje{Oo~!Ja8UASL4IarY8C5)mw<#ts)8vH4BadeNh%YU@BS@qK)9Bj=|^Pr z<0n=#iDJ%k0YdfH09UNg_Wlq^n4mf?b|FHsR^YjKmJcPMAT%4C^1R}3QPlQFeG#AQ zfEab^YakWzMeNnCAB4jGkfr*zX<}eY3Qyi47gY$A1l|Zo?Bds=cEpZ21E2cw#~&Rx z1V%Cs_M`wpE*|eg-J&%nK^dWhh?&S|5=BuCEEZZfd`D{>I!WW!9N3$#+hLiy4Fub? zW;3rDS#6U|c1*St5CsF4a-n4m$^p#14oY)U*jxy?6#!hWkETNrY7sHr_Mjz)q6D*v zHM$*uCt?7zv2S6xl}Lr07~AP~MA|f+Y@av8Sb`G ze;+M@4wx88b-{N$ugYKiKs;ZR2CMvHLOg1_2{0pel|R*$r9xFsG6kjasLzFuLJ?n; z6OTG>HK&~_f7mnw2eGGlKlZ?R%AP;M9{5JvlV25tJt&52iDH7+@_Z4)FvS@au&m7@Nth(C3NNI6vzCwYf7Y?*4QGEg+r zR~7Lke<#dx#ViL!Gwmug8iENy+DwWbO*7%ju1>7iPXBd$fpJix3DTw zs9lj?70FksJg!h~4p&9O%FWS0RV2WU?xY;u3255Wot39MgA|X(m8;{NQ@J{?Dw5aI z)jB|iz?KBY83oc7Vn9awg{mTIY(}9~E}}FPsRAioXcsriq%I((j*<+)sz|Un9!=%LP|LyF-&B;xo}%3(ha7?qt)&RMyg@57_&|bbyq^&;h%W4hdYvm zdSv|;t%^kV@tbt?Xq&1?8yyIdsz@aHzO;5v)!IEl(ahFWk=8nddMRcvP&BhuRiu>; zp&G@k0Y!ySw4f?dpo6GZF>67IN9$Cq)Il;H?X8gB&>4>&qL4#SdOX@kMNuEvdGIJY zR7KID5X^DlVahj$!3XhZy*rA1QxD(dIIyo0>WjV>kM>hu?uS0;j)u5<3;^Z;o6bPr zE6I!_YW6XkZ0`OTzCtOCF^)S1DNod-FyF{bpvJX$o{Vf5cK69154*=fHc;ib$Kx9l z#{TDBo6x|7IWl55jf3EvsXTD`K&!{dM!M|GOc+8*<000B@h!C1=qrMl*2#2{1QYYT z9w%YHp5u`UnN~9dFg(lnd^&iTh9fBnddzH=+ZsZc19_wlRyndf;W*f-sbVBtxHnXVB2lJ~u+$QJtT~t&((8%O5Ta_W zg!vKc3E?$k4Pl#2S*WIo21w{>g3Ux_VH_X~f_W^he4aH)Cn~2J5+R}9xpD`gsQYV6l#uvqyCPFS&M4@P){%}kvk$^i1*lf&Y zMxNhAQ-%7T%|$g~&awMVmQqVO)5!pS@ow^aR8}umL@7$qi>X0=!emP0s-Y5o(z=} z)0A(qQheZ4nSP7&>rnvwm9adaEWZJ!h~t@!i|P#}Fhf-%b%bi5J^#LugJ!#$G=4=G=~ibhbr2q|CWMe@A9$Wy-H7##$o zk)OvdIu6Zw7XH|a_~3Mrr~v?L89E=|X?nCKOj97&1r~XlOoBg@nhrzI*RceM#gGy1 zb(DLt5V4bQi+laby;!B#sTnNK%ZX2g6;Lc*aLUw+1?N5!l#H8LfTqr3r+$rm33QiG zY9^yT%ZtXNK^Z!Ed73k>ht#;PSE@XY^GcOt;T%pQ7HlVRE|MG!E|U-pHa|_7JQ6wX zd7z1{8s`RHI13<=1n7$)3(s1f#+Sjc9)k>%l?u)xWE0P5E#t{2@x*Nut;wfi_`hol zf1_POp;QVmE(^XG{KEbYDt}`w;ZGLB8@wz}KnlxpEVvM?bR~~8@fcF+uHbF2Je417 z9$R;Zu*%O)QLTdZ+NV`y^ylV6g3ZWR!+k!U7^IzAHcWvU{0qRxm?BRU>4i}%HMO_m zM~c>JMrd6X$eFw&s{JC|W^Gj8#EQjEufK9TcvRI0tDSTQFH|+c5;EPv+bShkRQsTW zV_!YbMR@@oLsWB}ij*QO-5u@PVx_1U6i;7B%$8OuQBO}LES{-8FXg2^2J93z|2C#w$BCw#tj{n2xUtblWBm6VW_(E>{%2cOk;x){Y9h^Ozd0 z@~=zG=M0FRCZ=$wJ8;O!K!A`_apUQj+3_GAEl>jh;w)zXXr%(E6$emj)%IA*+UW*~ zs0P8(SGxmc=_)NmfGfk-B^HRWZCorkLJ7nT7s^7`#&c&FHssDhTFj`~rK7d-6%5K?04EMHYw@j^jpZ}Gm5UZ*1!GI^^ED|l5d*8Nn& z^6L4Ex1g4DGvtY@QLIuU&lZccp6#>XUD;`L7enD9UgdgdzW}XhwR;h>*nQhE_j$#+ zr)ja{Fa%L`xNlNwCQxUJEeKGMkv1LOVI`Qs_g1pYO75&+G%X zy=dHb&(#wj#m#NG)U;0M#k!42UY*|9fdpa)>kS<{9$iIgUEaEhyw!~0SI>lgItz8{ z&E;4wzCtcW0jD1~q~%@$JgI3>b=j=BS6|Z=n%{Y7nq% zI0S-4X7YU$D~oq0TR3lVI_^~vfOt=&mBI&*-%`6&SShK|5uw#`$)r@vtduh~*=mGB z?C~QqJSF?KN(~~nr5ZEow+~>N4177d9i`jhEk(cD^YB*%J-Znkdltn|gTvsf6vn}{ z4;=SunBurt_d98?^RxIc$coRUspnwQG5T`fEDF`brD$<>U}1j*oKCyffZ#3%=%?lQ zE3>Q0{dE)wXxNBa8lv-=O&ncnUo)f=5eKFu=Ym&JI?j?JL@AsusNlykgV6jv3ddx&8BCWwHx9ek%as@yml;p5v~9RBAN<4@v&ni{Lw6z~iBtchm#!uQ=gXk9FHJk9B_a zSQm(-pV!%cn3&`dS3MCR?sVaJjKV~hc?7_2e8hH7fZp^rlbDEc%yDl5Rkkl6!4I|S zbjiILNxc%04dbb(F)t(#8@!U^=b83MdNz~XHNg2{YAuWKgLRBMnLHJAWD-AFN1ce{ zxVHdR%P&=?evsBhuLIYS$ain$q?iTk4gta=gn`+_q?2jLE%JD3MA44?;c@lAX%W#f zYMkxQ?cP*^#`2CCo^j7=W@d|dhbI{|zrYoAICNM%j7eCeI4o|1z}i3!klc<(7Cn7#1nS-mkopZk zp1&w{4^uV)AU%{@dNa~h{_PleRUR{i+0k)-%VbrE-~AmB$GsOYjIKx&fWxCT0UD3A z^!I*J(JM0Xza6+Kiv`QQ57`p;11O=pGeVa80I14GUu3A6&JW$`d8$F$q_jnw*!+y6 z=1sk)p=ON-p|xm$Sk%@B*KYw1*Ee&1ZX?ys=!t0OvVC-R&^j@UqZu7#^lGmj&Xd)1 zr0BuwDkomNb127#=?;c6j#DnbOS+8Yzo1wQGq=Nd<+5fjz)U@aY|X5<8}<*|P%HEr zG+287(EUBw5)W!Paq1^PDZ0!g&}U;o!TW~~BOMRR5Ro5n{{Sp;6wHX)j^BL*7#7=) z;!ACzy~D$DdZ#SFX&FTd^LIe&G>G-PIWz1yYS{5d{NCLpzZ!N-k8O`3pC0f6^)V>L z*u)k_F;Zk|5b=q@64&5MS=pVdgYM#WL)3D=ucxY0h%11-suG&HM2!I^Jw%6oo*oap z=hHmqa~p=~Nn{$veqCd*XW){@!E1gDjz=0CXRk9HFm)aHp`tu98FsL_`~-x;^%IfV zG5vwWAA!0nAz>!lfE_4#&D{zTdfZ-HMR*CwDHIl(k1cuDZbHbfGHwIeU-FvP?U$_K zK-&Sgz~PR`dtyO<;wdiTX$D-5R^mPbLewX{GU7|e7f%8_?wJXWJ+7yBzGE7?LpupA z@GO~QS>AHF@2Hhef9NoqSV*i*Vk$n*eU`IkiB?NO@;H{_>SWfK@E!VD1@xpxJk`~dgFSg8^pW{E% z9Se7+eamBv=#`E9X090Ox^I7XGkF~9k!5d zNqL^Uc4lswoyq%C`b@jIl5H)o$)g3Z=NPWPM`t$wlB`Y{Tk`7TkXH+pc^DpzSqwsE z8%n$eYw`6u8$A_9>-EWY9w;!1ky@T)4iiuhoFJSI=^fqFeBFimP2poIY}2F714u|+ zEoEzZtXA!eQgdR+BDPD*Gt+EhsE7WmxE3o>6c%F`QM5lDnv6VSr0ua%u`Txvh|EE- znIWeyvSaE?0aTeyF|Zvs3B++P01Vgjux&Q67cG81ck=v+SCP9fn{8Tt<$?4JnO?gh zb|RLYngw}u9aZL=oQnf0O<*HtD^!GbD65L4(|jyE?32D>IkdkG1b)Mv%phw(jY1r2i%XYAmc+J zRA&8Zxf}!8x>(ebzstifuL;sQ$bWpUKXmWx436rZ=@8;@wbbbYPX)#K@6?J6`a!7Q zOF!K?i}JveHf$sL!+SB|N1T>sP+P@MV@+R~46E%?{&hi}S#JoZ1HMWHD@9vp~n%UPG$ zmih=qHFFf3F*uN#F-q7uA#36a+Id`PGk_Yo)I1Y}_z09)m{X=rQN zGuzQVZ~==+ta#TtFDYKvG%rB2pjY+7m%$rq0p-s`{6UILvx$Y3k1>YW^>8hjC1w*N z?kmWnHZvf|l&qk}*#j~y^y9mdW^tk9snYY0O%bT$m@qADm+~xGqX!d1KL8h9EXFC3Cqkh@QZ9@gAIm zMLUmmYUWG53uWofmaB_pMbe|ovvw0*GJEFmd2CRzCbmw3dNqaUu9@!dXcV-6HR_pK z#ztwj_=#gq`RVV-j`n9UsN=fuSnBm*b9eAe_exO3SMAb#vxzw}8ORLdehgjBSgvLF zNX2JOJweJ8FJ-VTjba~*%R`H-AIv7fKY^l=(;*<^Dz~%pOD||mt@ug5u-Jk8QUjjsRqf*!=Ct%nEoIp zUs&Ah7s1nh;VG(_bN|2lMf7RE_+-7H+B_ew!qfYw_>!9l&ApY~0YZ196VYY|xGOXj zY-@Hv7Pk&>xH7uRsx0SyU71UiZ|J^p^j(&AYt$$bP_zDp@*sqLwT}& zsHgq0%4AU_+51M2LrFXCSIF~De-QNQoX4e~Se+NCZI3Y5VkNTA$8&aTcmi)x4?Z@# z4k&_B!~);P#ncdL`Wj$wc343ix22_sdJIMpeLVqJ9J?e-pqKA9VgQx9ja}3M<`7gP z9)nIweFKi1&GZz}uI{%i+BLo9Jv=M9hb6J4p1r%wzK=({sk_VF`0jGG`yFSm=0oKM zmXCIK|HYKZ9S`kY5l#F zeq4aWIr6`y$QpMq3*p*hmza$@UKz|*;tY|HEmd{xs8;y~*C5lk66(~}XdNhNlT|Jp zhDP-5jNU32u1H3d$!BMaK!?+}Ly|X((TAwhU0k8m#}O^vFZUgi_lj^IG&!s5%X$!$__y=#KR zw=wnYbh*1L{VN{rtJ)Y>GI55{&$U<*S2EEGw&>~a`k2%|d()Kf;p+Myu1@a`2PGaI z;5sZhfF%crr}$3lK9*y)fH1bK^YsxnUk6TZ7fX*&`i=lauDQgc1Jya;K)ILOpiZkB zGWU}`F^mx&YOy&U*0mm_bPWO}9Z^TBL+m4wKj#p8usX&WEEi^nxPGq25L6@QtY@ea z8Y*{Zk5Z9z6j}+9WO8?#AqL>iNtv$9(JGRT21PkxpGd-~d7-v;n2Mxfe4DtdXu_F% z8{J03RT~WlB_182?hcHAq*A+2%;Ates$*`Xin)=Xq+@QBin&q9pT2xBTE*OGSgKnN zw`ocb&~mtcQ=(fAC;COYvd1WIAHx^wj}ZI$LVfZLY1U&E^H@-HvyM^CItG+@bgV+g zB5yo;oND9aP>M2OpEkzrpknQ{!4W=)IKVAZ;Oa-9~?+8>y zV!9WDCz4+XRGT-XCwb2UY^K0aNoVkN3h}m3-h&~uqN}ao%x)=vsZZv zN=ollHuZtV6wW0=&72b54q;9|NPeDr()r!nxmaH77a|X@BTP}b@7@SMCko%GvY3H!=%W`%i(uxRw0wPkIx5)laMZ50%#w^eu@|7(<@ z*H0h*^!f>9sr6EfIt@;*mu$k;;ZNBryGz^_C-@N?5Pb5YKY zW+tu=eB6Ln5TKQ?qq%QPtNn_R>j>y)|45Ib5v8Bf7eOrI_k2*nAK@*RkSggR1&`m0=-xu26Dw!#?j7E-hgOBX(dm zV%l*3$dyTFyfmZ5QFrGTWhUdGi-n7$pUZHCzY3-6wKd*%k%dtX|2xYahu4atreanv^JGc+Jc&_u#koKYRXWi zmE?xNMJ0?UxVR)=ZzamvHeut<6vS2tMTr|O<0+}Vl&#zliA20^i0IS8?E5KE2*(ju z{QfOx#7Evwc?9@kAOjJPX*%Cgxe~P595!>%C`Kq9O2r^!M0o-3Q0Mbk!R&DT6p$Al ziq(wmwu8;MLxCO;;>%x%R!ZIgA-VbIFLH5-FywXsD0Zoe%L4q+3P8ep50XrXmp;nt~S#T@}M9X8#USr#wDA=EM~tP=|8m(YW?LL}9RS!T8m z=GPAfE9mKgxYWQs(3T#^J+3c1)O1w$3-AR4ryhM(NZdmMd^GO_RdfB8n~P_?plU6= z1mkTjpAK65!T@A&8wKN&8gJXs#It0u1T%gzViOuY`>*}Cv=A8;Ga1+PVWiAfd? zWS?aNPlb?g4mu`oxo86_LGrtBKuCV%pcrS7I**?(NjUY@5Gd-0Je2%Zx*2`yy|a&i zXUC58ju(%UKDnzipMATQee3v|A4hapw&|qag4Vsx+kmML061V|J=2OVMj;D0&n-F+ zMoZ*q;xG44knPJ6DoQYyKnZ3HkCOUf;HJf2$PxdGi2pTLoR3sR*-qiUo5MvDpdKN5 z0N)GQrgh3D@m@0qINNDrsNKkoYdL1>Z=&kGj4F%(PCeR;9Mv$*7}JoGq0ZS8Oc>1+ zRmZfdU83s!TveXd*!>^12KNDePH<2MEKfS4q32wV0I*$*26!Em<}WRWNGx97^YTz_#*F645zqmt^9R-y~^%9NE=2|rsBdyg2Q zWEsto_1;H9#K^v|DK>E(lZOW})+A8MqQl_Qg$x$)$b<&va=6hOxE#+IJ7Ad#&fx6e_fFsXFn_^e(|3DbyK{7 zXY#fU84pPt=T`lO3}}?Z%@zdaFU@(W{%B ztn1uf$QF`;p?-Kvo6#y{mhP*Kt93AK3C?GV_ljRr%^`@{#Eu@g5=$pOSLLF!=c;Xy ziTjN{Q@5HMAdA!VLHI&wpwBqO-y+k*)TYN-qKW0y=E|Z>jjeTVZ`Q73Y9HN4a!$K> zFus&-&z*=SVKy;O27JqX9;IS@vgLwej*UZ&_g}_Qs*IyEW!TyKXiZGj%2*DU3o;a!N6y5d07^OR+HV~& zS+OtB`up)SGi7=)n^@q#>gN$k_sEQHub-J}HnE6IIvGD(@-xMt%Ne^2UhYR|0@Vg3 zs7DUZDqgK*KGibbduWNnlwWgbpR4xt%eaS;J5rQqVR%H#hVtvXLcbaCuXewIlv;u? z_BF8jV4C+&VE*FQ^8CSpd{?fU_!a;kXu*ATZ(5D@OQk-%CGLa+MXN)B;?;Rrw(;p6 zFSBG$;Bk{>laq)3n-iK$c&QQgqU;t zgD0~}USU|%AIi`iskxhSPck^GMz2``e9&Q z`XQa?dVUIGUu5sFnzCMIT5=m~P;(>KBv}_qqg-)y-yxfS01RN7cWz)B=8a=S96WWV z-N5BFF)=gU2#>87TXYVdqd1O)W;Pv*4EhdK7c_n|3XbEF&Htd}htv8^{tXxVr%G0BP=K-<#Ai8f zLi-%-eTicA2Gf`9G_`cFnmNDfOIe-lqm1i+Lg)V8!QlK^ofyH<-?$}mb(U%WSw)BE zA(@<)=P`@Lr9Y@Xt8}n`4x~f93i7yD2aE5cgImX|!#gH$Napf9V}g!XGxN?b7LjZv zeo=)B0>-NM?ZXp6^^O~ix9r18COd9u8Sl8kq?~a>@?^)2fczE^_g5BEyB+>8JbD}V zj_m$lahAqMX}q1ydvDmO#8wAmypGgZwhh%dkoXe8Z}K$2otvLG&rY`?m+1piKM4Q} zj1ypP;s~A;;@q~GHLaz(TO3yGyN=~dR?C%SdJ*Ycrw@3_@x0D`?=p!i&f^Y5L9)jc zmiI{28A__Q2cvlq zlJG-0eh06>B)ST=wV}OjZ-;9vtRC7}D&59b-$kKJih+Hs76bi-^j2efmXdVh?+J97VB;!F(<>Vyk zD5V_@$=YjKdl;G-*P63A0`4%l#%wwhEh?QfRL>Szso@~cf2Rx5(Xbf_yCZ;e1M+V~ zahO?cS@j^s1H-{5di@HcYVq;J{=BWMI2jJ{KMd{CRWW*@X+of||k)M1`7<2jF)TtLPLs@mlD`J@;dfEX<1` z6NOE89II#=511c}vA6nn0tm*~S^Y~S$@T2dAQ+1QBAlZ5Lh*Qp!21*vkT+C69W54& zRps!piIm|F6aIo6K6aKe{Dkl;bNG0`&+r4nPvr3NxSipDgy;#zy5{il#GTpXF^h@$+PX2&i5sVGv2vHhQvFLSng2&6C{Zlf%+o^VJ{XP)E{#Il8v0i#sQKiauSZe5K?%D4UrF+Grv6=$XK)fb;1IwNK>c5lBzg?>e_E0= zIQa)jp2f-EN|I-g{&z@nCMSO-$#XclT$1Nkl zvwS?cXZYOss*f#1qwudc@+fP%*pI|-8Ox4~+lX{CccuFF8EMl!ktN)d1Wp1>oeY3Y zLl#W0+TQ8*19sblD0C|yi3Rv@HGI-}OreRi} zgNF-4|0LRMPB$T@LaC`NsZVR&X-qjilef6v#_xapQO=SE9RfA@w8h`o=&~UUHeA}> zP?V+zL2HjcF7bZQqj+~3hqJT-;}fq(?gFh4zrTA13UJQ^2+L@c9j0Q)2q*HCV#;Br zI~`K>+^%X2%P>#Ih^?3${AyIoFsEdg*zN?An>oWgJ;VIz_EVb1@h4FP4ty#GrZg35;G|3w>ubSnxuNK86nLy?PpS*M;Oe<6le ztnzt{@qjxMP`#8{#JitPJ%?~T=PB;@3ZCTtha)(Ov-fW>aUW!C35&hyB@oYnnL%K2 z+K=_qW%3^crd~4~$w{?l7|cnvW*EXrwPqN~NwsD;ij!*1a5N{?nn9KiYRw?a2eoF9 z<%3!?$nrt08F&WKYlhLBRBHxVKBzSVPjY(AAj=1}W{~BBS~JMSUcp#z zPVxX6l*^F1>^@9YEA&G-*+~R=fu&dI^_=AR4aWKcV(ivs_y51>ZBe`nxEW>~WPZ#8 z8|$TuGjmHd2n#WOgih65$fq$))DIITGkQj=~~ti>gih65qF3(Sx2a+YgtDu7pklymP%6A5$fq$))D6k zRn`%z@5(wt^<7y@7?X8`>btUzP<>a{5hWrZ>j>pER*Al=oW}l< zjS1znpMC=SpPn-1*^684ApN)%*w}sJaZN@ZvGsr25Pk+^=dQ#vpzN(*`3Rnl<1vt^ zk^%muwY5gQLRN`eH<#eg4;)=M%_jnm>EDBAUwt^J{vY|;K0ayv3KRT4*Pp!;kib0y zC*`6WEV=-rT>>Y{8s))HomaJiop>0Cm?)gGvN*F*T0otPy|93O$n zXYc&*qj<%9>XF$~<%UY)(3wtkr9UI0*k8sxECip6(Gnh;^my|WOzuPh7JJeHjgph-B|YGRIvy9+pZAmu)2 z(vMF!pnaUCW=Q9~QwlQQ)L+PXg9|T(Gu;c3RvZ6*G2BTk0!6)V!Mile*HY=-h^~Xy z5oZWLRYx}IzfeaO*L6IA^8c)km;Kl3*oF{Rw0b}7sG2Gqh#_dWB0QKC=I`5BnuMFz z+{~tkOGR`e)bmIjNS_(aWGy)D+NaIaH2h$kZgoFIMNuLD2w%$>Nz)v>#c+Uvc_a?R z0)Q~TGrmkdG}=$Qr7exJjEY0kU5whKegQE59p0x@KZh=X6mb?_elRa*&yTpzwT6E? z;E$E4xWb(vq;K4h&&60B1vod{kEy1dPI&z*LC(9KVq4&#rt#S?$9TWtD#rOND-Mc` z3*utlx8tRmnZg5e$X&usDvSgU()3v3d5nAxZv(5agn$fGd#~N`+JA=3asDL@33Mvur2)j2uFk3w@p9?7RsJ?x3coMVZj;n z;VlFaUVQhp4^lVft+d-fj5 ztr;De)BNP-3{9DWU%#6GjqV8mYA)qPS=I;m@etuC|M2sb@KRU6m}ahvob~eanKus( zEHDqQVc*Oy#=PPtm|)L_twu8{YMZ{<@R8AcGizYo7R8{W0rsn3)GK!Qj08$p+Pws& z#Y6o*z%+;Q{QO(1E$aHHr(PJ@@rn?(7(;!zGA6FYVE^5b|LS6?XT&Bh*p|1Ql>dVA zE)u9X-&XniB0v0%VNAb7q^??8={G0+d_;pE9vs9)G+||(E>q^LBZZ3bE=}d-4e~Ox zDu9Feu&O6+jkK)i-aPKO{nm43k>EQWW%%ZpQa;6<3Y-f$avp4*bM4H9>p02PH&bz_Y35c}cANbF6C`i)J#HQZL1+<+CyjzM%8HT|8I+6A z@xa6O2(B}Ji);2G4x-6W&;2Dt=->oJo<4RotGEq4Wxkmn&XeOaw4$(?SccN`gK`lV z?=)~=Si!(*Q5qoki8HNsm!r(kJD)Sh6cX-UiaJ5eCeA4T#1_TeiFCRcZjI)< z6oJ1~F&mMpZ&u2L*~EEPCvZZk-f?Zdo3oTQb5+&NZ?lO>>V>;SJpA0w5@lu+e{1vj zfmh2-LpbRliW4O!AJaLoiVK46Vxu5t6I(MdiTL4{AGo4b@Az30pL#eucoD((YiyAC zg9ep97U2)~vvgm9ezsEH;cNa4Qf3oFv<6@2k#&j?JIay%RX9nwdea@tjN1MslT zf4K7~&fu{~r&XP#zl$#K`#qabEI+hm12#AIx}uthJr>h;aGZ-BiEfJb2gNI|DWzvn zXYdizI6lb6-mgHOhiv?fNeI5@Jo}41#Zf`G88UDbi%Ms0{w)(q)T_VHqMwtMMCNrC z76QV6_iu+|wOe?;v5+dKn<{K1o~RGBz|EeZIQZ8zL?8iDKn;M#@dt% z~ljIDPU6i;1W~+G0W<}dNGB&bYVihFZ)d0($!T_P}D|0FygC-LE zX0@FbLm1P|`@ECt$QnCW*8_6#Un#>Zq?MJW6)oCV(f*$^Y|sCM;#f+*S;O(^liwx9 zOp3LTUFafsvmksqEqX#Rupk^iQj$iENYkvqxSeqW(`dyWEbBksKE`uzkS z`S1FP{4Mz{{I@A5dkg=$ zEh8@XeY|(>D{oys>xfJ#yjfE8hEEeyZ#6KAS#^&$;3A z`qOrvKD6k(!|qt~iz_Ox>SJ3*=^q|1?Q;2VTEEq%%_W6*o-y(t3wNK9+U6X0$kR6# zy|SYAkKq9u@B7`UJ1?2G<&0C$dG*-|!_8mhxo7qH?)ndUVOl%#_z~Ey-N120o;n>l(>;xa@xf}L^$8vB&kG_!|1x^BgShlJ@HY&9Tk*FU1ZgY3>FJnGi26AF0LKVC zLEy>G=e`i7Bz=j%l>+Y&_*;Qb2z*K4PC$pgcO1V%t^KA`KyCe1{tylIv!3yQZD^W5 z2$>n+I^_DB{B7tepWf3qJ!aqJIUPU=f9b3_A+!8mvM3Xh|P0 z@Kk~413I)g*o0hHNcy@U+jC2BGw4qQUjTeB$ffTNIL-+A1x^NRLo-4_(33*BLf{&K8wCDd;PV3C5cr9}Zv=+&*teYp9wu;@z$tkn19*cg zk4wKu;BukdCh$&B9C{@0`MeOlA>6+T{7hgl%(*%Uj0>z4c!a<)0#6j^3cN_*)nT^e zrts!)h@J^^>8}a>9|Hd++`@d8sTSBjpG!GWpet~$z^ee;&`tSw=ZEMKq#b%G|F8KD z{U9*EfYa>-))b5^z}rOytY=XHm%CEZcS-s&Nk3JnY8v%pmXw}|A6ZMc8EEtD?>`lGBNKguOe zx44ujC`D8ubo@_f=&S>5Lx)G%V?!b7&{%;}1kQ|(EO6+u=;}ZlS__#r^e|uNs-n1CJFM;tdTpucumcdvl^%JZR*!xC18bSt+A-W2$9W3-A-w;&E zhZB-vB00gB0h=qZV&L*F7OXuTCz4l)WGDQRm#?xlVflyOQt5(+W((L_U{&~C$0jm? ztpnD_>PF3y?>CaK8-D$R^F0Er8-5}1a^XEIyzaD2cz+gNFZ@;!Tc&t5xHZPMya!vp zKwVc0_N`dfht>-Az0u0_q2`Z@mTF9EN8mTcU&~l_B=yBS$vOvs=c5LCORz@4j-(xe z@xMm*!AE})>}tV|qMd@>DcCUjn_y1}HiCAMff~JKwle!zqv!*{KGxVLV%Z*G8_ZGk zxnTKLEAuw|irW{`VpYI=z`hh-A7D3`$I!n7I~v$VI+pedHc@zE$#1YHrVH-`DizGN zuC_LtjrhGQ=G_788FL186YP(`3~L?@6l^CjA6-Gm3g)*9Z68jN#~2)E{PK(szMWuj zjk$GVkHF*si8MDkXeYH<4vz#`%(>@c2bq}zQ2_7o8{;Ee?D2--yFWOzs7E|M$d zjk8Zhd5=&^uzB{Cz#gLurG%T1kFkrS^ey%dl>Qi9BiKJQcAa3IeTs+YCs;iY80T9f z*eJms=U4=3>Xw);2W?d=TH zrhD4q_crLhb^*Xg+Jym!R4`?Pz%dnE)_6dRW`bhTg#s@VZlL{CP~z<`1ndQ9)4=u@ z0gh_V@)LwUxdk01w!hgtgtoM2Z+u<$Z=;Y_Ri0u+={`V5m zDWKKSKq=9VS0LR<(!D#f03J95AG(RJS6F7 zkd9JeC$^y-AUx2Cz4xb1|3vyPo%R6U-I*!(3Vf(D`|U}A+XX85E^+oM=bEr+V%#t% zESejmBeh&GD1w-2Q!;vMb(4gH7G{Hhb6 ze<|Sqj()b>TgsvI?f+6)HudVV+_q_17jDh#1+Ei#PnSDEf1nHZkw?4SV@K&((4+LS zz_$P`@+Ce&^cD%M6xc&xeS#x!SfZnC(L_m47dTfamrDAE1ow!IiB}-=gyecw;L9TO zj=&Fv@_B;$$hQLb3iNel{cXFl{`jmc*`N_qi{lDw+gmMVPcDqRtRE4-peyIwMpn^Q3eWLpQDPFV&E36;7E9?ltX7*Irc)_mW z2U$p-DcG1gg>|&?5h>3)t_AUd1lTG~q1`yi@cx_PZ8o>>ovs09&Cj zsY3y65^NFqiq~Kzw?nY&OQOX)flcqjC8%~NphbeIc4$Q_1XGf&Xk!kq^P!xNB_F|k zkOsk&7OWE$FVIx-Hg?+mEDE)_c)C`P_`Pp*&5qgb~CUQ3Zoaw)&bk3u@B2O7zx@g znDStPb_=HJkRba=E-%fCWw1w)Z&C)^YILPd8SFWunuZOQe1QSwFB#owlE#LY-)!`t zxf*LMe;wFH!L|ivl~Z7T@(+zeXs*T{E8hTYg_isjr5{313$~cPK)yq% z^HEC6_vO2dLurU$OUZ7x+c=D-X{pf%fgbF#1yA(VTB-pbl6+su%3yz@T=I z8vSXSVA}%Y+U+q8rzILYS+LtQHoe_lbT~b%u?qxyRWQ{?hf~QgF5x1&q}>i^sS%7@ z>|J93O;i}=SH#SLv`k}5%d5-=+N!bA3dVM6EKyMnjD{0Fu)1Qk(Ln7q)=x0`89!2` zH_$lYZKDs%4ne-zN)n~VfL-Ik2GK?>d49!RB*B8&h3y<*9!cG?*~_EpuPO$c(sdd=gRc?=zL3~O0RsrFZx zV`;L+E^oiwJdXBgY)$)>=JC|-Se8`t-#FSdLGnT88r&s+K7-wAj-&7iY2Ks8cxq4> zaUYvNlQs5E``yL~v_NB@qEAnxjT-x~{i8-BB_@g%;{JXjP1M-u?KhYw(Qb|X8`vc3 z+^8h24jasqsX=3HfSp2<1iL=avBM_wRNA4jKES5X9xZt!B&Sm66IsjkfzchdnA501 zV~xPhpvf9L1No-Y0*%cEb{5^Bv0rp}%$!O0YV0~-=h7~X-2&`9qLWk!4**M2jbKY@ zTZgC3+4PuT+X6f)&Y_(e<8g3~{1Y%9KY1LSL$OJ$Wt)tcbErXMJnCOS(*#>e90eE9 zBEeLjzJP8}l7Uw`ykK5H_v(C~c6b%oE{*N!um-=v{6hxoZOo-#oyz5HrEu&ma~{=C zR#-#qeRBa#)7X^Q$7VAv5^M>bAN#^wNHtSfatU1&`xe+J!4}i<7+DKxhsM^$@~p+w z`ZShYOuvnlSWDjSiQB39@N+#U{}&k!IqF+IncU_ z!qYh45-P47W?fD7f^iR6L)XwK!Biw)L$d|Dh`LvfwXUIGY3$U>iPm!Ymmxd~F9LQg zjX9ll@~Ak&x{l5hjCCe~E%RX4(|Rq*eRCx(Ig|6TlA-Cpr~>+TG;tzxUSNn>2w0p@1f=+W}KjO5j2&sz6pu-hxP zTlZ%$*76W-&)C^${GN7su!m`n*2%H^2g!%{wBPR7Xgrd^KIr(Y^(aj~iwWE4Gw>d# zO9fj>KX!b}dV?H7Bp+j6%_vxK)HeRJM z8au!9>%it}YzcUOrWG3dC3t_Phc&hqyw~Urjj^v^qpvi^zJ84Y7+#6gSob=07i?*O zJ^wn5(HMLF4Vo>O^8A~$R`D<{{o8tr_6W9(II?z7;Vjm<7#D>7_B*spW9*lmRFY)g zR$|ZZq}hTkq0)Gx@i$tpdE7?t(ZhnN$l6732&U}ZMW5&JTFWW&QmTo!v3Joh4E(%W ztB;r3@6$Am4U0c#yiaolQ}Ou$jXPf?(c`M^59quMR%d@m%Q9GB`y*PP!5Zwp(^kP2 z)8FH3=wqS_Sj#s0Dt@&6F|BP9owPST8rXvi6xOP0wEYPk-mI`fV7uutjgio<8u#IYJ^v^PPGy*GuGe0*ka00tf4Yvmtbo5 zuFR-er6hR;QD%%0OzA8$W_qx4<5DfjD~NW+f*Z3s+ZhiErgXM5>|ZfYX=!J4&R}oY z6-Gk_+i7<&CS|aX>`LP?!M4&>UDr@&<14|o(aNs>v^yIOHz_;s>iRFc%9y0Fd%^2! z%oS{D;IXczui99lu|IVU09&uIx4O3SbvL$Z?1QdtfxRKv^?}yi>U=#6x|vJ3K2R>$ zD2;U$Y=vMdl6x5I1yilk!`P}M>Gq04p=DPF8|14o>@}hjyb-=S1H&|z0BpRkk1-^J zo#H#pn3ln2`1%=3GFZ}gxN%zsyU^EQY|UVo`vx1kGT8OLqYZm)x`eg95k}_>cDHY| zjzO-&0P|R5i11YGj@3Ml-A9e%G8jJzAD_WEb|+{|X*t1|7~6u^5%A#>_^JF1>T zgCSF<7oTZ-Bs^6*-&If7;T}3GgWX>7ln3P#mm0)GuXC@9lrS*Q(Bsgnp?73@P@0-cXioV3orj^?DmSk`7X?0+bTZw@og-F zO50}PY@|(U7Rr;7eqPew3T#`Hl~lQ&2F0Woh5n<5>y~6Cy>yjJaXGI+Wu**N+6xB= zeULzUlaSlc3jD*b1qO?p=-oy(ca&?NlhFQoY&9U0`+kQ53-Q`0Y$ zw9>FIH%m$D>DG>QDtN4CGdMS@fUPXDulZ)x;*5tH7 z3aSznY^-GZTtJf+?34Dk{(z1wb2MN&iY5qUvWMadWuAv}sZf6Dq1+;rO&-caLV3nR z**DU-UIsBVY2QfOSDwd*bltOQOY+(|QTmFMtDuT%RaQ2aLEnpv7pnGBz0%v8R9-J- zUp-!mSC6-k@0<4ORHdjWQuJ(p#IIJvOP224-m<(p`^2;D_?D#qB5ZWL{tHs~e~h0_M95_d{E)P*Suc9L`g&?MD&_y_4sVhYZrB`S^)$^<}zyy;m& zQE=Y~QKK}&{YBI#qVqG+z-faH4ln4+G7L>xh^-F{%k!y4JZ{_4Q_?!K^fZM>3`<6gT*)jw zEy*KHZi#Bl*|$a1OvEs^MUF=u4cb>;)r;YOCV!yW>OlIvtBY(})g?WK94PPpN7Vl; zuksJWmh}o#cbsFNj??Rt@%yafafN-TS&Uaci;*tHn}?+|+AO6%Q#sP*NS7mB0r?8t zGK@i{3*-~{tEI$XKS`^Tk5=Ne9*1EGn~HZ7I!K^X*Al= zr{&eoy9VzEcLik))e4M|ue#QWP}}OmoLVzpeFR`#^-!lTewlC-(j$QkM|=z{8L&J=TL^;ym=a0i$(%xkO91NWBd3!KH~=IUl=x%pW2Mb13) z+3G7`&ui5yoDGm{G=|fz>hZ<~^V8}ZLE&AwN@A;q(^u7vxKGDv!{Br!{kwXdGefww z8ZvzbIo*dkmGnWkp{N(vVmL**Z*WSfL-%`}u+(=r)poxNH%+<3H>JK?G>li@>kK!K z=>B_Wi#Zs2-ZYN|d`Y;EYsmV)Gf(cm6{Vlq{RQU>$p6`SSGc7#r~6-ovdei!D9@M+ zyN|XF>*VgALeHh$zlF|~-SUz}r z2U!Dq9Omz9eOf-qzu3H>dYHdE@hJ;?`xyT!>3_dTbwbF6X>ONVX~(|ilpZtv^Q<#_%<>PCyexBp{|n>f z?h9f0+#Z+s8%0~A#ckIpdB?(6SHZ$1J#O%KMS2Y+ukEqHzgQ#}TetMs1poZ5#{>R( zw6({0#Q#e@9`RQi961r%(c@7gC}l;&hl@>?8BXu_c+$Vw{I17yevY&?wAo_$EmpYn zC4YqcJ$D-~nfVp3`(HBI^DmiYJ^$)|M*PF^@V@^UWvAJ-=Wh7_ke*-q-?fJH+~fZO z^hTVg&nVvu{bPFCftN(`C2La8Mx0o4|C(o>)iV?b)54x@0-SCaSZrS0bDgu=_SqwV^FA#k{{~E#Q+q>jU%X)t*NL`hq?r zP$}|*1kNzeFCQ70ff^kT_-@aWA^&O5>48#vMCqx{Jo;BpH!#l{-C+(i1bW?temq3D-s{qUf%MgZ zS)yka)%3#XWcBNH9q7mQx-syK!9DsJqY?B~#HV_I^@Y(7rgQI$t!M8zH8KU2ivJ5^! zU1hN48;DPmzk~jFjV&ngL(utN#43aDJS?{t_Szg+ZZ9q09N^wG(cCPSj1xEw@_z`- zM9wK24ON;P`J1KHHrw2HDoq|$wg_d5y|8wIDNs7VCBL*4=AD(X|yBe)8Y-zSJxhU7f! z{EB|TS;Xx-&l*&*0D}86y z%m~&Rb82=NV-e5iA-%BX6#Va;Wi@l4hx^3>@&6JHIVys*1~T*Lj+#Y5!~Si}cw>pg z$sk|9it!i+xLpkUp_+>VmASwq}kHo_ZxgJp_PrueMdj`kE^LGAB?gN&lun?Y}^d=Tjl zwT}g-_1z8jz&d*hYS-6Zj&uWHW9=?l?sIEL z+pBzwYTrbO?c4q(xZHPX?Q(FJ*RBEg#@dfTxxMyNz~4#wVWDrW{SuTHP}XwaTd-%j z?{9!xp!vJt46$&zZ+EQ`nqmCA`gP-R&|eNdc$)IBHf6*ow~FM zZ7_LcfK`=^#@~JUbq4PI`#TpyLz}urW0ui3RuN)uN5HbWuAy15wpZvo@r>c{sRF~Z z1@-|wUbih6bb8edK)Qe3;LvhoMBVU^;Y_GA02}LIzcH!qc+jVU{v9;GZUh|{xxN#> zedn89Hwo{KEvP#KT&^j5^&H4^8-3@yv~CX4E9+i2A}F`f7;D^)8ZGzT18cw2@b`6( ziB~bYI#1Ls2$ed|0WOocTA?9F*9wWH$Bnn@ei7PYd{%d9XfsOLX>T^{-fM81lXr|a z8zsH33e7_tULWe}bnJaosMe|JeQRhCO2;@ObHQ+B3E)(tQRq_y&H~&JnkVVSfQ`mw z0+$P1CGZx38w73^xJBUO0t=`!-4iOn+bNF#cBdBsYiMsMK+}v?c>$V^-}(>GOrsR= zJfl6}Y@-w4Tq6Ovz~}+E$fyHcVjKo|sc|^qGGh?nHO5hZD~yqVHyC38ZxfyCMdwD* zxk+^1D>@$(oezu7$3*8=(fPFKd|q^J7oD$)&NoEoF46gs=-e$jKNp=}iOxNu^9RvM zCfjM7Y-hk^JHsa1+1g|~3r)7O#AG|Wn_TxAlk0wn$#t(cx$cLXT=xc(>psNfx(^eb zqeSNz(K$|ZP86M!MCWAa>`5c@H%EKYN%^tDo-{cB3#9Y%&jO6*&jRe9{{Ucl{xyKd zi)1H()dG71zGu80Zk0FK{Ac*-zYJd={*7@r-k{t?|D>trEB5{2`{}LlU&9}gFMnkI znEYSlZ^?fo|Ec`v^Iy*YApev6Pw311ynf=3T*R_1q}X@Qn#NieY__=p9`OJG5*y#OfeLKGd#>x;z&aLs*y zd+$<&W2U?CC>@UNaowrhDz>IpXWUwUC}VrVNJ{L9oy2WDQ`dDGPa;n}6V24^$gSEq zu9JG=#Qpugb65J~Yx9d3vPVW9<9r$R|dAr|(N6FO7U{ieme`+k`E-zjDE8T*XejC+jxjbp}X<6&dQFtE0M z5G&1hV9l@8H_|^v_{Ke-LHHN%TSb^S- z)hB#+tUKR=^=1aKd!Q@Euo682ZE-)=m=CBar0{u|JN^g4&rA5f?uf^fx^n1D5a#aW zIYQ}9o*!6u@-*P5cYX$ZLL8?B7>zi%-G|b6tRJO42$NXD_9OpZjNAy$MxoEtA%q!q z7s43s@eZK$I6|B;U{vGkl!Q|l&zO2x!gt|*eOx^vVGg4m$9s(j5xxhz4{^MCcRRv4 zP#05=W87ov35+~;)g`y8#GJCreXpM)QdT}1vbA&jYCj{PFy*ChN0u_qDl#3~58F%z@nchuKo2JWuB z1kT+B?jKcI&_4sdErBB|7~#vf1^7$2lJQZzx7pj_u8;l~!=qq)9P;AlFh8dLE2L(7 z-ahpwyYjGe+rJwQuLb37s9t-=4TLMPX+I{QVZM-3B4Pmez$rXekUP$r!ksm5dH$5??fp79-3>e zv}@MGD%Wj zwXKrdY?*7;nW23fNw(1)-)3r)g-E6pa)!9WZ$oGL%A&BLc9T)ApL53fhZq_&Kn$=Q%#`%_Y zzSXQ+j)RWum8n^$mYYp`b#ts*YdYVO?8e+6WnH63fk1JSL80A6-fr1DocT97TUFMkCyM+SK3j4LdR7a-=M<&f||v|$Z0{oVAd{qf?u;8mnD#fCFghC4bem8swOxDixVEl|`%GykbGXphC>$ znssa}dnQ}So~cxJS4X8cpT-q`8heN)#!j5BOq>87uc@GkEQJ4L<@kgYRccK%?PMpi zfD01GqlpvI#3T~ilxKkU)Y!zaY-RG8p!Fp#BBuf1>9JFjm2CE}g*zh%Pfh}`?o^wt zn!Owjh|gP9+H|OOe8~$U zkxhj)2^p!V=_xd>V3dd{Qn74ZbuYAR7dX8>LeN=V>F*tddebitbDZ(ghE=uAn*AXw z?B{f|;R2~D_$(oW4%gvw0OH6qeT%@8IbXUvc}b+&ESTpNnJfH{JW6glzCS~&bFXe>k! z=BXLj==E8<<+#2V2x-9-i6FrE+0ezp30=6bp&ccmAOaaiL&f2D+m~KGn+;J$o*BYm znsp0ggXw;jy|;+s!cNVi2As_(Wi_n&q6QlV39@;%-xU=oV~T(V3|c$fNz%s=x+5z|ILO zA_DKY*@C~|2}8csYPKSS;N>sDjqs*Kq>zfSMNZ6`aAM(H`s)CVGyX52w`)*b zJ~TaGZRmsX@yQv7M}bT~>62M*V!){UcUL&?DU?vf2`YXQX?M7&aBYCp?l)%N$CBWwFb8kSv9C*-%`p-T}FH7^ps4|md({r2YIS;_mqnY z<7~4PC@59J%z`V(`PEp9Ac|B7xuBsVX=<9LHtHL2C|X!s!a0cmoNG1P8#~h)DqIeF znvX0F)QL84(AJjOfH~Qq@rPhFu0WO*cSc|XwWsSdACu{L7TYbBMR@=%i&nL{rakL$ zh~Rk4FBOX-Q1i{lZE`wSb3=f$n=Z_l_)F7pk|NPk)oftDMiuDHP=`UyU9rW0Z&C67;F$$8U;&n1qN z=QqySEv$h!dkc@X;V)yAn{QmPTg`^7T(mXhq|@uFV4XYT&;FKn&q`I8f>3+HAu!>| zX-ytxhqHqFKpdP%nZFtiv*v&TRu(T8Y#1yi71TsNdRj zb7Qk*udVM7wgaphPIPBu$b>av~cKiRgbRJpsjqAIPBa21U&G<26j)u&6tbe?-OFwwFA*FKbK zH(b~#>3RW9m5KFy;N0l!dCPIF6|v|ZAk2+eAkj}doey0Jq7?c+;Fxdi_V736%uC!t z5C=$k9S2p~Z=kaw+oQRv-oTX%2m7VlGPv<(sWI7R5Zl}a&CS)UzsiBi!(Z}lwU05iiP*)%7umb z%EH-q=cmijIy3p%+)}Yz3F@9-TsWU!EMN3c2KCPtffwpp=by`$HLjUlIais>mmgj5 z8Z7Ai#r$l3F)zdfl}hEsd~U8Ve{OnVzMOxe9KjbfLoy5VvkQ^3>4mxTg<^j3Tz)>k zm_wg)?T>{2;O&^9LL8jU7J9J}D$dG};nMn|^wJ;~?bMAMZhaI|2dv2zb-p|N8Z zh#TXPQv_A9!y^UExWEs=X1hBJZ%(A6;6$8I<#N$gw9G3$y)bTAgKX+^3;HM6Ra;Y4 z&-0|Sj=AQiNPzaxFgt^2{;E~w!HQQ>X^Ic$8wNU(YE_hiC-%yg4J7R4+bd8#^tJSS zBKoqYMkFT?@7=jk*bswo+rtp%64tXs+6l%+s|dW{uPg@yRIu#<$J@bda-U4bHZp~kt=&=H&@1#vk(Qs&Bvzrl_k>vNQr&>Cd7Oxv-7 z6c~R@Siw^%i3lPcEjWwVdRu6~x(YX(S^KKBBD8GNz`^m(sR>mPMprNd0#{iF28g6L z3XmOd|KDRF`sWozc`vLMiacr}loTRVz>v|2rgV7tV2!WX&9)OB%K5zuJEtlz6IWEF z!F`tshqrP?%nJFb`RHJ^Ur}T7$l`=#4x>F!@G-VEmd&0C3XfsVYjY}lMG73nriZm= zH@vxl9WK33YjW3Cj5Hij>{!aokxXw}7Z2=MlGW4g7Ixfq9J{sfT_d@|oeHGS1JVFq zN*7k#b=%1v=c$H{Wg)9rjj6FQD?E)H8>@OKd@vq%=nA++jh@Qlf;%;h6~{YE5bXj5 z;W1q2QgsMy!3P8N++&uRD8{fY(gblw6&hRnEIcG!#6dQ^EjkE}cP5K% z0obIXwd&5;T-WeWLM_s@jHH_zl~Q||wn!|Psg@P8upRsq1b?B0E%Kl$^1b#P!pt{4 zcRo_#38;#&dakj;R1wF3Wz4UIR{nC^tVOdJKSz6u)Fqb^bp>uFTqS)->8PjlDF`%+&Jm%FIQ1Yx`o0qS`Ea=8G)7Oqvo!0@Y^zz{ zRW(2w#V3>0UyTAHaK4@J1X#fh=3*5?r>XMXgdnY*=~X#tLT1i89A^LIl$bTkO>a-ez7RB7$ zm^gkCX=tJ#eQGRw0#^`%^l79gd`)$N2c9yyaT5R_p&36N6r9;sfL?hua6cegBO9Q~ z%E4)XDl12)VKUGs*^}E5kOHA43(jLUnV9Me4Sxs`)gq)h=EcDTOdKy!!aBl}ex=zv z={--1u8c}AO?xz5iO!2?feG{J(=ZDMWd-Xl8v31?VnVkAvZ^L+Tg7czQZ1STo^Hp2 zjA;4x!e~jk>1}bs;6diZ+mwZ8kO(4c=zVD&z8|)1>f*_SDV7zRIKlIaf&@$E?8ZVB zCc=?gVM@x(tI@I`#WE?tp`5BXY**owy|IQhZh(&NfmgfIWD5iisS1b)n}m1ZRS1yd zV58iG%Ft|~CrUnesY+8cprb5Y+E5j3NlTmcz?VvqoOyw>S$6sQPV(v= zeRYq(I>^SaM;c>7TivI+?onCSWY#ZLgg6+c$}!wQh$JT}Dq0l8{d{aL;<92*?zhy1 z=kU7gGznItBI&R{v))N+m=if(UG)m1jb|f`BM#?-CwQ8t&5E z;&C1l9O1O6VYxn4g(pq7PL2d6yRfQ{(7ZieO9V*Y`q9b=I~Xl5U06MDx-j$&m9IIl zj$RzTT~L5c98HI3vMUy(v@I(V(OUR3PWlP8P9p)w(!%zStSsH;hVq=4ntyReF72o> z6lEu2BXGF}yU8#dvI>Q^KLKsuUdEOu;!ucwHb9|T#Ar-rQG`~5^l_vkMrHB@(h;LF z2_rKZF)EWVGLtYWL46pR$*iQ|G0Gjl>@mWCD!{A+C*#>;gam;?Jc#3yH|AF5MpAYX zM6K8@2S6YPF+@OzJC;2OpcOpO>M9<(G3m@n+#Wiq8`9|sq|t;=+(~ryB$`4xB<>9I zLWX1~h(j|ZLhJ-FfesRR@(k>Nz9NNbiQ5d=P|_##vpCY`l@cX8%je+GLE9guQ zI(bqUSt-NMm-|+tR)YjJr*}6%3jBBlTDwx!yP(G;^+a4uj0-F(Vl zz(LB}S20HfAS^66x_}ED_6^u+pO@vdphC0n?M&*p_-xTA4t%t_<#hoVkI?4=59sdG zwe;>CkV4PjQ8-2AeI$6jxLd=5qSaV)*EQ+Ec~?vNUx=g$8nmJN7QH+`Wxg}*pK=Q> zE*Yee$n0e$SvyDpCO?)e><)O0-Cis#*eI9UR1k~<#2;Eo`d10WYgomQ%dwrV6<#1< zTJP7&GUyyPHe_j}eQNE|l9uq60ISIU4Nix!@{%5e`$gR5B7E88rioCty{bO&!ik17 zNMu-U$dyD)_Dy${WA*-B$J91Cp0GA3(T9-?Rmq1{@yUz5ik}bgE3V3`UcjmvaZ7dcAFIWQY_sIg@MViIi_UM3QMNf#LPh# zWZzp3134tYCMKHI3neV2^uDW>ynKWAlQD{Vk5k53yoL?RroROg*{UVh$cQj0N2{6x z-Ug){tg}dC)n)$9AqkB%A*)z2G#nJY6(EBjk!j-7XJlFs{5bRVQ8Y0}&c9g@Ak{SZ zhec2<+|$W%K1n9H&V5EK!yP#+)PV38#QBolX)S`xUSptn`D zSm~2&$%!fw#vN3_bd^!`WyQOlDI!Y-MHC#9k#lR!R&4Evc@?%qW)Jt8BoFIK&Zo#J z?+(&w7s{n7s>q$Q>2+M+;;9)=(JL(H_Fc(dhNrj2q+Ghf(H%IWhYg3YxXOde%C3FI z@~#~S->0JmbUu0P3y+bbQG?6QO*_G+&+RJ3+yooO`xRw+`W1zxMP>TY+@fmZej3*q zbsU6A8401Q*MMDNT;vo_?3K0aIDEl@oKJ~fwbInBD@F;O0HJ( z-65&%ZBVkxLdiQNmQMRe`tttFKpyWzrk z@ow2NAX%;k=McJh=d6i$k<|WGlv#jBJc0Y>_)&){s6m)Eo(;U|Wum;zx&zpN#7|AB z;fksvzY07S>kc;2!UqwrqMq7c0ZbF|ONf(7w%kIkIpj9bqKhZe2g|6>woKr#QL+Y1 z(Y`43@v6Y9{nNl{qEC)eollm4!o;tQMl$F$JH9N9I7mCvy*4VWAk{!)E}kqWtPPYT zI)m5FNXj~53|U%24#}t?MIsw}urQ;rnn9`VR0Fl{!xxY6qGR7tzV6t0{{IdMiK*d- z1Y3A7jvUJfIUHFNx$G_*CZQw&w}++cnh;xk2qo-RRS0LT7RHSuLT)=CE`y(YRqXh^ zc&?*1nMJl(z@7mN+1Qk}i3bifQgy^or|(|WkKMjEZZ$W9x~srQrjywlsA~h0Pw(FK zGALickEWY+N?WNMsIDTHqb-t>SVIbuH9V%~C7*qU+@&~NK7Uw4^^7TX*ExY+L`d0P zh|1=$riAsc65~6@ww2FHdnJ^Te%2;mwWv`X)Bsyt1(X^ll#9R5u!-KH<9bc0!)5#}$sFEu%;T-O3_^b{5!)Ja z)LXVg6oJVE--GgZP5^2OKN)GS0bwNt*wFwCDOds~PJ?m0p-7#;5hUmFsj^|tKF(=R zcR*V-JlfQLwy+^3>agqafbvS2@~0g26J=UW7r-Y<*9KC)Ob`R7p61^;-ryt`3dkkL zS|U$c3hpB{_xM=D8U?h*QQ$a8ePB%`0 z`vftW7S?0=!}vW0qK*P9CyI$yGzs_vqLrUVgI1?DuxmD)#S@!*7!+T(V&43-|UalEuk0Vl+Lcx{tEYCu21>f7s}ii)?6 zI(5;CR4xjWN2_a3fe&aUg1?9WqoM0i`4QIVk*Z7sR|7v<3v=jBb zO$rXvQA)AVeelO`+2a%yoX;!h+n%zRF^($vsBYb>wmRY)O9~b_Pqk9OlR_Cx;YXsz zXB<s-@Yj&qiw`sce-m!2{TwaY zM40ITnwmAt0M(blOWUy?sGR|t680iW4-4Egg=U5M3?|sh#B+#FEImEU*A)$hx zgi$4shvb+qu~e`$e=s~f_+7{e1)86P`hUckdLNYTec;9Yn8EMEpWRhYa-M1BMLBN4 zb%6gp@88xEF~yD+ zK`Tv(HheVpZ=%BS{a9)P%A@Z~Ii8-eF-z!1Qe$#%X;VYaF|JZn@*HXE8Zj)BoWoJA zpnWr9*I4L=aIR?2O!HKAp8k1?_5Yu8*Vb|ZY}-@4pZob#go8pANV`izv4Yke~c9-9wtN?SdXl z;BTqX1SRtDsb=wf2qKO2z4K5_Mf_BtY8TQ@X3$Ovan?i5u+GtVG|oKo z%cy~N_v3I3J_tP()iZ$KefS*&Rd2(03{>BTF{$tevhhoIA4akVYo)$-;QJM7(18y_ zBOq&|0V*-^EeD^%h-d}Rk7v)lRUxHA9ofAGp_KXR!}I*ii&96cOit)fl4#AFPVJ^oCc-T*l)h~z`uL<$KL&~ z4&~0d>#uxFC13i``yM}Z;*YNOjVVxy)?+atVM^eqSTzUuDg5TprqD7)EV*748zO?3(M|UDGXU%ykfiaOl-@ih zc}u7}$0~C`kj5`^W8kbX&s{wP+-TGQII|mM2*ff(ZsU> z&==AWM!-KyHiE!slc1{eGMbRc7Z5=Knt2YaNrHu-|9T(#-ubx4v{BvhtLXSumVT1K z%M8BI*N+JYL}=iu(LI2??g2kF!qkYQy0=Kp?k&RcAIs@`Q!yb}5i0Ym_&mn&jRN{A3Wc5j1{d4;9f_}WHA72!% z-awQ5yXR_jAJM;}dG!@YFx&l#PJYcxeog0oybO$7d$}o12 zizuTzste?i>T8yyQc#qT-|mh0;U0eU)58v_sl zXnCVMN0?OiF+7a!lBXDn1bM_aq07N#Hj4lFPMFcXHi{Ta5S5ZmpCa2&ZVF%*fSVey zs~>iPEty72C!f}jO)$25T}Ui2`bmjCBjIxcU{?2YV1!5B=Omkx0wj~q#++6a-6KQN zd>$PomQj2VEuKz8>X9Kuou7;vn()p~guoX-pq^LV7bN2uWaKqb&&Z?W5so>epffa0 z7lnjZF&E;-5sH1%XFyk4I|5|@cE2Iv4<&>MU__G=|4U&)-k2Hy4>_$+z}|rHIRb3m z%JDY}Bp|hQNU}y5c3#soz2>!f9a;qgjn=R4lNe;GdtH-1i3+2}6qR<`*n$B@^%o#% z$)SGLGc=S0De>e`5)3i->TkV{cy|fopv&T(x_*V`rKg87FuD(Y0$pDR z3zYGZJ{iStVANwsfDVdH=XtawSz9+?2_S$h)b(!YIn((DI0)lJB%RkJq_$2|Yon*e z)KFaTQ`Ax)fvCk};LP@2^%1(xT)T5&NmqaTZ?{K2QrJswjVYQ6ITDSi~fIh&uc_K z@@V8I^gFsQP9$z2H(&Idj3D9I8r>HJi}0!Q6Qldg(85_BmXhXLWzXnZiXl_ z@dJroPyss7WDBViR!akUFoEr#VL&6dHOL^v;oTZU#K=Qphgdqr zfUBUw9f^Gy!2;AjhHWS^dTdS|NH9#2(4n{(y)eMgi9AGNL@?lbejDx3N*S$67r-T<3*`rZ!R)3#hB<{ z&blqmx-H7tR=pcb)F^0M7XbwrhLlT$FG{|t$3&#eMpa?|vn(Tv9@ebK_)s(;6oVX8$%I6|AhKg@T}I{+{_2M(JDvpj^5w%5Xd9g0gkbZE zju=~7C5YUx(L7}zf}BkB=wZb0x(5<{hU<;0OI~A%#<6h+5^#pe)Hr54tRA@6`Lay> zFJqyRerv*j=aj}u1Spr%LunAXG>SB*Hh=VJbTRtydTDb-e|{NNJTR>7^k8F2igp16 z{|GI24@fv7;VshQ%MyNJU!q@{F_w}8i9TXN%o7wW6B4~c$x)d21RNBsgh20e=mgC& zl7r|cmX022``I~JL}A4XvAFTp#1Ke;EqoqcjE-yzw{FDA6%nad#Y)_O-ArQM+%UFY zWi-)?ddNv6dUT|JbRYORx=+?vAL$zcDPnbrck9NjP@IyCw6=9)Ke(lVwr+4RI^Q1x zK6Zh2r1N_W{w;wzzlVs?`St1uSAX=~&;Pjg!K2Ur=ub}k{x|!6^oKwA&MSX# zU6ud%k(WO`c>Evdre9j`{L#lgF#F#(e(fv2-dz8+>&t(7*NcDl@>*i~Q{S33|I5*T z`K!-8_=y)jG4jeEET3J!`!_yya^*w6`{SQ|ck1Eae)UY+eEZ?+`S(6F`Hp{c?9=mS z{&f5$^Y@?sXLj>129AvX;Xq9F;yxE{p|~;C+~cLt9UpRgAme`#@PUkXwPh+hevCg4 zWW0MZQw^()*WPLmWX`v7*n8ReGI)bgoQ=45s9ZLV1v{aM*e(Oz!xV|S{L9*V+8sZZb) z;mm^>vys6qD_)|)n{1hlR&xzEX7Sp`TBhcsz=sJkc> literal 0 HcmV?d00001 diff --git a/BooScriptExecutorFactory/lib/Boo.Lang.dll b/BooScriptExecutorFactory/lib/Boo.Lang.dll new file mode 100644 index 0000000000000000000000000000000000000000..83dc5fbc52b26ef9a7916040df05d3b79b5f07ce GIT binary patch literal 110592 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 + /// Description of Assertions. + /// + public static class Assertions + { + /** + * Throws {@link NullPointerException} if the parameter o + * is null. + * + * @param o + * check if the object is null. + * @param param + * name of the parameter to check for null. + * @throws NullPointerException + * if o is null and constructs a + * message with the name of the parameter. + */ + public static void NullCheck(Object o, String param) { + String FORMAT = "Parameter '{0}' must not be null."; + if (o == null) { + throw new ArgumentNullException(String.Format(FORMAT, param)); + } + } + } +} diff --git a/Common/CollectionUtil.cs b/Common/CollectionUtil.cs new file mode 100644 index 00000000..8d94f3d8 --- /dev/null +++ b/Common/CollectionUtil.cs @@ -0,0 +1,755 @@ +/* + * Copyright 2008 Sun Microsystems, Inc. All rights reserved. + * + * U.S. Government Rights - Commercial software. Government users + * are subject to the Sun Microsystems, Inc. standard license agreement + * and applicable provisions of the FAR and its supplements. + * + * Use is subject to license terms. + * + * This distribution may include materials developed by third parties. + * Sun, Sun Microsystems, the Sun logo, Java and Project Identity + * Connectors are trademarks or registered trademarks of Sun + * Microsystems, Inc. or its subsidiaries in the U.S. and other + * countries. + * + * UNIX is a registered trademark in the U.S. and other countries, + * exclusively licensed through X/Open Company, Ltd. + * + * ----------- + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER. + * + * Copyright 2008 Sun Microsystems, Inc. All rights reserved. + * + * The contents of this file are subject to the terms of the Common Development + * and Distribution License(CDDL) (the License). You may not use this file + * except in compliance with the License. + * + * You can obtain a copy of the License at + * http://identityconnectors.dev.java.net/CDDLv1.0.html + * See the License for the specific language governing permissions and + * limitations under the License. + * + * When distributing the Covered Code, include this CDDL Header Notice in each + * file and include the License file at identityconnectors/legal/license.txt. + * If applicable, add the following below this CDDL Header, with the fields + * enclosed by brackets [] replaced by your own identifying information: + * "Portions Copyrighted [year] [name of copyright owner]" + * ----------- + */ +using System; +using System.Runtime.CompilerServices; +using System.Reflection; +using System.Collections.Generic; + +namespace Org.IdentityConnectors.Common +{ + internal class IdentityEqualityComparer : IEqualityComparer where T : class { + public bool Equals(T x, T y) { + return Object.ReferenceEquals(x,y); + } + public int GetHashCode(T o) { + return RuntimeHelpers.GetHashCode(o); + } + } + + internal class ReadOnlyCollection : ICollection { + private readonly ICollection _target; + public ReadOnlyCollection(ICollection target) { + _target = target; + } + public void Add(T item) { + throw new NotSupportedException(); + } + public void Clear() { + throw new NotSupportedException(); + } + public bool Contains(T item) { + return _target.Contains(item); + } + System.Collections.IEnumerator System.Collections.IEnumerable.GetEnumerator() { + return _target.GetEnumerator(); + } + public IEnumerator GetEnumerator() { + return _target.GetEnumerator(); + } + public bool IsReadOnly { + get { + return true; + } + } + public int Count { + get { + return _target.Count; + } + } + public bool Remove(T item) { + throw new NotSupportedException(); + } + public void CopyTo(T[] array, + int arrayIndex) { + _target.CopyTo(array,arrayIndex); + } + + protected ICollection GetTarget() { + return _target; + } + + } + + + internal class ReadOnlyList : ReadOnlyCollection, IList { + + public ReadOnlyList(IList target) : base(target) { + + } + public int IndexOf(T item) { + return GetTarget().IndexOf(item); + } + public void Insert(int index, T item) { + throw new NotSupportedException(); + } + public void RemoveAt(int index) { + throw new NotSupportedException(); + } + + public T this[int index] { + get { + return GetTarget()[index]; + } + set { + throw new NotSupportedException(); + } + } + + protected new IList GetTarget() { + return (IList)base.GetTarget(); + } + } + + internal class ReadOnlyDictionary : + ReadOnlyCollection>, + IDictionary { + public ReadOnlyDictionary(IDictionary target) : base(target) { + + } + public void Add(TKey key, TValue val) { + throw new NotSupportedException(); + } + protected new IDictionary GetTarget() { + return (IDictionary)base.GetTarget(); + } + public ICollection Keys { + get { + return new ReadOnlyCollection(GetTarget().Keys); + } + } + public ICollection Values { + get { + return new ReadOnlyCollection(GetTarget().Values); + } + } + public bool Remove(TKey key) { + throw new NotSupportedException(); + } + public bool ContainsKey(TKey key) { + return GetTarget().ContainsKey(key); + } + public bool TryGetValue(TKey key, out TValue value) { + return GetTarget().TryGetValue(key,out value); + } + public TValue this[TKey key] { + get { + return GetTarget()[key]; + } + set { + throw new NotSupportedException(); + } + } + } + + /// + /// Delegate that returns the Key, given a value + /// + public delegate TKey KeyFunction(TValue value); + + + /// + /// Description of CollectionUtil. + /// + public static class CollectionUtil + { + /// + /// Creates a dictionary where the keys are looked up using + /// reference equality + /// + /// + public static IDictionary NewIdentityDictionary() + where K : class + { + IdentityEqualityComparer comp = new IdentityEqualityComparer(); + return new Dictionary(comp); + } + + /// + /// Computes a hashCode over the enumeration. The hashCode is computed + /// such that it doesn't matter what order the elements are listed in. Thus, it + /// is suitable for arrays, lists, sets, and dictionaries + /// + /// The enumerable + /// The optional equality comparer + /// The hashcode + public static int HashCode(IEnumerable enum1, + IEqualityComparer comp) { + if ( comp == null ) { + comp = EqualityComparer.Default; + } + if ( enum1 == null ) { + return 0; + } + int rv = 0; + foreach (T val1 in enum1) { + unchecked { + rv+=comp.GetHashCode(val1); + } + } + return rv; + } + + /// + /// Returns true iff the two sets contain the same elements. This is only for + /// sets and dictionaries. Does not work for Lists or Arrays. + /// + /// The first collection + /// The second collection + /// + public static bool SetsEqual(ICollection collection1, + ICollection collection2) { + if ( collection1 == null || collection1 == null ) { + return collection1 == null && collection1 == null; + } + if ( collection1.Count != collection2.Count ) { + return false; + } + foreach (T val1 in collection1) { + if (!collection2.Contains(val1)) { + return false; + } + } + return true; + } + + /// + /// Gets the given value from the map or default if not exists. Always + /// use this rather than map[] since map[] throws an exception if not + /// exists. + /// + /// The map + /// The key + /// The default value + /// + public static TValue GetValue(IDictionary map, + TKey key, + TValue def) { + TValue rv; + bool present = map.TryGetValue(key,out rv); + if (present) { + return rv; + } + else { + return def; + } + } + + /// + /// Adds all the elements from the given enumerable to the given collection. + /// + /// The collection to add to + /// The enumerable to get from + public static void AddAll(ICollection collection, + IEnumerable enumerable) + where U : T { + if ( enumerable != null ) { + foreach(U obj in enumerable) { + collection.Add(obj); + } + } + } + /// + /// Adds all the elements from the given enumerable to the given collection. + /// + /// The collection to add to + /// The enumerable to get from + public static void RemoveAll(ICollection collection, + IEnumerable enumerable) + where U : T { + if ( enumerable != null ) { + foreach(U obj in enumerable) { + collection.Remove(obj); + } + } + } + + /// + /// Adds all the elements from the given enumerable to the given collection. + /// + /// The collection to add to + /// The enumerable to get from + public static void RemovalAll(ICollection collection, + IEnumerable enumerable) + where U : T { + if ( enumerable != null ) { + foreach(U obj in enumerable) { + collection.Remove(obj); + } + } + } + + + /// + /// Returns c or an empty collection iff c is null. + /// + /// The collection + /// c or an empty collection iff c is null. + public static ICollection NullAsEmpty(ICollection c) { + return c ?? new HashSet(); + } + + /// + /// Returns c or an empty collection iff c is null. + /// + /// The collection + /// c or an empty collection iff c is null. + public static IDictionary NullAsEmpty(IDictionary c) { + return c ?? new Dictionary(); + } + + /// + /// Returns c or an empty collection iff c is null. + /// + /// The collection + /// c or an empty collection iff c is null. + public static IList NullAsEmpty(IList c) { + return c ?? new List(); + } + + /// + /// Returns c or an empty array iff c is null. + /// + /// The array + /// c or an empty collection iff c is null. + public static T[] NullAsEmpty(T [] c) { + return c ?? new T[0]; + } + + /// + /// Given a collection of values a key function, builds a dictionary + /// + /// List of values + /// Key function, mapping from key to value + /// The dictionay + public static IDictionary NewDictionary( + TKey k1, + TValue v1) { + IDictionary rv = new Dictionary(); + rv[k1] = v1; + return rv; + } + + /// + /// Given a collection of values a key function, builds a dictionary + /// + /// List of values + /// Key function, mapping from key to value + /// The dictionay + public static IDictionary NewDictionary( + IEnumerable values, + KeyFunction keyFunction) { + IDictionary rv = new Dictionary(); + if ( values != null ) { + foreach (TValue value in values) { + TKey key = keyFunction(value); + //DONT use Add - it throws exceptions if already there + rv[key] = value; + } + } + return rv; + } + + /// + /// Given a collection of values a key function, builds a dictionary + /// + /// List of values + /// Key function, mapping from key to value + /// The dictionay + public static IDictionary NewReadOnlyDictionary( + IEnumerable values, + KeyFunction keyFunction) { + IDictionary rv = + NewDictionary(values,keyFunction); + return new ReadOnlyDictionary(rv); + } + + /// + /// Given a collection of values a key function, builds a dictionary + /// + /// List of values + /// Key function, mapping from key to value + /// The dictionay + public static IDictionary NewDictionary( + IDictionary original) { + return NewDictionary(original); + } + + /// + /// Given a collection of values a key function, builds a dictionary + /// + /// List of values + /// Key function, mapping from key to value + /// The dictionay + public static IDictionary NewDictionary( + IDictionary original) { + IDictionary rv = new Dictionary(); + if ( original != null ) { + foreach (KeyValuePair entry in original) { + //DONT use Add - it throws exceptions if already there + rv[(TKey2)(object)entry.Key] = (TValue2)(object)entry.Value; + } + } + return rv; + } + + /// + /// Given a collection of values a key function, builds a dictionary + /// + /// List of values + /// Key function, mapping from key to value + /// The dictionay + public static IDictionary NewReadOnlyDictionary( + IDictionary original) { + return NewReadOnlyDictionary(original); + } + + /// + /// Given a collection of values a key function, builds a dictionary + /// + /// List of values + /// Key function, mapping from key to value + /// The dictionay + public static IDictionary NewReadOnlyDictionary( + IDictionary original) { + IDictionary rv = NewDictionary(original); + return new ReadOnlyDictionary(rv); + } + + + /// + /// Returns a modifiable list, after first copying the collection. + /// + /// A collection. May be null. + /// a modifiable list, after first copying the collection. + public static IList NewList(IEnumerable collection) { + return NewList(collection); + } + + /// + /// Returns a modifiable list, after first copying the collection. + /// + /// A collection. May be null. + /// a modifiable list, after first copying the collection. + public static IList NewList(IEnumerable collection) { + IList rv = new List(); + if ( collection != null ) { + foreach (T element in collection) { + rv.Add((U)(object)element); + } + } + return rv; + } + + /// + /// Returns a modifiable set, after first copying the collection. + /// + /// A collection. May be null. + /// a modifiable set, after first copying the collection. + public static ICollection NewSet(IEnumerable collection) { + return NewSet(collection); + } + + /// + /// Returns a modifiable set, after first copying the array. + /// + /// An array maybe null. + /// a modifiable set, after first copying the array. + public static ICollection NewSet(params T[] items) { + return NewSet((IEnumerable)items); + } + + /// + /// Returns a modifiable set, after first copying the collection. + /// + /// A collection. May be null. + /// a modifiable set, after first copying the collection. + public static ICollection NewSet(IEnumerable collection) { + ICollection rv = new HashSet(); + if ( collection != null ) { + foreach (T element in collection) { + rv.Add((U)(object)element); + } + } + return rv; + } + + + /// + /// Returns an unmodifiable list, after first copying the collection. + /// + /// A collection. May be null. + /// an unmodifiable list, after first copying the collection. + public static IList NewReadOnlyList(ICollection collection) { + return NewReadOnlyList(collection); + } + + /// + /// Returns an unmodifiable list, after first copying the collection. + /// + /// A collection. May be null. + /// an unmodifiable list, after first copying the collection. + public static IList NewReadOnlyList(ICollection collection) { + IList list = NewList(collection); + return new ReadOnlyList(list); + } + + /// + /// Returns an unmodifiable list, after first copying the collection. + /// + /// A collection. May be null. + /// an unmodifiable list, after first copying the collection. + public static ICollection NewReadOnlySet(ICollection collection) { + return NewReadOnlySet(collection); + } + + /// + /// Returns an unmodifiable list, after first copying the collection. + /// + /// A collection. May be null. + /// an unmodifiable list, after first copying the collection. + private static ICollection NewReadOnlySet(ICollection collection) { + ICollection list = NewSet(collection); + return new ReadOnlyCollection(list); + } + + /// + /// Returns an unmodifiable set, backed by the original + /// + /// A collection. May be null. + /// an unmodifiable list, after first copying the collection. + public static ICollection AsReadOnlySet(ICollection collection) { + ICollection list = NullAsEmpty(collection); + return new ReadOnlyCollection(list); + } + + /// + /// Returns an unmodifiable list, backed by the original + /// + /// A collection. May be null. + /// an unmodifiable list, after first copying the collection. + public static IList AsReadOnlyList(IList collection) { + IList list = NullAsEmpty(collection); + return new ReadOnlyList(list); + } + + /// + /// Returns an unmodifiable list, backed by the original + /// + /// A collection. May be null. + /// an unmodifiable list, after first copying the collection. + public static IDictionary AsReadOnlyDictionary(IDictionary d) { + d = NullAsEmpty(d); + return new ReadOnlyDictionary(d); + } + + /// + /// Creates a new read-only list from an array. + /// + /// + /// + public static IList NewReadOnlyList(params T [] args) { + return NewReadOnlyList(args); + } + + /// + /// Creates a new read-only list from an array. + /// + /// + /// + private static IList NewReadOnlyList(params T [] args) { + IList list = CollectionUtil.NewList(args); + return new ReadOnlyList(list); + } + + /// + /// Creates a new read-only set from an array. + /// + /// + /// + public static ICollection NewReadOnlySet(params T [] args) { + return NewReadOnlySet(args); + } + /// + /// Creates a new read-only set from an array. + /// + /// + /// + private static ICollection NewReadOnlySet(params T [] args) { + ICollection list = CollectionUtil.NewSet(args); + return new ReadOnlyCollection(list); + } + + public static bool DictionariesEqual(IDictionary m1, + IDictionary m2) + { + if ( m1.Count != m2.Count ) { + return false; + } + foreach (KeyValuePair entry1 in m1) { + K key1 = entry1.Key; + V val1 = entry1.Value; + if (!m2.ContainsKey(key1)) { + return false; + } + Object val2 = m2[key1]; + if (!CollectionUtil.Equals(val1,val2)) { + return false; + } + } + return true; + } + + public static bool ListsEqual(IList v1, + IList v2) + { + if ( v1.Count != v2.Count ) { + return false; + } + for ( int i = 0; i < v1.Count; i++ ) { + if (!CollectionUtil.Equals(v1[i],v2[i])) { + return false; + } + } + return true; + } + + + /** + * Equality function that properly handles arrays, + * lists, maps, lists of arrays, and maps of arrays. + *

+ * NOTE: For Sets, this relies on the equals method + * of the Set to do the right thing. This is a reasonable + * assumption since, in order for Sets to behave + * properly as Sets, their values must already have + * a proper implementation of equals. (Or they must + * be specialized Sets that define a custom comparator that + * knows how to do the right thing). The same holds true for Map keys. + * Map values, on the other hand, are compared (so Map values + * can be arrays). + * @param o1 The first object. May be null. + * @param o2 The second object. May be null. + * @return true iff the two objects are equal. + */ + public new static bool Equals(Object o1, Object o2) { + if ( o1 == o2 ) { //same object or both null + return true; + } + else if ( o1 == null ) { + return false; + } + else if ( o2 == null ) { + return false; + } + else if ( o1 is Array ) { + Type clazz1 = o1.GetType(); + Type clazz2 = o2.GetType(); + if ( !clazz1.Equals(clazz2)) { + return false; + } + Array array1 = (Array)o1; + Array array2 = (Array)o2; + int length1 = array1.Length; + int length2 = array2.Length; + if ( length1 != length2 ) { + return false; + } + for ( int i = 0; i < length1; i++ ) { + Object el1 = array1.GetValue(i); + Object el2 = array2.GetValue(i); + if (!CollectionUtil.Equals(el1,el2)) { + return false; + } + } + return true; + } + else if ( ReflectionUtil.IsParentTypeOf(typeof(IList<>),o1.GetType()) ) { + Type parent1 = ReflectionUtil.FindInHierarchyOf(typeof(IList<>),o1.GetType()); + Type parent2 = ReflectionUtil.FindInHierarchyOf(typeof(IList<>),o2.GetType()); + if (!parent1.Equals(parent2)) { + return false; + } + Type [] genericArguments = + parent1.GetGenericArguments(); + + + Type collectionUtil = typeof(CollectionUtil); + MethodInfo info = collectionUtil.GetMethod("ListsEqual"); + + info = info.MakeGenericMethod(genericArguments); + + Object rv = info.Invoke(null, new object[]{o1,o2}); + return (bool)rv; + } + else if ( ReflectionUtil.IsParentTypeOf(typeof(IDictionary<,>),o1.GetType()) ) { + Type parent1 = ReflectionUtil.FindInHierarchyOf(typeof(IDictionary<,>),o1.GetType()); + Type parent2 = ReflectionUtil.FindInHierarchyOf(typeof(IDictionary<,>),o2.GetType()); + if (!parent1.Equals(parent2)) { + return false; + } + Type [] genericArguments = + parent1.GetGenericArguments(); + + + Type collectionUtil = typeof(CollectionUtil); + MethodInfo info = collectionUtil.GetMethod("DictionariesEqual"); + + info = info.MakeGenericMethod(genericArguments); + + Object rv = info.Invoke(null, new object[]{o1,o2}); + return (bool)rv; + } + else if ( ReflectionUtil.IsParentTypeOf(typeof(ICollection<>),o1.GetType()) ) { + Type parent1 = ReflectionUtil.FindInHierarchyOf(typeof(ICollection<>),o1.GetType()); + Type parent2 = ReflectionUtil.FindInHierarchyOf(typeof(ICollection<>),o2.GetType()); + if (!parent1.Equals(parent2)) { + return false; + } + Type [] genericArguments = + parent1.GetGenericArguments(); + + + Type collectionUtil = typeof(CollectionUtil); + MethodInfo info = collectionUtil.GetMethod("SetsEqual"); + + info = info.MakeGenericMethod(genericArguments); + + Object rv = info.Invoke(null, new object[]{o1,o2}); + return (bool)rv; + } + else { + return o1.Equals(o2); + } + } + + } + +} diff --git a/Common/Common.csproj b/Common/Common.csproj new file mode 100644 index 00000000..17c9d69a --- /dev/null +++ b/Common/Common.csproj @@ -0,0 +1,108 @@ + + + + {F140E8DA-52B4-4159-992A-9DA10EA8EEFB} + Debug + AnyCPU + Library + Org.IdentityConnectors.Common + Common + v3.5 + True + False + 4 + false + + + bin\Debug\ + true + Full + False + True + DEBUG;TRACE + + + bin\Release\ + False + None + True + False + TRACE + + + False + Auto + 4194304 + AnyCPU + 4096 + + + + + + 3.5 + + + + 3.5 + + + + 3.5 + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/Common/DateTimeUtil.cs b/Common/DateTimeUtil.cs new file mode 100644 index 00000000..cc97173a --- /dev/null +++ b/Common/DateTimeUtil.cs @@ -0,0 +1,61 @@ +/* + * Copyright 2008 Sun Microsystems, Inc. All rights reserved. + * + * U.S. Government Rights - Commercial software. Government users + * are subject to the Sun Microsystems, Inc. standard license agreement + * and applicable provisions of the FAR and its supplements. + * + * Use is subject to license terms. + * + * This distribution may include materials developed by third parties. + * Sun, Sun Microsystems, the Sun logo, Java and Project Identity + * Connectors are trademarks or registered trademarks of Sun + * Microsystems, Inc. or its subsidiaries in the U.S. and other + * countries. + * + * UNIX is a registered trademark in the U.S. and other countries, + * exclusively licensed through X/Open Company, Ltd. + * + * ----------- + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER. + * + * Copyright 2008 Sun Microsystems, Inc. All rights reserved. + * + * The contents of this file are subject to the terms of the Common Development + * and Distribution License(CDDL) (the License). You may not use this file + * except in compliance with the License. + * + * You can obtain a copy of the License at + * http://identityconnectors.dev.java.net/CDDLv1.0.html + * See the License for the specific language governing permissions and + * limitations under the License. + * + * When distributing the Covered Code, include this CDDL Header Notice in each + * file and include the License file at identityconnectors/legal/license.txt. + * If applicable, add the following below this CDDL Header, with the fields + * enclosed by brackets [] replaced by your own identifying information: + * "Portions Copyrighted [year] [name of copyright owner]" + * ----------- + */ +using System; + +namespace Org.IdentityConnectors.Common +{ + ///

+ /// Description of DateTimeUtil. + /// + public static class DateTimeUtil + { + public static DateTime GetDateTimeFromUtcMillis(long dateTime) { + return new DateTime(dateTime * 1000, DateTimeKind.Utc); + } + + public static long GetUtcTimeMillis(DateTime dateTime) { + return dateTime.ToFileTimeUtc()/1000; + } + + public static long GetCurrentUtcTimeMillis() { + return GetUtcTimeMillis(DateTime.Now); + } + } +} diff --git a/Common/FrameworkInternalBridge.cs b/Common/FrameworkInternalBridge.cs new file mode 100644 index 00000000..d9523d98 --- /dev/null +++ b/Common/FrameworkInternalBridge.cs @@ -0,0 +1,71 @@ +/* + * Copyright 2008 Sun Microsystems, Inc. All rights reserved. + * + * U.S. Government Rights - Commercial software. Government users + * are subject to the Sun Microsystems, Inc. standard license agreement + * and applicable provisions of the FAR and its supplements. + * + * Use is subject to license terms. + * + * This distribution may include materials developed by third parties. + * Sun, Sun Microsystems, the Sun logo, Java and Project Identity + * Connectors are trademarks or registered trademarks of Sun + * Microsystems, Inc. or its subsidiaries in the U.S. and other + * countries. + * + * UNIX is a registered trademark in the U.S. and other countries, + * exclusively licensed through X/Open Company, Ltd. + * + * ----------- + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER. + * + * Copyright 2008 Sun Microsystems, Inc. All rights reserved. + * + * The contents of this file are subject to the terms of the Common Development + * and Distribution License(CDDL) (the License). You may not use this file + * except in compliance with the License. + * + * You can obtain a copy of the License at + * http://identityconnectors.dev.java.net/CDDLv1.0.html + * See the License for the specific language governing permissions and + * limitations under the License. + * + * When distributing the Covered Code, include this CDDL Header Notice in each + * file and include the License file at identityconnectors/legal/license.txt. + * If applicable, add the following below this CDDL Header, with the fields + * enclosed by brackets [] replaced by your own identifying information: + * "Portions Copyrighted [year] [name of copyright owner]" + * ----------- + */ +using System; +using System.Reflection; +namespace Org.IdentityConnectors.Common +{ + /// + /// Description of FrameworkInternalBridge. + /// + internal static class FrameworkInternalBridge { + private static readonly Object LOCK = new Object(); + private static Assembly _assembly = null; + /// + /// Loads a class from the FrameworkInternal module + /// + /// + /// + public static Type LoadType(String typeName) { + + Assembly assembly; + lock(LOCK) { + if (_assembly == null) { + AssemblyName assemName = new AssemblyName(); + assemName.Name = "FrameworkInternal"; + _assembly = Assembly.Load(assemName); + } + assembly = _assembly; + } + + return assembly.GetType(typeName,true); + + } + } +} diff --git a/Common/IOUtil.cs b/Common/IOUtil.cs new file mode 100644 index 00000000..3e72ef9d --- /dev/null +++ b/Common/IOUtil.cs @@ -0,0 +1,64 @@ +/* + * Copyright 2008 Sun Microsystems, Inc. All rights reserved. + * + * U.S. Government Rights - Commercial software. Government users + * are subject to the Sun Microsystems, Inc. standard license agreement + * and applicable provisions of the FAR and its supplements. + * + * Use is subject to license terms. + * + * This distribution may include materials developed by third parties. + * Sun, Sun Microsystems, the Sun logo, Java and Project Identity + * Connectors are trademarks or registered trademarks of Sun + * Microsystems, Inc. or its subsidiaries in the U.S. and other + * countries. + * + * UNIX is a registered trademark in the U.S. and other countries, + * exclusively licensed through X/Open Company, Ltd. + * + * ----------- + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER. + * + * Copyright 2008 Sun Microsystems, Inc. All rights reserved. + * + * The contents of this file are subject to the terms of the Common Development + * and Distribution License(CDDL) (the License). You may not use this file + * except in compliance with the License. + * + * You can obtain a copy of the License at + * http://identityconnectors.dev.java.net/CDDLv1.0.html + * See the License for the specific language governing permissions and + * limitations under the License. + * + * When distributing the Covered Code, include this CDDL Header Notice in each + * file and include the License file at identityconnectors/legal/license.txt. + * If applicable, add the following below this CDDL Header, with the fields + * enclosed by brackets [] replaced by your own identifying information: + * "Portions Copyrighted [year] [name of copyright owner]" + * ----------- + */ +using System; +using System.Net; +namespace Org.IdentityConnectors.Common +{ + /// + /// Description of IOUtil. + /// + public static class IOUtil + { + /// + /// Given an ip address or host, returns the + /// IPAddress + /// + /// + /// + public static IPAddress GetIPAddress(String hostOrIp) + { + if ( hostOrIp.Equals("0.0.0.0") || + hostOrIp.Equals("::0") ) { + return IPAddress.Parse(hostOrIp); + } + return Dns.GetHostAddresses(hostOrIp)[0]; + } + } +} diff --git a/Common/Locale.cs b/Common/Locale.cs new file mode 100644 index 00000000..8bb9232f --- /dev/null +++ b/Common/Locale.cs @@ -0,0 +1,227 @@ +/* + * Copyright 2008 Sun Microsystems, Inc. All rights reserved. + * + * U.S. Government Rights - Commercial software. Government users + * are subject to the Sun Microsystems, Inc. standard license agreement + * and applicable provisions of the FAR and its supplements. + * + * Use is subject to license terms. + * + * This distribution may include materials developed by third parties. + * Sun, Sun Microsystems, the Sun logo, Java and Project Identity + * Connectors are trademarks or registered trademarks of Sun + * Microsystems, Inc. or its subsidiaries in the U.S. and other + * countries. + * + * UNIX is a registered trademark in the U.S. and other countries, + * exclusively licensed through X/Open Company, Ltd. + * + * ----------- + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER. + * + * Copyright 2008 Sun Microsystems, Inc. All rights reserved. + * + * The contents of this file are subject to the terms of the Common Development + * and Distribution License(CDDL) (the License). You may not use this file + * except in compliance with the License. + * + * You can obtain a copy of the License at + * http://identityconnectors.dev.java.net/CDDLv1.0.html + * See the License for the specific language governing permissions and + * limitations under the License. + * + * When distributing the Covered Code, include this CDDL Header Notice in each + * file and include the License file at identityconnectors/legal/license.txt. + * If applicable, add the following below this CDDL Header, with the fields + * enclosed by brackets [] replaced by your own identifying information: + * "Portions Copyrighted [year] [name of copyright owner]" + * ----------- + */ +using System; +using System.Collections.Generic; +using System.Globalization; +namespace Org.IdentityConnectors.Common +{ + /// + /// Represents a Java Locale and has facilities for converting to/from + /// C# CultureInfo + /// + public sealed class Locale + { + private static readonly IDictionary + Locale2CultureInfoName = new Dictionary(); + private static readonly IDictionary + CultureInfoName2Locale = new Dictionary(); + + private static void AddMapping(Locale locale, String name) { + Locale2CultureInfoName[locale] = name; + CultureInfoName2Locale[name] = locale; + } + + /// + /// Special cases + /// + static Locale() + { + AddMapping(new Locale(),""); + AddMapping(new Locale("iw"),"he"); + AddMapping(new Locale("zh"),"zh-CHS"); + AddMapping(new Locale("iw","IL"),"he-IL"); + AddMapping(new Locale("no","NO"),"nb-NO"); + AddMapping(new Locale("no","NO","NY"),"nn-NO"); + } + + private String _language; + private String _country; + private String _variant; + + public Locale() + : this("") + { + + } + + public Locale(String language) + : this(language,"") + { + } + public Locale(String language, String country) + : this(language,country,"") + { + } + public Locale(String language, String country, String variant) + { + _language = language ?? ""; + _country = country ?? ""; + _variant = variant ?? ""; + } + + public string Language { + get { + return _language; + } + } + + public string Country { + get { + return _country; + } + } + + public string Variant { + get { + return _variant; + } + } + + public override bool Equals(Object o) { + Locale other = o as Locale; + if ( other != null ) { + if (!Object.Equals(Language,other.Language)) { + return false; + } + if (!Object.Equals(Country,other.Country)) { + return false; + } + if (!Object.Equals(Variant,other.Variant)) { + return false; + } + return true; + } + return false; + } + + public override int GetHashCode() { + unchecked { + return _language.GetHashCode()+_country.GetHashCode(); + } + } + + public override string ToString() { + return Language+"_"+Country+"_"+Variant; + } + + /// + /// Creates the closest CultureInfo that maps to this locale + /// + /// The culture info + public CultureInfo ToCultureInfo() + { + CultureInfo rv = null; + String code = CollectionUtil.GetValue(Locale2CultureInfoName,this,null); + if (code != null ) { + rv = TryCultureCode(code); + } + if ( rv == null ) { + if (Country.Length > 0) { + rv = TryCultureCode(Language+"-"+Country); + } + } + if ( rv == null ) { + rv = TryCultureCode(Language); + } + if ( rv == null ) { + rv = CultureInfo.InvariantCulture; + } + return rv; + } + + public static Locale FindLocale(CultureInfo info) + { + String code = info.Name; + Locale rv = CollectionUtil.GetValue(CultureInfoName2Locale,code,null); + if (rv == null) { + String [] parts = code.Split(new string[]{"-"}, + StringSplitOptions.RemoveEmptyEntries); + String language = ""; + String country = ""; + if ( parts.Length > 0 ) { + String l = parts[0]; + if (LooksLikeValidLanguageCode(l)) { + language = l; + if ( parts.Length > 1 ) { + String c = parts[1]; + if (LooksLikeValidCountryCode(c)) { + country = c; + } + } + } + } + rv = new Locale(language,country); + } + return rv; + } + + /// + /// Weeds out some junk + /// + /// + /// + private static bool LooksLikeValidLanguageCode(String l) { + char [] chars = l.ToCharArray(); + return (chars.Length == 2 && + Char.IsLower(chars[0]) && + Char.IsLower(chars[1])); + } + /// + /// Weeds out some junk + /// + /// + /// + private static bool LooksLikeValidCountryCode(String l) { + char [] chars = l.ToCharArray(); + return (chars.Length == 2 && + Char.IsUpper(chars[0]) && + Char.IsUpper(chars[1])); + } + + private static CultureInfo TryCultureCode(String code) { + try { + return new CultureInfo(code); + } + catch (Exception) { + return null; + } + } + } +} diff --git a/Common/Pair.cs b/Common/Pair.cs new file mode 100644 index 00000000..77658393 --- /dev/null +++ b/Common/Pair.cs @@ -0,0 +1,90 @@ +/* + * Copyright 2008 Sun Microsystems, Inc. All rights reserved. + * + * U.S. Government Rights - Commercial software. Government users + * are subject to the Sun Microsystems, Inc. standard license agreement + * and applicable provisions of the FAR and its supplements. + * + * Use is subject to license terms. + * + * This distribution may include materials developed by third parties. + * Sun, Sun Microsystems, the Sun logo, Java and Project Identity + * Connectors are trademarks or registered trademarks of Sun + * Microsystems, Inc. or its subsidiaries in the U.S. and other + * countries. + * + * UNIX is a registered trademark in the U.S. and other countries, + * exclusively licensed through X/Open Company, Ltd. + * + * ----------- + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER. + * + * Copyright 2008 Sun Microsystems, Inc. All rights reserved. + * + * The contents of this file are subject to the terms of the Common Development + * and Distribution License(CDDL) (the License). You may not use this file + * except in compliance with the License. + * + * You can obtain a copy of the License at + * http://identityconnectors.dev.java.net/CDDLv1.0.html + * See the License for the specific language governing permissions and + * limitations under the License. + * + * When distributing the Covered Code, include this CDDL Header Notice in each + * file and include the License file at identityconnectors/legal/license.txt. + * If applicable, add the following below this CDDL Header, with the fields + * enclosed by brackets [] replaced by your own identifying information: + * "Portions Copyrighted [year] [name of copyright owner]" + * ----------- + */ +using System; + +namespace Org.IdentityConnectors.Common +{ + /// + /// Represents a Pair of objects + /// + public class Pair + { + public Pair() + { + } + + public Pair(T1 first, T2 second) + { + First = first; + Second = second; + } + + public T1 First { get; set; } + public T2 Second { get; set; } + + public override bool Equals(object obj) + { + Pair other = obj as Pair; + if ( other != null ) + { + return Object.Equals(First,other.First) && + Object.Equals(Second,other.Second); + } + return false; + } + + public override int GetHashCode() + { + int rv = 0; + if ( First != null ) { + rv ^= First.GetHashCode(); + } + if ( Second != null ) { + rv ^= Second.GetHashCode(); + } + return rv; + } + + public override string ToString() + { + return "( "+First+", "+Second+" )"; + } + } +} diff --git a/Common/Pooling.cs b/Common/Pooling.cs new file mode 100644 index 00000000..d605da0b --- /dev/null +++ b/Common/Pooling.cs @@ -0,0 +1,206 @@ +/* + * Copyright 2008 Sun Microsystems, Inc. All rights reserved. + * + * U.S. Government Rights - Commercial software. Government users + * are subject to the Sun Microsystems, Inc. standard license agreement + * and applicable provisions of the FAR and its supplements. + * + * Use is subject to license terms. + * + * This distribution may include materials developed by third parties. + * Sun, Sun Microsystems, the Sun logo, Java and Project Identity + * Connectors are trademarks or registered trademarks of Sun + * Microsystems, Inc. or its subsidiaries in the U.S. and other + * countries. + * + * UNIX is a registered trademark in the U.S. and other countries, + * exclusively licensed through X/Open Company, Ltd. + * + * ----------- + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER. + * + * Copyright 2008 Sun Microsystems, Inc. All rights reserved. + * + * The contents of this file are subject to the terms of the Common Development + * and Distribution License(CDDL) (the License). You may not use this file + * except in compliance with the License. + * + * You can obtain a copy of the License at + * http://identityconnectors.dev.java.net/CDDLv1.0.html + * See the License for the specific language governing permissions and + * limitations under the License. + * + * When distributing the Covered Code, include this CDDL Header Notice in each + * file and include the License file at identityconnectors/legal/license.txt. + * If applicable, add the following below this CDDL Header, with the fields + * enclosed by brackets [] replaced by your own identifying information: + * "Portions Copyrighted [year] [name of copyright owner]" + * ----------- + */ +using System; + +using System.Collections.Generic; + +namespace Org.IdentityConnectors.Common.Pooling +{ + /** + * Configuration for pooling objects + */ + public sealed class ObjectPoolConfiguration { + + /** + * Max objects (idle+active). + */ + private int _maxObjects = 5; + + /** + * Max idle objects. + */ + private int _maxIdle = 3; + + /** + * Max time to wait if the pool is waiting for a free object to become + * available before failing. Zero means don't wait + */ + private long _maxWait = 60*1000; + + /** + * Minimum time to wait before evicting an idle object. + * Zero means don't wait + */ + private long _minEvictableIdleTimeMillis = 60 * 1000; + + /** + * Minimum number of idle objects. + */ + private int _minIdle = 0; + + + /** + * Get the set number of maximum objects (idle+active) + */ + public int MaxObjects { + get { + return _maxObjects; + } + set { + _maxObjects = value; + } + } + + /** + * Get the maximum number of idle objects. + */ + public int MaxIdle { + get { + return _maxIdle; + } + set { + _maxIdle = value; + } + } + + /** + * Max time to wait if the pool is waiting for a free object to become + * available before failing. Zero means don't wait + */ + public long MaxWait { + get { + return _maxWait; + } + set { + _maxWait = value; + } + } + + /** + * Minimum time to wait before evicting an idle object. + * Zero means don't wait + */ + public long MinEvictableIdleTimeMillis { + get { + return _minEvictableIdleTimeMillis; + } + set { + _minEvictableIdleTimeMillis = value; + } + } + + /** + * Minimum number of idle objects. + */ + public int MinIdle { + get { + return _minIdle; + } + set { + _minIdle = value; + } + } + + public void Validate() { + if (_minIdle < 0) { + throw new InvalidOperationException("Min idle is less than zero."); + } + if (_maxObjects < 0) { + throw new InvalidOperationException("Max active is less than zero."); + } + if (_maxIdle < 0) { + throw new InvalidOperationException("Max idle is less than zero."); + } + if (_maxWait < 0) { + throw new InvalidOperationException("Max wait is less than zero."); + } + if (_minEvictableIdleTimeMillis < 0) { + throw new InvalidOperationException("Min evictable idle time millis less than zero."); + } + if ( _minIdle > _maxIdle ) { + throw new InvalidOperationException("Min idle is greater than max idle."); + } + if ( _maxIdle > _maxObjects ) { + throw new InvalidOperationException("Max idle is greater than max objects."); + } + } + + public override int GetHashCode() { + unchecked { + return (int)(MaxObjects+MaxIdle+MaxWait+MinEvictableIdleTimeMillis+MinIdle); + } + } + + public override bool Equals(Object obj) { + if ( obj is ObjectPoolConfiguration) { + ObjectPoolConfiguration other = (ObjectPoolConfiguration)obj; + + if (MaxObjects != other.MaxObjects) { + return false; + } + if (MaxIdle != other.MaxIdle) { + return false; + } + if (MaxWait != other.MaxWait) { + return false; + } + if (MinEvictableIdleTimeMillis != other.MinEvictableIdleTimeMillis) { + return false; + } + if (MinIdle != other.MinIdle) { + return false; + } + return true; + } + return false; + } + + public override String ToString() { + // poor man's toString() + IDictionary bld = new Dictionary(); + bld["MaxObjects"] = MaxObjects; + bld["MaxIdle"] = MaxIdle; + bld["MaxWait"] = MaxWait; + bld["MinEvictableIdleTimeMillis"] = MinEvictableIdleTimeMillis; + bld["MinIdle"] = MinIdle; + return bld.ToString(); + } + } +} diff --git a/Common/Proxy.cs b/Common/Proxy.cs new file mode 100644 index 00000000..33c6cc30 --- /dev/null +++ b/Common/Proxy.cs @@ -0,0 +1,284 @@ +/* + * Copyright 2008 Sun Microsystems, Inc. All rights reserved. + * + * U.S. Government Rights - Commercial software. Government users + * are subject to the Sun Microsystems, Inc. standard license agreement + * and applicable provisions of the FAR and its supplements. + * + * Use is subject to license terms. + * + * This distribution may include materials developed by third parties. + * Sun, Sun Microsystems, the Sun logo, Java and Project Identity + * Connectors are trademarks or registered trademarks of Sun + * Microsystems, Inc. or its subsidiaries in the U.S. and other + * countries. + * + * UNIX is a registered trademark in the U.S. and other countries, + * exclusively licensed through X/Open Company, Ltd. + * + * ----------- + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER. + * + * Copyright 2008 Sun Microsystems, Inc. All rights reserved. + * + * The contents of this file are subject to the terms of the Common Development + * and Distribution License(CDDL) (the License). You may not use this file + * except in compliance with the License. + * + * You can obtain a copy of the License at + * http://identityconnectors.dev.java.net/CDDLv1.0.html + * See the License for the specific language governing permissions and + * limitations under the License. + * + * When distributing the Covered Code, include this CDDL Header Notice in each + * file and include the License file at identityconnectors/legal/license.txt. + * If applicable, add the following below this CDDL Header, with the fields + * enclosed by brackets [] replaced by your own identifying information: + * "Portions Copyrighted [year] [name of copyright owner]" + * ----------- + */ +using System; +using System.Reflection; +using System.Reflection.Emit; +using System.Collections.Generic; +namespace Org.IdentityConnectors.Common.Proxy +{ + /// + /// Similar to java.lang.reflect.InvocationHandler + /// + public interface InvocationHandler + { + Object + Invoke(Object proxy, MethodInfo method, Object [] args); + } + + + /// + /// Similar to java.lang.reflect.Proxy + /// + public static class Proxy + { + private static readonly MethodInfo INVOCATION_HANDLER_METHOD = + typeof(InvocationHandler).GetMethod("Invoke", + new Type[]{typeof(object), + typeof(MethodInfo), + typeof(object[])}); + private static readonly MethodInfo GET_METHOD_FROM_HANDLE_METHOD = + typeof(MethodBase).GetMethod("GetMethodFromHandle", + new Type[]{typeof(RuntimeMethodHandle)}); + + private static readonly object LOCK = new Object(); + + private static IDictionary + _implementationMap = new Dictionary(); + + private static int _count = 0; + + public static Object NewProxyInstance(Type interfaze, + InvocationHandler handler) + { + ConstructorInfo constructor = GetOrCreateConstructorInfo(interfaze); + return constructor.Invoke(new object[]{handler}); + } + + private static ConstructorInfo GetOrCreateConstructorInfo(Type type) + { + lock(LOCK) + { + ConstructorInfo rv = + CollectionUtil.GetValue(_implementationMap,type,null); + if ( rv == null ) + { + Type impl = GenerateImplementation(type); + rv = + impl.GetConstructor(new Type[]{typeof(InvocationHandler)}); + + _implementationMap[type] = rv; + } + return rv; + } + } + + private static String NextName() + { + int count; + lock(LOCK) { + count = _count; + count++; + } + return "___proxy"+count; + } + + + + private static Type GenerateImplementation(Type interfaze) + { + if (!interfaze.IsInterface) + { + throw new ArgumentException("Type is not an interface: "+interfaze); + } + if ( interfaze.IsGenericType ) + { + throw new ArgumentException("Type is a generic type: "+interfaze); + } + + String uniqueName = NextName(); + + AssemblyName assemblyName = new AssemblyName(uniqueName); + AssemblyBuilder assemblyBuilder = + AppDomain.CurrentDomain.DefineDynamicAssembly( + assemblyName, + AssemblyBuilderAccess.RunAndSave); + // For a single-module assembly, the module name is usually + // the assembly name plus an extension. + ModuleBuilder moduleBuilder = + assemblyBuilder.DefineDynamicModule(assemblyName.Name, assemblyName.Name + ".dll"); + + TypeBuilder typeBuilder = moduleBuilder.DefineType( + uniqueName, + TypeAttributes.Public); + typeBuilder.AddInterfaceImplementation(interfaze); + + MethodInfo [] methods = interfaze.GetMethods(); + ConstructorBuilder classInitializer = + typeBuilder.DefineTypeInitializer(); + ILGenerator classInitializerCode = + classInitializer.GetILGenerator(); + //generate constructor and _handler field + FieldBuilder handlerField = + typeBuilder.DefineField("_handler", + typeof(InvocationHandler), + FieldAttributes.Private|FieldAttributes.InitOnly); + int count = 0; + foreach (MethodInfo method in methods) + { + GenerateMethod(typeBuilder,method,classInitializerCode,count, + handlerField); + count++; + } + classInitializerCode.Emit(OpCodes.Ret); + + + ConstructorBuilder constructorBuilder = typeBuilder.DefineConstructor( + MethodAttributes.Public, + CallingConventions.Standard, + new Type[]{typeof(InvocationHandler)}); + ILGenerator constructorCode = constructorBuilder.GetILGenerator(); + // For a constructor, argument zero is a reference to the new + // instance. Push it on the stack before calling the base + // class constructor. Specify the default constructor of the + // base class (System.Object) by passing an empty array of + // types (Type.EmptyTypes) to GetConstructor. + constructorCode.Emit(OpCodes.Ldarg_0); + constructorCode.Emit(OpCodes.Call, + typeof(object).GetConstructor(Type.EmptyTypes)); + constructorCode.Emit(OpCodes.Ldarg_0); + constructorCode.Emit(OpCodes.Ldarg_1); + constructorCode.Emit(OpCodes.Stfld, handlerField); + constructorCode.Emit(OpCodes.Ret); + + + Type t = typeBuilder.CreateType(); + + //assemblyBuilder.Save(assemblyName.Name+".dll"); + + return t; + } + + private static void ValidateMethod(MethodInfo info) + { + if ( info.GetGenericArguments().Length != 0 ) { + throw new ArgumentException( + "Method not supported since it has generic arguments: "+info); + + } + foreach (ParameterInfo parameter in info.GetParameters()) { + if ( parameter.IsOut ) { + throw new ArgumentException( + "Method not supported since it has output paramaters: "+info); + } + if ( parameter.IsRetval ) { + throw new ArgumentException( + "Method not supported since it has retval paramaters: "+info); + } + } + } + + private static void GenerateMethod(TypeBuilder typeBuilder, + MethodInfo method, + ILGenerator classInitializerCode, + int methodCount, + FieldBuilder handlerField) + { + ValidateMethod(method); + + FieldBuilder methodField = + typeBuilder.DefineField("METHOD"+methodCount, + typeof(MethodInfo), + FieldAttributes.Private|FieldAttributes.InitOnly|FieldAttributes.Static); + + + + classInitializerCode.Emit(OpCodes.Ldtoken, + method); + classInitializerCode.Emit(OpCodes.Call, + GET_METHOD_FROM_HANDLE_METHOD); + classInitializerCode.Emit(OpCodes.Castclass,typeof(MethodInfo)); + classInitializerCode.Emit(OpCodes.Stsfld,methodField); + + + Type [] parameterTypes = ReflectionUtil.GetParameterTypes(method); + MethodBuilder methodBuilder = typeBuilder.DefineMethod( + method.Name, + MethodAttributes.Public| + MethodAttributes.HideBySig| + MethodAttributes.NewSlot| + MethodAttributes.Virtual| + MethodAttributes.Final, + method.CallingConvention, + method.ReturnType, + parameterTypes); + + ILGenerator methodCode = methodBuilder.GetILGenerator(); + methodCode.Emit(OpCodes.Ldarg_0); + methodCode.Emit(OpCodes.Ldfld,handlerField); + methodCode.Emit(OpCodes.Ldarg_0); + methodCode.Emit(OpCodes.Ldsfld,methodField); + methodCode.Emit(OpCodes.Ldc_I4,parameterTypes.Length); + methodCode.Emit(OpCodes.Newarr,typeof(object)); + + for (int index = 0; index < parameterTypes.Length; index++) + { + Type parameterType = parameterTypes[index]; + methodCode.Emit(OpCodes.Dup); + methodCode.Emit(OpCodes.Ldc_I4,index); + methodCode.Emit(OpCodes.Ldarg,index+1); + if (parameterType.IsValueType) + { + methodCode.Emit(OpCodes.Box,parameterType); + } + methodCode.Emit(OpCodes.Stelem_Ref); + } + + methodCode.Emit(OpCodes.Callvirt,INVOCATION_HANDLER_METHOD); + Type returnType = method.ReturnType; + + if (typeof(void).Equals(returnType)) + { + methodCode.Emit(OpCodes.Pop); + } + else if (returnType.IsValueType) + { + methodCode.Emit(OpCodes.Unbox_Any,returnType); + } + else + { + methodCode.Emit(OpCodes.Castclass,returnType); + } + + methodCode.Emit(OpCodes.Ret); + + typeBuilder.DefineMethodOverride(methodBuilder,method); + } + } +} diff --git a/Common/ReflectionUtil.cs b/Common/ReflectionUtil.cs new file mode 100644 index 00000000..f66d2490 --- /dev/null +++ b/Common/ReflectionUtil.cs @@ -0,0 +1,166 @@ +/* + * Copyright 2008 Sun Microsystems, Inc. All rights reserved. + * + * U.S. Government Rights - Commercial software. Government users + * are subject to the Sun Microsystems, Inc. standard license agreement + * and applicable provisions of the FAR and its supplements. + * + * Use is subject to license terms. + * + * This distribution may include materials developed by third parties. + * Sun, Sun Microsystems, the Sun logo, Java and Project Identity + * Connectors are trademarks or registered trademarks of Sun + * Microsystems, Inc. or its subsidiaries in the U.S. and other + * countries. + * + * UNIX is a registered trademark in the U.S. and other countries, + * exclusively licensed through X/Open Company, Ltd. + * + * ----------- + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER. + * + * Copyright 2008 Sun Microsystems, Inc. All rights reserved. + * + * The contents of this file are subject to the terms of the Common Development + * and Distribution License(CDDL) (the License). You may not use this file + * except in compliance with the License. + * + * You can obtain a copy of the License at + * http://identityconnectors.dev.java.net/CDDLv1.0.html + * See the License for the specific language governing permissions and + * limitations under the License. + * + * When distributing the Covered Code, include this CDDL Header Notice in each + * file and include the License file at identityconnectors/legal/license.txt. + * If applicable, add the following below this CDDL Header, with the fields + * enclosed by brackets [] replaced by your own identifying information: + * "Portions Copyrighted [year] [name of copyright owner]" + * ----------- + */ +using System; +using System.Reflection; +using System.Collections.Generic; +using System.Linq; +namespace Org.IdentityConnectors.Common +{ + /// + /// Miscellaenous reflection utilities. + /// + public static class ReflectionUtil + { + /// + /// Gets all the interfaces in the hierarchy + /// + /// + /// + public static Type [] GetAllInterfaces(Type type) { + HashSet temp = new HashSet(); + GetAllInterfaces2(type,temp); + return temp.ToArray(); + } + + private static void GetAllInterfaces2(Type type, HashSet rv) { + if ( type != null ) { + if ( type.IsInterface ) { + rv.Add(type); + } + GetAllInterfaces2(type.BaseType,rv); + foreach (Type inter in type.GetInterfaces()) { + GetAllInterfaces2(inter,rv); + } + } + } + + /// + /// Returns the generic type definition of the given type + /// + /// + /// + public static Type [] GetTypeErasure(Type [] types) { + Type [] rv = new Type[types.Length]; + for ( int i = 0; i < types.Length; i++ ) { + rv[i] = GetTypeErasure(types[i]); + } + return rv; + } + /// + /// Returns the generic type definition of the given type + /// + /// + /// + public static Type GetTypeErasure(Type type) { + if ( type.IsGenericType ) { + type = type.GetGenericTypeDefinition(); + } + return type; + } + + /// + /// Returns true iff t1 is a parent type of t2. Unlike + /// Type.isAssignableFrom, this handles generic types as + /// well. + /// + /// + /// + /// + public static bool IsParentTypeOf(Type t1, + Type t2) { + if (t2 == null) { + return t1 == null; + } + Type found = FindInHierarchyOf(t1,t2); + return found != null; + } + + /// + /// Finds t1 in the hierarchy of t2 or null if not found. The + /// returned value will have generic parameters applied to it. + /// + /// + /// + /// + public static Type FindInHierarchyOf(Type t1, Type t2) { + if (t2 == null) { + return null; + } + if ( EqualsIgnoreGeneric(t1,t2) ) { + return t2; + } + Type found1 = FindInHierarchyOf(t1,t2.BaseType); + if (found1 != null) { + return found1; + } + foreach (Type inter in t2.GetInterfaces()) { + Type found2 = FindInHierarchyOf(t1,inter); + if (found2 != null) { + return found2; + } + } + return null; + } + + private static bool EqualsIgnoreGeneric(Type t1, + Type t2) { + if ( t1 == null || t2 == null ) { + return t1 == null && t2 == null; + } + if ( t1.IsGenericType ) { + t1 = t1.GetGenericTypeDefinition(); + } + if ( t2.IsGenericType ) { + t2 = t2.GetGenericTypeDefinition(); + } + return t1.Equals(t2); + } + + public static Type [] GetParameterTypes(MethodInfo method) + { + ParameterInfo [] parameters = method.GetParameters(); + Type [] rv = new Type[parameters.Length]; + for (int i = 0; i < parameters.Length; i++) { + rv[i] = parameters[i].ParameterType; + } + return rv; + } + } +} diff --git a/Common/Script.cs b/Common/Script.cs new file mode 100644 index 00000000..cfe7ea8a --- /dev/null +++ b/Common/Script.cs @@ -0,0 +1,184 @@ +/* + * Copyright 2008 Sun Microsystems, Inc. All rights reserved. + * + * U.S. Government Rights - Commercial software. Government users + * are subject to the Sun Microsystems, Inc. standard license agreement + * and applicable provisions of the FAR and its supplements. + * + * Use is subject to license terms. + * + * This distribution may include materials developed by third parties. + * Sun, Sun Microsystems, the Sun logo, Java and Project Identity + * Connectors are trademarks or registered trademarks of Sun + * Microsystems, Inc. or its subsidiaries in the U.S. and other + * countries. + * + * UNIX is a registered trademark in the U.S. and other countries, + * exclusively licensed through X/Open Company, Ltd. + * + * ----------- + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER. + * + * Copyright 2008 Sun Microsystems, Inc. All rights reserved. + * + * The contents of this file are subject to the terms of the Common Development + * and Distribution License(CDDL) (the License). You may not use this file + * except in compliance with the License. + * + * You can obtain a copy of the License at + * http://identityconnectors.dev.java.net/CDDLv1.0.html + * See the License for the specific language governing permissions and + * limitations under the License. + * + * When distributing the Covered Code, include this CDDL Header Notice in each + * file and include the License file at identityconnectors/legal/license.txt. + * If applicable, add the following below this CDDL Header, with the fields + * enclosed by brackets [] replaced by your own identifying information: + * "Portions Copyrighted [year] [name of copyright owner]" + * ----------- + */ +using System; +using System.IO; +using System.Reflection; +using System.Collections.Generic; +using System.Diagnostics; + +namespace Org.IdentityConnectors.Common.Script +{ + public interface ScriptExecutor { + /// + /// Executes the script with the given arguments. + /// + /// key/value set of variables to + /// pass to the script. + /// + object Execute(IDictionary arguments); + } + public abstract class ScriptExecutorFactory { + + private static readonly object LOCK = new object(); + + /// + /// Loaded w/ all supported languages. + /// + private static IDictionary _supportedLanguages = null; + + /// + /// Load all script executor factory assemblies in the same directory + /// the 'Common' assembly. + /// + private static IDictionary LoadSupportedLanguages() { + // attempt to process all assemblies.. + IDictionary ret = new Dictionary(); + Assembly assembly = Assembly.GetExecutingAssembly(); + FileInfo thisAssemblyFile = new FileInfo(assembly.Location); + DirectoryInfo directory = thisAssemblyFile.Directory; + // get all *ScriptExecutorFactory assmebly from the current directory + FileInfo [] files = directory.GetFiles("*.ScriptExecutorFactory.dll"); + Type t = typeof(ScriptExecutorFactoryClassAttribute); + foreach (FileInfo file in files) { + try { + Assembly lib = Assembly.LoadFrom(file.ToString()); + foreach (Type type in lib.GetTypes()) { + Object [] attributes = type.GetCustomAttributes(t, false); + if ( attributes.Length > 0 ) { + ScriptExecutorFactoryClassAttribute attribute = + (ScriptExecutorFactoryClassAttribute)attributes[0]; + // attempt to test assembly.. + Activator.CreateInstance(type); + // if we made it this far its okay + ret[attribute.Language.ToUpper()] = type; + } + } + } + catch (Exception e) { + TraceUtil.TraceException("Unable to load assembly: "+ + assembly.FullName+". This is a fatal exception: ",e); + throw; + } + } + return ret; + } + + private static IDictionary GetSupportedLanguages() + { + lock (LOCK) + { + if (_supportedLanguages == null) + { + _supportedLanguages = LoadSupportedLanguages(); + } + return _supportedLanguages; + } + } + + /** + * Returns the set of supported languages. + * @return The set of supported languages. + */ + public static ICollection SupportedLanguages + { + get + { + IDictionary map = + GetSupportedLanguages(); + return CollectionUtil.AsReadOnlySet(map.Keys); + } + } + + /** + * Creates a ScriptExecutorFactory for the given language + * @param language The name of the language + * @return The script executor factory + * @throws IllegalArgumentException If the given language is not + * supported. + */ + public static ScriptExecutorFactory NewInstance(String language) { + if ( language == null ) { + throw new ArgumentException("Language must be specified"); + } + Type type = CollectionUtil.GetValue(GetSupportedLanguages(),language.ToUpper(),null); + if ( type == null ) { + throw new ArgumentException("Language not supported: "+language); + } + return (ScriptExecutorFactory) Activator.CreateInstance(type); + } + + + /** + * Creates a script executor for the given script. + * @param loader The classloader that contains the java classes + * that the script should have access to. + * @param script The script text. + * @param compile A hint to tell the script executor whether or + * not to compile the given script. This need not be implemented + * by all script executors. If true, the caller is saying that + * they intend to call the script multiple times with different + * arguments, so compile if possible. + * @return A script executor. + */ + public abstract ScriptExecutor NewScriptExecutor( + Assembly [] referencedAssemblies, + String script, + bool compile); + } + + [AttributeUsage(AttributeTargets.Class, AllowMultiple=false)] + public class ScriptExecutorFactoryClassAttribute : System.Attribute { + private readonly string _lang; + + /// + /// Determine the language supported by the factory. + /// + /// + public ScriptExecutorFactoryClassAttribute(string lang) { + _lang = lang; + } + + public string Language { + get { + return _lang; + } + } + } +} diff --git a/Common/Security.cs b/Common/Security.cs new file mode 100644 index 00000000..48234821 --- /dev/null +++ b/Common/Security.cs @@ -0,0 +1,559 @@ +/* + * Copyright 2008 Sun Microsystems, Inc. All rights reserved. + * + * U.S. Government Rights - Commercial software. Government users + * are subject to the Sun Microsystems, Inc. standard license agreement + * and applicable provisions of the FAR and its supplements. + * + * Use is subject to license terms. + * + * This distribution may include materials developed by third parties. + * Sun, Sun Microsystems, the Sun logo, Java and Project Identity + * Connectors are trademarks or registered trademarks of Sun + * Microsystems, Inc. or its subsidiaries in the U.S. and other + * countries. + * + * UNIX is a registered trademark in the U.S. and other countries, + * exclusively licensed through X/Open Company, Ltd. + * + * ----------- + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER. + * + * Copyright 2008 Sun Microsystems, Inc. All rights reserved. + * + * The contents of this file are subject to the terms of the Common Development + * and Distribution License(CDDL) (the License). You may not use this file + * except in compliance with the License. + * + * You can obtain a copy of the License at + * http://identityconnectors.dev.java.net/CDDLv1.0.html + * See the License for the specific language governing permissions and + * limitations under the License. + * + * When distributing the Covered Code, include this CDDL Header Notice in each + * file and include the License file at identityconnectors/legal/license.txt. + * If applicable, add the following below this CDDL Header, with the fields + * enclosed by brackets [] replaced by your own identifying information: + * "Portions Copyrighted [year] [name of copyright owner]" + * ----------- + */ +using System; +using System.Security; +using System.Security.Cryptography; +using System.Runtime.InteropServices; +using System.Text; +namespace Org.IdentityConnectors.Common.Security +{ + #region UnmanagedArray + /// + /// Places an array facade on an unmanaged memory data structure that + /// holds senstive data. (In C# placing senstive data in managed + /// memory is considered unsecure since the memory model allows + /// data to be copied around). + /// + public interface UnmanagedArray : IDisposable + { + int Length {get;} + T this[int index] {get;set;} + } + #endregion + + /** + * Secure string implementation that solves the problems associated with + * keeping passwords as java.lang.String. That is, anything + * represented as a String is kept in memory as a clear + * text password and stays in memory at least until it is garbage collected. + *

+ * The GuardedString class alleviates this problem by storing the characters in + * memory in an encrypted form. The encryption key will be a randomly-generated + * key. + *

+ * In their serialized form, GuardedString will be encrypted using a known + * default key. This is to provide a minimum level of protection regardless + * of the transport. For communications with the Remote Connector Framework + * it is recommended that deployments enable SSL for true encryption. + *

+ * Applications may also wish to persist GuardedStrings. In the case of + * Identity Manager, it should convert GuardedStrings to EncryptedData so + * that they can be stored and managed using the Manage Encryption features + * of Identity Manager. Other applications may wish to serialize APIConfiguration + * as a whole. These applications are responsible for encrypting the APIConfiguration + * blob for an additional layer of security (beyond the basic default key encryption + * provided by GuardedString). + */ + public sealed class GuardedString : IDisposable { + /** + * This method will be called with the clear text of the string. + * After the call the clearChars array will be automatically zeroed + * out, thus keeping the window of potential exposure to a bare-minimum. + * @param clearChars + */ + public delegate void Accessor(UnmanagedArray clearChars); + + + private SecureString _target; + private String _base64SHA1Hash; + + /** + * Creates an empty secure string + */ + public GuardedString() { + _target = new SecureString(); + ComputeHash(); + } + + public GuardedString(SecureString str) { + _target = str.Copy(); + ComputeHash(); + } + + + /** + * Provides access to the clear-text value of the string in a controlled fashion. + * The clear-text characters will only be available for the duration of the call + * and automatically zeroed out following the call. + * + *

+ * NOTE: Callers are encouraged to use {@link #verifyBase64SHA1Hash(String)} + * where possible if the intended use is merely to verify the contents of + * the string match an expected hash value. + * @param accessor Accessor callback. + * @throws IllegalStateException If the string has been disposed + */ + public void Access(Accessor accessor) { + using (SecureStringAdapter adapter = new SecureStringAdapter(_target)) { + accessor(adapter); + } + } + + /** + * Appends a single clear-text character to the secure string. + * The in-memory data will be decrypted, the character will be + * appended, and then it will be re-encrypted. + * @param c The character to append. + * @throws IllegalStateException If the string is read-only + * @throws IllegalStateException If the string has been disposed + */ + public void AppendChar(char c) { + _target.AppendChar(c); + ComputeHash(); + } + + /** + * Clears the in-memory representation of the string. + */ + public void Dispose() { + _target.Dispose(); + } + + /** + * Returns true iff this string has been marked read-only + * @return true iff this string has been marked read-only + * @throws IllegalStateException If the string has been disposed + */ + public bool IsReadOnly() { + return _target.IsReadOnly(); + } + + /** + * Mark this string as read-only. + * @throws IllegalStateException If the string has been disposed + */ + public void MakeReadOnly() { + _target.MakeReadOnly(); + } + + /** + * Create a copy of the string. If this instance is read-only, + * the copy will not be read-only. + * @return A copy of the string. + * @throws IllegalStateException If the string has been disposed + */ + public GuardedString Copy() { + SecureString t2 = _target.Copy(); + GuardedString rv = new GuardedString(t2); + return rv; + } + + /** + * Verifies that this base-64 encoded SHA1 hash of this string + * matches the given value. + * @param hash The hash to verify against. + * @return True if the hash matches the given parameter. + * @throws IllegalStateException If the string has been disposed + */ + public bool VerifyBase64SHA1Hash(String hash) { + CheckNotDisposed(); + return _base64SHA1Hash.Equals(hash); + } + + public string GetBase64SHA1Hash() { + CheckNotDisposed(); + return _base64SHA1Hash; + } + + + + private void CheckNotDisposed() + { + //this throws if disposed + _target.IsReadOnly(); + } + + + public override bool Equals(Object o) { + if ( o is GuardedString ) { + GuardedString other = (GuardedString)o; + //not the true contract of equals. however, + //due to the high mathematical improbability of + //two unequal strings having the same secure hash, + //this approach feels good. the alternative, + //decrypting for comparison, is simply too + //performance intensive to be used for equals + return _base64SHA1Hash.Equals(other._base64SHA1Hash); + } + return false; + } + + public override int GetHashCode() { + return _base64SHA1Hash.GetHashCode(); + } + + public SecureString ToSecureString() { + return _target.Copy(); + } + + private void ComputeHash() + { + Access(array=> { + _base64SHA1Hash = SecurityUtil.ComputeBase64SHA1Hash(array); + }); + } + + } + + + #region AbstractUnmanagedArray + public abstract class AbstractUnmanagedArray : UnmanagedArray + { + private readonly int _length; + private bool _disposed; + public AbstractUnmanagedArray(int length) { + if (length < 0) { + throw new ArgumentException("Invalid length: "+length); + } + _length = length; + } + public int Length { + get { + if (_disposed) { + throw new ObjectDisposedException("UnmanagedArray"); + } + return _length; + } + } + public T this[int index] { + get { + if (_disposed) { + throw new ObjectDisposedException("UnmanagedArray"); + } + if ( index < 0 || index >= Length ) { + throw new IndexOutOfRangeException(); + } + return GetValue(index); + } + set { + if (_disposed) { + throw new ObjectDisposedException("SecureStringAdapter"); + } + if ( index < 0 || index >= Length ) { + throw new IndexOutOfRangeException(); + } + SetValue(index,value); + } + } + public void Dispose() { + if (!_disposed) { + for ( int i = 0; i < Length; i++ ) { + this[i] = default(T); + } + _disposed = true; + FreeMemory(); + } + } + + abstract protected T GetValue(int index); + abstract protected void SetValue(int index, T val); + abstract protected void FreeMemory(); + } + #endregion + + #region SecureStringAdapter + internal class SecureStringAdapter : AbstractUnmanagedArray + { + private IntPtr _bstrPtr; + public SecureStringAdapter(SecureString secureString) : base(secureString.Length) { + Assertions.NullCheck(secureString,"secureString"); + _bstrPtr = Marshal.SecureStringToBSTR(secureString); + } + protected override char GetValue(int index) + { + unsafe { + char * charPtr = (char*)_bstrPtr; + return *(charPtr+index); + } + } + protected override void SetValue(int index, char c) + { + unsafe { + char * charPtr = (char*)_bstrPtr; + *(charPtr+index) = c; + } + } + protected override void FreeMemory() + { + Marshal.ZeroFreeBSTR( _bstrPtr ); + } + } + #endregion + + #region UnmanagedCharArray + public class UnmanagedCharArray : AbstractUnmanagedArray + { + private IntPtr _ptr; + public UnmanagedCharArray(int length) : base(length) { + unsafe { + _ptr = Marshal.AllocHGlobal(length*sizeof(char)); + } + } + protected override char GetValue(int index) + { + unsafe { + char * charPtr = (char*)_ptr; + return *(charPtr+index); + } + } + protected override void SetValue(int index, char c) + { + unsafe { + char * charPtr = (char*)_ptr; + *(charPtr+index) = c; + } + } + protected override void FreeMemory() + { + Marshal.FreeHGlobal( _ptr ); + } + } + #endregion + + #region UnmanagedByteArray + public class UnmanagedByteArray : AbstractUnmanagedArray + { + private IntPtr _ptr; + public UnmanagedByteArray(int length) : base(length) { + unsafe { + _ptr = Marshal.AllocHGlobal(length*sizeof(byte)); + } + } + protected override byte GetValue(int index) + { + unsafe { + byte * charPtr = (byte*)_ptr; + return *(charPtr+index); + } + } + protected override void SetValue(int index, byte c) + { + unsafe { + byte * charPtr = (byte*)_ptr; + *(charPtr+index) = c; + } + } + protected override void FreeMemory() + { + Marshal.FreeHGlobal( _ptr ); + } + } + #endregion + + #region SecurityUtil + ///

+ /// Description of SecurityUtil. + /// + public static class SecurityUtil + { + + /** + * Converts chars to bytes without using any external functions + * that might allocate additional buffers for the potentially + * sensitive data. This guarantees the caller that they only + * need to cleanup the input and result. + * @param chars The chars + * @return The bytes + */ + public static UnmanagedArray CharsToBytes(UnmanagedArray chars) + { + UnmanagedByteArray bytes = new UnmanagedByteArray(chars.Length*2); + + for ( int i = 0; i < chars.Length; i++ ) { + char v = chars[i]; + bytes[i*2] = (byte)(0xff & (v >> 8)); + bytes[i*2+1] = (byte)(0xff & (v)); + } + return bytes; + } + + /** + * Converts bytes to chars without using any external functions + * that might allocate additional buffers for the potentially + * sensitive data. This guarantees the caller that they only + * need to cleanup the input and result. + * @param chars The chars + * @return The bytes + */ + public static UnmanagedArray BytesToChars(UnmanagedArray bytes) + { + UnmanagedCharArray chars = new UnmanagedCharArray(bytes.Length/2); + for ( int i = 0; i < chars.Length; i++ ) { + char v = (char)((bytes[i*2]<<8) | bytes[i*2+1]); + chars[i] = v; + } + return chars; + } + + + public unsafe static string ComputeBase64SHA1Hash(UnmanagedArray input) + { + using (UnmanagedArray bytes = SecurityUtil.CharsToBytes(input)) { + byte [] managedBytes = new byte[bytes.Length]; + fixed (byte*dummy=managedBytes) { //pin it + try { + //populate it in pinned block + SecurityUtil.UnmanagedBytesToManagedBytes(bytes,managedBytes); + SHA1 hasher = SHA1.Create(); + byte[] data = hasher.ComputeHash(managedBytes); + return Convert.ToBase64String(data); + } + finally { + //clear it before we leave pinned block + SecurityUtil.Clear(managedBytes); + } + } + } + } + + /** + * Copies an unmanaged byte array into a managed byte array. + * NOTE: it is imperative for security reasons that this only + * be done in a context where the byte array in question is pinned. + * moreover, the byte array must be cleared prior to leaving the + * pinned block + */ + public static void UnmanagedBytesToManagedBytes(UnmanagedArray array, + byte [] bytes) + { + for ( int i = 0 ; i < array.Length; i++ ) + { + bytes[i] = array[i]; + } + } + + /** + * Clears an array of potentially sensitive bytes + * @param bytes The bytes. May be null. + * NOTE: because this is C#, this alone is not enough. The + * array must be pinned during the interval it is in-use or + * it could be copied out from under you. + */ + public static void Clear(byte [] bytes) + { + if ( bytes != null ) + { + for ( int i = 0; i < bytes.Length; i++ ) + { + bytes[i] = 0; + } + } + } + + /** + * Clears an array of potentially sensitive chars + * @param chars The characters. May be null. + * NOTE: because this is C#, this alone is not enough. The + * array must be pinned during the interval it is in-use or + * it could be copied out from under you. + */ + public static void Clear(char [] chars) + { + if ( chars != null ) + { + for ( int i = 0; i < chars.Length; i++) + { + chars[i] = (char)0; + } + } + } + + public static bool VerifyBase64SHA1Hash(UnmanagedArray input, string hash) + { + string inputHash = ComputeBase64SHA1Hash(input); + return inputHash.Equals(hash); + } + } + #endregion + + #region Encryptor + /** + * Responsible for encrypting/decrypting bytes. Implementations + * are intended to be thread-safe. + */ + public interface Encryptor { + /** + * Decrypts the given byte array + * @param bytes The encrypted bytes + * @return The decrypted bytes + */ + UnmanagedArray Decrypt(byte [] bytes); + + /** + * Encrypts the given byte array + * @param bytes The clear bytes + * @return The ecnrypted bytes + */ + byte [] Encrypt(UnmanagedArray bytes); + } + #endregion + + #region EncryptorFactory + public abstract class EncryptorFactory { + private static readonly object LOCK = new object(); + + // At some point we might make this pluggable, but for now, hard-code + private const String IMPL_NAME = "Org.IdentityConnectors.Common.Security.Impl.EncryptorFactoryImpl"; + + private static EncryptorFactory _instance; + + /** + * Get the singleton instance of the {@link EncryptorFactory}. + */ + public static EncryptorFactory GetInstance() { + lock(LOCK) { + if (_instance == null) { + Type type = FrameworkInternalBridge.LoadType(IMPL_NAME); + _instance = (EncryptorFactory)Activator.CreateInstance(type); + } + return _instance; + } + } + + /** + * Default encryptor that encrypts/descrypts using a default key + */ + public abstract Encryptor GetDefaultEncryptor(); + + + } + #endregion + +} diff --git a/Common/StringUtil.cs b/Common/StringUtil.cs new file mode 100644 index 00000000..0db4931e --- /dev/null +++ b/Common/StringUtil.cs @@ -0,0 +1,109 @@ +/* + * Copyright 2008 Sun Microsystems, Inc. All rights reserved. + * + * U.S. Government Rights - Commercial software. Government users + * are subject to the Sun Microsystems, Inc. standard license agreement + * and applicable provisions of the FAR and its supplements. + * + * Use is subject to license terms. + * + * This distribution may include materials developed by third parties. + * Sun, Sun Microsystems, the Sun logo, Java and Project Identity + * Connectors are trademarks or registered trademarks of Sun + * Microsystems, Inc. or its subsidiaries in the U.S. and other + * countries. + * + * UNIX is a registered trademark in the U.S. and other countries, + * exclusively licensed through X/Open Company, Ltd. + * + * ----------- + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER. + * + * Copyright 2008 Sun Microsystems, Inc. All rights reserved. + * + * The contents of this file are subject to the terms of the Common Development + * and Distribution License(CDDL) (the License). You may not use this file + * except in compliance with the License. + * + * You can obtain a copy of the License at + * http://identityconnectors.dev.java.net/CDDLv1.0.html + * See the License for the specific language governing permissions and + * limitations under the License. + * + * When distributing the Covered Code, include this CDDL Header Notice in each + * file and include the License file at identityconnectors/legal/license.txt. + * If applicable, add the following below this CDDL Header, with the fields + * enclosed by brackets [] replaced by your own identifying information: + * "Portions Copyrighted [year] [name of copyright owner]" + * ----------- + */ +using System; +using Org.IdentityConnectors.Common.Security; +namespace Org.IdentityConnectors.Common +{ + public static class StringUtil + { + /** + * Determines if a string is empty. Empty is defined as null or empty + * string. + * + *
+         *  StringUtil.isEmpty(null)               = true
+         *  StringUtil.isEmpty("")       = true
+         *  StringUtil.isEmpty(" ")      = false
+         *  StringUtil.isEmpty("bob")    = false
+         *  StringUtil.isEmpty(" bob ")  = false
+         * 
+ * + * @param val + * string to evaluate as empty. + * @return true if the string is empty else false. + */ + public static bool IsEmpty(String val) { + return (val == null) ? true : val.Length == 0; + } + + /** + *
+         *      StringUtil.isBlank(null)                = true
+         *      StringUtil.isBlank("")        = true
+         *      StringUtil.isBlank(" ")       = true
+         *      StringUtil.isBlank("bob")     = false
+         *      StringUtil.isBlank("  bob  ") = false
+         * 
+ */ + public static bool IsBlank(String val) { + return (val == null) ? true : IsEmpty(val.Trim()); + } + + /// + /// Constructs a secure string from a char []. The char[] will + /// be cleared out when finished. + /// + /// The characters to use. Will be cleared + /// out. + /// A secure string representation + public static GuardedString NewGuardedString(char [] val) + { + GuardedString rv = new GuardedString(); + for( int i = 0; i < val.Length; i++ ) + { + rv.AppendChar(val[i]); + val[i] = (char)0; + } + return rv; + } + + + public static bool IsTrue(string val) { + if (!IsBlank(val)) { + // clean up the value.. + val = val.Trim().ToLower(); + if (val.Equals("1") || val.Equals("on") || val.Equals("true")) { + return true; + } + } + return false; + } + } +} diff --git a/Common/TraceUtil.cs b/Common/TraceUtil.cs new file mode 100644 index 00000000..7163dca9 --- /dev/null +++ b/Common/TraceUtil.cs @@ -0,0 +1,68 @@ +/* + * Copyright 2008 Sun Microsystems, Inc. All rights reserved. + * + * U.S. Government Rights - Commercial software. Government users + * are subject to the Sun Microsystems, Inc. standard license agreement + * and applicable provisions of the FAR and its supplements. + * + * Use is subject to license terms. + * + * This distribution may include materials developed by third parties. + * Sun, Sun Microsystems, the Sun logo, Java and Project Identity + * Connectors are trademarks or registered trademarks of Sun + * Microsystems, Inc. or its subsidiaries in the U.S. and other + * countries. + * + * UNIX is a registered trademark in the U.S. and other countries, + * exclusively licensed through X/Open Company, Ltd. + * + * ----------- + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER. + * + * Copyright 2008 Sun Microsystems, Inc. All rights reserved. + * + * The contents of this file are subject to the terms of the Common Development + * and Distribution License(CDDL) (the License). You may not use this file + * except in compliance with the License. + * + * You can obtain a copy of the License at + * http://identityconnectors.dev.java.net/CDDLv1.0.html + * See the License for the specific language governing permissions and + * limitations under the License. + * + * When distributing the Covered Code, include this CDDL Header Notice in each + * file and include the License file at identityconnectors/legal/license.txt. + * If applicable, add the following below this CDDL Header, with the fields + * enclosed by brackets [] replaced by your own identifying information: + * "Portions Copyrighted [year] [name of copyright owner]" + * ----------- + */ +using System; +using System.Diagnostics; +using System.Text; +namespace Org.IdentityConnectors.Common +{ + /// + /// Description of TraceUtil. + /// + public static class TraceUtil + { + /// + /// Traces an exception with its stack trace + /// + /// An optional error message to display in addition to the exception + /// The exception + public static void TraceException(String msg, Exception e) { + StringBuilder builder = new StringBuilder(); + if ( msg != null ) + { + builder.AppendLine(msg); + } + if ( e != null ) + { + builder.AppendLine(e.ToString()); + } + Trace.TraceError(builder.ToString()); + } + } +} diff --git a/Common/XmlUtil.cs b/Common/XmlUtil.cs new file mode 100644 index 00000000..9da3656d --- /dev/null +++ b/Common/XmlUtil.cs @@ -0,0 +1,209 @@ +/* + * Copyright 2008 Sun Microsystems, Inc. All rights reserved. + * + * U.S. Government Rights - Commercial software. Government users + * are subject to the Sun Microsystems, Inc. standard license agreement + * and applicable provisions of the FAR and its supplements. + * + * Use is subject to license terms. + * + * This distribution may include materials developed by third parties. + * Sun, Sun Microsystems, the Sun logo, Java and Project Identity + * Connectors are trademarks or registered trademarks of Sun + * Microsystems, Inc. or its subsidiaries in the U.S. and other + * countries. + * + * UNIX is a registered trademark in the U.S. and other countries, + * exclusively licensed through X/Open Company, Ltd. + * + * ----------- + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER. + * + * Copyright 2008 Sun Microsystems, Inc. All rights reserved. + * + * The contents of this file are subject to the terms of the Common Development + * and Distribution License(CDDL) (the License). You may not use this file + * except in compliance with the License. + * + * You can obtain a copy of the License at + * http://identityconnectors.dev.java.net/CDDLv1.0.html + * See the License for the specific language governing permissions and + * limitations under the License. + * + * When distributing the Covered Code, include this CDDL Header Notice in each + * file and include the License file at identityconnectors/legal/license.txt. + * If applicable, add the following below this CDDL Header, with the fields + * enclosed by brackets [] replaced by your own identifying information: + * "Portions Copyrighted [year] [name of copyright owner]" + * ----------- + */ +using System; +using System.Xml; +using System.Text; +namespace Org.IdentityConnectors.Common +{ + /// + /// Description of XmlUtil. + /// + public static class XmlUtil + { + ///////////////////////////////////////////////////////////// + // + // DOM Navigation utilities + // + //////////////////////////////////////////////////////////// + + /** + * Return the value of an attribute on an element.

The DOM getAttribute + * method returns an empty string if the attribute doesn't exist. Here, we + * detect this and return null. + */ + public static String GetAttribute(XmlElement e, String name) { + String value = e.GetAttribute(name); + if (value != null && value.Length == 0) + value = null; + return value; + } + + /** + * Find an immediate child of the given name + */ + public static XmlElement FindImmediateChildElement(XmlNode node, String name) { + + XmlElement found = null; + + if (node != null) { + + for (XmlNode child = node.FirstChild; child != null + && found == null; child = child.NextSibling) { + + if (child.NodeType == XmlNodeType.Element) { + XmlElement tmp = (XmlElement) child; + if ( tmp.LocalName.Equals(name) ) { + return tmp; + } + } + } + } + + return found; + } + + /** + * Returns the First child element or null if none found + * @param node The node. May be null. + * @return the First child element or null if none found + */ + public static XmlElement GetFirstChildElement(XmlNode node) { + if ( node == null ) { + return null; + } + XmlNode child = node.FirstChild; + if ( child != null && child.NodeType == XmlNodeType.Element ) { + return (XmlElement)child; + } + else { + return GetNextElement(child); + } + } + + /** + * Get the next right sibling that is an element. + */ + public static XmlElement GetNextElement(XmlNode node) { + + XmlElement found = null; + + if (node != null) { + + for (XmlNode next = node.NextSibling; next != null + && found == null; next = next.NextSibling) { + + if (next.NodeType == XmlNodeType.Element) + found = (XmlElement) next; + } + } + + return found; + } + + /** + * Locate the first text node at any level below the given node. If the + * ignoreEmpty flag is true, we will ignore text nodes that contain only + * whitespace characteres.

Note that if you're trying to extract + * element content, you probably don't want this since parser's can break up + * pcdata into multiple adjacent text nodes. See getContent() for a more + * useful method. + */ + private static XmlText FindText(XmlNode node, bool ignoreEmpty) { + + XmlText found = null; + + if (node != null) { + + if (node.NodeType == XmlNodeType.Text + || node.NodeType == XmlNodeType.CDATA) { + + XmlText t = (XmlText) node; + if (!ignoreEmpty) + found = t; + else { + String s = t.Data.Trim(); + if (s.Length > 0) + found = t; + } + } + + if (found == null) { + + for (XmlNode child = node.FirstChild; child != null + && found == null; child = child.NextSibling) { + + found = FindText(child, ignoreEmpty); + } + } + } + + return found; + } + + + /** + * Return the content of the given element.

We will descend to an + * arbitrary depth looking for the first text node.

Note that + * the parser may break what was originally a single string of pcdata into + * multiple adjacent text nodes. Xerces appears to do this when it + * encounters a '$' in the text, not sure if there is specified behavior, or + * if its parser specific.

Here, we will congeal adjacent text nodes. + *

We will NOT ignore text nodes that have only whitespace. + */ + public static String GetContent(XmlElement e) { + + String content = null; + + if (e != null) { + + // find the first inner text node, + XmlText t = FindText(e, false); + if (t != null) { + // we have at least some text + StringBuilder b = new StringBuilder(); + while (t != null) { + b.Append(t.Data); + XmlNode n = t.NextSibling; + + t = null; + if (n != null + && ((n.NodeType == XmlNodeType.Text) || + (n.NodeType == XmlNodeType.CDATA))) { + t = (XmlText) n; + } + } + content = b.ToString(); + } + } + + return content; + } + } +} diff --git a/Connector Gateway.sln b/Connector Gateway.sln new file mode 100644 index 00000000..4dca1949 --- /dev/null +++ b/Connector Gateway.sln @@ -0,0 +1,166 @@ + +Microsoft Visual Studio Solution File, Format Version 10.00 +# Visual Studio 2008 +# SharpDevelop 3.0.0.2970 +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Service", "Service\Service.csproj", "{A9D6374A-D51F-4FA3-8C02-5B1D23FAA82E}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Framework", "Framework\Framework.csproj", "{8B24461B-456A-4032-89A1-CD418F7B5B62}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Common", "Common\Common.csproj", "{F140E8DA-52B4-4159-992A-9DA10EA8EEFB}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "FrameworkTests", "FrameworkTests\FrameworkTests.csproj", "{32804F5A-812C-4FA6-835C-BDAE5B24D355}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "TestBundleV1", "TestBundleV1\TestBundleV1.csproj", "{0BC2A013-56FE-46DD-90FC-2D2D57748FB6}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "TestBundleV2", "TestBundleV2\TestBundleV2.csproj", "{3E737796-3A83-4924-9FF1-DC542F21F59E}" +EndProject +Project("{CFEE4113-1246-4D54-95CB-156813CB8593}") = "ServiceInstall", "ServiceInstall\ServiceInstall.wixproj", "{1CBA8F74-050C-432B-8437-08BD13BDC684}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Console", "Console\Console.csproj", "{D09E6A1F-55F6-4661-8E5A-C485E4B08BB3}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "FrameworkInternal", "FrameworkInternal\FrameworkInternal.csproj", "{5B011775-B121-4EEE-A410-BA2D2F5BFB8B}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "BooScriptExecutorFactory", "BooScriptExecutorFactory\BooScriptExecutorFactory.csproj", "{0747C440-70E4-4E63-9F9D-03B3A010C991}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "ShellScriptExecutorFactory", "ShellScriptExecutorFactory\ShellScriptExecutorFactory.csproj", "{4700690A-2D29-40A0-86AC-E5A9F71A479A}" +EndProject +Global + GlobalSection(SolutionConfigurationPlatforms) = preSolution + Debug|Any CPU = Debug|Any CPU + Release|Any CPU = Release|Any CPU + EndGlobalSection + GlobalSection(ProjectConfigurationPlatforms) = postSolution + {A9D6374A-D51F-4FA3-8C02-5B1D23FAA82E}.Debug.ActiveCfg = Debug + {A9D6374A-D51F-4FA3-8C02-5B1D23FAA82E}.Debug.Build.0 = Debug + {A9D6374A-D51F-4FA3-8C02-5B1D23FAA82E}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {A9D6374A-D51F-4FA3-8C02-5B1D23FAA82E}.Release.ActiveCfg = Release + {A9D6374A-D51F-4FA3-8C02-5B1D23FAA82E}.Release.Build.0 = Release + {A9D6374A-D51F-4FA3-8C02-5B1D23FAA82E}.Release|Any CPU.ActiveCfg = Release|Any CPU + {BCD7E3AD-1CF9-48A6-8FE4-C81E84B87CC1}.Debug.ActiveCfg = Debug + {BCD7E3AD-1CF9-48A6-8FE4-C81E84B87CC1}.Debug.Build.0 = Debug + {BCD7E3AD-1CF9-48A6-8FE4-C81E84B87CC1}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {BCD7E3AD-1CF9-48A6-8FE4-C81E84B87CC1}.Release.ActiveCfg = Release + {BCD7E3AD-1CF9-48A6-8FE4-C81E84B87CC1}.Release.Build.0 = Release + {BCD7E3AD-1CF9-48A6-8FE4-C81E84B87CC1}.Release|Any CPU.ActiveCfg = Release|Any CPU + {D09E6A1F-55F6-4661-8E5A-C485E4B08BB3}.Debug.ActiveCfg = Debug + {D09E6A1F-55F6-4661-8E5A-C485E4B08BB3}.Debug.Build.0 = Debug + {D09E6A1F-55F6-4661-8E5A-C485E4B08BB3}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {D09E6A1F-55F6-4661-8E5A-C485E4B08BB3}.Release.ActiveCfg = Release + {D09E6A1F-55F6-4661-8E5A-C485E4B08BB3}.Release.Build.0 = Release + {D09E6A1F-55F6-4661-8E5A-C485E4B08BB3}.Release|Any CPU.ActiveCfg = Release|Any CPU + {82DC8049-B0E4-4FA5-98D3-80C58A9B97F2}.Debug|Any CPU.Build.0 = Debug|Any CPU + {82DC8049-B0E4-4FA5-98D3-80C58A9B97F2}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {82DC8049-B0E4-4FA5-98D3-80C58A9B97F2}.Release|Any CPU.Build.0 = Release|Any CPU + {82DC8049-B0E4-4FA5-98D3-80C58A9B97F2}.Release|Any CPU.ActiveCfg = Release|Any CPU + {B5C52AB5-CE4F-4165-AFC6-4680F162B8DA}.Debug|Any CPU.Build.0 = Debug|Any CPU + {B5C52AB5-CE4F-4165-AFC6-4680F162B8DA}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {B5C52AB5-CE4F-4165-AFC6-4680F162B8DA}.Release|Any CPU.Build.0 = Release|Any CPU + {B5C52AB5-CE4F-4165-AFC6-4680F162B8DA}.Release|Any CPU.ActiveCfg = Release|Any CPU + {BCD7E3AD-1CF9-48A6-8FE4-C81E84B87CC1}.Debug|Any CPU.Build.0 = Debug|Any CPU + {BCD7E3AD-1CF9-48A6-8FE4-C81E84B87CC1}.Release|Any CPU.Build.0 = Release|Any CPU + {D09E6A1F-55F6-4661-8E5A-C485E4B08BB3}.Debug|Any CPU.Build.0 = Debug|Any CPU + {D09E6A1F-55F6-4661-8E5A-C485E4B08BB3}.Release|Any CPU.Build.0 = Release|Any CPU + {A9D6374A-D51F-4FA3-8C02-5B1D23FAA82E}.Debug|Any CPU.Build.0 = Debug|Any CPU + {A9D6374A-D51F-4FA3-8C02-5B1D23FAA82E}.Release|Any CPU.Build.0 = Release|Any CPU + {2420D04D-8EA0-4D5E-BB6D-4FEAC74F0D01}.Debug.Build.0 = Debug + {2420D04D-8EA0-4D5E-BB6D-4FEAC74F0D01}.Debug.ActiveCfg = Debug + {2420D04D-8EA0-4D5E-BB6D-4FEAC74F0D01}.Release.Build.0 = Release + {2420D04D-8EA0-4D5E-BB6D-4FEAC74F0D01}.Release.ActiveCfg = Release + {2420D04D-8EA0-4D5E-BB6D-4FEAC74F0D01}.Debug|Any CPU.Build.0 = Debug|Any CPU + {2420D04D-8EA0-4D5E-BB6D-4FEAC74F0D01}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {2420D04D-8EA0-4D5E-BB6D-4FEAC74F0D01}.Release|Any CPU.Build.0 = Release|Any CPU + {2420D04D-8EA0-4D5E-BB6D-4FEAC74F0D01}.Release|Any CPU.ActiveCfg = Release|Any CPU + {8B24461B-456A-4032-89A1-CD418F7B5B62}.Debug.Build.0 = Debug + {8B24461B-456A-4032-89A1-CD418F7B5B62}.Debug.ActiveCfg = Debug + {8B24461B-456A-4032-89A1-CD418F7B5B62}.Release.Build.0 = Release + {8B24461B-456A-4032-89A1-CD418F7B5B62}.Release.ActiveCfg = Release + {8B24461B-456A-4032-89A1-CD418F7B5B62}.Debug|Any CPU.Build.0 = Debug|Any CPU + {8B24461B-456A-4032-89A1-CD418F7B5B62}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {8B24461B-456A-4032-89A1-CD418F7B5B62}.Release|Any CPU.Build.0 = Release|Any CPU + {8B24461B-456A-4032-89A1-CD418F7B5B62}.Release|Any CPU.ActiveCfg = Release|Any CPU + {F140E8DA-52B4-4159-992A-9DA10EA8EEFB}.Debug|Any CPU.Build.0 = Debug|Any CPU + {F140E8DA-52B4-4159-992A-9DA10EA8EEFB}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {F140E8DA-52B4-4159-992A-9DA10EA8EEFB}.Release|Any CPU.Build.0 = Release|Any CPU + {F140E8DA-52B4-4159-992A-9DA10EA8EEFB}.Release|Any CPU.ActiveCfg = Release|Any CPU + {32804F5A-812C-4FA6-835C-BDAE5B24D355}.Debug|Any CPU.Build.0 = Debug|Any CPU + {32804F5A-812C-4FA6-835C-BDAE5B24D355}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {32804F5A-812C-4FA6-835C-BDAE5B24D355}.Release|Any CPU.Build.0 = Release|Any CPU + {32804F5A-812C-4FA6-835C-BDAE5B24D355}.Release|Any CPU.ActiveCfg = Release|Any CPU + {0BC2A013-56FE-46DD-90FC-2D2D57748FB6}.Debug|Any CPU.Build.0 = Debug|Any CPU + {0BC2A013-56FE-46DD-90FC-2D2D57748FB6}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {0BC2A013-56FE-46DD-90FC-2D2D57748FB6}.Release|Any CPU.Build.0 = Release|Any CPU + {0BC2A013-56FE-46DD-90FC-2D2D57748FB6}.Release|Any CPU.ActiveCfg = Release|Any CPU + {3E737796-3A83-4924-9FF1-DC542F21F59E}.Debug|Any CPU.Build.0 = Debug|Any CPU + {3E737796-3A83-4924-9FF1-DC542F21F59E}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {3E737796-3A83-4924-9FF1-DC542F21F59E}.Release|Any CPU.Build.0 = Release|Any CPU + {3E737796-3A83-4924-9FF1-DC542F21F59E}.Release|Any CPU.ActiveCfg = Release|Any CPU + 3F54A749-E66F-4034-88A6-5B01DEA03909.Debug|Any CPU.Build.0 = Debug|Any CPU + 3F54A749-E66F-4034-88A6-5B01DEA03909.Debug|Any CPU.ActiveCfg = Debug|Any CPU + 3F54A749-E66F-4034-88A6-5B01DEA03909.Release|Any CPU.Build.0 = Release|Any CPU + 3F54A749-E66F-4034-88A6-5B01DEA03909.Release|Any CPU.ActiveCfg = Release|Any CPU + 4F63241B-68F8-45A5-B8DA-9A331D933ECC.Debug|Any CPU.Build.0 = Debug|Any CPU + 4F63241B-68F8-45A5-B8DA-9A331D933ECC.Debug|Any CPU.ActiveCfg = Debug|Any CPU + 4F63241B-68F8-45A5-B8DA-9A331D933ECC.Release|Any CPU.Build.0 = Release|Any CPU + 4F63241B-68F8-45A5-B8DA-9A331D933ECC.Release|Any CPU.ActiveCfg = Release|Any CPU + {B50CB653-E406-42E5-97F9-7CFCAB240D26}.Debug|Any CPU.Build.0 = Debug|Any CPU + {B50CB653-E406-42E5-97F9-7CFCAB240D26}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {B50CB653-E406-42E5-97F9-7CFCAB240D26}.Release|Any CPU.Build.0 = Release|Any CPU + {B50CB653-E406-42E5-97F9-7CFCAB240D26}.Release|Any CPU.ActiveCfg = Release|Any CPU + {1CBA8F74-050C-432B-8437-08BD13BDC684}.Debug|Any CPU.Build.0 = Debug|Any CPU + {1CBA8F74-050C-432B-8437-08BD13BDC684}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {1CBA8F74-050C-432B-8437-08BD13BDC684}.Release|Any CPU.Build.0 = Release|Any CPU + {1CBA8F74-050C-432B-8437-08BD13BDC684}.Release|Any CPU.ActiveCfg = Release|Any CPU + {B0D1B8FA-EFEC-437C-A54D-8EC570B3781F}.Debug|Any CPU.Build.0 = Debug|Any CPU + {B0D1B8FA-EFEC-437C-A54D-8EC570B3781F}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {B0D1B8FA-EFEC-437C-A54D-8EC570B3781F}.Release|Any CPU.Build.0 = Release|Any CPU + {B0D1B8FA-EFEC-437C-A54D-8EC570B3781F}.Release|Any CPU.ActiveCfg = Release|Any CPU + {028822D2-4309-41AE-A91C-97E895FA024B}.Debug|Any CPU.Build.0 = Debug|Any CPU + {028822D2-4309-41AE-A91C-97E895FA024B}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {028822D2-4309-41AE-A91C-97E895FA024B}.Release|Any CPU.Build.0 = Release|Any CPU + {028822D2-4309-41AE-A91C-97E895FA024B}.Release|Any CPU.ActiveCfg = Release|Any CPU + {C8D6EEBE-FA37-4154-81E2-A7E469D0461A}.Debug|Any CPU.Build.0 = Debug|Any CPU + {C8D6EEBE-FA37-4154-81E2-A7E469D0461A}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {C8D6EEBE-FA37-4154-81E2-A7E469D0461A}.Release|Any CPU.Build.0 = Release|Any CPU + {C8D6EEBE-FA37-4154-81E2-A7E469D0461A}.Release|Any CPU.ActiveCfg = Release|Any CPU + {0B3DF008-F9B5-46FF-BF12-05037A4DE359}.Debug|Any CPU.Build.0 = Debug|Any CPU + {0B3DF008-F9B5-46FF-BF12-05037A4DE359}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {0B3DF008-F9B5-46FF-BF12-05037A4DE359}.Release|Any CPU.Build.0 = Release|Any CPU + {0B3DF008-F9B5-46FF-BF12-05037A4DE359}.Release|Any CPU.ActiveCfg = Release|Any CPU + {CE006C46-4B5C-4E6C-BAA3-EAD79C38B9D9}.Debug|Any CPU.Build.0 = Debug|Any CPU + {CE006C46-4B5C-4E6C-BAA3-EAD79C38B9D9}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {CE006C46-4B5C-4E6C-BAA3-EAD79C38B9D9}.Release|Any CPU.Build.0 = Release|Any CPU + {CE006C46-4B5C-4E6C-BAA3-EAD79C38B9D9}.Release|Any CPU.ActiveCfg = Release|Any CPU + {5B011775-B121-4EEE-A410-BA2D2F5BFB8B}.Debug|Any CPU.Build.0 = Debug|Any CPU + {5B011775-B121-4EEE-A410-BA2D2F5BFB8B}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {5B011775-B121-4EEE-A410-BA2D2F5BFB8B}.Release|Any CPU.Build.0 = Release|Any CPU + {5B011775-B121-4EEE-A410-BA2D2F5BFB8B}.Release|Any CPU.ActiveCfg = Release|Any CPU + {2583369C-4DBF-4578-8350-AF454D8136CF}.Debug|Any CPU.Build.0 = Debug|Any CPU + {2583369C-4DBF-4578-8350-AF454D8136CF}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {2583369C-4DBF-4578-8350-AF454D8136CF}.Release|Any CPU.Build.0 = Release|Any CPU + {2583369C-4DBF-4578-8350-AF454D8136CF}.Release|Any CPU.ActiveCfg = Release|Any CPU + {F2AE3129-23A0-4C02-8DEA-E839D4DF4ACA}.Debug|Any CPU.Build.0 = Debug|Any CPU + {F2AE3129-23A0-4C02-8DEA-E839D4DF4ACA}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {F2AE3129-23A0-4C02-8DEA-E839D4DF4ACA}.Release|Any CPU.Build.0 = Release|Any CPU + {F2AE3129-23A0-4C02-8DEA-E839D4DF4ACA}.Release|Any CPU.ActiveCfg = Release|Any CPU + {5BCC455D-0E5F-46C6-8E54-812FD4AF11BE}.Debug|Any CPU.Build.0 = Debug|Any CPU + {5BCC455D-0E5F-46C6-8E54-812FD4AF11BE}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {5BCC455D-0E5F-46C6-8E54-812FD4AF11BE}.Release|Any CPU.Build.0 = Release|Any CPU + {5BCC455D-0E5F-46C6-8E54-812FD4AF11BE}.Release|Any CPU.ActiveCfg = Release|Any CPU + {0747C440-70E4-4E63-9F9D-03B3A010C991}.Debug|Any CPU.Build.0 = Debug|Any CPU + {0747C440-70E4-4E63-9F9D-03B3A010C991}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {0747C440-70E4-4E63-9F9D-03B3A010C991}.Release|Any CPU.Build.0 = Release|Any CPU + {0747C440-70E4-4E63-9F9D-03B3A010C991}.Release|Any CPU.ActiveCfg = Release|Any CPU + {BDF495CA-0FCD-4E51-A871-D467CDE3B43E}.Debug|Any CPU.Build.0 = Debug|Any CPU + {BDF495CA-0FCD-4E51-A871-D467CDE3B43E}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {BDF495CA-0FCD-4E51-A871-D467CDE3B43E}.Release|Any CPU.Build.0 = Release|Any CPU + {BDF495CA-0FCD-4E51-A871-D467CDE3B43E}.Release|Any CPU.ActiveCfg = Release|Any CPU + {4700690A-2D29-40A0-86AC-E5A9F71A479A}.Debug|Any CPU.Build.0 = Debug|Any CPU + {4700690A-2D29-40A0-86AC-E5A9F71A479A}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {4700690A-2D29-40A0-86AC-E5A9F71A479A}.Release|Any CPU.Build.0 = Release|Any CPU + {4700690A-2D29-40A0-86AC-E5A9F71A479A}.Release|Any CPU.ActiveCfg = Release|Any CPU + {DB8F7DA9-11A2-4BEB-8C14-25D8229EBE7E}.Debug|Any CPU.Build.0 = Debug|Any CPU + {DB8F7DA9-11A2-4BEB-8C14-25D8229EBE7E}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {DB8F7DA9-11A2-4BEB-8C14-25D8229EBE7E}.Release|Any CPU.Build.0 = Release|Any CPU + {DB8F7DA9-11A2-4BEB-8C14-25D8229EBE7E}.Release|Any CPU.ActiveCfg = Release|Any CPU + EndGlobalSection +EndGlobal diff --git a/Console/AssemblyInfo.cs b/Console/AssemblyInfo.cs new file mode 100644 index 00000000..3580eb04 --- /dev/null +++ b/Console/AssemblyInfo.cs @@ -0,0 +1,70 @@ +/* + * Copyright 2008 Sun Microsystems, Inc. All rights reserved. + * + * U.S. Government Rights - Commercial software. Government users + * are subject to the Sun Microsystems, Inc. standard license agreement + * and applicable provisions of the FAR and its supplements. + * + * Use is subject to license terms. + * + * This distribution may include materials developed by third parties. + * Sun, Sun Microsystems, the Sun logo, Java and Project Identity + * Connectors are trademarks or registered trademarks of Sun + * Microsystems, Inc. or its subsidiaries in the U.S. and other + * countries. + * + * UNIX is a registered trademark in the U.S. and other countries, + * exclusively licensed through X/Open Company, Ltd. + * + * ----------- + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER. + * + * Copyright 2008 Sun Microsystems, Inc. All rights reserved. + * + * The contents of this file are subject to the terms of the Common Development + * and Distribution License(CDDL) (the License). You may not use this file + * except in compliance with the License. + * + * You can obtain a copy of the License at + * http://identityconnectors.dev.java.net/CDDLv1.0.html + * See the License for the specific language governing permissions and + * limitations under the License. + * + * When distributing the Covered Code, include this CDDL Header Notice in each + * file and include the License file at identityconnectors/legal/license.txt. + * If applicable, add the following below this CDDL Header, with the fields + * enclosed by brackets [] replaced by your own identifying information: + * "Portions Copyrighted [year] [name of copyright owner]" + * ----------- + */ +#region Using directives + +using System.Reflection; +using System.Runtime.CompilerServices; +using System.Runtime.InteropServices; + +#endregion + +// General Information about an assembly is controlled through the following +// set of attributes. Change these attribute values to modify the information +// associated with an assembly. +[assembly: AssemblyTitle("Console")] +[assembly: AssemblyDescription("")] +[assembly: AssemblyConfiguration("")] +[assembly: AssemblyCompany("")] +[assembly: AssemblyProduct("Console")] +[assembly: AssemblyCopyright("")] +[assembly: AssemblyTrademark("")] +[assembly: AssemblyCulture("")] + +// This sets the default COM visibility of types in the assembly to invisible. +// If you need to expose a type to COM, use [ComVisible(true)] on that type. +[assembly: ComVisible(false)] + +// The assembly version has following format : +// +// Major.Minor.Build.Revision +// +// You can specify all the values or you can use the default the Revision and +// Build Numbers by using the '*' as shown below: +[assembly: AssemblyVersion("1.0.0.0")] diff --git a/Console/Console.csproj b/Console/Console.csproj new file mode 100644 index 00000000..31a7f6ee --- /dev/null +++ b/Console/Console.csproj @@ -0,0 +1,100 @@ + + + + {D09E6A1F-55F6-4661-8E5A-C485E4B08BB3} + Debug + AnyCPU + WinExe + Console + Console + v3.5 + + + ./bin/Debug/ + True + Full + False + True + DEBUG;TRACE + WinExe + Console + False + 4 + + + ./bin/Release/ + False + None + True + False + TRACE + WinExe + Console + False + 4 + + + + + ..\..\..\..\..\Program Files\SharpDevelop\3.0\AddIns\AddIns\BackendBindings\BooBinding\Boo.Lang.dll + + + + 3.5 + + + + + + + + + + + MainForm.cs + + + + + + {8B24461B-456A-4032-89A1-CD418F7B5B62} + Framework + + + \ No newline at end of file diff --git a/Console/MainForm.Designer.cs b/Console/MainForm.Designer.cs new file mode 100644 index 00000000..1782f06d --- /dev/null +++ b/Console/MainForm.Designer.cs @@ -0,0 +1,47 @@ +/* + * Created by SharpDevelop. + * User: Administrator + * Date: 3/18/2008 + * Time: 4:30 PM + * + * To change this template use Tools | Options | Coding | Edit Standard Headers. + */ +namespace Console +{ + partial class MainForm + { + ///

+ /// Designer variable used to keep track of non-visual components. + /// + private System.ComponentModel.IContainer components = null; + + /// + /// Disposes resources used by the form. + /// + /// true if managed resources should be disposed; otherwise, false. + protected override void Dispose(bool disposing) + { + if (disposing) { + if (components != null) { + components.Dispose(); + } + } + base.Dispose(disposing); + } + + /// + /// This method is required for Windows Forms designer support. + /// Do not change the method contents inside the source code editor. The Forms designer might + /// not be able to load this method if it was changed manually. + /// + private void InitializeComponent() + { + // + // MainForm + // + this.AutoScaleMode = System.Windows.Forms.AutoScaleMode.Font; + this.Text = "Console"; + this.Name = "MainForm"; + } + } +} diff --git a/Console/MainForm.cs b/Console/MainForm.cs new file mode 100644 index 00000000..d7ba3af2 --- /dev/null +++ b/Console/MainForm.cs @@ -0,0 +1,64 @@ +/* + * Copyright 2008 Sun Microsystems, Inc. All rights reserved. + * + * U.S. Government Rights - Commercial software. Government users + * are subject to the Sun Microsystems, Inc. standard license agreement + * and applicable provisions of the FAR and its supplements. + * + * Use is subject to license terms. + * + * This distribution may include materials developed by third parties. + * Sun, Sun Microsystems, the Sun logo, Java and Project Identity + * Connectors are trademarks or registered trademarks of Sun + * Microsystems, Inc. or its subsidiaries in the U.S. and other + * countries. + * + * UNIX is a registered trademark in the U.S. and other countries, + * exclusively licensed through X/Open Company, Ltd. + * + * ----------- + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER. + * + * Copyright 2008 Sun Microsystems, Inc. All rights reserved. + * + * The contents of this file are subject to the terms of the Common Development + * and Distribution License(CDDL) (the License). You may not use this file + * except in compliance with the License. + * + * You can obtain a copy of the License at + * http://identityconnectors.dev.java.net/CDDLv1.0.html + * See the License for the specific language governing permissions and + * limitations under the License. + * + * When distributing the Covered Code, include this CDDL Header Notice in each + * file and include the License file at identityconnectors/legal/license.txt. + * If applicable, add the following below this CDDL Header, with the fields + * enclosed by brackets [] replaced by your own identifying information: + * "Portions Copyrighted [year] [name of copyright owner]" + * ----------- + */ +using System; +using System.Collections.Generic; +using System.Drawing; +using System.Windows.Forms; + +namespace Console +{ + /// + /// Description of MainForm. + /// + public partial class MainForm : Form + { + public MainForm() + { + // + // The InitializeComponent() call is required for Windows Forms designer support. + // + InitializeComponent(); + + // + // TODO: Add constructor code after the InitializeComponent() call. + // + } + } +} diff --git a/Console/Program.cs b/Console/Program.cs new file mode 100644 index 00000000..ce4f71a5 --- /dev/null +++ b/Console/Program.cs @@ -0,0 +1,62 @@ +/* + * Copyright 2008 Sun Microsystems, Inc. All rights reserved. + * + * U.S. Government Rights - Commercial software. Government users + * are subject to the Sun Microsystems, Inc. standard license agreement + * and applicable provisions of the FAR and its supplements. + * + * Use is subject to license terms. + * + * This distribution may include materials developed by third parties. + * Sun, Sun Microsystems, the Sun logo, Java and Project Identity + * Connectors are trademarks or registered trademarks of Sun + * Microsystems, Inc. or its subsidiaries in the U.S. and other + * countries. + * + * UNIX is a registered trademark in the U.S. and other countries, + * exclusively licensed through X/Open Company, Ltd. + * + * ----------- + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER. + * + * Copyright 2008 Sun Microsystems, Inc. All rights reserved. + * + * The contents of this file are subject to the terms of the Common Development + * and Distribution License(CDDL) (the License). You may not use this file + * except in compliance with the License. + * + * You can obtain a copy of the License at + * http://identityconnectors.dev.java.net/CDDLv1.0.html + * See the License for the specific language governing permissions and + * limitations under the License. + * + * When distributing the Covered Code, include this CDDL Header Notice in each + * file and include the License file at identityconnectors/legal/license.txt. + * If applicable, add the following below this CDDL Header, with the fields + * enclosed by brackets [] replaced by your own identifying information: + * "Portions Copyrighted [year] [name of copyright owner]" + * ----------- + */ +using System; +using System.Windows.Forms; + +namespace Console +{ + /// + /// Class with program entry point. + /// + internal sealed class Program + { + /// + /// Program entry point. + /// + [STAThread] + private static void Main(string[] args) + { + Application.EnableVisualStyles(); + Application.SetCompatibleTextRenderingDefault(false); + Application.Run(new MainForm()); + } + + } +} diff --git a/Framework/Api.cs b/Framework/Api.cs new file mode 100644 index 00000000..031fe1ff --- /dev/null +++ b/Framework/Api.cs @@ -0,0 +1,535 @@ +/* + * Copyright 2008 Sun Microsystems, Inc. All rights reserved. + * + * U.S. Government Rights - Commercial software. Government users + * are subject to the Sun Microsystems, Inc. standard license agreement + * and applicable provisions of the FAR and its supplements. + * + * Use is subject to license terms. + * + * This distribution may include materials developed by third parties. + * Sun, Sun Microsystems, the Sun logo, Java and Project Identity + * Connectors are trademarks or registered trademarks of Sun + * Microsystems, Inc. or its subsidiaries in the U.S. and other + * countries. + * + * UNIX is a registered trademark in the U.S. and other countries, + * exclusively licensed through X/Open Company, Ltd. + * + * ----------- + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER. + * + * Copyright 2008 Sun Microsystems, Inc. All rights reserved. + * + * The contents of this file are subject to the terms of the Common Development + * and Distribution License(CDDL) (the License). You may not use this file + * except in compliance with the License. + * + * You can obtain a copy of the License at + * http://identityconnectors.dev.java.net/CDDLv1.0.html + * See the License for the specific language governing permissions and + * limitations under the License. + * + * When distributing the Covered Code, include this CDDL Header Notice in each + * file and include the License file at identityconnectors/legal/license.txt. + * If applicable, add the following below this CDDL Header, with the fields + * enclosed by brackets [] replaced by your own identifying information: + * "Portions Copyrighted [year] [name of copyright owner]" + * ----------- + */ +using System; +using System.Reflection; +using System.Globalization; +using System.Collections.Generic; +using System.Net.Security; +using System.Security; +using System.Security.Cryptography.X509Certificates; +using System.Text; +using Org.IdentityConnectors.Common.Pooling; +using Org.IdentityConnectors.Common.Security; +using Org.IdentityConnectors.Framework.Api.Operations; +using Org.IdentityConnectors.Framework.Common; +using Org.IdentityConnectors.Framework.Common.Objects; + +namespace Org.IdentityConnectors.Framework.Api +{ + public static class APIConstants { + public const int NO_TIMEOUT = -1; + } + + public interface APIConfiguration { + ConfigurationProperties ConfigurationProperties { get; } + bool IsConnectorPoolingSupported { get; } + ObjectPoolConfiguration ConnectorPoolConfiguration { get; } + CultureInfo CultureInfo { get; set; } + ICollection SupportedOperations { get; } + + int GetTimeout(Type operation); + void SetTimeout(Type operation, int timeout); + + int ProducerBufferSize { get; set; } + } + + /** + * Configuration properties encapsulates the {@link Configuration} and uses + * {@link Reflection} to determine the properties available for manipulation. + */ + public interface ConfigurationProperties { + + /** + * Get the list of properties names for this {@link Configuration}. + * + * @return get the list of properties names. + */ + IList PropertyNames { get; } + + /** + * Get a particular {@link ConfigurationProperty} by name. + * + * @param name + * the unique name of the property. + * @return a {@link ConfigurationProperty} if it exists otherwise null. + */ + ConfigurationProperty GetProperty(string name); + + /** + * Set the value of the {@link Configuration} property by name. + * + * @param name + * Name of the property to set the value against. + * @param value + * Value to set on the configuration property. + * @throws IllegalArgumentException + * iff the property name does not exist. + */ + void SetPropertyValue(string name, Object value); + + } + + /** + * Translation from {@link Configuration} at the SPI layer to the API. + */ + public interface ConfigurationProperty { + + int Order { get; } + + /** + * Get the unique name of the configuration property. + */ + string Name { get; } + + /** + * Get the help message from the message catalog. + */ + string GetHelpMessage(string def); + + /** + * Get the display name for the is configuration + */ + string GetDisplayName(string def); + + /** + * Get the value from the property. This should be the default value. + */ + object Value { get; set; } + + /** + * Get the type of the property. + */ + Type ValueType { get; } + + /** + * Is this a confidential property whose value should be encrypted by + * the application when persisted? + */ + bool IsConfidential { get; } + } + + /** + * Main interface for which consumers call the Connector API logic. + */ + public interface ConnectorFacade : CreateApiOp, DeleteApiOp, + SearchApiOp, UpdateApiOp, SchemaApiOp, AuthenticationApiOp, GetApiOp, + ValidateApiOp, TestApiOp, ScriptOnConnectorApiOp, ScriptOnResourceApiOp, + SyncApiOp { + + /** + * Get the set of operations that this {@link ConnectorFacade} will support. + */ + ICollection SupportedOperations { get; } + + /** + * Get an instance of an operation that this facade supports. + */ + APIOperation GetOperation(Type type); + + } + + /** + * Manages a pool of connectors for use by a provisioner. + * + */ + public abstract class ConnectorFacadeFactory { + + // At some point we might make this pluggable, but for now, hard-code + private const string IMPL_NAME = + "Org.IdentityConnectors.Framework.Impl.Api.ConnectorFacadeFactoryImpl"; + private static ConnectorFacadeFactory _instance; + private static object LOCK = new Object(); + + /** + * Get the singleton instance of the {@link ConnectorFacadeFactory}. + */ + public static ConnectorFacadeFactory GetInstance() { + lock(LOCK) { + if (_instance == null) { + Type t = FrameworkInternalBridge.LoadType(IMPL_NAME); + _instance = (ConnectorFacadeFactory)Activator.CreateInstance(t); + } + } + return _instance; + } + + /** + * Get a new instance of {@link ConnectorFacade}. + * + * @param config + * all the configuration that the framework, connector, and + * pooling needs. + * @return {@link ConnectorFacade} to call API operations against. + * @throws ClassNotFoundException + */ + public abstract ConnectorFacade NewInstance(APIConfiguration config); + + + /** + * Dispose of all connection pools, resources, etc. + */ + public abstract void Dispose(); + } + + /** + * The connector meta-data for a given connector. + */ + public interface ConnectorInfo { + /** + * Returns a friendly name suitable for display in the UI. + * + * @return The friendly name + */ + string GetConnectorDisplayName(CultureInfo info); + + ConnectorKey ConnectorKey { get; } + + /** + * Loads the {@link Connector} and {@link Configuration} class in order to + * determine the proper default configuration parameters. + */ + APIConfiguration CreateDefaultAPIConfiguration(); + } + /** + * Class responsible for maintaing a list of ConnectorInfo + * associated with a set of connector bundles. + */ + public interface ConnectorInfoManager { + /** + * Returns the list of ConnectorInfo + * @return the list of ConnectorInfo + */ + IList ConnectorInfos { get; } + + /** + * Given a connectorName and connectorVersion, returns the + * associated ConnectorInfo. + * @param key The connector key. + * @return The ConnectorInfo or null if it couldn't + * be found. + */ + ConnectorInfo FindConnectorInfo(ConnectorKey key); + } + /** + * The main entry point into connectors. This allows you + * to load the connector classes from a set of bundles. + */ + public abstract class ConnectorInfoManagerFactory { + //At some point we might make this pluggable, but for now, hard-code + private const string IMPL_NAME = + "Org.IdentityConnectors.Framework.Impl.Api.ConnectorInfoManagerFactoryImpl"; + private static ConnectorInfoManagerFactory _instance; + private static object LOCK = new Object(); + /// + /// Singleton pattern for getting an instance of the + /// ConnectorInfoManagerFactory. + /// + /// + public static ConnectorInfoManagerFactory GetInstance() { + lock(LOCK) { + if (_instance == null) { + Type t = + FrameworkInternalBridge.LoadType(IMPL_NAME); + _instance = (ConnectorInfoManagerFactory)Activator.CreateInstance(t); + } + } + return _instance; + } + public abstract ConnectorInfoManager GetLocalManager(); + public abstract ConnectorInfoManager GetRemoteManager(RemoteFrameworkConnectionInfo info); + + /** + * Clears the bundle manager cache. Generally intended for unit testing + */ + public abstract void ClearRemoteCache(); + } + + /** + * Uniquely identifies a connector within an installation. + * Consists of the triple (bundleName, bundleVersion, connectorName) + */ + public sealed class ConnectorKey { + private readonly string _bundleName; + private readonly string _bundleVersion; + private readonly string _connectorName; + + public ConnectorKey(String bundleName, + String bundleVersion, + String connectorName) { + if (bundleName == null) { + throw new ArgumentException("bundleName may not be null"); + } + if (bundleVersion == null) { + throw new ArgumentException("bundleVersion may not be null"); + } + if (connectorName == null) { + throw new ArgumentException("connectorName may not be null"); + } + _bundleName = bundleName; + _bundleVersion = bundleVersion; + _connectorName = connectorName; + } + + public string BundleName { + get { + return _bundleName; + } + } + + public string BundleVersion { + get { + return _bundleVersion; + } + } + + public string ConnectorName { + get { + return _connectorName; + } + } + + public override bool Equals(object o) { + if ( o is ConnectorKey ) { + ConnectorKey other = (ConnectorKey)o; + if (!_bundleName.Equals(other._bundleName)) { + return false; + } + if (!_bundleVersion.Equals(other._bundleVersion)) { + return false; + } + if (!_connectorName.Equals(other._connectorName)) { + return false; + } + return true; + } + return false; + } + + public override int GetHashCode() { + int rv = 0; + rv ^= _connectorName.GetHashCode(); + return rv; + } + + public override string ToString() { + StringBuilder builder = new StringBuilder(); + builder.Append("ConnectorKey("); + builder.Append(" bundleName=").Append(_bundleName); + builder.Append(" bundleVersion=").Append(_bundleVersion); + builder.Append(" connectorName=").Append(_connectorName); + builder.Append(" )"); + return builder.ToString(); + } + } + + + + public sealed class RemoteFrameworkConnectionInfo { + private readonly String _host; + private readonly int _port; + private readonly GuardedString _key; + private readonly bool _useSSL; + private readonly RemoteCertificateValidationCallback _certificateValidationCallback; + private readonly int _timeout; + + /** + * Creates a new instance of RemoteFrameworkConnectionInfo, using + * a clear (non-ssl) connection and a 60-second timeout. + * @param host The host to connect to + * @param port The port to connect to + */ + public RemoteFrameworkConnectionInfo(String host, + int port, + GuardedString key) + :this(host,port,key,false,null,60*1000) { + } + + /** + * Creates a new instance of RemoteFrameworkConnectionInfo. + * @param host The host to connect to + * @param port The port to connect to + * @param useSSL Set to true if we are to connect via SSL. + * @param certificateValidationCallback to use + * for establising the SSL connection. May be null or empty, + * in which case the default installed providers for the JVM will + * be used. Ignored if 'useSSL' is false. + * @param timeout The timeout to use (in milliseconds). A value of 0 + * means infinite timeout; + */ + public RemoteFrameworkConnectionInfo(String host, + int port, + GuardedString key, + bool useSSL, + RemoteCertificateValidationCallback certificateValidationCallback, + int timeout) { + + if ( host == null ) { + throw new ArgumentException("Parameter 'host' is null."); + } + if ( key == null ) { + throw new ArgumentException("Parameter 'key' is null."); + } + + _host = host; + _port = port; + _key = key; + _useSSL = useSSL; + _certificateValidationCallback = certificateValidationCallback; + _timeout = timeout; + } + + /** + * Returns the host to connect to. + * @return The host to connect to. + */ + public String Host { + get { + return _host; + } + } + + /** + * Returns the port to connect to + * @return The port to connect to + */ + public int Port { + get { + return _port; + } + } + + public GuardedString Key { + get { + return _key; + } + } + + /** + * Returns true iff we are to use SSL to connect. + * @return true iff we are to use SSL to connect. + */ + public bool UseSSL { + get { + return _useSSL; + } + } + + /** + * Returns the list of {@link TrustManager}'s. to use when establishing + * the connection. + * @return The list of {@link TrustManager}'s. + */ + public RemoteCertificateValidationCallback CertificateValidationCallback { + get { + return _certificateValidationCallback; + } + } + + /** + * Returns the timeout (in milliseconds) to use for the connection. + * A value of zero means infinite timeout. + * @return the timeout (in milliseconds) to use for the connection. + */ + public int Timeout { + get { + return _timeout; + } + } + + /** + * {@inheritDoc} + */ + public override bool Equals(Object o) { + if ( o is RemoteFrameworkConnectionInfo ) { + RemoteFrameworkConnectionInfo other = + (RemoteFrameworkConnectionInfo)o; + if (!Object.Equals(Host,other.Host)) { + return false; + } + if (Port != other.Port) { + return false; + } + if (UseSSL != other.UseSSL) { + return false; + } + if (CertificateValidationCallback == null || + other.CertificateValidationCallback == null) { + if (CertificateValidationCallback != null || + other.CertificateValidationCallback != null) { + return false; + } + } + else { + if (!CertificateValidationCallback.Equals + (other.CertificateValidationCallback)) { + return false; + } + } + + if (!Key.Equals(other.Key)) { + return false; + } + + if (Timeout != other.Timeout) { + return false; + } + + return true; + } + return false; + } + + /** + * {@inheritDoc} + */ + public override int GetHashCode() { + return _host.GetHashCode() ^ _port; + } + + /** + * {@inheritDoc} + */ + public override String ToString() { + return "{host="+_host+", port="+_port+"}"; + } + + + } +} diff --git a/Framework/ApiOperations.cs b/Framework/ApiOperations.cs new file mode 100644 index 00000000..c8a78897 --- /dev/null +++ b/Framework/ApiOperations.cs @@ -0,0 +1,323 @@ +/* + * Copyright 2008 Sun Microsystems, Inc. All rights reserved. + * + * U.S. Government Rights - Commercial software. Government users + * are subject to the Sun Microsystems, Inc. standard license agreement + * and applicable provisions of the FAR and its supplements. + * + * Use is subject to license terms. + * + * This distribution may include materials developed by third parties. + * Sun, Sun Microsystems, the Sun logo, Java and Project Identity + * Connectors are trademarks or registered trademarks of Sun + * Microsystems, Inc. or its subsidiaries in the U.S. and other + * countries. + * + * UNIX is a registered trademark in the U.S. and other countries, + * exclusively licensed through X/Open Company, Ltd. + * + * ----------- + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER. + * + * Copyright 2008 Sun Microsystems, Inc. All rights reserved. + * + * The contents of this file are subject to the terms of the Common Development + * and Distribution License(CDDL) (the License). You may not use this file + * except in compliance with the License. + * + * You can obtain a copy of the License at + * http://identityconnectors.dev.java.net/CDDLv1.0.html + * See the License for the specific language governing permissions and + * limitations under the License. + * + * When distributing the Covered Code, include this CDDL Header Notice in each + * file and include the License file at identityconnectors/legal/license.txt. + * If applicable, add the following below this CDDL Header, with the fields + * enclosed by brackets [] replaced by your own identifying information: + * "Portions Copyrighted [year] [name of copyright owner]" + * ----------- + */ +using System; +using System.Reflection; +using System.Globalization; +using System.Collections.Generic; + +using Org.IdentityConnectors.Common.Security; +using Org.IdentityConnectors.Framework.Common.Objects; +using Org.IdentityConnectors.Framework.Common.Objects.Filters; + +namespace Org.IdentityConnectors.Framework.Api.Operations +{ + /** + * Base interface for all API operations. + */ + public interface APIOperation { + } + + public interface AuthenticationApiOp : APIOperation { + + /** + * Most basic authentication available. + * + * @param username + * string that represents the account or user id. + * @param password + * string that represents the password for the account or user. + * @throws RuntimeException + * iff the credentials do not pass authentication otherwise + * nothing. + */ + void Authenticate(string username, GuardedString password, OperationOptions options); + } + + /// + /// Operation to create connector objects based on the attributes provided. + /// + public interface CreateApiOp : APIOperation { + + /// + /// Creates a user based on the ConnectorAttributes provide. The only + /// required attribute is the ObjectClass and those required by the + /// Connector. The API will validate the existence of the + /// ObjectClass attribute and that there are no duplicate name'd + /// attributes. + /// + /// ConnectorAttribtes to create the object. + /// Unique id for the created object. + Uid Create(ObjectClass oclass, ICollection attrs, OperationOptions options); + } + + /// + /// Deletes an object with the specified Uid and ObjectClass on the + /// resource. + /// + public interface DeleteApiOp : APIOperation { + /// + /// Delete the object that the specified Uid identifies (if any). + /// + /// The type of object to delete. + /// The unique identitfier for the object to delete. + /// Throws UnknowUid if the object does not exist. + void Delete(ObjectClass objectClass, Uid uid, OperationOptions options); + } + + /** + * Get a particular {@link ConnectorObject} based on the {@link Uid}. + */ + public interface GetApiOp : APIOperation { + + /** + * Get a particular {@link ConnectorObject} based on the {@link Uid}. + * + * @param uid + * the unique id of the object that to get. + * @return {@link ConnectorObject} based on the {@link Uid} provided. + */ + ConnectorObject GetObject(ObjectClass objClass, Uid uid, OperationOptions options); + } + + /** + * Get the schema from the {@link Connector}. + */ + public interface SchemaApiOp : APIOperation { + /** + * Retrieve the basic schema of this {@link Connector}. + */ + Schema Schema(); + } + + + public interface SearchApiOp : APIOperation { + + /** + * Search the resource for all objects that match the filter. + * + * @param filter + * Reduces the number of entries to only those that match the + * {@link Filter} provided. + * @param handler + * class responsible for working with the objects returned from + * the search. + * @throws RuntimeException + * iff there is problem during the processing of the results. + */ + void Search(ObjectClass oclass, Filter filter, ResultsHandler handler, OperationOptions options); + } + + /** + * Runs a script in the same JVM or .Net Runtime as the Connector. + * That is, if you are using a local framework, the script will be + * run in your JVM. If you are connected to a remote framework, the + * script will be run in the remote JVM or .Net Runtime. + *

+ * This API allows an application to run a script in the context + * of any connector. (A connector need not implement any particular interface + * in order to enable this.) The minimum contract to which each connector + * must adhere is as follows: + *

    + *
  1. Script will run in the same classloader/execution environment + * as the connector, so the script will have access to all the classes + * to which the connector has access. + *
  2. + *
  3. Script will have access to a "connector" variable + * that is equivalent to an initialized instance of a connector. + * Thus, at a minimum the script will be able to access + * {@link Connector#getConfiguration() the configuration of the connector}. + *
  4. + *
  5. Script will have access to any + * {@link ScriptContext#getScriptArguments() script-arguments} + * passed in by the application. + *
  6. + *
+ *

+ * A connector that implements {@link ScriptOnConnectorOp} + * may provide more variables than what is described above. + * A connector also may perform special processing + * for {@link OperationOptions} specific to that connector. + * Consult the javadoc of each particular connector to find out what + * additional capabilities, if any, that connector exposes for use in scripts. + *

+ * NOTE: A caller who wants to execute scripts on a connector + * should assume that a script must not use any method of the connector + * beyond the minimum contract described above, + * unless the connector explicitly documents that method as + * "for use by connector script". The primary function of a connector + * is to implement the SPI in the context of the Connector framework. + * In general, no caller should invoke Connector methods directly + * --whether by a script or by other means. + */ + public interface ScriptOnConnectorApiOp : APIOperation { + + /** + * Runs the script. + * @param request - The script and arguments to run. + * @param options - Additional options that control how the script is + * run. The framework does not currently recognize any options + * but specific connectors might. Consult the documentation + * for each connector to identify supported options. + * @return The result of the script. The return type must be + * a type that the framework supports for serialization. + * @see ObjectSerializerFactory for a list of supported return types. + */ + Object RunScriptOnConnector(ScriptContext request, + OperationOptions options); + } + /** + * Runs a script on the target resource that a connector manages. + * This API operation is supported only for a connector that implements + * {@link ScriptOnResourceOp}. + *

+ * The contract here at the API level is intentionally very loose. + * Each connector decides what script languages it supports, + * what running a script on a target resource actually means, + * and what script options (if any) that connector supports. + * Refer to the javadoc of each particular connector for more information. + */ + public interface ScriptOnResourceApiOp : APIOperation { + + /** + * Runs a script on a specific target resource. + * @param request The script and arguments to run. + * @param options Additional options which control how the script is + * run. Please refer to the connector documentation for supported + * options. + * @return The result of the script. The return type must be + * a type that the connector framework supports for serialization. + * See {@link ObjectSerializerFactory} for a list of supported return types. + */ + Object RunScriptOnResource(ScriptContext request, + OperationOptions options); + } + /** + * Receive synchronization events from the resource. This will be supported by + * connectors that implement {@link SyncOp}. + *

+ * TODO: define quality of service level. For example, on JMS sync, when + * synchronizing from a queue, the connector should return one SyncDelta + * per-call to {@link #sync(ObjectClass, SyncToken, SyncResultsHandler)}. Each + * call to {@link #sync(ObjectClass, SyncToken, SyncResultsHandler)} should + * delete the previous entry and return the next. That would guarantee that no + * items get dropped. + * + * @see SyncOp + */ + public interface SyncApiOp : APIOperation { + /** + * Perform a synchronization. + * + * @param objClass + * The object class to synchronize. Must not be null. + * @param token + * The token representing the last token from the previous sync. + * Should be null if this is the first sync for the given + * resource. + * @param handler + * The result handler Must not be null. + * @param options + * additional options that impact the way this operation is run. + * May be null. + */ + void Sync(ObjectClass objClass, SyncToken token, + SyncResultsHandler handler, + OperationOptions options); + } + + /// + /// Determines the type of update to perform. + /// + public enum UpdateApiType { + /** + * Replace each attribute value with the one provided. + */ + REPLACE, + /** + * Added the values provided to the existing attribute values on the + * native target. + */ + ADD, + /** + * Remove the attribute values from the existing target values. + */ + DELETE + } + + public interface UpdateApiOp : APIOperation { + /** + * Update the object specified. The type is used to determine if the updates + * are additive, subtractive, or replacement of values provided. + * + * @param type + * determines the type of update to expect. + * @param obj + * information to find the object and the attributes to perform + * the type of update against. + * @return the {@link Uid} of the updated object in case the update changes + * the formation of the unique identifier. + */ + Uid Update(UpdateApiType type, ObjectClass objectclass, + ICollection attrs, OperationOptions options); + + } + + public interface ValidateApiOp : APIOperation { + /** + * Tests connectivity and validity of the {@link Configuration}. + * + * @throws RuntimeException + * iff the {@link Configuration} is not valid or a + * {@link Connection} to the resource could not be established. + */ + void Validate(); + } + + public interface TestApiOp : APIOperation { + /** + * Tests connectivity and validity of the {@link Configuration}. + * + * @throws RuntimeException + * iff the {@link Configuration} is not valid or a + * {@link Connection} to the resource could not be established. + */ + void Test(); + } +} diff --git a/Framework/AssemblyInfo.cs b/Framework/AssemblyInfo.cs new file mode 100644 index 00000000..c4afaf29 --- /dev/null +++ b/Framework/AssemblyInfo.cs @@ -0,0 +1,70 @@ +/* + * Copyright 2008 Sun Microsystems, Inc. All rights reserved. + * + * U.S. Government Rights - Commercial software. Government users + * are subject to the Sun Microsystems, Inc. standard license agreement + * and applicable provisions of the FAR and its supplements. + * + * Use is subject to license terms. + * + * This distribution may include materials developed by third parties. + * Sun, Sun Microsystems, the Sun logo, Java and Project Identity + * Connectors are trademarks or registered trademarks of Sun + * Microsystems, Inc. or its subsidiaries in the U.S. and other + * countries. + * + * UNIX is a registered trademark in the U.S. and other countries, + * exclusively licensed through X/Open Company, Ltd. + * + * ----------- + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER. + * + * Copyright 2008 Sun Microsystems, Inc. All rights reserved. + * + * The contents of this file are subject to the terms of the Common Development + * and Distribution License(CDDL) (the License). You may not use this file + * except in compliance with the License. + * + * You can obtain a copy of the License at + * http://identityconnectors.dev.java.net/CDDLv1.0.html + * See the License for the specific language governing permissions and + * limitations under the License. + * + * When distributing the Covered Code, include this CDDL Header Notice in each + * file and include the License file at identityconnectors/legal/license.txt. + * If applicable, add the following below this CDDL Header, with the fields + * enclosed by brackets [] replaced by your own identifying information: + * "Portions Copyrighted [year] [name of copyright owner]" + * ----------- + */ +#region Using directives + +using System.Reflection; +using System.Runtime.CompilerServices; +using System.Runtime.InteropServices; + +#endregion + +// General Information about an assembly is controlled through the following +// set of attributes. Change these attribute values to modify the information +// associated with an assembly. +[assembly: AssemblyTitle("Open Connectors Framework")] +[assembly: AssemblyDescription("")] +[assembly: AssemblyConfiguration("")] +[assembly: AssemblyCompany("")] +[assembly: AssemblyProduct("Open Connectors Framework")] +[assembly: AssemblyCopyright("")] +[assembly: AssemblyTrademark("")] +[assembly: AssemblyCulture("")] + +// This sets the default COM visibility of types in the assembly to invisible. +// If you need to expose a type to COM, use [ComVisible(true)] on that type. +[assembly: ComVisible(false)] + +// The assembly version has following format : +// +// Major.Minor.Build.Revision +// +// You can specify all the values or you can use the default the Revision and +// Build Numbers by using the '*' as shown below: +[assembly: AssemblyVersion("1.0.0.0")] diff --git a/Framework/Common.cs b/Framework/Common.cs new file mode 100644 index 00000000..21d57a86 --- /dev/null +++ b/Framework/Common.cs @@ -0,0 +1,289 @@ +/* + * Copyright 2008 Sun Microsystems, Inc. All rights reserved. + * + * U.S. Government Rights - Commercial software. Government users + * are subject to the Sun Microsystems, Inc. standard license agreement + * and applicable provisions of the FAR and its supplements. + * + * Use is subject to license terms. + * + * This distribution may include materials developed by third parties. + * Sun, Sun Microsystems, the Sun logo, Java and Project Identity + * Connectors are trademarks or registered trademarks of Sun + * Microsystems, Inc. or its subsidiaries in the U.S. and other + * countries. + * + * UNIX is a registered trademark in the U.S. and other countries, + * exclusively licensed through X/Open Company, Ltd. + * + * ----------- + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER. + * + * Copyright 2008 Sun Microsystems, Inc. All rights reserved. + * + * The contents of this file are subject to the terms of the Common Development + * and Distribution License(CDDL) (the License). You may not use this file + * except in compliance with the License. + * + * You can obtain a copy of the License at + * http://identityconnectors.dev.java.net/CDDLv1.0.html + * See the License for the specific language governing permissions and + * limitations under the License. + * + * When distributing the Covered Code, include this CDDL Header Notice in each + * file and include the License file at identityconnectors/legal/license.txt. + * If applicable, add the following below this CDDL Header, with the fields + * enclosed by brackets [] replaced by your own identifying information: + * "Portions Copyrighted [year] [name of copyright owner]" + * ----------- + */ +using System; +using System.Reflection; +using System.Collections.Generic; +using Org.IdentityConnectors.Common; +using Org.IdentityConnectors.Common.Security; +using Org.IdentityConnectors.Framework.Spi; +using Org.IdentityConnectors.Framework.Api.Operations; +using Org.IdentityConnectors.Framework.Spi.Operations; +using Org.IdentityConnectors.Framework.Common.Objects; +namespace Org.IdentityConnectors.Framework.Common +{ + internal static class FrameworkInternalBridge { + private static readonly Object LOCK = new Object(); + private static Assembly _assembly = null; + ///

+ /// Loads a class from the FrameworkInternal module + /// + /// + /// + public static Type LoadType(String typeName) { + + Assembly assembly; + lock(LOCK) { + if (_assembly == null) { + AssemblyName assemName = new AssemblyName(); + assemName.Name = "FrameworkInternal"; + _assembly = Assembly.Load(assemName); + } + assembly = _assembly; + } + + return assembly.GetType(typeName,true); + + } + } + + public static class FrameworkUtil { + private static readonly IDictionary SPI_TO_API; + private static readonly ICollection CONFIG_SUPPORTED_TYPES; + private static readonly ICollection ATTR_SUPPORTED_TYPES; + + static FrameworkUtil() { + IDictionary temp = + new Dictionary(); + temp[typeof(AuthenticateOp)]=typeof(AuthenticationApiOp); + temp[typeof(CreateOp)]=typeof(CreateApiOp); + temp[typeof(DeleteOp)]=typeof(DeleteApiOp); + temp[typeof(SearchOp<>)]=typeof(SearchApiOp); + temp[typeof(UpdateOp)]=typeof(UpdateApiOp); + temp[typeof(AdvancedUpdateOp)]=typeof(UpdateApiOp); + temp[typeof(SchemaOp)]=typeof(SchemaApiOp); + temp[typeof(TestOp)]=typeof(TestApiOp); + temp[typeof(ScriptOnConnectorOp)]=typeof(ScriptOnConnectorApiOp); + temp[typeof(ScriptOnResourceOp)]=typeof(ScriptOnResourceApiOp); + temp[typeof(SyncOp)]=typeof(SyncApiOp); + SPI_TO_API = CollectionUtil.NewReadOnlyDictionary(temp); + + CONFIG_SUPPORTED_TYPES = CollectionUtil.NewReadOnlySet + ( + typeof(string), + typeof(long), + typeof(long?), + typeof(char), + typeof(char?), + typeof(double), + typeof(double?), + typeof(float), + typeof(float?), + typeof(int), + typeof(int?), + typeof(bool), + typeof(bool?), + typeof(Uri), + typeof(FileName), + typeof(GuardedString) + ); + ATTR_SUPPORTED_TYPES = CollectionUtil.NewReadOnlySet + ( + typeof(string), + typeof(long), + typeof(long?), + typeof(char), + typeof(char?), + typeof(double), + typeof(double?), + typeof(float), + typeof(float?), + typeof(int), + typeof(int?), + typeof(bool), + typeof(bool?), + typeof(byte[]), + typeof(BigDecimal), + typeof(BigInteger), + typeof(GuardedString) + ); + + } + + + /** + * Determines if the class is a supported attribute type. If not it throws + * an {@link IllegalArgumentException}. + * + *
    + *
  • string
  • + *
  • long
  • + *
  • long?
  • + *
  • char
  • + *
  • char?
  • + *
  • double
  • + *
  • double?
  • + *
  • float
  • + *
  • float?
  • + *
  • int
  • + *
  • int?
  • + *
  • bool
  • + *
  • bool?
  • + *
  • byte[]
  • + *
  • BigDecimal
  • + *
  • BigInteger
  • + *
+ * + * @param clazz + * type to check against the support list of types. + * @throws IllegalArgumentException + * iff the type is not on the supported list. + */ + public static void CheckAttributeType(Type type) { + if (!FrameworkUtil.IsSupportedAttributeType(type)) { + String MSG = "Attribute type ''"+type+"'' is not supported."; + throw new ArgumentException(MSG); + } + } + public static void CheckAttributeValue(Object value) { + if ( value != null ) { + CheckAttributeType(value.GetType()); + } + } + public static ICollection Spi2Apis(Type type) { + type = ReflectionUtil.GetTypeErasure(type); + AssertSpiOperation(type); + HashSet set = new HashSet(); + set.Add(SPI_TO_API[type]); + // add GetApiOp if search is available.. + + if (type.Equals(typeof(SearchOp<>))) { + set.Add(typeof(GetApiOp)); + } + return set; + } + public static ICollection AllSPIOperations() { + return SPI_TO_API.Keys; + } + public static ICollection AllAPIOperations() { + ICollection set = new HashSet(); + CollectionUtil.AddAll(set, + SPI_TO_API.Values); + // add Get because it doesn't have a corresponding SPI. + set.Add(typeof(GetApiOp)); + set.Add(typeof(ValidateApiOp)); + return CollectionUtil.AsReadOnlySet(set); + } + public static ICollection GetDefaultSupportedOperations(Type connector) { + AssertConnectorType(connector); + ICollection rv = new HashSet(); + ICollection interfaces = ReflectionUtil.GetTypeErasure(ReflectionUtil.GetAllInterfaces(connector)); + foreach (Type spi in AllSPIOperations()) { + if (interfaces.Contains(spi)) { + CollectionUtil.AddAll(rv,Spi2Apis(spi)); + } + } + //finally add unconditionally supported ops + CollectionUtil.AddAll(rv,GetUnconditionallySupportedOperations()); + return CollectionUtil.AsReadOnlySet(rv); + } + public static ICollection GetUnconditionallySupportedOperations() { + HashSet ret; + ret = new HashSet(); + //add validate api op always + ret.Add(typeof(ValidateApiOp)); + //add ScriptOnConnectorApiOp always + ret.Add(typeof(ScriptOnConnectorApiOp)); + return ret; + } + public static ICollection GetAllSupportedConfigTypes() { + return CONFIG_SUPPORTED_TYPES; + } + public static bool IsSupportedConfigurationType (Type type) { + if ( type.IsArray) { + return IsSupportedConfigurationType(type.GetElementType()); + } + else { + return CONFIG_SUPPORTED_TYPES.Contains(type); + } + } + public static ICollection GetAllSupportedAttributeTypes() { + return ATTR_SUPPORTED_TYPES; + } + public static bool IsSupportedAttributeType(Type clazz) { + return ATTR_SUPPORTED_TYPES.Contains(clazz); + } + + public static void AssertConnectorType(Type connector) { + Type connectorInter = typeof(Connector); + if (!connectorInter.IsAssignableFrom(connector)) { + throw new ArgumentException(connector+" does not implement IConnector"); + } + } + public static void AssertSpiOperation(Type operation) { + Type spiInter = typeof(SPIOperation); + if (!spiInter.IsAssignableFrom(operation)) { + throw new ArgumentException(operation+" does not implement ISPIOperation"); + } + } + public static void AssertApiOperation(Type operation) { + Type spiInter = typeof(APIOperation); + if (!spiInter.IsAssignableFrom(operation)) { + throw new ArgumentException(operation+" does not implement APIOperation"); + } + } + /** + * Determines if the class is a supported type for an OperationOption. If not it throws + * an {@link IllegalArgumentException}. + * + * @param clazz + * type to check against the support list of types. + * @throws IllegalArgumentException + * iff the type is not on the supported list. + */ + public static void CheckOperationOptionType(Type clazz) { + //the set of supported operation option types + //is the same as that for configuration beans + if (!FrameworkUtil.IsSupportedConfigurationType(clazz)) { + String MSG = "ConfigurationOption type '+"+clazz.Name+"+' is not supported."; + throw new ArgumentException(MSG); + } + } + /** + * Determines if the class of the object is a supported attribute type. + * If not it throws an {@link IllegalArgumentException}. + * @param value The value to check or null. + */ + public static void CheckOperationOptionValue(Object val) { + if ( val != null ) { + CheckOperationOptionType(val.GetType()); + } + } + } +} diff --git a/Framework/CommonExceptions.cs b/Framework/CommonExceptions.cs new file mode 100644 index 00000000..e9c84f61 --- /dev/null +++ b/Framework/CommonExceptions.cs @@ -0,0 +1,251 @@ +/* + * Copyright 2008 Sun Microsystems, Inc. All rights reserved. + * + * U.S. Government Rights - Commercial software. Government users + * are subject to the Sun Microsystems, Inc. standard license agreement + * and applicable provisions of the FAR and its supplements. + * + * Use is subject to license terms. + * + * This distribution may include materials developed by third parties. + * Sun, Sun Microsystems, the Sun logo, Java and Project Identity + * Connectors are trademarks or registered trademarks of Sun + * Microsystems, Inc. or its subsidiaries in the U.S. and other + * countries. + * + * UNIX is a registered trademark in the U.S. and other countries, + * exclusively licensed through X/Open Company, Ltd. + * + * ----------- + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER. + * + * Copyright 2008 Sun Microsystems, Inc. All rights reserved. + * + * The contents of this file are subject to the terms of the Common Development + * and Distribution License(CDDL) (the License). You may not use this file + * except in compliance with the License. + * + * You can obtain a copy of the License at + * http://identityconnectors.dev.java.net/CDDLv1.0.html + * See the License for the specific language governing permissions and + * limitations under the License. + * + * When distributing the Covered Code, include this CDDL Header Notice in each + * file and include the License file at identityconnectors/legal/license.txt. + * If applicable, add the following below this CDDL Header, with the fields + * enclosed by brackets [] replaced by your own identifying information: + * "Portions Copyrighted [year] [name of copyright owner]" + * ----------- + */ +using System; + +namespace Org.IdentityConnectors.Framework.Common.Exceptions +{ + + public class AlreadyExistsException : ConnectorException { + + public AlreadyExistsException() : base() { + } + + public AlreadyExistsException(String message) : base(message) { + + } + + public AlreadyExistsException(Exception ex) : base(ex) { + } + + public AlreadyExistsException(String message, Exception ex) : base(message,ex) { + } + } + + public class ConfigurationException : ConnectorException { + + public ConfigurationException() : base() { + } + + public ConfigurationException(String message) : base(message) { + } + + public ConfigurationException(Exception ex) : base(ex) { + } + + public ConfigurationException(String message, Exception ex) : base(message,ex) { + } + } + + public class ConnectionBrokenException : ConnectorIOException { + + + public ConnectionBrokenException() : base() { + } + + public ConnectionBrokenException(String msg) : base(msg) { + } + + public ConnectionBrokenException(Exception ex) : base(ex) { + } + + public ConnectionBrokenException(String message, Exception ex) : base(message,ex) { + } + } + public class ConnectionFailedException : ConnectorIOException { + + + public ConnectionFailedException() : base() { + } + + public ConnectionFailedException(String msg) : base(msg) { + } + + public ConnectionFailedException(Exception ex) : base(ex) { + } + + public ConnectionFailedException(String message, Exception ex) : base(message,ex) { + } + + } + + public class ConnectorException : ApplicationException { + + public ConnectorException() : base() { + } + + /** + * Sets a message for the {@link Exception}. + * + * @param message + * passed to the {@link RuntimeException} message. + */ + public ConnectorException(String message) :base(message) { + } + + /** + * Sets the stack trace to the original exception, so this exception can + * masquerade as the original only be a {@link RuntimeException}. + * + * @param originalException + * the original exception adapted to {@link RuntimeException}. + */ + public ConnectorException(Exception ex) : base(ex.Message,ex) { + } + + /** + * Sets the stack trace to the original exception, so this exception can + * masquerade as the original only be a {@link RuntimeException}. + * + * @param message + * @param originalException + * the original exception adapted to {@link RuntimeException}. + */ + public ConnectorException(String message, Exception originalException) : base(message,originalException) { + } + + } + + public class ConnectorIOException : ConnectorException { + + public ConnectorIOException() : base() { + } + + public ConnectorIOException(String msg) : base(msg) { + } + + public ConnectorIOException(Exception ex) : base(ex) { + } + + public ConnectorIOException(String message, Exception ex) : base(message,ex) { + } + } + + public class ConnectorSecurityException : ConnectorException { + + public ConnectorSecurityException() : base() { + } + + public ConnectorSecurityException(String message) : base(message) { + } + + public ConnectorSecurityException(Exception ex) : base(ex) { + } + + public ConnectorSecurityException(String message, Exception ex) : base(message,ex) { + } + } + + public class InvalidCredentialException : ConnectorSecurityException { + + public InvalidCredentialException() : base() { + } + + public InvalidCredentialException(String message) : base(message) { + } + + public InvalidCredentialException(Exception ex) : base(ex) { + } + + public InvalidCredentialException(String message, Exception ex) : base(message,ex) { + } + } + public class InvalidPasswordException : InvalidCredentialException { + + public InvalidPasswordException() : base() { + } + + public InvalidPasswordException(String message) : base(message) { + } + + public InvalidPasswordException(Exception ex) : base(ex) { + } + + public InvalidPasswordException(String message, Exception ex) : base(message,ex) { + } + } + + public class OperationTimeoutException : ConnectorException { + + public OperationTimeoutException() : base() { + } + + public OperationTimeoutException(String msg) : base(msg) { + } + + public OperationTimeoutException(Exception e) : base(e) { + } + + public OperationTimeoutException(String msg, Exception e) : base(msg,e) { + } + + } + + public class PermissionDeniedException : ConnectorSecurityException { + + public PermissionDeniedException() : base() { + } + + public PermissionDeniedException(String message) : base(message) { + } + + public PermissionDeniedException(Exception ex) : base(ex) { + } + + public PermissionDeniedException(String message, Exception ex) : base(message,ex) { + } + } + + public class UnknownUidException : InvalidCredentialException { + + public UnknownUidException() : base() { + } + + public UnknownUidException(String message) : base(message) { + } + + public UnknownUidException(Exception ex) : base(ex) { + } + + public UnknownUidException(String message, Exception ex) : base(message,ex) { + } + } + + +} diff --git a/Framework/CommonObjects.cs b/Framework/CommonObjects.cs new file mode 100644 index 00000000..356529c0 --- /dev/null +++ b/Framework/CommonObjects.cs @@ -0,0 +1,3278 @@ +/* + * Copyright 2008 Sun Microsystems, Inc. All rights reserved. + * + * U.S. Government Rights - Commercial software. Government users + * are subject to the Sun Microsystems, Inc. standard license agreement + * and applicable provisions of the FAR and its supplements. + * + * Use is subject to license terms. + * + * This distribution may include materials developed by third parties. + * Sun, Sun Microsystems, the Sun logo, Java and Project Identity + * Connectors are trademarks or registered trademarks of Sun + * Microsystems, Inc. or its subsidiaries in the U.S. and other + * countries. + * + * UNIX is a registered trademark in the U.S. and other countries, + * exclusively licensed through X/Open Company, Ltd. + * + * ----------- + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER. + * + * Copyright 2008 Sun Microsystems, Inc. All rights reserved. + * + * The contents of this file are subject to the terms of the Common Development + * and Distribution License(CDDL) (the License). You may not use this file + * except in compliance with the License. + * + * You can obtain a copy of the License at + * http://identityconnectors.dev.java.net/CDDLv1.0.html + * See the License for the specific language governing permissions and + * limitations under the License. + * + * When distributing the Covered Code, include this CDDL Header Notice in each + * file and include the License file at identityconnectors/legal/license.txt. + * If applicable, add the following below this CDDL Header, with the fields + * enclosed by brackets [] replaced by your own identifying information: + * "Portions Copyrighted [year] [name of copyright owner]" + * ----------- + */ +using System; +using System.Security; +using System.Collections; +using System.Collections.Generic; +using System.Globalization; +using System.Text; + +using Org.IdentityConnectors.Common; +using Org.IdentityConnectors.Common.Security; + +using Org.IdentityConnectors.Framework.Spi.Operations; +using Org.IdentityConnectors.Framework.Api.Operations; +using Org.IdentityConnectors.Framework.Common.Serializer; +using Org.IdentityConnectors.Framework.Common.Objects.Filters; +namespace Org.IdentityConnectors.Framework.Common.Objects +{ + #region ConnectorAttributeUtil + public static class ConnectorAttributeUtil { + + /** + * Gets the string value from the single value attribute. + * + * @param attr + * ConnectorAttribute to retrieve the string value from. + * @return null if the value is null otherwise the string value for the + * attribute. + * @throws ClassCastException + * iff the object in the attribute is not an string. + * @throws IllegalArgumentException + * iff the attribute is a multi valued instead of single valued. + */ + public static string GetStringValue(ConnectorAttribute attr) { + return (string)GetSingleValue(attr); + } + + /** + * Gets the string value from the single value attribute. + * + * @param attr + * ConnectorAttribute to retrieve the string value from. + * @return null if the value is null otherwise the string value for the + * attribute. + * @throws IllegalArgumentException + * iff the attribute is a multi valued instead of single valued. + */ + public static string GetAsStringValue(ConnectorAttribute attr) { + object obj = GetSingleValue(attr); + return obj != null ? obj.ToString() : null; + } + + public static GuardedString GetGuardedStringValue(ConnectorAttribute attr) { + object obj = GetSingleValue(attr); + return obj != null ? (GuardedString)obj : null; + } + /** + * Gets the integer value from the single value attribute. + * + * @param attr + * ConnectorAttribute to retrieve the integer value from. + * @return null if the value is null otherwise the integer value for the + * attribute. + * @throws ClassCastException + * iff the object in the attribute is not an integer. + * @throws IllegalArgumentException + * iff the attribute is a multi valued instead of single valued. + */ + public static int? GetIntegerValue(ConnectorAttribute attr) { + object obj = GetSingleValue(attr); + return obj != null ? (int?) obj : null; + } + + /** + * Gets the long value from the single value attribute. + * + * @param attr + * ConnectorAttribute to retrieve the long value from. + * @return null if the value is null otherwise the long value for the + * attribute. + * @throws ClassCastException + * iff the object in the attribute is not an long. + * @throws IllegalArgumentException + * iff the attribute is a multi valued instead of single valued. + */ + public static long? GetLongValue(ConnectorAttribute attr) { + Object obj = GetSingleValue(attr); + return obj != null ? (long?) obj : null; + } + + /** + * Gets the date value from the single value attribute. + * + * @param attr + * ConnectorAttribute to retrieve the date value from. + * @return null if the value is null otherwise the date value for the + * attribute. + * @throws ClassCastException + * iff the object in the attribute is not an long. + * @throws IllegalArgumentException + * iff the attribute is a multi valued instead of single valued. + */ + public static DateTime? GetDateTimeValue(ConnectorAttribute attr) { + long? val = GetLongValue(attr); + if (val != null) { + return DateTimeUtil.GetDateTimeFromUtcMillis(val.Value); + } + return null; + } + + /** + * Gets the integer value from the single value attribute. + * + * @param attr + * ConnectorAttribute to retrieve the integer value from. + * @return null if the value is null otherwise the integer value for the + * attribute. + * @throws ClassCastException + * iff the object in the attribute is not an integer. + * @throws IllegalArgumentException + * iff the attribute is a multi valued instead of single valued. + */ + public static double? GetDoubleValue(ConnectorAttribute attr) { + Object obj = GetSingleValue(attr); + return obj != null ? (double?) obj : null; + } + + public static bool? GetBooleanValue(ConnectorAttribute attr) { + object obj = GetSingleValue(attr); + return obj != null ? (bool?) obj : null; + } + + /** + * Get the single value from the ConnectorAttribute. + */ + public static object GetSingleValue(ConnectorAttribute attr) { + Object ret = null; + IList val = attr.Value; + if (val != null) { + // make sure this only called for single value.. + if (val.Count > 1) { + const string MSG = "The method is only for single value attributes."; + throw new ArgumentException(MSG); + } + ret = val[0]; + } + return ret; + } + + /// + /// Transform a Collection of ConnectorAttribute} instances into a {@link Map}. + /// The key to each element in the map is the name of an ConnectorAttribute. + /// The value of each element in the map is the ConnectorAttribute instance with that name. + /// + /// + /// + public static IDictionary ToMap( + ICollection attributes) { + IDictionary ret = + new Dictionary( + StringComparer.OrdinalIgnoreCase); + foreach (ConnectorAttribute attr in attributes) { + ret[attr.Name] = attr; + } + return ret; + } + + /** + * Get the {@link Uid} from the attribute set. + * + * @param attrs + * set of {@link ConnectorAttribute}s that may contain a {@link Uid}. + * @return null if the set does not contain a {@link Uid} object the first + * one found. + */ + public static Uid GetUidAttribute(ICollection attrs) { + return (Uid)Find(Uid.NAME, attrs); + } + + /** + * Filters out all special attributes from the set. These special attributes + * include {@link Password}, {@link Uid} etc.. + * + * @param attrs + * set of {@link ConnectorAttribute}s to filter out the operational and + * default attributes. + * @return a set that only contains plain attributes or empty. + */ + public static ICollection GetBasicAttributes(ICollection attrs) { + ICollection ret = new HashSet(); + foreach (ConnectorAttribute attr in attrs) { + // note this is dangerous because we need to be consistent + // in the naming of special attributes. + if (!IsSpecial(attr)) { + ret.Add(attr); + } + } + return ret; + } + /** + * Filter out any basic attributes from the specified set, leaving only + * special attributes. Special attributes include {@link Name}, {@link Uid}, + * and {@link OperationalAttributes}. + * + * @param attrs + * set of {@link Attribute}s to filter out the basic attributes + * @return a set that only contains special attributes or an empty set if + * there are none. + */ + public static ICollection GetSpecialAttributes(ICollection attrs) { + ICollection ret = new HashSet(); + foreach (ConnectorAttribute attr in attrs) { + if (IsSpecial(attr)) { + ret.Add(attr); + } + } + return ret; + } + + /** + * Determines if this attribute is a special attribute. + * + * @param attr + * {@link ConnectorAttribute} to test for against. + * @return true iff the attribute value is a {@link Uid}, + * {@link ObjectClass}, {@link Password}, or + * {@link OperationalAttributes}. + * @throws NullPointerException + * iff the attribute parameter is null. + */ + public static bool IsSpecial(ConnectorAttribute attr) { + // note this is dangerous because we need to be consistent + // in the naming of special attributes. + String name = attr.Name; + return IsSpecialName(name); + } + + /** + * Determines if this attribute is a special attribute. + * + * @param attr + * {@link ConnectorAttribute} to test for against. + * @return true iff the attribute value is a {@link Uid}, + * {@link ObjectClass}, {@link Password}, or + * {@link OperationalAttributes}. + * @throws NullPointerException + * iff the attribute parameter is null. + */ + public static bool IsSpecial(ConnectorAttributeInfo attr) { + // note this is dangerous because we need to be consistent + // in the naming of special attributes. + String name = attr.Name; + return IsSpecialName(name); + } + + private static bool IsSpecialName(String name) { + // note this is dangerous because we need to be consistent + // in the naming of special attributes. + return (name.StartsWith("@@") && name.EndsWith("@@")); + } + + /// + /// Creates the special naming for operational type attributes. + /// + /// string to make special + /// name constructed for use as an operational attribute. + public static string CreateSpecialName(string name) { + if (StringUtil.IsBlank(name)) { + const string ERR = "Name parameter must not be blank!"; + throw new ArgumentException(ERR); + } + return "@@" + name + "@@"; + } + + /// + /// Gets the 'Name' attribute from a set of ConnectorAttributes. + /// + /// set of attributes to search against. + /// the 'Name' attribute it if exsist otherwisenull + public static Name GetNameFromAttributes(ICollection attrs) { + return (Name)Find(Name.NAME, attrs); + } + + + /** + * Find the {@link ConnectorAttribute} of the given name in the {@link Set}. + * + * @param name + * {@link ConnectorAttribute}'s name to search for. + * @param attrs + * {@link Set} of attribute to search. + * @return {@link ConnectorAttribute} with the specified otherwise null. + */ + public static ConnectorAttribute Find(string name, ICollection attrs) { + Assertions.NullCheck(name, "name"); + ICollection attributes = CollectionUtil.NullAsEmpty(attrs); + foreach (ConnectorAttribute attr in attributes) { + if (attr.Is(name)) { + return attr; + } + } + return null; + } + /** + * Get the password value from the provided set of {@link ConnectorAttribute}s. + */ + public static GuardedString GetPasswordValue(ICollection attrs) { + ConnectorAttribute pwd = Find(OperationalAttributes.PASSWORD_NAME, attrs); + return (pwd == null) ? null : GetGuardedStringValue(pwd); + } + /** + * Get the reset password value from the provided set of {@link Attribute}s. + * + * @param attrs + * Set of {@link Attribute}s that may contain the reset password + * {@link OperationalAttributes#RESET_PASSWORD_NAME} + * {@link Attribute}. + * @return null if it does not exist in the {@link Set} else + * the value. + */ + public static GuardedString GetResetPasswordValue(ICollection attrs) { + ConnectorAttribute pwd = Find(OperationalAttributes.RESET_PASSWORD_NAME, attrs); + return (pwd == null) ? null : GetGuardedStringValue(pwd); + } + + /** + * Get the current password value from the provided set of {@link Attribute}s. + * + * @param attrs + * Set of {@link Attribute}s that may contain the current password + * {@link OperationalAttributes#CURRENT_PASSWORD_NAME} + * {@link Attribute}. + * @return null if it does not exist in the {@link Set} else + * the value. + */ + public static GuardedString GetCurrentPasswordValue(ICollection attrs) { + ConnectorAttribute pwd = Find(OperationalAttributes.CURRENT_PASSWORD_NAME, attrs); + return (pwd == null) ? null : GetGuardedStringValue(pwd); + } + /** + * Determine if the {@link ConnectorObject} is locked out. By getting the + * value of the {@link OperationalAttributes#LOCK_OUT_NAME}. + * + * @param obj + * {@link ConnectorObject} object to inspect. + * @throws NullPointerException + * iff the parameter 'obj' is null. + * @return null if the attribute does not exist otherwise to + * value of the {@link ConnectorAttribute}. + */ + public static bool? IsLockedOut(ConnectorObject obj) { + ConnectorAttribute attr = obj.GetAttributeByName(OperationalAttributes.LOCK_OUT_NAME); + return (attr == null) ? null : GetBooleanValue(attr); + } + + /** + * Determine if the {@link ConnectorObject} is enable. By getting the value + * of the {@link OperationalAttributes#ENABLE_NAME}. + * + * @param obj + * {@link ConnectorObject} object to inspect. + * @throws IllegalStateException + * if the object does not contain attribute in question. + * @throws NullPointerException + * iff the parameter 'obj' is null. + * @return null if the attribute does not exist otherwise to + * value of the {@link ConnectorAttribute}. + */ + public static bool? IsEnabled(ConnectorObject obj) { + ConnectorAttribute attr = obj.GetAttributeByName(OperationalAttributes.ENABLE_NAME); + return (attr == null) ? null : GetBooleanValue(attr); + } + + /** + * Retrieve the password expiration date from the {@link ConnectorObject}. + * + * @param obj + * {@link ConnectorObject} object to inspect. + * @throws IllegalStateException + * if the object does not contain attribute in question. + * @throws NullPointerException + * iff the parameter 'obj' is null. + * @return null if the {@link ConnectorAttribute} does not exist + * otherwise the value of the {@link ConnectorAttribute}. + */ + public static DateTime? GetPasswordExpirationDate(ConnectorObject obj) { + DateTime? ret = null; + ConnectorAttribute attr = obj.GetAttributeByName(OperationalAttributes.PASSWORD_EXPIRATION_DATE_NAME); + if (attr != null) { + long? date = GetLongValue(attr); + if (date != null) { + ret = DateTime.FromFileTimeUtc(date.Value); + } + } + return ret; + } + /** + * Get the password expired attribute from a {@link Collection} of + * {@link Attribute}s. + * + * @param attrs + * set of attribute to find the expired password + * {@link Attribute}. + * @return null if the attribute does not exist and the value + * of the {@link Attribute} if it does. + */ + public static bool? GetPasswordExpired(ICollection attrs) { + ConnectorAttribute pwd = Find(OperationalAttributes.PASSWORD_EXPIRED_NAME, attrs); + return (pwd == null) ? null : GetBooleanValue(pwd); + } + + /** + * Determine if the password is expired for this object. + * + * @param obj + * {@link ConnectorObject} that should contain a password expired + * attribute. + * @return null if the attribute does not exist and the value + * of the {@link Attribute} if it does. + */ + public static bool? IsPasswordExpired(ConnectorObject obj) { + ConnectorAttribute pwd = obj.GetAttributeByName(OperationalAttributes.PASSWORD_EXPIRED_NAME); + return (pwd == null) ? null : GetBooleanValue(pwd); + } + + /** + * Get the enable date from the set of attributes. + * + * @param attrs + * set of attribute to find the enable date + * {@link Attribute}. + * @return null if the attribute does not exist and the value + * of the {@link Attribute} if it does. + */ + public static DateTime? GetEnableDate(ICollection attrs) { + ConnectorAttribute attr = Find(OperationalAttributes.ENABLE_DATE_NAME, attrs); + return (attr == null) ? null : GetDateTimeValue(attr); + } + } + #endregion + + #region ConnectorAttributeInfoUtil + public static class ConnectorAttributeInfoUtil { + /** + * Transform a Collection of {@link AttributeInfo} instances into + * a {@link Map}. The key to each element in the map is the name of + * an AttributeInfo. The value of each element in the map is the + * AttributeInfo instance with that name. + * + * @param attributes + * set of AttributeInfo to transform to a map. + * @return a map of string and AttributeInfo. + * @throws NullPointerException + * iff the parameter attributes is + * null. + */ + public static IDictionary ToMap( + ICollection attributes) { + IDictionary + ret = new Dictionary( + StringComparer.OrdinalIgnoreCase); + foreach (ConnectorAttributeInfo attr in attributes) { + ret[attr.Name] = attr; + } + return ret; + } + + /** + * Find the {@link AttributeInfo} of the given name in the {@link Set}. + * + * @param name + * {@link AttributeInfo}'s name to search for. + * @param attrs + * {@link Set} of AttributeInfo to search. + * @return {@link AttributeInfo} with the specified otherwise null. + */ + public static ConnectorAttributeInfo Find(string name, ICollection attrs) { + Assertions.NullCheck(name, "name"); + ICollection attributes = CollectionUtil.NullAsEmpty(attrs); + foreach (ConnectorAttributeInfo attr in attributes) { + if (attr.Is(name)) { + return attr; + } + } + return null; + } + } + #endregion + + #region BigDecimal + /// + /// Placeholder since C# doesn't have a BigInteger + /// + public sealed class BigDecimal { + private BigInteger _unscaledVal; + private int _scale; + public BigDecimal(BigInteger unscaledVal, + int scale) { + if ( unscaledVal == null ) { + throw new ArgumentNullException(); + } + _unscaledVal = unscaledVal; + _scale = scale; + } + public BigInteger UnscaledValue { + get { + return _unscaledVal; + } + } + public int Scale { + get { + return _scale; + } + } + public override bool Equals(object o) { + BigDecimal other = o as BigDecimal; + if ( other != null ) { + return UnscaledValue.Equals(other.UnscaledValue) && + Scale == other.Scale; + } + return false; + } + public override int GetHashCode() { + return _unscaledVal.GetHashCode(); + } + + public override string ToString() + { + return UnscaledValue.ToString(); + } + } + #endregion + + #region BigInteger + /// + /// Placeholder since C# doesn't have a BigInteger + /// + public sealed class BigInteger { + private string _value; + public BigInteger(string val) { + if ( val == null ) { + throw new ArgumentNullException(); + } + _value = val; + } + public string Value { + get { + return _value; + } + } + public override bool Equals(object o) { + BigInteger other = o as BigInteger; + if ( other != null ) { + return Value.Equals(other.Value); + } + return false; + } + public override int GetHashCode() { + return _value.GetHashCode(); + } + public override string ToString() { + return _value; + } + } + #endregion + + #region ConnectorAttribute + /// + /// Represents a named collection of values within a resource object, + /// although the simplest case is a name-value pair (e.g., email, + /// employeeID). Values can be empty, null, or set with various types. + /// Empty and null are supported because it makes a difference on some + /// resources (in particular database resources). The developer of a + /// Connector will use an builder to construct an instance of + /// ConnectorAttribute. + /// + public class ConnectorAttribute { + private readonly string _name; + private readonly IList _value; + + internal ConnectorAttribute(string name, IList val) { + if (name == null) { + throw new ArgumentException("Name may not be null."); + } + _name = name; + // copy to prevent corruption preserve null + _value = (val == null) ? null : CollectionUtil.NewReadOnlyList(val); + } + + public string Name { + get { + return _name; + } + } + + public IList Value { + get { + return _value; + } + } + + public bool Is(string name) { + return Name.ToUpper().Equals(name.ToUpper()); + } + + public sealed override bool Equals(Object obj) { + // test identity + if (this == obj) { + return true; + } + // test for null.. + if (obj == null) { + return false; + } + // test that the exact class matches + if (!(GetType().Equals(obj.GetType()))) { + return false; + } + // test name field.. + ConnectorAttribute other = (ConnectorAttribute) obj; + if (!_name.ToUpper().Equals(other._name.ToUpper())) { + return false; + } + + if (!CollectionUtil.Equals(_value,other._value)) { + return false; + } + return true; + } + + public sealed override int GetHashCode() { + return _name.ToUpper().GetHashCode(); + } + + + public override string ToString() { + // poor man's consistent toString impl.. + StringBuilder bld = new StringBuilder(); + bld.Append("ConnectorAttribute: "); + IDictionary map = new Dictionary(); + map["Name"] = Name; + map["Value"] = Value; + bld.Append(map.ToString()); + return bld.ToString(); + } + } + #endregion + + #region ConnectorAttributeBuilder + public sealed class ConnectorAttributeBuilder { + private const String NAME_ERROR = "Name must not be blank!"; + + private string _name; + private IList _value; + + public ConnectorAttributeBuilder() { + } + public static ConnectorAttribute Build(String name) { + return new ConnectorAttributeBuilder(){ Name=name }.Build(); + } + public static ConnectorAttribute Build(String name, + params Object [] args) { + ConnectorAttributeBuilder bld = new ConnectorAttributeBuilder(); + bld.Name = name; + bld.AddValue(args); + return bld.Build(); + } + public static ConnectorAttribute Build(String name, + ICollection val) { + ConnectorAttributeBuilder bld = new ConnectorAttributeBuilder(); + bld.Name = name; + bld.AddValue(val); + return bld.Build(); + } + + public string Name { + get { + return _name; + } + set { + if (StringUtil.IsBlank(value)) { + throw new ArgumentException(NAME_ERROR); + } + _name = value; + } + } + + public IList Value { + get { + return _value == null ? null : CollectionUtil.AsReadOnlyList(_value); + } + } + + public void AddValue(params Object [] args) { + AddValuesInternal(args); + } + public void AddValue(ICollection values) { + AddValuesInternal(values); + } + + public ConnectorAttribute Build() { + if (StringUtil.IsBlank(Name)) { + throw new ArgumentException(NAME_ERROR); + } + if (Uid.NAME.Equals(_name)) { + return new Uid(GetSingleStringValue()); + } else if (Org.IdentityConnectors.Framework.Common.Objects.Name.NAME.Equals(_name)) { + return new Name(GetSingleStringValue()); + } else if (OperationalAttributes.PASSWORD_NAME.Equals(_name) || + OperationalAttributes.CURRENT_PASSWORD_NAME.Equals(_name) || + OperationalAttributes.RESET_PASSWORD_NAME.Equals(_name)) { + CheckSingleValue(); + if (!(_value[0] is GuardedString)) { + const string MSG = "Password value must be an instance of GuardedString."; + throw new ArgumentException(MSG); + } + } + return new ConnectorAttribute(Name, _value); + } + private void CheckSingleValue() { + if (_value == null || _value.Count != 1) { + const String MSG = "Must be a single value."; + throw new ArgumentException(MSG); + } + } + private String GetSingleStringValue() { + CheckSingleValue(); + if (!(_value[0] is String)) { + const String MSG = "Must be single string value."; + throw new ArgumentException(MSG); + } + return (String) _value[0]; + } + private void AddValuesInternal(IEnumerable values) { + if (values != null) { + // make sure the list is ready to receive values. + if (_value == null) { + _value = new List(); + } + // add each value checking to make sure its correct + foreach (Object v in values) { + FrameworkUtil.CheckAttributeValue(v); + _value.Add(v); + } + } + } + + // ======================================================================= + // Operational Attributes + // ======================================================================= + /** + * Builds an password expiration date {@link ConnectorAttribute}. This + * {@link ConnectorAttribute} represents the date/time a password will expire on a + * resource. + * + * @param dateTime + * UTC time in milliseconds. + * @return an {@link ConnectorAttribute} built with the pre-defined name for password + * expiration date. + */ + public static ConnectorAttribute BuildPasswordExpirationDate(DateTime dateTime) { + return BuildPasswordExpirationDate(DateTimeUtil.GetUtcTimeMillis(dateTime)); + } + + /** + * Builds an password expiration date {@link ConnectorAttribute}. This + * {@link ConnectorAttribute} represents the date/time a password will expire on a + * resource. + * + * @param dateTime + * UTC time in milliseconds. + * @return an {@link ConnectorAttribute} built with the pre-defined name for password + * expiration date. + */ + public static ConnectorAttribute BuildPasswordExpirationDate(long dateTime) { + return Build(OperationalAttributes.PASSWORD_EXPIRATION_DATE_NAME, + dateTime); + } + + /** + * Builds the operational attribute password. + * + * @param password + * the string that represents a password. + * @return an attribute that represents a password. + */ + public static ConnectorAttribute BuildPassword(GuardedString password) { + return Build(OperationalAttributes.PASSWORD_NAME, password); + } + + /** + * Builds the operational attribute current password. The current password + * indicates this a password change by the account owner and not an + * administrator. The use case is that an administrator password change may + * not keep history or validate against policy. + * + * @param password + * the string that represents a password. + * @return an attribute that represents a password. + */ + public static ConnectorAttribute BuildCurrentPassword(GuardedString password) { + return Build(OperationalAttributes.CURRENT_PASSWORD_NAME, password); + } + /** + * Builds the operational attribute reset password. + * + * @param password + * the string that represents a password. + * @return an attribute that represents a reset password operation. + */ + public static ConnectorAttribute BuildResetPassword(GuardedString password) { + return Build(OperationalAttributes.RESET_PASSWORD_NAME, password); + } + + public static ConnectorAttribute BuildPassword(SecureString password) { + return Build(OperationalAttributes.PASSWORD_NAME, new GuardedString(password)); + } + public static ConnectorAttribute BuildCurrentPassword(SecureString password) { + return Build(OperationalAttributes.CURRENT_PASSWORD_NAME, new GuardedString(password)); + } + public static ConnectorAttribute BuildResetPassword(SecureString password) { + return Build(OperationalAttributes.RESET_PASSWORD_NAME, new GuardedString(password)); + } + /** + * Builds ant operational attribute that either represents the object is + * enabled or sets in disabled depending on where its used for instance on + * {@link CreateApiOp} it could be used to create a disabled account. In + * {@link SearchApiOp} it would show the object is enabled or disabled. + * + * @param value + * true indicates the object is enabled otherwise false. + * @return {@link ConnectorAttribute} that determines the enable/disable state of an + * object. + */ + public static ConnectorAttribute BuildEnabled(bool val) { + return Build(OperationalAttributes.ENABLE_NAME, val); + } + + /** + * Builds out an operational {@link ConnectorAttribute} that determines the enable + * date for an object. + * + * @param date + * The date and time to enable a particular object, or the date + * time an object will be enabled. + * @return {@link ConnectorAttribute} + */ + public static ConnectorAttribute BuildEnableDate(DateTime date) { + return BuildEnableDate(DateTimeUtil.GetUtcTimeMillis(date)); + } + + /** + * Builds out an operational {@link ConnectorAttribute} that determines the enable + * date for an object. The time parameter is UTC in milliseconds. + * + * @param date + * The date and time to enable a particular object, or the date + * time an object will be enabled. + * @return {@link ConnectorAttribute} + */ + public static ConnectorAttribute BuildEnableDate(long date) { + return Build(OperationalAttributes.ENABLE_DATE_NAME, date); + } + + /** + * Builds out an operational {@link ConnectorAttribute} that determines the disable + * date for an object. + * + * @param date + * The date and time to enable a particular object, or the date + * time an object will be enabled. + * @return {@link ConnectorAttribute} + */ + public static ConnectorAttribute BuildDisableDate(DateTime date) { + return BuildDisableDate(DateTimeUtil.GetUtcTimeMillis(date)); + } + + /** + * Builds out an operational {@link ConnectorAttribute} that determines the disable + * date for an object. The time parameter is UTC in milliseconds. + * + * @param date + * The date and time to enable a particular object, or the date + * time an object will be enabled. + * @return {@link ConnectorAttribute} + */ + public static ConnectorAttribute BuildDisableDate(long date) { + return Build(OperationalAttributes.DISABLE_DATE_NAME, date); + } + + /** + * Builds the lock attribute that determines if an object is locked out. + * + * @param lock + * true if the object is locked otherwise false. + * @return {@link ConnectorAttribute} that represents the lock state of an object. + */ + public static ConnectorAttribute BuildLockOut(bool lck) { + return Build(OperationalAttributes.LOCK_OUT_NAME, lck); + } + + /** + * Builds out an operational {@link Attribute} that determines if a password + * is expired or expires a password. + * + * @param value + * from the API true expires and from the SPI its shows its + * either expired or not. + * @return {@link Attribute} + */ + public static ConnectorAttribute BuildPasswordExpired(bool expired) { + return Build(OperationalAttributes.PASSWORD_EXPIRED_NAME, expired); + } + + // ======================================================================= + // Pre-defined Attributes + // ======================================================================= + + /** + * Builds out a pre-defined {@link ConnectorAttribute} that determines the last login + * date for an object. + * + * @param date + * The date and time of the last login. + * @return {@link ConnectorAttribute} + */ + public static ConnectorAttribute BuildLastLoginDate(DateTime date) { + return BuildLastLoginDate(DateTimeUtil.GetUtcTimeMillis(date)); + } + + /** + * Builds out a pre-defined {@link ConnectorAttribute} that determines the last login + * date for an object. The time parameter is UTC in milliseconds. + * + * @param date + * The date and time of the last login. + * @return {@link ConnectorAttribute} + */ + public static ConnectorAttribute BuildLastLoginDate(long date) { + return Build(PredefinedAttributes.LAST_LOGIN_DATE_NAME, date); + } + + /** + * Builds out a pre-defined {@link ConnectorAttribute} that determines the last + * password change date for an object. + * + * @param date + * The date and time the password was changed. + * @return {@link ConnectorAttribute} + */ + public static ConnectorAttribute BuildLastPasswordChangeDate(DateTime date) { + return BuildLastPasswordChangeDate(DateTimeUtil.GetUtcTimeMillis(date)); + } + + /** + * Builds out a pre-defined {@link ConnectorAttribute} that determines the last + * password change date for an object. + * + * @param date + * The date and time the password was changed. + * @return {@link ConnectorAttribute} + */ + public static ConnectorAttribute BuildLastPasswordChangeDate(long date) { + return Build(PredefinedAttributes.LAST_PASSWORD_CHANGE_DATE_NAME, date); + } + + /** + * Common password policy attribute where the password must be changed every + * so often. The value for this attribute is milliseconds since its the + * lowest common denominator. + */ + public static ConnectorAttribute BuildPasswordChangeInterval(long val) { + return Build(PredefinedAttributes.PASSWORD_CHANGE_INTERVAL_NAME, val); + } + } + #endregion + + #region ConnectorMessages + /** + * Message catalog for a given connector. + */ + public interface ConnectorMessages { + /** + * Formats the given message key. + * @param locale The locale to format in. If null, uses the default locale. + * @param key The message key to format. + * @param dflt The default message if key is not found. If null, defaults + * to key. + * @param args Parameters with which to format the message. + * @return The formatted string. + */ + String Format(CultureInfo locale, String key, String dflt, params object [] args); + } + #endregion + + #region ConnectorObject + public sealed class ConnectorObject { + private readonly ObjectClass _objectClass; + private readonly IDictionary _attrs; + public ConnectorObject(ObjectClass objectClass, ICollection attrs) { + if (objectClass == null) { + throw new ArgumentException("ObjectClass may not be null"); + } + if ( attrs == null || attrs.Count == 0 ) { + throw new ArgumentException("attrs cannot be empty or null."); + } + _objectClass = objectClass; + _attrs = + CollectionUtil.NewReadOnlyDictionary(attrs, + value => { return value.Name;}); + if (!_attrs.ContainsKey(Uid.NAME)) { + const String MSG = "The ConnectorAttribute set must contain a Uid."; + throw new ArgumentException(MSG); + } + if (!_attrs.ContainsKey(Name.NAME)) { + const string MSG = "The ConnectorAttribute set must contain a Name."; + throw new ArgumentException(MSG); + } + } + public ICollection GetAttributes() { + return _attrs.Values; + } + public ConnectorAttribute GetAttributeByName(string name) { + return CollectionUtil.GetValue(_attrs,name,null); + } + public Uid Uid { + get { + return (Uid)GetAttributeByName(Uid.NAME); + } + } + public Name Name { + get { + return (Name)GetAttributeByName(Name.NAME); + } + } + public ObjectClass ObjectClass { + get { + return _objectClass; + } + } + public override int GetHashCode() { + return CollectionUtil.HashCode(_attrs,null); + } + public override bool Equals(Object o) { + ConnectorObject other = o as ConnectorObject; + if ( other != null ) { + if (!_objectClass.Equals(other.ObjectClass)) { + return false; + } + return CollectionUtil.Equals(_attrs,other._attrs); + } + return false; + } + } + #endregion + + #region ConnectorObjectBuilder + public sealed class ConnectorObjectBuilder { + private IDictionary _attributes; + public ConnectorObjectBuilder() { + _attributes = new Dictionary(); + // default always add the account object class.. + ObjectClass = ObjectClass.ACCOUNT; + } + + public void SetUid(string uid) { + AddAttribute(new Uid(uid)); + } + + public void SetUid(Uid uid) { + AddAttribute(uid); + } + + public void SetName(string name) { + AddAttribute(new Name(name)); + } + + public void SetName(Name name) { + AddAttribute(name); + } + + public ObjectClass ObjectClass { get; set; } + + // ======================================================================= + // Clone basically.. + // ======================================================================= + /** + * Takes all the attribute from a {@link ConnectorObject} and add/overwrite + * the current attributes. + */ + public void Add(ConnectorObject obj) { + // simply add all the attributes it will include (Uid, ObjectClass..) + foreach (ConnectorAttribute attr in obj.GetAttributes()) { + AddAttribute(attr); + } + ObjectClass = obj.ObjectClass; + } + + public void AddAttribute(params ConnectorAttribute [] attrs) { + ValidateParameter(attrs, "attrs"); + foreach (ConnectorAttribute a in attrs) { + //DONT use Add - it throws exceptions if already there + _attributes[a.Name] = a; + } + } + public void AddAttributes(ICollection attrs) { + ValidateParameter(attrs, "attrs"); + foreach (ConnectorAttribute a in attrs) { + _attributes[a.Name] = a; + } + } + /** + * Adds values to the attribute. + */ + public void AddAttribute(String name, params object [] objs) { + AddAttribute(ConnectorAttributeBuilder.Build(name, objs)); + } + + /** + * Adds each object in the collection. + */ + public void AddAttribute(String name, ICollection obj) { + AddAttribute(ConnectorAttributeBuilder.Build(name, obj)); + } + public ConnectorObject Build() { + // check that there are attributes to return.. + if (_attributes.Count == 0) { + throw new InvalidOperationException("No attributes set!"); + } + return new ConnectorObject(ObjectClass,_attributes.Values); + } + private static void ValidateParameter(Object param, String paramName) { + if (param == null) { + String FORMAT = "Parameter "+param+" must not be null!"; + throw new NullReferenceException(FORMAT); + } + } + } + #endregion + + #region ConnectorAttributeInfo + public sealed class ConnectorAttributeInfo { + private readonly string _name; + private readonly Type _type; + private readonly bool _required; + private readonly bool _readable; + private readonly bool _writeable; + private readonly bool _multivalue; + private readonly bool _returnedByDefault; + + public ConnectorAttributeInfo(string name, Type type, + bool readable, bool writeable, + bool required, bool multivalue, + bool returnedByDefault) { + _name = name; + _type = type; + _readable = readable; + _writeable = writeable; + _required = required; + _multivalue = multivalue; + _returnedByDefault = returnedByDefault; + } + + + /** + * The native name of the attribute. + * + * @return the native name of the attribute its describing. + */ + public string Name { + get { + return _name; + } + } + + /** + * The basic type associated with this attribute. All primitives are + * supported. + * + * @return the native type if uses. + */ + public Type ValueType { + get { + return _type; + } + } + + public bool Is(string name) { + return Name.ToUpper().Equals(name.ToUpper()); + } + + /** + * Determines if the attribute is readable. + * + * @return true if the attribute is readable else false. + */ + public bool IsReadable { + get { + return _readable; + } + } + + /** + * Determines if the attribute is writable. + * + * @return true if the attribute is writable else false. + */ + public bool IsWritable { + get { + return _writeable; + } + } + + /** + * Determines whether this attribute is required for creates. + * + * @return true if the attribute is required for an object else false. + */ + public bool IsRequired { + get { + return _required; + } + } + + /** + * Determines if this attribute can handle multiple values. There is a + * special case with byte[] since in most instances this denotes a single + * object. + * + * @return true if the attribute is multi-value otherwise false. + */ + public bool IsMultiValue { + get { + return _multivalue; + } + } + + /** + * Determines if the attribute is returned by default. Indicates if an + * {@link ConnectorAttribute} will be returned during {@link SearchApiOp} or + * {@link GetApiOp} inside a {@link ConnectorObject} by default. The default + * value is true. + * + * @return false iff the attribute should not be returned by default. + */ + public bool IsReturnedByDefault { + get { + return _returnedByDefault; + } + } + + public override bool Equals(Object o) { + ConnectorAttributeInfo other = o as ConnectorAttributeInfo; + if ( other != null ) { + if (!Name.ToUpper().Equals(other.Name.ToUpper())) { + return false; + } + if (!ValueType.Equals(other.ValueType)) { + return false; + } + if (IsReadable != other.IsReadable) { + return false; + } + if (IsWritable != other.IsWritable) { + return false; + } + if (IsRequired != other.IsRequired) { + return false; + } + if (IsMultiValue != other.IsMultiValue) { + return false; + } + if (IsReturnedByDefault != other.IsReturnedByDefault) { + return false; + } + return true; + } + return false; + } + + public override int GetHashCode() { + return _name.ToUpper().GetHashCode(); + } + + public override string ToString() { + IDictionary map = new Dictionary(); + map["Name"] = Name; + map["Type"] = ValueType; + map["Required"] = IsRequired; + map["Readable"] = IsReadable; + map["Writeable"] = IsWritable; + map["MultiValue"] = IsMultiValue; + map["ReturnedByDefault"] = IsReturnedByDefault; + return map.ToString(); + } + } + #endregion + + #region ConnectorAttributeInfoBuilder + public sealed class ConnectorAttributeInfoBuilder { + public string Name { get; set; } + /// + /// Determines the type for the attribute. Please see + /// {@link FrameworkUtil#checkAttributeType(Class)} for + /// more information. + /// + public Type ValueType { get; set; } + public bool Readable { get; set; } + public bool Writeable { get; set; } + public bool Required { get; set; } + public bool MultiValue { get; set; } + public bool ReturnedByDefault { get; set; } + + public ConnectorAttributeInfoBuilder() { + Name = null; + Readable = true; + Writeable = true; + Required = false; + MultiValue = false; + ValueType = typeof(string); + ReturnedByDefault = true; + } + + public ConnectorAttributeInfo Build() { + if (StringUtil.IsBlank(Name)) { + throw new InvalidOperationException("Name must not be blank!"); + } + if ((OperationalAttributes.PASSWORD_NAME.Equals(Name) || + OperationalAttributes.RESET_PASSWORD_NAME.Equals(Name) || + OperationalAttributes.CURRENT_PASSWORD_NAME.Equals(Name)) && + !typeof(GuardedString).Equals(ValueType)) { + string MSG = "Password based attributes must be of type GuardedString."; + throw new ArgumentException(MSG); + } + FrameworkUtil.CheckAttributeType(ValueType); + return new ConnectorAttributeInfo(Name, + ValueType, + Readable, + Writeable, + Required, + MultiValue, + ReturnedByDefault); + } + public static ConnectorAttributeInfo Build(String name) { + return new ConnectorAttributeInfoBuilder() { + Name = name + }.Build(); + } + public static ConnectorAttributeInfo Build(String name, Type type) { + return new ConnectorAttributeInfoBuilder() { + Name = name, + ValueType = type + }.Build(); + } + public static ConnectorAttributeInfo Build(String name, Type type, bool required) { + return new ConnectorAttributeInfoBuilder() { + Name = name, + ValueType = type, + Required = required + }.Build(); + } + public static ConnectorAttributeInfo Build( String name, + bool required, bool readable, bool writeable) { + ConnectorAttributeInfoBuilder bld = new ConnectorAttributeInfoBuilder(); + bld.Name = name; + bld.Required = required; + bld.Readable = readable; + bld.Writeable = writeable; + return bld.Build(); + } + + public static ConnectorAttributeInfo Build( String name, Type type, + bool required, bool readable, bool writeable) { + ConnectorAttributeInfoBuilder bld = new ConnectorAttributeInfoBuilder(); + bld.Name = name; + bld.ValueType = type; + bld.Required = required; + bld.Readable = readable; + bld.Writeable = writeable; + return bld.Build(); + } + } + #endregion + + #region FileName + /// + /// Placeholder for java.io.File since C#'s + /// FileInfo class throws exceptions if the + /// file doesn't exist. + /// + public sealed class FileName { + private string _path; + public FileName(string path) { + if ( path == null ) { + throw new ArgumentNullException(); + } + _path = path; + } + public string Path { + get { + return _path; + } + } + public override bool Equals(object o) { + FileName other = o as FileName; + if ( other != null ) { + return Path.Equals(other.Path); + } + return false; + } + public override int GetHashCode() { + return _path.GetHashCode(); + } + public override string ToString() { + return _path; + } + } + #endregion + + #region Name + public sealed class Name : ConnectorAttribute { + + public readonly static string NAME = ConnectorAttributeUtil.CreateSpecialName("NAME"); + + public Name(String value) : base(NAME, CollectionUtil.NewReadOnlyList(value)) { + } + + /** + * The single value of the attribute that is the unique id of an object. + * + * @return value that identifies an object. + */ + public String GetNameValue() { + return ConnectorAttributeUtil.GetStringValue(this); + } + } + #endregion + + #region ObjectClass + public sealed class ObjectClass { + public const String ACCOUNT_NAME = "account"; + public const String PERSON_NAME = "person"; + public const String GROUP_NAME = "group"; + public const String ORGANIZATION_NAME = "organization"; + /** + * Denotes an account based object. + */ + public static readonly ObjectClass ACCOUNT = new ObjectClass(ACCOUNT_NAME); + /** + * Denotes a person based object. + */ + public static readonly ObjectClass PERSON = new ObjectClass(PERSON_NAME); + /** + * Denotes a group based object. + */ + public static readonly ObjectClass GROUP = new ObjectClass(GROUP_NAME); + /** + * Denotes a organization based object. + */ + public static readonly ObjectClass ORGANIZATION = new ObjectClass(ORGANIZATION_NAME); + + private readonly String _type; + + public ObjectClass(String type) { + if ( type == null ) { + throw new ArgumentException("Type cannot be null."); + } + _type = type; + } + public String GetObjectClassValue() { + return _type; + } + + public override int GetHashCode() { + return _type.GetHashCode(); + } + + public override bool Equals(object o) { + if ( o is ObjectClass ) { + ObjectClass other = (ObjectClass)o; + return _type.Equals(other._type); + } + return false; + } + + public override string ToString() + { + return "ObjectClass: " + _type; + } + } + #endregion + + #region ObjectClassInfo + public sealed class ObjectClassInfo { + + private readonly String _type; + private readonly ICollection _info; + + public ObjectClassInfo(String type, + ICollection attrInfo) { + _type = type; + _info = CollectionUtil.NewReadOnlySet(attrInfo); + // check to make sure name exists + IDictionary dict + = ConnectorAttributeInfoUtil.ToMap(attrInfo); + if (!dict.ContainsKey(Name.NAME)) { + const string MSG = "Missing 'Name' connector attribute info."; + throw new ArgumentException(MSG); + } + } + + public ICollection ConnectorAttributeInfos { + get { + return this._info; + } + } + + public String ObjectType { + get { + return this._type; + } + } + + public override int GetHashCode() { + return _type.GetHashCode(); + } + + public override bool Equals(Object o) { + ObjectClassInfo other = o as ObjectClassInfo; + if ( other != null ) { + if (!ObjectType.Equals(other.ObjectType)) { + return false; + } + if (!CollectionUtil.Equals(ConnectorAttributeInfos, + other.ConnectorAttributeInfos)) { + return false; + } + return true; + } + return false; + } + + public override string ToString() + { + IDictionary map = new Dictionary(); + map["Type"] = _type; + map["ConnectorAttributes"] = _info; + return map.ToString(); + } + } + #endregion + + #region ObjectClassInfoBuilder + /** + * Used to help facilitate the building of {@link ObjectClassInfo} objects. + */ + public sealed class ObjectClassInfoBuilder { + + private IDictionary _info; + + public ObjectClassInfoBuilder() { + _info = new Dictionary(); + ObjectType = ObjectClass.ACCOUNT_NAME; + } + + public string ObjectType { get; set; } + + /** + * Add each {@link AttributeInfo} object to the {@link ObjectClassInfo}. + */ + public void AddAttributeInfo(ConnectorAttributeInfo info) { + if (_info.ContainsKey(info.Name)) { + const string MSG = "ConnectorAttributeInfo of name ''{0}'' already exists!"; + throw new ArgumentException(String.Format(MSG, info.Name)); + } + _info[info.Name] = info; + } + + public void AddAllAttributeInfo(ICollection info) { + foreach (ConnectorAttributeInfo cainfo in info) { + AddAttributeInfo(cainfo); + } + } + + public ObjectClassInfo Build() { + // determine if name is missing and add it by default + if (!_info.ContainsKey(Name.NAME)) { + ConnectorAttributeInfo info = + ConnectorAttributeInfoBuilder.Build( + Name.NAME, typeof(string), true); + _info[info.Name] = info; + } + return new ObjectClassInfo(ObjectType, _info.Values); + } + } + #endregion + + #region OperationalAttributeInfos + /** + * {@link AttributeInfo} for each operational attribute. + */ + public static class OperationalAttributeInfos { + /** + * Gets/sets the enable status of an object. + */ + public static readonly ConnectorAttributeInfo ENABLE = + ConnectorAttributeInfoBuilder.Build( + OperationalAttributes.ENABLE_NAME, typeof(bool)); + + /** + * Gets/sets the enable date for an object. + */ + public static readonly ConnectorAttributeInfo ENABLE_DATE = + ConnectorAttributeInfoBuilder.Build( + OperationalAttributes.ENABLE_DATE_NAME, typeof(long)); + + /** + * Gets/sets the disable date for an object. + */ + public static readonly ConnectorAttributeInfo DISABLE_DATE = + ConnectorAttributeInfoBuilder.Build( + OperationalAttributes.DISABLE_DATE_NAME, typeof(long)); + + /** + * Gets/sets the lock out attribute for an object. + */ + public static readonly ConnectorAttributeInfo LOCK_OUT = + ConnectorAttributeInfoBuilder.Build( + OperationalAttributes.LOCK_OUT_NAME, typeof(bool)); + + /** + * Gets/sets the password expiration date for an object. + */ + public static readonly ConnectorAttributeInfo PASSWORD_EXPIRATION_DATE = + ConnectorAttributeInfoBuilder.Build( + OperationalAttributes.PASSWORD_EXPIRATION_DATE_NAME, typeof(long)); + + /** + * Normally this is a write-only attribute. Sets the password for an object. + */ + public static readonly ConnectorAttributeInfo PASSWORD = + ConnectorAttributeInfoBuilder.Build( + OperationalAttributes.PASSWORD_NAME, typeof(GuardedString), + true, false, true); + + /** + * Used in conjunction with password to do an account level password change. + * This is for a non-administrator change of the password and therefore + * requires the current password. + */ + public static readonly ConnectorAttributeInfo CURRENT_PASSWORD = + ConnectorAttributeInfoBuilder.Build( + OperationalAttributes.CURRENT_PASSWORD_NAME, typeof(GuardedString), + false, false, true); + + /** + * Used to do an administrator reset of the password. The value is the reset + * password value. + */ + public static readonly ConnectorAttributeInfo RESET_PASSWORD = + ConnectorAttributeInfoBuilder.Build( + OperationalAttributes.RESET_PASSWORD_NAME, typeof(GuardedString), + false, false, true); + + /** + * Used to determine if a password is expired or to expire a password. + */ + public static readonly ConnectorAttributeInfo PASSWORD_EXPIRED = + ConnectorAttributeInfoBuilder.Build( + OperationalAttributes.PASSWORD_EXPIRED_NAME, typeof(bool)); + + } + #endregion + + #region OperationalAttributes + /** + * Operational attributes have special meaning and cannot be represented by pure + * operations. For instance some administrators would like to create an account + * in the disabled state. The do not want this to be a two operation process + * since this can leave the door open to abuse. Therefore special attributes + * that can perform operations were introduced. The + * {@link OperationalAttributes#DISABLED} attribute could be added to the set of + * attribute sent to a Connector for the {@link CreateOp} operation. To tell the + * {@link Connector} to create the account with it in the disabled state whether + * the target resource itself has an attribute or an additional method must be + * called. + */ + public static class OperationalAttributes { + /** + * Gets/sets the enable status of an object. + */ + public static readonly string ENABLE_NAME = ConnectorAttributeUtil.CreateSpecialName("ENABLE"); + /** + * Gets/sets the enable date for an object. + */ + public static readonly string ENABLE_DATE_NAME = ConnectorAttributeUtil.CreateSpecialName("ENABLE_DATE"); + /** + * Gets/sets the disable date for an object. + */ + public static readonly string DISABLE_DATE_NAME = ConnectorAttributeUtil.CreateSpecialName("DISABLE_DATE"); + /** + * Gets/sets the lock out attribute for an object. + */ + public static readonly string LOCK_OUT_NAME = ConnectorAttributeUtil.CreateSpecialName("LOCK_OUT"); + /** + * Gets/sets the password expiration date for an object. + */ + public static readonly string PASSWORD_EXPIRATION_DATE_NAME = ConnectorAttributeUtil.CreateSpecialName("PASSWORD_EXPIRATION_DATE"); + /** + * Gets/sets the password expired for an object. + */ + public static readonly string PASSWORD_EXPIRED_NAME = ConnectorAttributeUtil.CreateSpecialName("PASSWORD_EXPIRED"); + /** + * Normally this is a write-only attribute. Sets the password for an object. + */ + public static readonly string PASSWORD_NAME = ConnectorAttributeUtil.CreateSpecialName("PASSWORD"); + /** + * Used in conjunction with password to do an account level password change. + * This is for a non-administrator change of the password and therefore + * requires the current password. + */ + public static readonly string CURRENT_PASSWORD_NAME = ConnectorAttributeUtil.CreateSpecialName("CURRENT_PASSWORD"); + /** + * Used to do an administrator reset of the password. The value is the reset + * password value. + */ + public static readonly string RESET_PASSWORD_NAME = ConnectorAttributeUtil.CreateSpecialName("RESET_PASSWORD"); + + // ======================================================================= + // Helper Methods.. + // ======================================================================= + public static readonly ICollection OPERATIONAL_ATTRIBUTE_NAMES = + CollectionUtil.NewReadOnlySet ( + LOCK_OUT_NAME, + ENABLE_NAME, + ENABLE_DATE_NAME, + DISABLE_DATE_NAME, + PASSWORD_EXPIRATION_DATE_NAME, + PASSWORD_NAME, + CURRENT_PASSWORD_NAME, + RESET_PASSWORD_NAME, + PASSWORD_EXPIRED_NAME + ); + + public static ICollection GetOperationalAttributeNames() { + return CollectionUtil.NewReadOnlySet(OPERATIONAL_ATTRIBUTE_NAMES); + } + public static bool IsOperationalAttribute(ConnectorAttribute attr) { + string name = (attr != null) ? attr.Name : null; + return OPERATIONAL_ATTRIBUTE_NAMES.Contains(name); + } + } + #endregion + + #region PredefinedAttributes + /** + * List of well known or pre-defined attributes. Common attributes that most + * resources have that are not operational in nature. + */ + public static class PredefinedAttributes { + /** + * Read-only attribute that shows the last date/time the password was + * changed. + */ + public static readonly string LAST_PASSWORD_CHANGE_DATE_NAME = ConnectorAttributeUtil.CreateSpecialName("LAST_PASSWORD_CHANGE_DATE"); + + /** + * Common password policy attribute where the password must be changed every + * so often. The value for this attribute is milliseconds since its the + * lowest common denominator. + */ + public static readonly string PASSWORD_CHANGE_INTERVAL_NAME = ConnectorAttributeUtil.CreateSpecialName("PASSWORD_CHANGE_INTERVAL"); + + /** + * Last login date for an account. This is usually used to determine inactivity. + */ + public static readonly string LAST_LOGIN_DATE_NAME = ConnectorAttributeUtil.CreateSpecialName("LAST_LOGIN_DATE"); + + /** + * Groups an account object belongs to. + */ + public static readonly string GROUPS_NAME = ConnectorAttributeUtil.CreateSpecialName("GROUPS"); + + /** + * Accounts that belong to a group or organization. + */ + public static readonly string ACCOUNTS_NAME = ConnectorAttributeUtil.CreateSpecialName("ACCOUNTS"); + + /** + * An organization that that an account/person belongs to. + */ + public static readonly string ORGANIZATION_NAME = ConnectorAttributeUtil.CreateSpecialName("ORGANIZATION"); + } + #endregion + + #region PredefinedAttributeInfos + public static class PredefinedAttributeInfos { + /** + * Read-only attribute that shows the last date/time the password was + * changed. + */ + public static readonly ConnectorAttributeInfo LAST_PASSWORD_CHANGE_DATE = + ConnectorAttributeInfoBuilder.Build( + PredefinedAttributes.LAST_PASSWORD_CHANGE_DATE_NAME, typeof(long), false, true, false); + + /** + * Common password policy attribute where the password must be changed every + * so often. The value for this attribute is milliseconds since its the + * lowest common denominator. + */ + public static readonly ConnectorAttributeInfo PASSWORD_CHANGE_INTERVAL = + ConnectorAttributeInfoBuilder.Build( + PredefinedAttributes.PASSWORD_CHANGE_INTERVAL_NAME, typeof(long)); + + /** + * Last login date for an account. This is usually used to determine + * inactivity. + */ + public static readonly ConnectorAttributeInfo LAST_LOGIN_DATE = + ConnectorAttributeInfoBuilder.Build( + PredefinedAttributes.LAST_LOGIN_DATE_NAME, typeof(long), false, true, false); + + static PredefinedAttributeInfos() { + // define GROUPS attribute info + ConnectorAttributeInfoBuilder bld = new ConnectorAttributeInfoBuilder(); + bld.Name = PredefinedAttributes.GROUPS_NAME; + bld.MultiValue = true; + bld.ReturnedByDefault = false; + GROUPS = bld.Build(); + // define ACCOUNTS attribute info + bld = new ConnectorAttributeInfoBuilder(); + bld.Name = PredefinedAttributes.ACCOUNTS_NAME; + bld.MultiValue = true; + bld.ReturnedByDefault = false; + ACCOUNTS = bld.Build(); + // define ORGANIZATION + bld = new ConnectorAttributeInfoBuilder(); + bld.Name = PredefinedAttributes.ORGANIZATION_NAME; + bld.ReturnedByDefault = false; + ORGANIZATIONS = bld.Build(); + } + + /** + * Groups that an account or person belong to. The Attribute values are the + * UID value of each group that an account has membership in. + */ + public static readonly ConnectorAttributeInfo GROUPS; + + /** + * Accounts that are members of a group or organization. The Attribute + * values are the UID value of each account the has a group or organization + * membership. + */ + public static readonly ConnectorAttributeInfo ACCOUNTS; + + /** + * Organizations that an account or person is a member of. The Attribute + * values are the UID value of each organization that an account or person is + * a member of. + */ + public static readonly ConnectorAttributeInfo ORGANIZATIONS; + } + #endregion + + #region OperationOptions + /** + * Arbitrary options to be passed into various operations. This serves + * as a catch-all for extra options. + */ + public sealed class OperationOptions { + /** + * An option to use with {@link ScriptOnResourceApiOp} and possibly others + * that specifies an account under which to execute the script/operation. + * The specified account will appear to have performed any action that the + * script/operation performs. + *

+ * Check the javadoc for a particular connector to see whether that + * connector supports this option. + */ + public static readonly string OP_RUN_AS_USER = "RUN_AS_USER"; + + /** + * An option to use with {@link ScriptOnResourceApiOp} and possibly others + * that specifies a password under which to execute the script/operation. + */ + public static readonly string OP_RUN_WITH_PASSWORD = "RUN_WITH_PASSWORD"; + + /** + * Determines the attributes to retrieve during {@link SearchApiOp} and + * {@link SyncApiOp}. + */ + public static readonly string OP_ATTRIBUTES_TO_GET = "ATTRS_TO_GET"; + + private readonly IDictionary _operationOptions; + + /** + * Public only for serialization; please use {@link OperationOptionsBuilder}. + * @param operationOptions The options. + */ + public OperationOptions(IDictionary operationOptions) { + foreach (Object val in operationOptions.Values) { + FrameworkUtil.CheckOperationOptionValue(val); + } + //clone options to do a deep copy in case anything + //is an array + IDictionary operationOptionsClone = (IDictionary)SerializerUtil.CloneObject(operationOptions); + _operationOptions = CollectionUtil.NewReadOnlyDictionary(operationOptionsClone); + } + + /** + * Returns a map of options. Each value in the map + * must be of a type that the framework can serialize. + * See {@link ObjectSerializerFactory} for a list of supported types. + * + * @return A map of options. + */ + public IDictionary Options { + get { + return _operationOptions; + } + } + + /** + * Get the string array of attribute names to return in the object. + */ + public string[] AttributesToGet { + get { + return (string[]) CollectionUtil.GetValue( + _operationOptions, OP_ATTRIBUTES_TO_GET, null); + } + } + + /** + * Get the account to run the operation as.. + */ + public string RunAsUser { + get { + return (string) CollectionUtil.GetValue( + _operationOptions, OP_RUN_AS_USER, null); + } + } + + /** + * Get the password to run the operation as.. + */ + public string RunWithPassword { + get { + return (string) CollectionUtil.GetValue( + _operationOptions, OP_RUN_WITH_PASSWORD, null); + } + } + + public override string ToString() + { + StringBuilder bld = new StringBuilder(); + bld.Append("OperationOptions: ").Append(Options); + return bld.ToString(); + } + } + #endregion + + #region OperationOptionsBuilder + /** + * Builder for {@link OperationOptions}. + */ + public sealed class OperationOptionsBuilder { + private readonly IDictionary _options = new + Dictionary(); + + /** + * Create a builder with an empty set of options. + */ + public OperationOptionsBuilder() { + + } + + /** + * Sets a given option and a value for that option. + * @param name The name of the option + * @param value The value of the option. Must be one of the types that + * we can serialize. + * See {@link ObjectSerializerFactory} for a list of supported types. + */ + public void SetOption(String name, Object value) { + if ( name == null ) { + throw new ArgumentException("Argument 'value' cannot be null."); + } + //don't validate value here - we do that implicitly when + //we clone in the constructor of OperationOptions + _options[name] = value; + } + + /** + * Returns a mutable reference of the options map. + * @return A mutable reference of the options map. + */ + public IDictionary Options { + get { + //might as well be mutable since it's the builder and + //we don't want to deep copy anyway + return _options; + } + } + + /** + * Creates the OperationOptions. + * @return The newly-created OperationOptions + */ + public OperationOptions Build() { + return new OperationOptions(_options); + } + + /** + * Sets the {@link OperationOptions#OP_ATTRIBUTES_TO_GET} option. + * + * @param attrNames + * list of {@link Attribute} names. + */ + public string[] AttributesToGet { + set { + Assertions.NullCheck(value, "AttributesToGet"); + // don't validate value here - we do that in + // the constructor of OperationOptions - that's + // really the only place we can truly enforce this + _options[OperationOptions.OP_ATTRIBUTES_TO_GET] = value; + } + } + + /** + * Set the run with password option. + */ + public string RunWithPassword { + set { + Assertions.NullCheck(value, "RunWithPassword"); + _options[OperationOptions.OP_RUN_WITH_PASSWORD] = value; + } + } + + /** + * Set the run as user option. + */ + public string RunAsUser { + set { + Assertions.NullCheck(value, "RunAsUser"); + _options[OperationOptions.OP_RUN_AS_USER] = value; + } + } + + } + #endregion + + #region OperationOptionInfo + public sealed class OperationOptionInfo { + private String _name; + private Type _type; + + public OperationOptionInfo(String name, + Type type) { + Assertions.NullCheck(name, "name"); + Assertions.NullCheck(type, "type"); + FrameworkUtil.CheckOperationOptionType(type); + _name = name; + _type = type; + } + + public String Name { + get { + return _name; + } + } + + public Type OptionType { + get { + return _type; + } + } + + public override bool Equals(Object o) { + if (o is OperationOptionInfo) { + OperationOptionInfo other = + (OperationOptionInfo)o; + if (!_name.Equals(other._name)) { + return false; + } + if (!_type.Equals(other._type)) { + return false; + } + return true; + } + return false; + } + + public override int GetHashCode() { + return _name.GetHashCode(); + } + + public override string ToString() + { + StringBuilder bld = new StringBuilder(); + bld.Append("OperationOptionInfo("); + bld.Append(_name); + bld.Append(_type.ToString()); + bld.Append(')'); + return bld.ToString(); + } + + } + #endregion + + #region OperationOptionInfoBuilder + public sealed class OperationOptionInfoBuilder { + private String _name; + private Type _type; + + public OperationOptionInfoBuilder() { + } + + public OperationOptionInfoBuilder(String name, + Type type) { + _name = name; + _type = type; + } + + public String Name { + get { + return _name; + } + set { + _name = value; + } + } + + public Type OptionType { + get { + return _type; + } + set { + _type = value; + } + } + + public OperationOptionInfo Build() { + return new OperationOptionInfo(_name,_type); + } + + public static OperationOptionInfo Build(String name, Type type) { + return new OperationOptionInfoBuilder(name,type).Build(); + } + + public static OperationOptionInfo Build(String name) { + return Build(name, typeof(string)); + } + + public static OperationOptionInfo BuildAttributesToGet() { + return Build(OperationOptions.OP_ATTRIBUTES_TO_GET, typeof(string[])); + } + + public static OperationOptionInfo BuildRunWithPassword() { + return Build(OperationOptions.OP_RUN_WITH_PASSWORD); + } + + public static OperationOptionInfo BuildRunAsUser() { + return Build(OperationOptions.OP_RUN_AS_USER); + } + } + #endregion + + #region ResultsHandler + /** + * Encapsulate the handling of each object returned by the search. + */ + public delegate bool ResultsHandler(ConnectorObject obj); + #endregion + + #region Schema + /** + * Determines the objects supported by a + * {@link com.sun.openconnectors.framework.spi.Connector}. + * The {@link Schema} object is used to represent the basic objects that a + * connector supports. This does not prevent a connector from supporting more. + * Rather, this is informational for the caller of the connector to understand + * a minimum support level. + * The schema defines 4 primary data structures + *

    + *
  1. Declared ObjectClasses ({@link #getObjectClassInfo()}).
  2. + *
  3. Declared OperationOptionInfo ({@link #getOperationOptionInfo()}).
  4. + *
  5. Supported ObjectClasses by operation ({@link #getSupportedObjectClassesByOperation()}).
  6. + *
  7. Supported OperationOptionInfo by operation({@link #getSupportedOptionsByOperation()()}).
  8. + *
+ * + * TODO: add more to describe and what is expected from this call and how it is + * used.. based on OperationalAttribute etc.. + */ + public sealed class Schema { + private readonly ICollection _declaredObjectClasses; + private readonly ICollection _declaredOperationOptions; + private readonly IDictionary> + _supportedObjectClassesByOperation; + private readonly IDictionary> + _supportedOptionsByOperation; + + /** + * Public only for serialization; please use + * SchemaBuilder instead. + * @param info + * @param supportedObjectClassesByOperation + */ + public Schema(ICollection info, + ICollection options, + IDictionary> supportedObjectClassesByOperation, + IDictionary> supportedOptionsByOperation) { + _declaredObjectClasses = CollectionUtil.NewReadOnlySet(info); + _declaredOperationOptions = CollectionUtil.NewReadOnlySet(options); + + //make read-only + { + IDictionary> temp = + new Dictionary>(); + foreach (KeyValuePair> entry in + supportedObjectClassesByOperation) { + Type op = + entry.Key; + ICollection resolvedClasses = + CollectionUtil.NewReadOnlySet(entry.Value); + temp[op] = resolvedClasses; + } + _supportedObjectClassesByOperation = CollectionUtil.AsReadOnlyDictionary(temp); + } + //make read-only + { + IDictionary> temp = + new Dictionary>(); + foreach (KeyValuePair> entry in + supportedOptionsByOperation) { + Type op = + entry.Key; + ICollection resolvedClasses = + CollectionUtil.NewReadOnlySet(entry.Value); + temp[op] = resolvedClasses; + } + _supportedOptionsByOperation = CollectionUtil.AsReadOnlyDictionary(temp); + } + } + + /** + * Returns the set of object classes that are defined in the schema, regardless + * of which operations support them. + */ + public ICollection ObjectClassInfo { + get { + return _declaredObjectClasses; + } + } + + /** + * Returns the ObjectClassInfo for the given type. + * @param type The type to find. + * @return the ObjectClassInfo for the given type or null if not found. + */ + public ObjectClassInfo FindObjectClassInfo(String type) { + foreach (ObjectClassInfo info in _declaredObjectClasses) { + if ( info.ObjectType.Equals(type) ) { + return info; + } + } + return null; + } + + /** + * Returns the set of operation options that are defined in the schema, regardless + * of which operations support them. + * @return The options defined in this schema. + */ + public ICollection OperationOptionInfo { + get { + return _declaredOperationOptions; + } + } + + /** + * Returns the OperationOptionInfo for the given name. + * @param name The name to find. + * @return the OperationOptionInfo for the given name or null if not found. + */ + public OperationOptionInfo FindOperationOptionInfo(String name) { + Assertions.NullCheck(name, "name"); + foreach (OperationOptionInfo info in _declaredOperationOptions) { + if ( info.Name.Equals(name) ) { + return info; + } + } + return null; + } + + /** + * Returns the supported object classes for the given operation. + * @param apiop The operation. + * @return the supported object classes for the given operation. + */ + public ICollection GetSupportedObjectClassesByOperation(Type apiop) { + ICollection rv = + CollectionUtil.GetValue(_supportedObjectClassesByOperation,apiop,null); + if ( rv == null ) { + ICollection empty = + CollectionUtil.NewReadOnlySet(); + + return empty; + } + else { + return rv; + } + } + + /** + * Returns the supported options for the given operation. + * @param apiop The operation. + * @return the supported options for the given operation. + */ + public ICollection GetSupportedOptionsByOperation(Type apiop) { + ICollection rv = + CollectionUtil.GetValue(_supportedOptionsByOperation,apiop,null); + if ( rv == null ) { + ICollection empty = + CollectionUtil.NewReadOnlySet(); + return empty; + } + else { + return rv; + } + } + + /** + * Returns the set of object classes that apply to a particular operation. + * @return the set of object classes that apply to a particular operation. + */ + public IDictionary> SupportedObjectClassesByOperation { + get { + return _supportedObjectClassesByOperation; + } + } + /** + * Returns the set of operation options that apply to a particular operation. + * @return the set of operation options that apply to a particular operation. + */ + public IDictionary> SupportedOptionsByOperation { + get { + return _supportedOptionsByOperation; + } + } + + + + public override int GetHashCode() { + return CollectionUtil.HashCode(_declaredObjectClasses,null); + } + + public override bool Equals(object o) { + Schema other = o as Schema; + if ( other != null ) { + if (!CollectionUtil.Equals(ObjectClassInfo,other.ObjectClassInfo)) { + return false; + } + if (!CollectionUtil.Equals(OperationOptionInfo,other.OperationOptionInfo)) { + return false; + } + if (!CollectionUtil.Equals(_supportedObjectClassesByOperation, + other._supportedObjectClassesByOperation)) { + return false; + } + if (!CollectionUtil.Equals(_supportedOptionsByOperation, + other._supportedOptionsByOperation)) { + return false; + } + return true; + } + return false; + } + + public override string ToString() + { + IDictionary map = new Dictionary(); + map["ObjectClasses"] = _declaredObjectClasses; + map["Options"] = _declaredOperationOptions; + map["SupportedClasses"] = _supportedObjectClassesByOperation; + map["SupportedOptions"] = _supportedOptionsByOperation; + return map.ToString(); + } + } + #endregion + + #region SchemaBuilder + /** + * Simple builder class to help facilitate creating a {@link Schema} object. + */ + public sealed class SchemaBuilder { + private readonly Type _connectorClass; + private readonly ICollection _declaredObjectClasses + = new HashSet(); + private readonly ICollection _declaredOperationOptions + = new HashSet(); + + private readonly IDictionary> + _supportedObjectClassesByOperation = + new Dictionary>(); + private readonly IDictionary> + _supportedOptionsByOperation = + new Dictionary>(); + + + /** + * + */ + public SchemaBuilder(Type connectorClass) { + Assertions.NullCheck(connectorClass, "connectorClass"); + FrameworkUtil.AssertConnectorType(connectorClass); + _connectorClass = connectorClass; + } + + /** + * Adds another ObjectClassInfo to the schema. Also, adds this + * to the set of supported classes for every operation defined by + * the Connector. + * + * @param info + * @throws IllegalStateException If already defined + */ + public void DefineObjectClass(ObjectClassInfo info) { + Assertions.NullCheck(info, "info"); + if (_declaredObjectClasses.Contains(info)) { + throw new InvalidOperationException("ObjectClass already defined: "+ + info.ObjectType); + } + _declaredObjectClasses.Add(info); + foreach (Type op in + FrameworkUtil.GetDefaultSupportedOperations(_connectorClass)) { + ICollection oclasses = + CollectionUtil.GetValue(_supportedObjectClassesByOperation,op,null); + if (oclasses == null) { + oclasses = new HashSet(); + _supportedObjectClassesByOperation[op] = oclasses; + } + oclasses.Add(info); + } + } + /** + * Adds another OperationOptionInfo to the schema. Also, adds this + * to the set of supported options for every operation defined by + * the Connector. + */ + public void DefineOperationOption(OperationOptionInfo info) { + Assertions.NullCheck(info, "info"); + if (_declaredOperationOptions.Contains(info)) { + throw new InvalidOperationException("OperationOption already defined: "+ + info.Name); + } + _declaredOperationOptions.Add(info); + foreach (Type op in + FrameworkUtil.GetDefaultSupportedOperations(_connectorClass)) { + ICollection oclasses = + CollectionUtil.GetValue(_supportedOptionsByOperation,op,null); + if (oclasses == null) { + oclasses = new HashSet(); + _supportedOptionsByOperation[op] = oclasses; + } + oclasses.Add(info); + } + } + + /** + * Adds another ObjectClassInfo to the schema. Also, adds this + * to the set of supported classes for every operation defined by + * the Connector. + * @throws IllegalStateException If already defined + */ + public void DefineObjectClass(String type, ICollection attrInfo) { + ObjectClassInfoBuilder bld = new ObjectClassInfoBuilder(); + bld.ObjectType = type; + bld.AddAllAttributeInfo(attrInfo); + ObjectClassInfo obj = bld.Build(); + DefineObjectClass(obj); + } + + /** + * Adds another OperationOptionInfo to the schema. Also, adds this + * to the set of supported options for every operation defined by + * the Connector. + * @throws IllegalStateException If already defined + */ + public void DefineOperationOption(String optionName, Type type) { + OperationOptionInfoBuilder bld = new OperationOptionInfoBuilder(); + bld.Name=(optionName); + bld.OptionType=(type); + OperationOptionInfo info = bld.Build(); + DefineOperationOption(info); + } + + /** + * Adds the given ObjectClassInfo as a supported ObjectClass for + * the given operation. + * @param op The SPI operation + * @param def The ObjectClassInfo + * @throws IllegalArgumentException If the given ObjectClassInfo was + * not already defined using {@link #defineObjectClass(ObjectClassInfo)}. + */ + public void AddSupportedObjectClass(Type op, + ObjectClassInfo def) + { + Assertions.NullCheck(op, "op"); + Assertions.NullCheck(def, "def"); + ICollection apis = + FrameworkUtil.Spi2Apis(op); + if (!_declaredObjectClasses.Contains(def)) { + throw new ArgumentException("ObjectClass "+def.ObjectType+ + " not defined in schema."); + } + foreach (Type api in apis) { + ICollection infos = + CollectionUtil.GetValue(_supportedObjectClassesByOperation,api,null); + if ( infos == null ) { + throw new ArgumentException("Operation "+op.Name+ + " not implement by connector."); + } + if ( infos.Contains(def)) { + throw new ArgumentException("ObjectClass "+def.ObjectType+ + " already supported for operation "+op.Name); + } + infos.Add(def); + } + } + + /** + * Removes the given ObjectClassInfo as a supported ObjectClass for + * the given operation. + * @param op The SPI operation + * @param def The ObjectClassInfo + * @throws IllegalArgumentException If the given ObjectClassInfo was + * not already defined using {@link #defineObjectClass(ObjectClassInfo)}. + */ + public void RemoveSupportedObjectClass(Type op, + ObjectClassInfo def) + { + Assertions.NullCheck(op, "op"); + Assertions.NullCheck(def, "def"); + ICollection apis = + FrameworkUtil.Spi2Apis(op); + if (!_declaredObjectClasses.Contains(def)) { + throw new ArgumentException("ObjectClass "+def.ObjectType+ + " not defined in schema."); + } + foreach (Type api in apis) { + ICollection infos = + CollectionUtil.GetValue(_supportedObjectClassesByOperation,api,null); + if ( infos == null ) { + throw new ArgumentException("Operation "+op.Name+ + " not implement by connector."); + } + if ( !infos.Contains(def)) { + throw new ArgumentException("ObjectClass "+def.ObjectType + +" already removed for operation "+op.Name); + } + infos.Remove(def); + } + } + /** + * Adds the given OperationOptionInfo as a supported option for + * the given operation. + * @param op The SPI operation + * @param def The OperationOptionInfo + * @throws IllegalArgumentException If the given OperationOptionInfo was + * not already defined using {@link #defineOperationOption(OperationOptionInfo)}. + */ + public void AddSupportedOperationOption(Type op, + OperationOptionInfo def) { + Assertions.NullCheck(op, "op"); + Assertions.NullCheck(def, "def"); + ICollection apis = + FrameworkUtil.Spi2Apis(op); + if (!_declaredOperationOptions.Contains(def)) { + throw new ArgumentException("OperationOption "+def.Name+ + " not defined in schema."); + } + foreach (Type api in apis) { + ICollection infos = + CollectionUtil.GetValue(_supportedOptionsByOperation,api,null); + if ( infos == null ) { + throw new ArgumentException("Operation "+op.Name+ + " not implement by connector."); + } + if ( infos.Contains(def) ) { + throw new ArgumentException("OperationOption "+def.Name+ + " already supported for operation "+op.Name); + } + infos.Add(def); + } + } + + /** + * Removes the given OperationOptionInfo as a supported option for + * the given operation. + * @param op The SPI operation + * @param def The OperationOptionInfo + * @throws IllegalArgumentException If the given OperationOptionInfo was + * not already defined using {@link #defineOperationOption(OperationOptionInfo)}. + */ + public void RemoveSupportedOperationOption(Type op, + OperationOptionInfo def) { + Assertions.NullCheck(op, "op"); + Assertions.NullCheck(def, "def"); + ICollection apis = + FrameworkUtil.Spi2Apis(op); + if (!_declaredOperationOptions.Contains(def)) { + throw new ArgumentException("OperationOption "+def.Name+ + " not defined in schema."); + } + foreach (Type api in apis) { + ICollection infos = + CollectionUtil.GetValue(_supportedOptionsByOperation,api,null); + if ( infos == null ) { + throw new ArgumentException("Operation "+op.Name+ + " not implement by connector."); + } + if ( !infos.Contains(def) ) { + throw new ArgumentException("OperationOption "+def.Name+ + " already removed for operation "+op.Name); + } + infos.Remove(def); + } + } + + /** + * Clears the operation-specific supported classes. Normally, when + * you add an ObjectClass, using {@link #defineObjectClass(ObjectClassInfo)}, + * it is added to all operations. You may then remove those that you need + * using {@link #removeSupportedObjectClass(Class, ObjectClassInfo)}. You + * may wish, as an alternative to clear everything out and instead add using + * {@link #addSupportedObjectClass(Class, ObjectClassInfo)}. + */ + public void ClearSupportedObjectClassesByOperation() { + foreach (ICollection values in + _supportedObjectClassesByOperation.Values) + { + values.Clear(); + } + } + /** + * Clears the operation-specific supported options. Normally, when + * you add an OperationOptionInfo, using {@link #defineOperationOption(OperationOptionInfo)(ObjectClassInfo)}, + * it is added to all operations. You may then remove those that you need + * using {@link #removeSupportedOperationOption(Class, OperationOptionInfo)}. You + * may wish, as an alternative to clear everything out and instead add using + * {@link #addSupportedOperationOption(Class, OperationOptionInfo)}. + */ + public void ClearSupportedOptionsByOperation() { + foreach (ICollection values in + _supportedOptionsByOperation.Values) + { + values.Clear(); + } + } + + /** + * Builds the {@link Schema} object based on the {@link ObjectClassInfo}s + * added so far. + * + * @return new Schema object based on the info provided. + */ + public Schema Build() { + if (_declaredObjectClasses.Count == 0) { + String ERR = "Must be at least one ObjectClassInfo object!"; + throw new InvalidOperationException(ERR); + } + return new Schema(_declaredObjectClasses, + _declaredOperationOptions, + _supportedObjectClassesByOperation, + _supportedOptionsByOperation); + } + } + #endregion + + #region ScriptContext + /** + * Encapsulates a script and all of its parameters. + * @see com.sun.openconnectors.framework.api.operations.ScriptOnResourceApiOp + * @see com.sun.openconnectors.framework.api.operations.ScriptOnConnectorApiOp + */ + public sealed class ScriptContext { + + private readonly String _scriptLanguage; + private readonly String _scriptText; + private readonly IDictionary _scriptArguments; + + /** + * Public only for serialization; please use {@link ScriptContextBuilder}. + * @param scriptLanguage The script language. Must not be null. + * @param scriptText The script text. Must not be null. + * @param scriptArguments The script arguments. May be null. + */ + public ScriptContext(String scriptLanguage, + String scriptText, + IDictionary scriptArguments) { + + if (scriptLanguage == null) { + throw new ArgumentException("Argument 'scriptLanguage' must be specified"); + } + if (scriptText == null) { + throw new ArgumentException("Argument 'scriptText' must be specified"); + } + //clone script arguments and options - this serves two purposes + //1)makes sure everthing is serializable + //2)does a deep copy + IDictionary scriptArgumentsClone = (IDictionary)SerializerUtil.CloneObject(scriptArguments); + _scriptLanguage = scriptLanguage; + _scriptText = scriptText; + _scriptArguments = CollectionUtil.NewReadOnlyDictionary(scriptArgumentsClone); + } + + /** + * Identifies the language in which the script is written + * (e.g., bash, csh, + * Perl4 or Python). + * @return The script language. + */ + public String ScriptLanguage { + get { + return _scriptLanguage; + } + } + + /** + * Returns the text (i.e., actual characters) of the script. + * @return The text of the script. + */ + public String ScriptText { + get { + return _scriptText; + } + } + + /** + * Returns a map of arguments to be passed to the script. + * Values must be types that the framework can serialize. + * See {@link ObjectSerializerFactory} for a list of supported types. + * @return A map of arguments to be passed to the script. + */ + public IDictionary ScriptArguments { + get { + return _scriptArguments; + } + } + + public override string ToString() + { + StringBuilder bld = new StringBuilder(); + bld.Append("ScriptContext: "); + // poor man's to string method. + IDictionary map = new Dictionary(); + map["Language"] = ScriptLanguage; + map["Text"] = ScriptText; + map["Arguments"] = ScriptArguments; + bld.Append(map.ToString()); + return bld.ToString(); + } + + } + #endregion + + #region ScriptContextBuilder + /** + * Builds an {@link ScriptContext}. + */ + public sealed class ScriptContextBuilder { + private String _scriptLanguage; + private String _scriptText; + private readonly IDictionary _scriptArguments = new + Dictionary(); + + /** + * Creates an empty builder. + */ + public ScriptContextBuilder() { + + } + + /** + * Creates a builder with the required parameters specified. + * @param scriptLanguage a string that identifies the language + * in which the script is written + * (e.g., bash, csh, + * Perl4 or Python). + * @param scriptText The text (i.e., actual characters) of the script. + */ + public ScriptContextBuilder(String scriptLanguage, + String scriptText) { + _scriptLanguage = scriptLanguage; + _scriptText = scriptText; + } + + /** + * Identifies the language in which the script is written + * (e.g., bash, csh, + * Perl4 or Python). + * @return The script language. + */ + public String ScriptLanguage { + get { + return _scriptLanguage; + } + set { + _scriptLanguage = value; + } + } + + /** + * Returns the actual characters of the script. + * @return the actual characters of the script. + */ + public String ScriptText { + get { + return _scriptText; + } + set { + _scriptText = value; + } + } + + /** + * Adds or sets an argument to pass to the script. + * @param name The name of the argument. Must not be null. + * @param value The value of the argument. Must be one of + * type types that the framework can serialize. + * @see ObjectSerializerFactory for a list of supported types. + */ + public void AddScriptArgument(String name, Object value) { + if ( name == null ) { + throw new ArgumentException("Argument 'name' cannot be null."); + } + //don't validate value here - we do that implicitly when + //we clone in the constructor of ScriptRequest + _scriptArguments[name] = value; + } + + /** + * Removes the given script argument. + * @param name The name of the argument. Must not be null. + */ + public void RemoveScriptArgument(String name) { + if ( name == null ) { + throw new ArgumentException("Argument 'name' cannot be null."); + } + _scriptArguments.Remove(name); + } + + /** + * Returns a mutable reference of the script arguments map. + * @return A mutable reference of the script arguments map. + */ + public IDictionary ScriptArguments { + get { + //might as well be mutable since it's the builder and + //we don't want to deep copy anyway + return _scriptArguments; + } + } + + /** + * Creates a ScriptContext. + * The scriptLanguage and scriptText + * must be set prior to calling this. + * @return The ScriptContext. + */ + public ScriptContext Build() { + return new ScriptContext(_scriptLanguage, + _scriptText, + _scriptArguments); + } + } + #endregion + + #region SyncDelta + /** + * Represents a change to an object in a resource. + * + * @see SyncApiOp + * @see SyncOp + */ + public sealed class SyncDelta { + private readonly Uid _uid; + private readonly SyncToken _token; + private readonly SyncDeltaType _deltaType; + private readonly ICollection _attributes; + + /** + * Creates a SyncDelata + * + * @param uid + * The uid. Must not be null. + * @param token + * The token. Must not be null. + * @param deltaType + * The delta. Must not be null. + * @param attributes + * May be null. + */ + internal SyncDelta(Uid uid, SyncToken token, SyncDeltaType deltaType, + ICollection attributes) { + Assertions.NullCheck(uid, "uid"); + Assertions.NullCheck(token, "token"); + Assertions.NullCheck(deltaType, "deltaType"); + + _uid = uid; + _token = token; + _deltaType = deltaType; + _attributes = CollectionUtil.NewReadOnlySet(attributes); + + // make sure attributes don't also contain uid + if (ConnectorAttributeUtil.GetUidAttribute(attributes) != null) { + throw new ArgumentException( + "Attributes must not contain a UID"); + } + } + + /** + * Returns the Uid of the object that changed. + * + * @return the Uid of the object that changed. + */ + public Uid Uid { + get { + return _uid; + } + } + + /** + * Returns the SyncToken of the object that changed. + * + * @return the SyncToken of the object that changed. + */ + public SyncToken Token { + get { + return _token; + } + } + + /** + * Returns the type of the change the occured. + * + * @return The type of change that occured. + */ + public SyncDeltaType DeltaType { + get { + return _deltaType; + } + } + + /** + * Returns the attributes associated with the change. TODO: Define whether + * this is the whole object or just those that changed. The argument for + * just the changes is that it will be faster. The argument against is that + * the application will need to whole object anyway in most cases and so for + * those cases it will actually be slower. Need some more emperical data + * here... + * + * @return The attributes + */ + public ICollection Attributes { + get { + return _attributes; + } + } + + public override String ToString() { + IDictionary values = new Dictionary(); + values["Uid"] = _uid; + values["Token"] = _token; + values["DeltaType"] = _deltaType; + values["Attributes"] = _attributes; + return values.ToString(); + } + + public override int GetHashCode() { + return _uid.GetHashCode(); + } + + public override bool Equals(Object o) { + if ( o is SyncDelta ) { + SyncDelta other = (SyncDelta)o; + if (!_uid.Equals(other._uid)) { + return false; + } + if (!_token.Equals(other._token)) { + return false; + } + if (!_deltaType.Equals(other._deltaType)) { + return false; + } + if (!CollectionUtil.SetsEqual(_attributes,other._attributes)) { + return false; + } + return true; + } + return false; + } + } + #endregion + + #region SyncDeltaBuilder + /** + * Builder for {@link SyncDelta}. + */ + public sealed class SyncDeltaBuilder { + private Uid _uid; + private SyncToken _token; + private SyncDeltaType _deltaType; + private ICollection _attributes = new HashSet(); + + /** + * Create a new SyncDeltaBuilder + */ + public SyncDeltaBuilder() { + + } + + /** + * Creates a new SyncDeltaBuilder whose + * values are initialized to those of the delta. + * @param delta The original delta. + */ + public SyncDeltaBuilder(SyncDelta delta) { + _uid = delta.Uid; + _token = delta.Token; + _deltaType = delta.DeltaType; + _attributes = new HashSet(delta.Attributes); + } + + /** + * Returns the Uid of the object that changed. + * + * @return the Uid of the object that changed. + */ + public Uid Uid { + get { + return _uid; + } + set { + _uid = value; + } + } + + /** + * Returns the SyncToken of the object that changed. + * + * @return the SyncToken of the object that changed. + */ + public SyncToken Token { + get { + return _token; + } + set { + _token = value; + } + } + + /** + * Returns the type of the change that occurred. + * + * @return The type of change that occurred. + */ + public SyncDeltaType DeltaType { + get { + return _deltaType; + } + set { + _deltaType = value; + } + } + + /** + * Returns the attributes associated with the change. TODO: Define whether + * this is the whole object or just those that changed. The argument for + * just the changes is that it will be faster. The argument against is that + * the application will need to whole object anyway in most cases and so for + * those cases it will actually be slower. Need some more emperical data + * here... + * + * @return The attributes + */ + public ICollection Attributes { + get { + return _attributes; + } + set { + _attributes = CollectionUtil.NullAsEmpty(value); + } + } + + /** + * Adds an attribute associated with the change. TODO: Define whether this + * is the whole object or just those that changed. The argument for just the + * changes is that it will be faster. The argument against is that the + * application will need to whole object anyway in most cases and so for + * those cases it will actually be slower. Need some more emperical data + * here... + * + * @param attribute + * The attribute + */ + public void AddAttribute(ConnectorAttribute attribute) { + Assertions.NullCheck(attribute, "attribute"); + _attributes.Add(attribute); + } + + /** + * Creates a SyncDelata. Prior to calling the following must be specified: + *
    + *
  1. {@link #setUid(Uid) Uid}
  2. + *
  3. {@link #setToken(SyncToken) Token}
  4. + *
  5. {@link #setDeltaType(SyncDeltaType) DeltaType}
  6. + *
+ */ + public SyncDelta Build() { + return new SyncDelta(_uid, _token, _deltaType, _attributes); + } + } + #endregion + + #region SyncDeltaType + /** + * The type of change TODO: decide if this is the correct set of types. Some + * resources may not know whether it is a create or delete. In addition, + * application probably doesn't care - the create/update case is generally + * handled the same way. + */ + public enum SyncDeltaType { + CREATE, UPDATE, DELETE + } + #endregion + + #region SyncResultsHandler + /** + * Called to handle a delta in the stream. Will be called multiple times, + * once for each result. Although a callback, this is still invoked + * synchronously. That is, it is guaranteed that following a call to + * {@link SyncApiOp#sync(ObjectClass, SyncToken, SyncResultsHandler)} no + * more invocations to {@link #handle(SyncDelta)} will be performed. + * + * @param delta + * The change + * @return True iff the application wants to continue processing more + * results. + * @throws RuntimeException + * If the application encounters an exception. This will stop + * the interation and the exception will be propogated back to + * the application. + */ + public delegate bool SyncResultsHandler(SyncDelta delta); + #endregion + + #region SyncToken + /** + * Abstract "place-holder" for synchronization. The application must not make + * any attempt to interpret the value of the token. From the standpoint of the + * application the token is merely a black-box. The application may only persist + * the value of the token for use on subsequent synchronization requests. + *

+ * What this token represents is entirely connector-specific. On some connectors + * this might be a last-modified value. On others, it might be a unique ID of a + * log table entry. + */ + public sealed class SyncToken { + + private Object _value; + + /** + * Creates a new + * + * @param value + * May not be null. TODO: define set of allowed value types + * (currently same as set of allowed attribute values). + */ + public SyncToken(Object value) { + Assertions.NullCheck(value, "value"); + FrameworkUtil.CheckAttributeValue(value); + _value = value; + } + + /** + * Returns the value for the token. + * + * @return The value for the token. + */ + public Object Value { + get { + return _value; + } + } + + public override String ToString() { + return "SyncToken: " + _value.ToString(); + } + + public override int GetHashCode() { + return _value.GetHashCode(); + } + + public override bool Equals(Object o) { + if ( o is SyncToken ) { + SyncToken other = (SyncToken)o; + return CollectionUtil.Equals(_value, other._value); + } + return false; + } + + + } + #endregion + + #region Uid + public sealed class Uid : ConnectorAttribute { + + public static readonly string NAME = ConnectorAttributeUtil.CreateSpecialName("UID"); + + public Uid(String val) : base(NAME, CollectionUtil.NewReadOnlyList(Check(val))) { + } + private static String Check(String value) { + if (StringUtil.IsBlank(value)) { + String ERR = "Uid value must not be blank!"; + throw new ArgumentException(ERR); + } + return value; + } + /** + * The single value of the attribute that is the unique id of an object. + * + * @return value that identifies an object. + */ + public String GetUidValue() { + return ConnectorAttributeUtil.GetStringValue(this); + } + } + #endregion +} diff --git a/Framework/CommonObjectsFilter.cs b/Framework/CommonObjectsFilter.cs new file mode 100644 index 00000000..94f44eba --- /dev/null +++ b/Framework/CommonObjectsFilter.cs @@ -0,0 +1,1331 @@ +/* + * Copyright 2008 Sun Microsystems, Inc. All rights reserved. + * + * U.S. Government Rights - Commercial software. Government users + * are subject to the Sun Microsystems, Inc. standard license agreement + * and applicable provisions of the FAR and its supplements. + * + * Use is subject to license terms. + * + * This distribution may include materials developed by third parties. + * Sun, Sun Microsystems, the Sun logo, Java and Project Identity + * Connectors are trademarks or registered trademarks of Sun + * Microsystems, Inc. or its subsidiaries in the U.S. and other + * countries. + * + * UNIX is a registered trademark in the U.S. and other countries, + * exclusively licensed through X/Open Company, Ltd. + * + * ----------- + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER. + * + * Copyright 2008 Sun Microsystems, Inc. All rights reserved. + * + * The contents of this file are subject to the terms of the Common Development + * and Distribution License(CDDL) (the License). You may not use this file + * except in compliance with the License. + * + * You can obtain a copy of the License at + * http://identityconnectors.dev.java.net/CDDLv1.0.html + * See the License for the specific language governing permissions and + * limitations under the License. + * + * When distributing the Covered Code, include this CDDL Header Notice in each + * file and include the License file at identityconnectors/legal/license.txt. + * If applicable, add the following below this CDDL Header, with the fields + * enclosed by brackets [] replaced by your own identifying information: + * "Portions Copyrighted [year] [name of copyright owner]" + * ----------- + */ +using System; +using System.Text; +using System.Collections.Generic; +using Org.IdentityConnectors.Common; +using Org.IdentityConnectors.Framework.Common.Objects; +namespace Org.IdentityConnectors.Framework.Common.Objects.Filters +{ + #region AbstractFilterTranslator + /** + * Base class to make it easier to implement Search. + * A search filter may contain operators (such as 'contains' or 'in') + * or may contain logical operators (such as 'AND', 'OR' or 'NOT') + * that a connector cannot implement using the native API + * of the target system or application. + * A connector developer should subclass AbstractFilterTranslator + * in order to declare which filter operations the connector does support. + * This allows the FilterTranslator instance to analyze + * a specified search filter and reduce the filter to its most efficient form. + * The default (and worst-case) behavior is to return a null expression, + * which means that the connector should return "everything" + * (that is, should return all values for every requested attribute) + * and rely on the common code in the framework to perform filtering. + * This "fallback" behavior is good (in that it ensures consistency + * of search behavior across connector implementations) but it is + * obviously better for performance and scalability if each connector + * performs as much filtering as the native API of the target can support. + *

+ * A subclass should override each of the following methods where possible: + *

    + *
  1. {@link #createAndExpression}
  2. + *
  3. {@link #createOrExpression}
  4. + *
  5. {@link #createContainsExpression(ContainsFilter, boolean)}
  6. + *
  7. {@link #createEndsWithExpression(EndsWithFilter, boolean)}
  8. + *
  9. {@link #createEqualsExpression(EqualsFilter, boolean)}
  10. + *
  11. {@link #createGreaterThanExpression(GreaterThanFilter, boolean)}
  12. + *
  13. {@link #createGreaterThanOrEqualExpression(GreaterThanOrEqualFilter, boolean)}
  14. + *
  15. {@link #createStartsWithExpression(StartsWithFilter, boolean)}
  16. + *
+ *

+ * Translation can then be performed using {@link #translate(Filter)}. + *

+ * @param The result type of the translator. Commonly this will + * be a string, but there are cases where you might need to return + * a more complex data structure. For example if you are building a SQL + * query, you will need not *just* the base WHERE clause but a list + * of tables that need to be joined together. + */ + abstract public class AbstractFilterTranslator : FilterTranslator + where T : class + { + + /** + * Main method to be called to translate a filter + * @param filter The filter to translate. + * @return The list of queries to be performed. The list + * size() may be one of the following: + *

    + *
  1. 0 - This + * signifies fetch everything. This may occur if your filter + * was null or one of your create* methods returned null.
  2. + *
  3. 1 - List contains a single query that will return the results from the filter. + * Note that the results may be a superset of those specified by + * the filter in the case that one of your create* methods returned null. + * That is OK from a behavior standpoint since ConnectorFacade performs + * a second level of filtering. However it is undesirable from a performance standpoint.
  4. + *
  5. >1 - List contains multiple queries that must be performed in order to + * meet the filter that was passed in. Note that this only occurs if your + * {@link #createOrExpression} method can return null. If this happens, it + * is the responsibility of the connector implementor to perform each query + * and combine the results. In order to eliminate duplicates, the connector + * implementation must keep an in-memory HashSet of those UID + * that have been visited thus far. This will not scale well if your + * result sets are large. Therefore it is recommended that if + * at all possible you implement {@link #createOrExpression}
  6. + *
+ */ + public IList Translate(Filter filter) { + if ( filter == null ) { + return new List(); + } + //this must come first + filter = NormalizeNot(filter); + filter = SimplifyAndDistribute(filter); + //might have simplified it to the everything filter + if ( filter == null ) { + return new List(); + } + IList result = TranslateInternal(filter); + //now "optimize" - we can eliminate exact matches at least + HashSet set = new HashSet(); + IList optimized = new List(result.Count); + foreach (T obj in result) { + if ( set.Add(obj) ) { + optimized.Add(obj); + } + } + return optimized; + } + + /** + * Pushes Not's so that they are just before the leaves of the tree + */ + private Filter NormalizeNot(Filter filter) { + if ( filter is AndFilter ) { + AndFilter af = (AndFilter)filter; + return new AndFilter(NormalizeNot(af.Left), + NormalizeNot(af.Right)); + } + else if ( filter is OrFilter ) { + OrFilter of = (OrFilter)filter; + return new OrFilter(NormalizeNot(of.Left), + NormalizeNot(of.Right)); + } + else if ( filter is NotFilter ) { + NotFilter nf = (NotFilter)filter; + return Negate(NormalizeNot(nf.Filter)); + } + else { + return filter; + } + } + + /** + * Given a filter, create a filter representing its negative. + * This is used by normalizeNot. + */ + private Filter Negate(Filter filter) + { + if ( filter is AndFilter ) { + AndFilter af = (AndFilter)filter; + return new OrFilter(Negate(af.Left), + Negate(af.Right)); + } + else if ( filter is OrFilter ) { + OrFilter of = (OrFilter)filter; + return new AndFilter(Negate(of.Left), + Negate(of.Right)); + } + else if ( filter is NotFilter ) { + NotFilter nf = (NotFilter)filter; + return nf.Filter; + } + else { + return new NotFilter(filter); + } + } + + /** + * Simultaneously prunes those portions of the + * filter than cannot be implemented and distributes + * Ands over Ors where needed if the resource does not + * implement Or. + * + * @param filter Nots must already be normalized + * @return a simplified filter or null to represent the + * "everything" filter. + */ + private Filter SimplifyAndDistribute(Filter filter) { + if ( filter is AndFilter ) { + AndFilter af = (AndFilter)filter; + Filter simplifiedLeft = + SimplifyAndDistribute(af.Left); + Filter simplifiedRight = + SimplifyAndDistribute(af.Right); + if ( simplifiedLeft == null ) { + //left is "everything" - just return the right + return simplifiedRight; + } + else if ( simplifiedRight == null ) { + //right is "everything" - just return the left + return simplifiedLeft; + } + else { + //simulate translation of the left and right + //to see where we end up + IList leftExprs = + TranslateInternal(simplifiedLeft); + IList rightExprs = + TranslateInternal(simplifiedRight); + if (leftExprs.Count == 0) { + //This can happen only when one of the create* methods + //is inconsistent from one invocation to the next + //(simplifiedLeft should have been null + //in the previous 'if' above). + throw new InvalidOperationException("Translation method is inconsistent: "+leftExprs); + } + if (rightExprs.Count == 0) { + //This can happen only when one of the create* methods + //is inconsistent from one invocation to the next + //(simplifiedRight should have been null + //in the previous 'if' above). + throw new InvalidOperationException("Translation method is inconsistent: "+rightExprs); + } + + //Simulate ANDing each pair(left,right). + //If all of them return null (i.e., "everything"), + //then the request cannot be filtered. + bool anyAndsPossible = false; + foreach ( T leftExpr in leftExprs ) { + foreach ( T rightExpr in rightExprs ) { + T test = CreateAndExpression( + leftExpr, + rightExpr); + if ( test != null ) { + anyAndsPossible = true; + break; + } + } + if ( anyAndsPossible ) { + break; + } + } + + //If no AND filtering is possible, + //return whichever of left or right + //contains the fewest expressions. + if (!anyAndsPossible) { + if ( leftExprs.Count <= rightExprs.Count ) { + return simplifiedLeft; + } + else { + return simplifiedRight; + } + } + + //Since AND filtering is possible for at least + //one expression, let's distribute. + if ( leftExprs.Count > 1 ) { + //The left can contain more than one expression + //only if the left-hand side is an unimplemented OR. + //Distribute our AND to the left. + OrFilter left = (OrFilter)simplifiedLeft; + OrFilter newFilter = + new OrFilter(new AndFilter(left.Left, + simplifiedRight), + new AndFilter(left.Right, + simplifiedRight)); + return SimplifyAndDistribute(newFilter); + } + else if ( rightExprs.Count > 1 ) { + //The right can contain more than one expression + //only if the right-hand side is an unimplemented OR. + //Distribute our AND to the right. + OrFilter right = (OrFilter)simplifiedRight; + OrFilter newFilter = + new OrFilter(new AndFilter(simplifiedLeft, + right.Left), + new AndFilter(simplifiedLeft, + right.Right)); + return SimplifyAndDistribute(newFilter); + } + else { + //Each side contains exactly one expression + //and the translator does implement AND + //(anyAndsPossible must be true + //for them to have hit this branch). + if (!anyAndsPossible) { + throw new Exception("expected anyAndsPossible"); + } + return new AndFilter(simplifiedLeft,simplifiedRight); + } + } + } + else if ( filter is OrFilter ) { + OrFilter of = (OrFilter)filter; + Filter simplifiedLeft = + SimplifyAndDistribute(of.Left); + Filter simplifiedRight = + SimplifyAndDistribute(of.Right); + //If either left or right reduces to "everything", + //then simplify the OR to "everything". + if ( simplifiedLeft == null || simplifiedRight == null ) { + return null; + } + //otherwise + return new OrFilter(simplifiedLeft, + simplifiedRight); + } + else { + //Otherwise, it's a NOT(LEAF) or a LEAF. + //Simulate creating it. + T expr = CreateLeafExpression(filter); + if ( expr == null ) { + //If the expression cannot be implemented, + //return the "everything" filter. + return null; + } + else { + //Otherwise, return the filter. + return filter; + } + } + } + + /** + * Translates the filter into a list of expressions. + * The filter must have already been transformed + * using normalizeNot followed by a simplifyAndDistribute. + * @param filter A filter (normalized, simplified, and distibuted) + * @return A list of expressions or empty list for everything. + */ + private IList TranslateInternal(Filter filter) { + if ( filter is AndFilter ) { + T result = TranslateAnd((AndFilter)filter); + IList rv = new List(); + if ( result != null ) { + rv.Add(result); + } + return rv; + } + else if ( filter is OrFilter ) { + return TranslateOr((OrFilter)filter); + } + else { + //otherwise it's either a leaf or a NOT (leaf) + T expr = CreateLeafExpression(filter); + IList exprs = new List(); + if ( expr != null ) { + exprs.Add(expr); + } + return exprs; + } + } + + private T TranslateAnd( AndFilter filter ) { + IList leftExprs = TranslateInternal(filter.Left); + IList rightExprs = TranslateInternal(filter.Right); + if ( leftExprs.Count != 1 ) { + //this can happen only if one of the create* methods + //is inconsistent from one invocation to the next + //(at this point we've already been simplified and + //distributed). + throw new InvalidOperationException("Translation method is inconsistent: "+leftExprs); + } + if ( rightExprs.Count != 1 ) { + //this can happen only if one of the create* methods + //is inconsistent from one invocation to the next + //(at this point we've already been simplified and + //distributed). + throw new InvalidOperationException("Translation method is inconsistent: "+rightExprs); + } + T rv = CreateAndExpression(leftExprs[0], rightExprs[0]); + if ( rv == null ) { + //This could happen only if we're inconsistent + //(since the simplify logic already should have removed + //any expression that cannot be filtered). + throw new InvalidOperationException("createAndExpression is inconsistent"); + } + return rv; + } + + private IList TranslateOr( OrFilter filter ) { + IList leftExprs = TranslateInternal(filter.Left); + IList rightExprs = TranslateInternal(filter.Right); + if ( leftExprs.Count == 0 ) { + //This can happen only if one of the create* methods + //is inconsistent from one invocation to the next. + throw new InvalidOperationException("Translation method is inconsistent"); + } + if ( rightExprs.Count == 0 ) { + //This can happen only if one of the create* methods + //methods is inconsistent from on invocation to the next. + throw new InvalidOperationException("Translation method is inconsistent"); + } + if ( leftExprs.Count == 1 && rightExprs.Count == 1 ) { + //If each side contains exactly one expression, + //try to create a combined expression. + T val = CreateOrExpression(leftExprs[0], rightExprs[0]); + if ( val != null ) { + IList rv = new List(); + rv.Add(val); + return rv; + } + //Otherwise, fall through + } + + //Return a list of queries from the left and from the right + IList rv2 = new List(leftExprs.Count+rightExprs.Count); + CollectionUtil.AddAll(rv2,leftExprs); + CollectionUtil.AddAll(rv2,rightExprs); + return rv2; + } + + /** + * Creates an expression for a LEAF or a NOT(leaf) + * @param filter Must be either a leaf or a NOT(leaf) + * @return The expression + */ + private T CreateLeafExpression(Filter filter) { + Filter leafFilter; + bool not; + if ( filter is NotFilter ) { + NotFilter nf = (NotFilter)filter; + leafFilter = nf.Filter; + not = true; + } + else { + leafFilter = filter; + not = false; + } + T expr = CreateLeafExpression(leafFilter,not); + return expr; + } + + /** + * Creates a Leaf expression + * @param filter Must be a leaf expression + * @param not Is ! to be applied to the leaf expression + * @return The expression or null (for everything) + */ + private T CreateLeafExpression(Filter filter, bool not) { + if ( filter is ContainsFilter ) { + return CreateContainsExpression((ContainsFilter)filter, not); + } + else if (filter is EndsWithFilter) { + return CreateEndsWithExpression((EndsWithFilter)filter,not); + } + else if ( filter is EqualsFilter ) { + return CreateEqualsExpression((EqualsFilter)filter, not); + } + else if ( filter is GreaterThanFilter ) { + return CreateGreaterThanExpression((GreaterThanFilter)filter, not); + } + else if ( filter is GreaterThanOrEqualFilter ) { + return CreateGreaterThanOrEqualExpression((GreaterThanOrEqualFilter)filter, not); + } + else if ( filter is LessThanFilter ) { + return CreateLessThanExpression((LessThanFilter)filter, not); + } + else if ( filter is LessThanOrEqualFilter ) { + return CreateLessThanOrEqualExpression((LessThanOrEqualFilter)filter, not); + } + else if (filter is StartsWithFilter) { + return CreateStartsWithExpression((StartsWithFilter)filter,not); + } + else if (filter is ContainsAllValuesFilter) { + return CreateContainsAllValuesExpression((ContainsAllValuesFilter)filter, not); + } + else { + //unrecognized expression - nothing we can do + return null; + } + } + + /** + * Should be overridden by subclasses to create an AND expression + * if the native resource supports AND. + * @param leftExpression The left expression. Will never be null. + * @param rightExpression The right expression. Will never be null. + * @return The AND expression. A return value of null means + * a native AND query cannot be created for the given expressions. + * In this case, the resulting query will consist of the + * leftExpression only. + */ + protected virtual T CreateAndExpression(T leftExpression, T rightExpression) { + return null; + } + + /** + * Should be overridden by subclasses to create an OR expression + * if the native resource supports OR. + * @param leftExpression The left expression. Will never be null. + * @param rightExpression The right expression. Will never be null. + * @return The OR expression. A return value of null means + * a native OR query cannot be created for the given expressions. + * In this case, {@link #translate} may return multiple queries, each + * of which must be run and results combined. + */ + protected virtual T CreateOrExpression(T leftExpression, T rightExpression) { + return null; + } + + /** + * Should be overridden by subclasses to create a CONTAINS expression + * if the native resource supports CONTAINS. + * @param filter The contains filter. Will never be null. + * @param not True if this should be a NOT CONTAINS + * @return The CONTAINS expression. A return value of null means + * a native CONTAINS query cannot be created for the given filter. + * In this case, {@link #translate} may return an empty query set, meaning + * fetch everything. The filter will be re-applied in memory + * to the resulting object stream. This does not scale well, so + * if possible, you should implement this method. + */ + protected virtual T CreateContainsExpression(ContainsFilter filter, bool not) { + return null; + } + + /** + * Should be overridden by subclasses to create a ENDS-WITH expression + * if the native resource supports ENDS-WITH. + * @param filter The contains filter. Will never be null. + * @param not True if this should be a NOT ENDS-WITH + * @return The ENDS-WITH expression. A return value of null means + * a native ENDS-WITH query cannot be created for the given filter. + * In this case, {@link #translate} may return an empty query set, meaning + * fetch everything. The filter will be re-applied in memory + * to the resulting object stream. This does not scale well, so + * if possible, you should implement this method. + */ + protected virtual T CreateEndsWithExpression(EndsWithFilter filter, bool not) { + return null; + } + + /** + * Should be overridden by subclasses to create a EQUALS expression + * if the native resource supports EQUALS. + * @param filter The contains filter. Will never be null. + * @param not True if this should be a NOT EQUALS + * @return The EQUALS expression. A return value of null means + * a native EQUALS query cannot be created for the given filter. + * In this case, {@link #translate} may return an empty query set, meaning + * fetch everything. The filter will be re-applied in memory + * to the resulting object stream. This does not scale well, so + * if possible, you should implement this method. + */ + protected virtual T CreateEqualsExpression(EqualsFilter filter, bool not) { + return null; + } + + /** + * Should be overridden by subclasses to create a GREATER-THAN expression + * if the native resource supports GREATER-THAN. + * @param filter The contains filter. Will never be null. + * @param not True if this should be a NOT GREATER-THAN + * @return The GREATER-THAN expression. A return value of null means + * a native GREATER-THAN query cannot be created for the given filter. + * In this case, {@link #translate} may return an empty query set, meaning + * fetch everything. The filter will be re-applied in memory + * to the resulting object stream. This does not scale well, so + * if possible, you should implement this method. + */ + protected virtual T CreateGreaterThanExpression(GreaterThanFilter filter, bool not) { + return null; + } + + /** + * Should be overridden by subclasses to create a GREATER-THAN-EQUAL expression + * if the native resource supports GREATER-THAN-EQUAL. + * @param filter The contains filter. Will never be null. + * @param not True if this should be a NOT GREATER-THAN-EQUAL + * @return The GREATER-THAN-EQUAL expression. A return value of null means + * a native GREATER-THAN-EQUAL query cannot be created for the given filter. + * In this case, {@link #translate} may return an empty query set, meaning + * fetch everything. The filter will be re-applied in memory + * to the resulting object stream. This does not scale well, so + * if possible, you should implement this method. + */ + protected virtual T CreateGreaterThanOrEqualExpression(GreaterThanOrEqualFilter filter, bool not) { + return null; + } + + /** + * Should be overridden by subclasses to create a LESS-THAN expression + * if the native resource supports LESS-THAN. + * @param filter The contains filter. Will never be null. + * @param not True if this should be a NOT LESS-THAN + * @return The LESS-THAN expression. A return value of null means + * a native LESS-THAN query cannot be created for the given filter. + * In this case, {@link #translate} may return an empty query set, meaning + * fetch everything. The filter will be re-applied in memory + * to the resulting object stream. This does not scale well, so + * if possible, you should implement this method. + */ + protected virtual T CreateLessThanExpression(LessThanFilter filter, bool not) { + return null; + } + + /** + * Should be overridden by subclasses to create a LESS-THAN-EQUAL expression + * if the native resource supports LESS-THAN-EQUAL. + * @param filter The contains filter. Will never be null. + * @param not True if this should be a NOT LESS-THAN-EQUAL + * @return The LESS-THAN-EQUAL expression. A return value of null means + * a native LESS-THAN-EQUAL query cannot be created for the given filter. + * In this case, {@link #translate} may return an empty query set, meaning + * fetch everything. The filter will be re-applied in memory + * to the resulting object stream. This does not scale well, so + * if possible, you should implement this method. + */ + protected virtual T CreateLessThanOrEqualExpression(LessThanOrEqualFilter filter, bool not) { + return null; + } + + /** + * Should be overridden by subclasses to create a STARTS-WITH expression + * if the native resource supports STARTS-WITH. + * @param filter The contains filter. Will never be null. + * @param not True if this should be a NOT STARTS-WITH + * @return The STARTS-WITH expression. A return value of null means + * a native STARTS-WITH query cannot be created for the given filter. + * In this case, {@link #translate} may return an empty query set, meaning + * fetch everything. The filter will be re-applied in memory + * to the resulting object stream. This does not scale well, so + * if possible, you should implement this method. + */ + protected virtual T CreateStartsWithExpression(StartsWithFilter filter, bool not) { + return null; + } + + protected virtual T CreateContainsAllValuesExpression(ContainsAllValuesFilter filter, bool not) { + return null; + } + } + #endregion + + #region AndFilter + public sealed class AndFilter : CompositeFilter { + + /** + * And the the left and right filters. + */ + public AndFilter(Filter left, Filter right) + : base(left, right) { + } + + /** + * Ands the left and right filters. + * + * @see Filter#accept(ConnectorObject) + */ + public override bool Accept(ConnectorObject obj) { + return Left.Accept(obj) && Right.Accept(obj); + } + + public override string ToString() + { + StringBuilder bld = new StringBuilder(); + bld.Append("AND: ").Append(Left).Append(", ").Append(Right); + return bld.ToString(); + } + } + #endregion + + #region AttributeFilter + public abstract class AttributeFilter : Filter { + private readonly ConnectorAttribute _attribute; + + /** + * Root filter for Attribute testing.. + */ + internal AttributeFilter(ConnectorAttribute attribute) { + _attribute = attribute; + if (attribute == null) { + throw new ArgumentException("Attribute not be null!"); + } + } + + /** + * Get the internal attribute. + */ + public ConnectorAttribute GetAttribute() { + return _attribute; + } + + /** + * Determines if the attribute provided is present in the + * {@link ConnectorObject}. + */ + public bool IsPresent(ConnectorObject obj) { + return obj.GetAttributeByName(_attribute.Name) != null; + } + public abstract bool Accept(ConnectorObject obj); + } + #endregion + + #region ComparableAttributeFilter + /** + * Filter for an attribute value that is comparable. + */ + public abstract class ComparableAttributeFilter : + SingleValueAttributeFilter { + + /** + * Attempt compare attribute values. + */ + internal ComparableAttributeFilter(ConnectorAttribute attr) + : base(attr) { + // determine if this attribute value is comparable.. + if (!(GetValue() is IComparable)) { + String ERR = "Must be a comparable value!"; + throw new ArgumentException(ERR); + } + } + + /** + * Call compareTo on the attribute values. If the attribute is not present + * in the {@link ConnectorObject} return -1. + */ + public int Compare(ConnectorObject obj) { + int ret = -1; + ConnectorAttribute attr = obj.GetAttributeByName(GetName()); + if (attr != null && attr.Value.Count == 1) { + // it must be a comparable because that's were testing against + if (!(attr.Value[0] is IComparable)) { + String ERR = "Attribute value must be comparable!"; + throw new ArgumentException(ERR); + } + // grab this value and the on from the attribute an compare.. + IComparable o1 = (IComparable)attr.Value[0]; + IComparable o2 = (IComparable)GetValue(); + ret = o1.CompareTo(o1); + } + return ret; + } + } + #endregion + + #region CompositeFilter + public abstract class CompositeFilter : Filter { + + public Filter Left { get; set; } + + public Filter Right { get; set; } + + internal CompositeFilter(Filter left, Filter right) { + Left = left; + Right = right; + } + public abstract bool Accept(ConnectorObject obj); + } + #endregion + + #region ContainsFilter + public sealed class ContainsFilter : StringFilter { + + public ContainsFilter(ConnectorAttribute attr) + : base(attr) { + } + + public override bool Accept(String value) { + return value.Contains(GetValue()); + } + + public override string ToString() + { + StringBuilder bld = new StringBuilder(); + bld.Append("CONTAINS: ").Append(GetAttribute()); + return bld.ToString(); + } + } + #endregion + + #region EndsWithFilter + public sealed class EndsWithFilter : StringFilter { + + public EndsWithFilter(ConnectorAttribute attr) + : base(attr) { + } + + public override bool Accept(String value) { + return value.EndsWith(GetValue()); + } + + public override string ToString() { + StringBuilder bld = new StringBuilder(); + bld.Append("ENDSWITH: ").Append(GetAttribute()); + return bld.ToString(); + } + } + #endregion + + #region EqualsFilter + public sealed class EqualsFilter : AttributeFilter { + + /** + * Determines if the attribute inside the {@link ConnectorObject} is equal + * to the {@link Attribute} provided. + */ + public EqualsFilter(ConnectorAttribute attr) + :base(attr) { + } + + /** + * Determines if the attribute exists in the {@link ConnectorObject} and if + * its equal to the one provided. + * + * @see Filter#accept(ConnectorObject) + */ + public override bool Accept(ConnectorObject obj) { + bool ret = false; + ConnectorAttribute thisAttr = GetAttribute(); + ConnectorAttribute attr = obj.GetAttributeByName(thisAttr.Name); + if (attr != null) { + ret = thisAttr.Equals(attr); + } + return ret; + } + + public override string ToString() { + StringBuilder bld = new StringBuilder(); + bld.Append("EQUALS: ").Append(GetAttribute()); + return bld.ToString(); + } + + } + #endregion + + #region Filter + public interface Filter { + bool Accept(ConnectorObject obj); + } + #endregion + + #region FilterBuilder + /** + * FilterBuilder creates a {@link Filter} object, that can determine if a + * ConnectorObject will be filtered or not. + * + * @author Will Droste + * @version $Revision: 1.7 $ + * @since 1.0 + */ + public static class FilterBuilder { + + /** + * Determine if the {@link ConnectorObject} {@link ConnectorAttribute} value ends + * with the {@link ConnectorAttribute} value provided. + * + * @param attr + * {@link ConnectorAttribute} value to test against the + * {@link ConnectorObject} attribute value. + * @return true if the {@link ConnectorObject} attribute value contains the + * attribute value provided. + */ + public static Filter EndsWith(ConnectorAttribute attr) { + return new EndsWithFilter(attr); + } + + /** + * Determine if the {@link ConnectorObject} {@link ConnectorAttribute} value starts + * with the {@link ConnectorAttribute} value provided. + * + * @param attr + * {@link ConnectorAttribute} value to test against the + * {@link ConnectorObject} attribute value. + * @return true if the {@link ConnectorObject} attribute value contains the + * attribute value provided. + */ + public static Filter StartsWith(ConnectorAttribute attr) { + return new StartsWithFilter(attr); + } + + public static Filter ContainsAllValues(ConnectorAttribute attr) { + return new ContainsAllValuesFilter(attr); + } + + /** + * Determine if the {@link ConnectorObject} {@link ConnectorAttribute} value contains + * the {@link ConnectorAttribute} value provided. + * + * @param attr + * {@link ConnectorAttribute} value to test against the + * {@link ConnectorObject} attribute value. + * @return true if the {@link ConnectorObject} attribute value contains the + * attribute value provided. + */ + public static Filter Contains(ConnectorAttribute attr) { + return new ContainsFilter(attr); + } + + /** + * The {@link ConnectorAttribute} value provided is less than or equal to the + * {@link ConnectorObject} attribute value. + * + * @param attr + * ConnectorAttribute to do the comparison. + * @return true if attribute provided is greater than or equal to the one + * provided by the {@link ConnectorObject}. + */ + public static Filter GreaterThanOrEqualTo(ConnectorAttribute attr) { + return new GreaterThanOrEqualFilter(attr); + } + + /** + * The {@link ConnectorAttribute} value provided is less than or equal to the + * {@link ConnectorObject} attribute value. + * + * @param attr + * ConnectorAttribute to do the comparison. + * @return true if attribute provided is less than or equal to the one + * provided by the {@link ConnectorObject}. + */ + public static Filter LessThanOrEqualTo(ConnectorAttribute attr) { + return new LessThanOrEqualFilter(attr); + } + + /** + * The {@link ConnectorAttribute} value provided is less than the + * {@link ConnectorObject} attribute value. + * + * @param attr + * ConnectorAttribute to do the comparison. + * @return true if attribute provided is less than the one provided by the + * {@link ConnectorObject}. + */ + public static Filter LessThan(ConnectorAttribute attr) { + return new LessThanFilter(attr); + } + + /** + * ConnectorAttribute value is greater than the {@link ConnectorObject} attribute + * value. + * + * @param attr + * ConnectorAttribute to do the comparison. + * @return true if attribute provided is greater than the one provided by + * the {@link ConnectorObject}. + */ + public static Filter GreaterThan(ConnectorAttribute attr) { + return new GreaterThanFilter(attr); + } + + /** + * Determines if the {@link ConnectorAttribute} provided exists in the + * {@link ConnectorObject} and is equal. + */ + public static Filter EqualTo(ConnectorAttribute attr) { + return new EqualsFilter(attr); + } + + /** + * Ands the two {@link Filter}. + * + * @param leftOperand + * left side operand. + * @param rightOperand + * right side operand. + * @return the result of leftOperand && rightOperand + */ + public static Filter And(Filter leftOperand, Filter rightOperand) { + return new AndFilter(leftOperand, rightOperand); + } + + /** + * ORs the two {@link Filter}. + * + * @param leftOperand + * left side operand. + * @param rightOperand + * right side operand. + * @return the result of leftOperand || rightOperand + */ + public static Filter Or(Filter leftOperand, Filter rightOperand) { + return new OrFilter(leftOperand, rightOperand); + } + + /** + * NOT the {@link Filter}. + * + * @param filter + * negate the result of {@link Filter}. + * @return the result of not {@link Filter}. + */ + public static Filter Not(Filter filter) { + return new NotFilter(filter); + } + } + #endregion + + #region FilterTranslator + public interface FilterTranslator { + IList Translate(Filter filter); + } + #endregion + + #region GreaterThanFilter + public sealed class GreaterThanFilter : ComparableAttributeFilter { + + /** + * Determine if the {@link ConnectorObject} {@link Attribute} value is + * greater than the one provided in the filter. + */ + public GreaterThanFilter(ConnectorAttribute attr) + : base (attr) { + } + + /** + * Determine if the {@link ConnectorObject} {@link Attribute} value is + * greater than the one provided in the filter. + * + * @see com.sun.openconnectors.framework.common.objects.Filter#accept(ConnectorObject) + */ + public override bool Accept(ConnectorObject obj) { + return IsPresent(obj) && this.Compare(obj) > 0; + } + + public override string ToString() + { + StringBuilder bld = new StringBuilder(); + bld.Append("GREATERTHAN: ").Append(GetAttribute()); + return bld.ToString(); + } + } + #endregion + + #region GreaterThanOrEqualFilter + public sealed class GreaterThanOrEqualFilter : ComparableAttributeFilter { + + /** + * Determine if the {@link ConnectorObject} {@link Attribute} value is + * greater than the one provided in the filter. + */ + public GreaterThanOrEqualFilter(ConnectorAttribute attr) + : base (attr) { + } + + /** + * Determine if the {@link ConnectorObject} {@link Attribute} value is + * greater than the one provided in the filter. + * + * @see com.sun.openconnectors.framework.common.objects.Filter#accept(ConnectorObject) + */ + public override bool Accept(ConnectorObject obj) { + return IsPresent(obj) && this.Compare(obj) >= 0; + } + + public override string ToString() + { + StringBuilder bld = new StringBuilder(); + bld.Append("GREATERTHANOREQUAL: ").Append(GetAttribute()); + return bld.ToString(); + } + } + #endregion + + #region LessThanFilter + public sealed class LessThanFilter : ComparableAttributeFilter { + + /** + * Determine if the {@link ConnectorObject} {@link Attribute} value is + * greater than the one provided in the filter. + */ + public LessThanFilter(ConnectorAttribute attr) + : base (attr) { + } + + /** + * Determine if the {@link ConnectorObject} {@link Attribute} value is + * greater than the one provided in the filter. + * + * @see com.sun.openconnectors.framework.common.objects.Filter#accept(ConnectorObject) + */ + public override bool Accept(ConnectorObject obj) { + return IsPresent(obj) && this.Compare(obj) < 0; + } + + public override string ToString() + { + StringBuilder bld = new StringBuilder(); + bld.Append("LESSTHAN: ").Append(GetAttribute()); + return bld.ToString(); + } + } + #endregion + + #region LessThanOrEqualFilter + public sealed class LessThanOrEqualFilter : ComparableAttributeFilter { + + /** + * Determine if the {@link ConnectorObject} {@link Attribute} value is + * greater than the one provided in the filter. + */ + public LessThanOrEqualFilter(ConnectorAttribute attr) + : base (attr) { + } + + /** + * Determine if the {@link ConnectorObject} {@link Attribute} value is + * greater than the one provided in the filter. + * + * @see com.sun.openconnectors.framework.common.objects.Filter#accept(ConnectorObject) + */ + public override bool Accept(ConnectorObject obj) { + return IsPresent(obj) && this.Compare(obj) <= 0; + } + + public override string ToString() + { + StringBuilder bld = new StringBuilder(); + bld.Append("LESSTHANOREQUAL: ").Append(GetAttribute()); + return bld.ToString(); + } + } + #endregion + + #region NotFilter + /** + * Proxy the filter to return the negative of the value. + */ + public sealed class NotFilter : Filter { + + private readonly Filter _filter; + + /** + * Take the value returned from the internal filter and NOT it. + */ + public NotFilter(Filter filter) { + _filter = filter; + } + + /** + * Get the internal filter that is being negated. + */ + public Filter Filter { + get { + return _filter; + } + } + + /** + * Return the opposite the internal filters return value. + * + * @see Filter#accept(ConnectorObject) + */ + public bool Accept(ConnectorObject obj) { + return !_filter.Accept(obj); + } + + public override string ToString() + { + StringBuilder bld = new StringBuilder(); + bld.Append("NOT: ").Append(Filter); + return bld.ToString(); + } + } + #endregion + + #region OrFilter + public sealed class OrFilter : CompositeFilter { + + /** + * Or the left and right filters. + */ + public OrFilter(Filter left, Filter right) + : base(left, right) { + } + + /** + * ORs the left and right filters. + * + * @see Filter#accept(ConnectorObject) + */ + public override bool Accept(ConnectorObject obj) { + return Left.Accept(obj) || Right.Accept(obj); + } + + public override string ToString() { + StringBuilder bld = new StringBuilder(); + bld.Append("OR: ").Append(Left).Append(", ").Append(Right); + return bld.ToString(); + } + } + #endregion + + #region SingleValueAttributeFilter + /** + * Get a single value out of the attribute to test w/. + */ + public abstract class SingleValueAttributeFilter : AttributeFilter { + + /** + * Attempt to single out the value for comparison. + */ + internal SingleValueAttributeFilter(ConnectorAttribute attr) : + base(attr) { + // make sure this is not a Uid.. + if (Uid.NAME.Equals(attr.Name)) { + String MSG = "Uid can only be used for equals comparison."; + throw new ArgumentException(MSG); + } + // actual runtime.. + if (attr.Value.Count != 1) { + String ERR = "Must only be one value!"; + throw new ArgumentException(ERR); + } + } + + /** + * Value to test against. + */ + public Object GetValue() { + return GetAttribute().Value[0]; + } + /** + * Name of the attribute to find in the {@link ConnectorObject}. + */ + public String GetName() { + return GetAttribute().Name; + } + } + #endregion + + #region StartsWithFilter + public sealed class StartsWithFilter : StringFilter { + + public StartsWithFilter(ConnectorAttribute attr) + : base(attr) { + } + + public override bool Accept(String value) { + return value.StartsWith(GetValue()); + } + + public override string ToString() + { + StringBuilder bld = new StringBuilder(); + bld.Append("STARTSWITH: ").Append(GetAttribute()); + return bld.ToString(); + } + } + #endregion + + #region StringFilter + /** + * Filter based on strings. + */ + public abstract class StringFilter : SingleValueAttributeFilter { + + /** + * Attempts to get a string from the attribute. + */ + internal StringFilter(ConnectorAttribute attr) + : base(attr) { + Object val = base.GetValue(); + if (!(val is string)) { + String MSG = "Value must be a string!"; + throw new ArgumentException(MSG); + } + } + + /** + * Get the string value from the afore mentioned attribute. + * + * @see SingleValueAttributeFilter#getValue() + */ + public new String GetValue() { + return (String) base.GetValue(); + } + + /** + * @throws ClassCastException + * iff the value from the {@link ConnectorObject}'s attribute + * of the same name as provided is not a string. + * @see com.sun.openconnectors.framework.common.objects.Filter#accept(ConnectorObject) + */ + public override bool Accept(ConnectorObject obj) { + bool ret = false; + ConnectorAttribute attr = obj.GetAttributeByName(GetName()); + if (attr != null) { + ret = Accept((string)attr.Value[0]); + } + return ret; + } + + public abstract bool Accept(String value); + } + #endregion + + #region ContainsAllValues Filter + public class ContainsAllValuesFilter : AttributeFilter { + private readonly string _name; + private readonly ICollection _values; + + public ContainsAllValuesFilter(ConnectorAttribute attr) : base(attr) { + _name = attr.Name; + _values = attr.Value; + } + /** + * Determine if the {@link ConnectorObject} contains an {@link Attribute} + * which contains all the values provided in the {@link Attribute} passed + * into the filter. + * + * {@inheritDoc} + */ + public override bool Accept(ConnectorObject obj) { + bool rv = false; + ConnectorAttribute found = obj.GetAttributeByName(_name); + if (found != null) { + // TODO: possible optimization using 'Set' + foreach (object o in _values) { + if (!(rv = found.Value.Contains(o))) { + break; + } + } + } + return rv; + } + } + #endregion + +} diff --git a/Framework/CommonSerializer.cs b/Framework/CommonSerializer.cs new file mode 100644 index 00000000..d3d4c2e1 --- /dev/null +++ b/Framework/CommonSerializer.cs @@ -0,0 +1,305 @@ +/* + * Copyright 2008 Sun Microsystems, Inc. All rights reserved. + * + * U.S. Government Rights - Commercial software. Government users + * are subject to the Sun Microsystems, Inc. standard license agreement + * and applicable provisions of the FAR and its supplements. + * + * Use is subject to license terms. + * + * This distribution may include materials developed by third parties. + * Sun, Sun Microsystems, the Sun logo, Java and Project Identity + * Connectors are trademarks or registered trademarks of Sun + * Microsystems, Inc. or its subsidiaries in the U.S. and other + * countries. + * + * UNIX is a registered trademark in the U.S. and other countries, + * exclusively licensed through X/Open Company, Ltd. + * + * ----------- + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER. + * + * Copyright 2008 Sun Microsystems, Inc. All rights reserved. + * + * The contents of this file are subject to the terms of the Common Development + * and Distribution License(CDDL) (the License). You may not use this file + * except in compliance with the License. + * + * You can obtain a copy of the License at + * http://identityconnectors.dev.java.net/CDDLv1.0.html + * See the License for the specific language governing permissions and + * limitations under the License. + * + * When distributing the Covered Code, include this CDDL Header Notice in each + * file and include the License file at identityconnectors/legal/license.txt. + * If applicable, add the following below this CDDL Header, with the fields + * enclosed by brackets [] replaced by your own identifying information: + * "Portions Copyrighted [year] [name of copyright owner]" + * ----------- + */ +using System; +using System.IO; +using System.Collections.Generic; +namespace Org.IdentityConnectors.Framework.Common.Serializer +{ + /** + * Interface for reading objects from a stream. + */ + public interface BinaryObjectDeserializer { + /** + * Reads the next object from the stream. Throws + * a wrapped {@link EOFException} if end of stream is reached. + * @return The next object from the stream. + */ + object ReadObject(); + + /** + * Closes the underlying stream + */ + void Close(); + } + /** + * Interface for writing objects to a stream. + */ + public interface BinaryObjectSerializer { + /** + * Writes the next object to the stream. + * @param obj The object to write. + * @see ObjectSerializerFactory for a list of supported types. + */ + void WriteObject(object obj); + + /** + * Flushes the underlying stream. + */ + void Flush(); + + /** + * Closes the underylying stream after first flushing it. + */ + void Close(); + } + + /** + * Serializer factory for serializing connector objects. The list of + * supported types are as follows: + * TODO: list supported types + *
    + *
+ * @see SerializerUtil + */ + public abstract class ObjectSerializerFactory { + // At some point we might make this pluggable, but for now, hard-code + private const string IMPL_NAME = "Org.IdentityConnectors.Framework.Impl.Serializer.ObjectSerializerFactoryImpl"; + + private static ObjectSerializerFactory _instance; + + private static readonly Object LOCK = new Object(); + + + /** + * Get the singleton instance of the {@link ObjectSerializerFactory}. + */ + public static ObjectSerializerFactory GetInstance() { + lock (LOCK) { + if (_instance == null) { + Type t = + FrameworkInternalBridge.LoadType(IMPL_NAME); + Object obj = Activator.CreateInstance(t); + _instance = (ObjectSerializerFactory)obj; + } + return _instance; + } + } + + /** + * Creates a BinaryObjectSerializer for writing objects to + * the given stream. + * + * NOTE: consider using {@link SerializerUtil#serializeBinaryObject(Object)} + * for convenience serializing a single object. + * + * NOTE2: do not mix and match {@link SerializerUtil#serializeBinaryObject(Object)} + * with {{@link #newBinaryDeserializer(InputStream)}. This is unsafe since there + * is header information and state associated with the stream. Objects written + * using one method must be read using the proper corresponding method. + * + * @param os The stream + * @return The serializer + */ + public abstract BinaryObjectSerializer NewBinarySerializer(Stream os); + + /** + * Creates a BinaryObjectDeserializer for reading objects from + * the given stream. + * + * NOTE: consider using {@link SerializerUtil#deserializeBinaryObject(byte[])} + * for convenience deserializing a single object. + * + * NOTE2: do not mix and match {@link SerializerUtil#deserializeBinaryObject(Object)} + * with {{@link #newBinarySerializer(OutputStream)}. This is unsafe since there + * is header information and state associated with the stream. Objects written + * using one method must be read using the proper corresponding method. + * + * @param os The stream + * @return The deserializer + */ + public abstract BinaryObjectDeserializer NewBinaryDeserializer(Stream i); + + /** + * Creates a BinaryObjectSerializer for writing objects to + * the given stream. + * + * NOTE: consider using {@link SerializerUtil#serializeXmlObject(Object,boolean)} + * for convenience serializing a single object. + * + * NOTE2: do not mix and match {@link SerializerUtil#serializeXmlObject(Object,boolean)} + * with {{@link #deserializeXmlStream(InputSource, XmlObjectResultsHandler, boolean)}. + * + * @param w The writer + * @param includeHeader True to include the xml header + * @param multiObject Is this to produce a multi-object document. If false, only + * a single object may be written. + * @return The serializer + */ + public abstract XmlObjectSerializer NewXmlSerializer(TextWriter w, + bool includeHeader, + bool multiObject); + + /** + * Deserializes XML objects from a stream + * + * NOTE: Consider using {@link SerializerUtil#deserializeXmlObject(String,boolean)} + * for convenience deserializing a single object. + * + * NOTE2: Do not mix and match {@link SerializerUtil#deserializeXmlObject(Object,boolean)} + * with {{@link #newXmlSerializer(Writer, boolean, boolean)}. + * + * @param is The input source + * @param handler The callback to receive objects from the stream + * @param validate True iff we are to validate + */ + public abstract void DeserializeXmlStream(TextReader reader, + XmlObjectResultsHandler handler, + bool validate); + + } + + /** + * Bag of utilities for serialization + */ + public static class SerializerUtil { + + + /** + * Serializes the given object to bytes + * @param object The object to serialize + * @return The bytes + * @see ObjectSerializerFactory for a list of supported types + */ + public static byte [] SerializeBinaryObject(object obj) { + ObjectSerializerFactory fact = ObjectSerializerFactory.GetInstance(); + MemoryStream mem = new MemoryStream(); + BinaryObjectSerializer ser = fact.NewBinarySerializer(mem); + ser.WriteObject(obj); + ser.Close(); + return mem.ToArray(); + } + + /** + * Deserializes the given object from bytes + * @param bytes The bytes to deserialize + * @return The object + * @see ObjectSerializerFactory for a list of supported types + */ + public static object DeserializeBinaryObject(byte [] bytes) { + ObjectSerializerFactory fact = ObjectSerializerFactory.GetInstance(); + MemoryStream mem = new MemoryStream(bytes); + BinaryObjectDeserializer des = fact.NewBinaryDeserializer(mem); + return des.ReadObject(); + } + + /** + * Serializes the given object to xml + * @param object The object to serialize + * @param includeHeader True if we are to include the xml header. + * @return The xml + * @see ObjectSerializerFactory for a list of supported types + */ + public static String SerializeXmlObject(Object obj, bool includeHeader) { + ObjectSerializerFactory fact = ObjectSerializerFactory.GetInstance(); + StringWriter w = new StringWriter(); + XmlObjectSerializer ser = fact.NewXmlSerializer(w, includeHeader, false); + ser.WriteObject(obj); + ser.Close(true); + return w.ToString(); + } + + /** + * Deserializes the given object from xml + * @param bytes The xml to deserialize + * @param validate True if we are to validate the xml + * @return The object + * @see ObjectSerializerFactory for a list of supported types + */ + public static Object DeserializeXmlObject(String str, bool validate) { + ObjectSerializerFactory fact = ObjectSerializerFactory.GetInstance(); + StringReader source = new StringReader(str); + IList rv = new List(); + fact.DeserializeXmlStream(source, + obj => { + rv.Add(obj); + return true; + },validate); + if ( rv.Count > 0 ) { + return rv[0]; + } + else { + return null; + } + } + + /** + * Clones the given object by serializing it to bytes and then + * deserializing it. + * @param object The object. + * @return A clone of the object + */ + public static object CloneObject(Object obj) { + byte [] bytes = SerializeBinaryObject(obj); + return DeserializeBinaryObject(bytes); + } + + } + + /** + * Callback interface to receive xml objects from a stream of objects. + */ + public delegate bool XmlObjectResultsHandler(Object obj); + + /** + * Interface for writing objects to a stream. + */ + public interface XmlObjectSerializer { + /** + * Writes the next object to the stream. + * @param object The object to write. + * @see ObjectSerializerFactory for a list of supported types. + * @throws ConnectorException if there is more than one object + * and this is not configured for multi-object document. + */ + void WriteObject(Object obj); + + /** + * Flushes the underlying stream. + */ + void Flush(); + + /** + * Adds document end tag and optinally closes the underlying stream + */ + void Close(bool closeUnderlyingStream); + } + + +} diff --git a/Framework/Framework.csproj b/Framework/Framework.csproj new file mode 100644 index 00000000..cc06e082 --- /dev/null +++ b/Framework/Framework.csproj @@ -0,0 +1,109 @@ + + + + {8B24461B-456A-4032-89A1-CD418F7B5B62} + Debug + AnyCPU + Library + Org.IdentityConnectors.Framework + Framework + v3.5 + False + False + 4 + false + + + bin\Debug\ + true + Full + False + True + DEBUG;TRACE + + + bin\Release\ + False + None + True + False + TRACE + + + False + Auto + 4194304 + AnyCPU + 4096 + + + + + + 3.5 + + + 3.5 + + + + + + + + + + + + + + + + + + + {F140E8DA-52B4-4159-992A-9DA10EA8EEFB} + Common + + + + + + + \ No newline at end of file diff --git a/Framework/Spi.cs b/Framework/Spi.cs new file mode 100644 index 00000000..1b246c34 --- /dev/null +++ b/Framework/Spi.cs @@ -0,0 +1,222 @@ +/* + * Copyright 2008 Sun Microsystems, Inc. All rights reserved. + * + * U.S. Government Rights - Commercial software. Government users + * are subject to the Sun Microsystems, Inc. standard license agreement + * and applicable provisions of the FAR and its supplements. + * + * Use is subject to license terms. + * + * This distribution may include materials developed by third parties. + * Sun, Sun Microsystems, the Sun logo, Java and Project Identity + * Connectors are trademarks or registered trademarks of Sun + * Microsystems, Inc. or its subsidiaries in the U.S. and other + * countries. + * + * UNIX is a registered trademark in the U.S. and other countries, + * exclusively licensed through X/Open Company, Ltd. + * + * ----------- + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER. + * + * Copyright 2008 Sun Microsystems, Inc. All rights reserved. + * + * The contents of this file are subject to the terms of the Common Development + * and Distribution License(CDDL) (the License). You may not use this file + * except in compliance with the License. + * + * You can obtain a copy of the License at + * http://identityconnectors.dev.java.net/CDDLv1.0.html + * See the License for the specific language governing permissions and + * limitations under the License. + * + * When distributing the Covered Code, include this CDDL Header Notice in each + * file and include the License file at identityconnectors/legal/license.txt. + * If applicable, add the following below this CDDL Header, with the fields + * enclosed by brackets [] replaced by your own identifying information: + * "Portions Copyrighted [year] [name of copyright owner]" + * ----------- + */ +using System; +using System.Globalization; +using System.Collections.Generic; +using Org.IdentityConnectors.Common.Pooling; +using Org.IdentityConnectors.Framework.Common.Objects; +namespace Org.IdentityConnectors.Framework.Spi +{ + #region AttributeNormalizer + /** + * Interface to be implemented by connectors that need + * to normalize certain attributes. This might, for + * example, be used to normalize whitespace within + * DN's to ensure consistent filtering whether that + * filtering is natively on the resource or by the + * connector framework. For connectors implementing + * this interface, the method {@link #normalizeAttribute(ObjectClass, Attribute)} + * will be applied to each of the following: + *
    + *
  1. The filter passed to {@link SearchOp}.
  2. + *
  3. The results returned from {@link SearchOp}.
  4. + *
  5. The results returned from {@link SyncOp}.
  6. + *
  7. The attributes passed to {@link AdvancedUpdateOp}.
  8. + *
  9. The Uid returned from {@link AdvancedUpdateOp}.
  10. + *
  11. The attributes passed to {@link UpdateOp}.
  12. + *
  13. The Uid returned from {@link UpdateOp}.
  14. + *
  15. The attributes passed to {@link CreateOp}.
  16. + *
  17. The Uid returned from {@link CreateOp}.
  18. + *
  19. The Uid passed to {@link DeleteOp}.
  20. + *
+ */ + public interface AttributeNormalizer + { + ConnectorAttribute NormalizeAttribute(ObjectClass oclass, ConnectorAttribute attribute); + } + #endregion + + #region Configuration + /// + /// Configuration information for the Connector. + /// + public interface Configuration { + /// + /// Called after the instance is created to make + /// sure that the has the proper locale to create messages + /// for Exceptions etc. We don't explicitly require a getter for + /// CultureInfo but we strongly recommend it. + /// + /// current locale the is operating in. + CultureInfo CultureInfo{get;set;} + + ConnectorMessages ConnectorMessages{get;set;} + + /** + * Determine if the configuration is valid based on the values set. + */ + void Validate(); + } + #endregion + + #region AbstractConfiguration + public abstract class AbstractConfiguration : Configuration { + public CultureInfo CultureInfo{get;set;} + + public ConnectorMessages ConnectorMessages{get;set;} + + public abstract void Validate(); + } + #endregion + + #region ConnectorClassAttribute + [AttributeUsage(AttributeTargets.Class,AllowMultiple=false)] + public class ConnectorClassAttribute : System.Attribute { + + private readonly String _connectorDisplayNameKey; + private readonly Type _connectorConfigurationClass; + + public ConnectorClassAttribute(String connectorDisplayNameKey, + Type connectorConfigurationClass) { + _connectorDisplayNameKey = connectorDisplayNameKey; + _connectorConfigurationClass = connectorConfigurationClass; + } + + public string ConnectorDisplayNameKey { + get { + return _connectorDisplayNameKey; + } + } + + public Type ConnectorConfigurationType { + get { + return _connectorConfigurationClass; + } + } + + public string MessageCatalogPath {get;set;} + + } + #endregion + + #region ConfigurationPropertyAttribute + /// + /// The interface is traversed through reflection. This + /// annotation provides a way to override the default configuration operation for + /// each property. + /// + /// + /// + /// public class MyClass : IConfiguration { + /// [ConfigurationPropertionOptions(Confidential=true)] + /// public string MyProperty {get ; set;} + /// } + /// + /// + [AttributeUsage(AttributeTargets.Property)] + public class ConfigurationPropertyAttribute : System.Attribute { + + /// + /// Order in which this property is displayed. + /// + public int Order {get;set;} + /// + /// Is this a confidential property whose value should be + /// encrypted by the application when persisted? + /// + public bool Confidential {get;set;} + /// + /// Change the default help message key. + /// + public string HelpMessageKey {get;set;} + /// + /// Change the default display message key. + /// + public string DisplayMessageKey {get;set;} + + /// + /// Default constructor + /// + public ConfigurationPropertyAttribute() { + Order = 1; + Confidential = false; + HelpMessageKey = null; + DisplayMessageKey = null; + } + } + #endregion + + #region Connector + /// + /// This is the main interface to declare a connector. Developers must implement + /// this interface. The life-cycle for a is as follows + /// is called then any of the operations implemented + /// in the Connector and finally dispose. The and + /// allow for block operations. For instance bulk creates or + /// deletes and the use of before and after actions. Once is + /// called the object is discarded. + /// + public interface Connector : IDisposable { + + /// + /// Initialize the connector with its configuration. For instance in a JDBC + /// Connector this would include the database URL, password, and user. + /// + /// instance of the object implemented by + /// the developer and populated with information + /// in order to initialize the . + void Init(Configuration configuration); + } + #endregion + + #region PoolableConnector + /** + * To be implemented by Connectors that wish to be pooled. + */ + public interface PoolableConnector : Connector { + /** + * Checks to see if the connector is still alive. + * @throws RuntimeException If no longer alive. + */ + void CheckAlive(); + } + #endregion + +} diff --git a/Framework/SpiOperations.cs b/Framework/SpiOperations.cs new file mode 100644 index 00000000..43644dd0 --- /dev/null +++ b/Framework/SpiOperations.cs @@ -0,0 +1,357 @@ +/* + * Copyright 2008 Sun Microsystems, Inc. All rights reserved. + * + * U.S. Government Rights - Commercial software. Government users + * are subject to the Sun Microsystems, Inc. standard license agreement + * and applicable provisions of the FAR and its supplements. + * + * Use is subject to license terms. + * + * This distribution may include materials developed by third parties. + * Sun, Sun Microsystems, the Sun logo, Java and Project Identity + * Connectors are trademarks or registered trademarks of Sun + * Microsystems, Inc. or its subsidiaries in the U.S. and other + * countries. + * + * UNIX is a registered trademark in the U.S. and other countries, + * exclusively licensed through X/Open Company, Ltd. + * + * ----------- + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER. + * + * Copyright 2008 Sun Microsystems, Inc. All rights reserved. + * + * The contents of this file are subject to the terms of the Common Development + * and Distribution License(CDDL) (the License). You may not use this file + * except in compliance with the License. + * + * You can obtain a copy of the License at + * http://identityconnectors.dev.java.net/CDDLv1.0.html + * See the License for the specific language governing permissions and + * limitations under the License. + * + * When distributing the Covered Code, include this CDDL Header Notice in each + * file and include the License file at identityconnectors/legal/license.txt. + * If applicable, add the following below this CDDL Header, with the fields + * enclosed by brackets [] replaced by your own identifying information: + * "Portions Copyrighted [year] [name of copyright owner]" + * ----------- + */ +using System; +using System.Collections.Generic; + +using Org.IdentityConnectors.Common.Security; +using Org.IdentityConnectors.Framework.Common.Objects; +using Org.IdentityConnectors.Framework.Common.Objects.Filters; + +namespace Org.IdentityConnectors.Framework.Spi.Operations +{ + /** + * Used as a parameter to specify the type of update to perform. + */ + public enum UpdateType { + /** + * Replace each attribute value with the one provided. If the value is + * null then remove the attribute on set it to + * null as applicable. + */ + REPLACE, + /** + * Adds the values provided to the existing attribute values on the + * native target. + */ + ADD, + /** + * Remove the attribute values from the existing target values. + */ + DELETE + } + + /** + * The implementation of this method is expected to handle the following types + * of update. + * + * @author Will Droste + * @version $Revision $ + * @since 1.0 + */ + public interface AdvancedUpdateOp : SPIOperation { + + + /** + * The {@link Connector} developer is responsible for updating the object + * provided based on the type provided. If the operation can not be + * accomplished with the information provided throw a type of + * {@link RuntimeException} that best describes the problem. + * + * @param type + * determines the type of update to expect. + * @param obj + * information to find the object and the attributes to perform + * the type of update against. + * @return the {@link Uid} of the updated object in case the update changes + * the formation of the unique identifier. + */ + Uid Update(UpdateType type, ObjectClass objclass, ICollection attrs, OperationOptions options); + } + + /** + * Authenticate an object based on their unique identifier and password. + */ + public interface AuthenticateOp : SPIOperation { + + /** + * Simple authentication with two parameters presumed to be user name and + * password. The {@link Connector} developer is expected to attempt to + * authenticate these credentials natively. If the authentication fails the + * developer should throw a type of {@link RuntimeException} either + * {@link IllegalArgumentException} or if a native exception is available + * and if its of type {@link RuntimeException} simple throw it. If the + * native exception is not a {@link RuntimeException} wrap it in one and + * throw it. This will provide the most detail for logging problem and + * failed attempts. + *

+ * The developer is of course encourage to try and throw the most + * informative exception as possible. In that regards there are several + * exceptions provided in the exceptions package. For instance one of the + * most common is {@link InvalidPassword}. + * + * @param username + * the name based credential for authentication. + * @param password + * the password based credential for authentication. + * @throws RuntimeException + * iff native authentication fails. If a native exception if + * available attempt to throw it. + */ + void Authenticate(String username, GuardedString password, OperationOptions options); + } + + /** + * The {@link Connector} developer is responsible for taking the attributes + * given (which always includes the {@link ObjectClass}) and create an object + * and its {@link Uid}. The {@link Connector} developer must return the + * {@link Uid} so that the caller can refer to the created object. + *

+ * The {@link Connector} developer should make a best effort to create the + * object otherwise throw an informative {@link RuntimeException} telling the + * caller why the operation could not be completed. It reasonable to use + * defaults for required {@link Attribute}s as long as they are documented. + * + * @author Will Droste + * @version $Revision $ + * @since 1.0 + */ + public interface CreateOp : SPIOperation { + /** + * The {@link Connector} developer is responsible for taking the attributes + * given (which always includes the {@link ObjectClass}) and create an + * object and its {@link Uid}. The {@link Connector} developer must return + * the {@link Uid} so that the caller can refer to the created object. + * + * @param name + * specifies the name of the object to create. + * @param attrs + * includes all the attributes necessary to create the resource + * object including the {@link ObjectClass} attribute. + * @return the unique id for the object that is created. For instance in + * LDAP this would be the 'dn', for a database this would be the + * primary key, and for 'ActiveDirectory' this would be the GUID. + */ + Uid Create(ObjectClass oclass, ICollection attrs, OperationOptions options); + } + + ///

+ /// Deletes an object with the specified Uid and ObjectClass on the + /// resource. + /// + public interface DeleteOp : SPIOperation { + /// + /// Delete the object that the specified Uid identifies (if any). + /// + /// The type of object to delete. + /// The unique identitfier for the object to delete. + /// Throws UnknowUid if the object does not exist. + void Delete(ObjectClass objClass, Uid uid, OperationOptions options); + } + + public interface SchemaOp : SPIOperation { + + /** + * Determines what types of objects this {@link Connector} supports. This + * method is considered an operation since determining supported objects may + * require configuration information and allows this determination to be + * dynamic. + * + * @return basic schema supported by this {@link Connector}. + */ + Schema Schema(); + } + /** + * Operation that runs a script in the environment of the connector. + * (Compare to {@link ScriptOnResourceOp}, which runs a script + * on the target resource that the connector manages.) + * A connector that intends to provide to scripts + * more than is required by the basic contract + * specified in the javadoc for {@link ScriptOnConnectorApiOp} + * should implement this interface. + *

+ * Each connector that implements this interface must support + * at least the behavior specified by {@link ScriptOnConnectorApiOp}. + * A connector also may expose additional variables for use by scripts + * and may respond to specific {@link OperationOptions options}. + * Each connector that implements this interface + * must describe in its javadoc as available "for use by connector scripts" + * any such additional variables or supported options. + */ + public interface ScriptOnConnectorOp : SPIOperation { + + /** + * Runs the script request. + * @param request The script and arguments to run. + * @param options Additional options that control how the script is + * run. + * @return The result of the script. The return type must be + * a type that the framework supports for serialization. + * See {@link ObjectSerializerFactory} for a list of supported types. + */ + Object RunScriptOnConnector(ScriptContext request, + OperationOptions options); + } + /** + * Operation that runs a script directly on a target resource. + * (Compare to {@link ScriptOnConnectorOp}, which runs a script + * in the context of a particular connector.) + *

+ * A connector that intends to support + * {@link ScriptOnResourceApiOp} + * should implement this interface. Each connector that implements + * this interface must document which script languages the connector supports, + * as well as any supported {@link OperationOptions}. + */ + public interface ScriptOnResourceOp : SPIOperation { + /** + * Run the specified script on the target resource + * that this connector manages. + * @param request The script and arguments to run. + * @param options Additional options that control + * how the script is run. + * @return The result of the script. The return type must be + * a type that the framework supports for serialization. + * See {@link ObjectSerializerFactory} for a list of supported types. + */ + Object RunScriptOnResource(ScriptContext request, + OperationOptions options); + } + + /** + * Implement this interface to allow the Connector to search for resource + * objects. + */ + public interface SearchOp : SPIOperation where T : class { + /** + * Creates a filter translator that will translate + * a specified filter to the native filter. The + * translated filters will be subsequently passed to + * {@link #search(ObjectClass, Object, ResultsHandler)} + * @param oclass The object class for the search. Will never be null. + * @param options + * additional options that impact the way this operation is run. + * If the caller passes null, the framework will convert this into + * an empty set of options, so SPI need not worry + * about this ever being null. + * @return A filter translator. + */ + FilterTranslator CreateFilterTranslator(ObjectClass oclass, OperationOptions options); + /** + * This will be called by ConnectorFacade, once for each native query produced + * by the FilterTranslator. If there is more than one query the results will + * automatically be merged together and duplicates eliminated. NOTE + * that this implies an in-memory data structure that holds a set of + * Uids, so memory usage in the event of multiple queries will be O(N) + * where N is the number of results. That is why it is important that + * the FilterTranslator implement OR if possible. + * @param oclass The object class for the search. Will never be null. + * @param query The native query to run. A value of null means 'return everything for the given object class'. + * @param handler + * Results should be returned to this handler + * @param options + * additional options that impact the way this operation is run. + * If the caller passes null, the framework will convert this into + * an empty set of options, so SPI need not worry + * about this ever being null. + */ + void ExecuteQuery(ObjectClass oclass, T query, ResultsHandler handler, OperationOptions options); + } + /** + * Receive synchronization events from the resource. + *

+ * TODO: define quality of service level. For example, on JMS sync, when + * synchronizing from a queue, the connector should return one SyncDelta + * per-call to {@link #sync(ObjectClass, SyncToken, SyncResultsHandler)}. Each + * call to {@link #sync(ObjectClass, SyncToken, SyncResultsHandler)} should + * delete the previous entry and return the next. That would guarantee that no + * items get dropped. Connectors that implement this interface will be exposed + * to the application via {@link SyncApiOp}. + * + * @see SyncApiOp + */ + public interface SyncOp : SPIOperation { + /** + * Perform a synchronization. + * + * @param objClass + * The object class to synchronize. Must not be null. + * @param token + * The token representing the last token from the previous sync. + * Should be null if this is the first sync for the given + * resource. + * @param handler + * The result handler Must not be null. + * @param options + * additional options that impact the way this operation is run. + * If the caller passes null, the framework will convert this into + * an empty set of options, so SPI need not worry + * about this ever being null. + */ + void Sync(ObjectClass objClass, SyncToken token, + SyncResultsHandler handler, + OperationOptions options); + } + + /** + * The developer of a Connector should implement this interface + * if the Connector will allow an authorized caller to update + * (i.e., modify or replace) objects on the target resource. + */ + public interface UpdateOp : SPIOperation { + ///

+ /// Update a particular object based on the ObjectClass and change set provided. + /// + /// Type of object to change. + /// Deltas for the object which include the Uid so the + /// object can be found. + /// a new Uid if the deltra prompt a change the Uid otherwise the orginal + /// one passed in. + Uid Update(ObjectClass objectclass, ICollection attrs, OperationOptions options); + } + + public interface TestOp : SPIOperation { + + /** + * Tests connectivity and validity of the {@link Configuration}. + * + * @throws RuntimeException + * iff the {@link Configuration} is not valid or a + * {@link Connection} to the resource could not be established. + */ + void Test(); + } + + /** + * Tagging interface for the {@link Connector} SPI. + */ + public interface SPIOperation { + + } +} diff --git a/Framework/Test.cs b/Framework/Test.cs new file mode 100644 index 00000000..713e5865 --- /dev/null +++ b/Framework/Test.cs @@ -0,0 +1,304 @@ +/* + * Copyright 2008 Sun Microsystems, Inc. All rights reserved. + * + * U.S. Government Rights - Commercial software. Government users + * are subject to the Sun Microsystems, Inc. standard license agreement + * and applicable provisions of the FAR and its supplements. + * + * Use is subject to license terms. + * + * This distribution may include materials developed by third parties. + * Sun, Sun Microsystems, the Sun logo, Java and Project Identity + * Connectors are trademarks or registered trademarks of Sun + * Microsystems, Inc. or its subsidiaries in the U.S. and other + * countries. + * + * UNIX is a registered trademark in the U.S. and other countries, + * exclusively licensed through X/Open Company, Ltd. + * + * ----------- + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER. + * + * Copyright 2008 Sun Microsystems, Inc. All rights reserved. + * + * The contents of this file are subject to the terms of the Common Development + * and Distribution License(CDDL) (the License). You may not use this file + * except in compliance with the License. + * + * You can obtain a copy of the License at + * http://identityconnectors.dev.java.net/CDDLv1.0.html + * See the License for the specific language governing permissions and + * limitations under the License. + * + * When distributing the Covered Code, include this CDDL Header Notice in each + * file and include the License file at identityconnectors/legal/license.txt. + * If applicable, add the following below this CDDL Header, with the fields + * enclosed by brackets [] replaced by your own identifying information: + * "Portions Copyrighted [year] [name of copyright owner]" + * ----------- + */ +using System; + +using System.IO; +using System.Xml; +using System.Collections; +using System.Collections.Generic; +using Org.IdentityConnectors.Common; +using Org.IdentityConnectors.Framework.Api; +using Org.IdentityConnectors.Framework.Api.Operations; +using Org.IdentityConnectors.Framework.Common; +using Org.IdentityConnectors.Framework.Common.Objects; +using Org.IdentityConnectors.Framework.Common.Objects.Filters; +using Org.IdentityConnectors.Framework.Spi; +using Org.IdentityConnectors.Framework.Spi.Operations; + +namespace Org.IdentityConnectors.Framework.Test +{ + public sealed class ToListResultsHandler { + private IList _objects + = new List(); + public bool Handle(ConnectorObject obj) { + _objects.Add(obj); + return true; + } + + public IList Objects { + get { + return _objects; + } + } + } + public abstract class TestHelpers { + + /** + * Method for convenient testing of local connectors. + */ + public static APIConfiguration CreateTestConfiguration(Type clazz, + Configuration config) { + return GetInstance().CreateTestConfigurationImpl(clazz, config); + } + + + public static IList SearchToList(SearchApiOp search, + ObjectClass oclass, + Filter filter) { + return SearchToList(search, oclass, filter, null); + } + + public static IList SearchToList(SearchApiOp search, + ObjectClass oclass, + Filter filter, + OperationOptions options) { + ToListResultsHandler handler = new + ToListResultsHandler(); + search.Search(oclass,filter, handler.Handle,options); + return handler.Objects; + } + /** + * Performs a raw, unfiltered search at the SPI level, + * eliminating duplicates from the result set. + * @param search The search SPI + * @param oclass The object class - passed through to + * connector so it may be null if the connecor + * allowing it to be null. (This is convenient for + * unit tests, but will not be the case in general) + * @param filter The filter to search on + * @param options The options - may be null - will + * be cast to an empty OperationOptions + * @return The list of results. + */ + public static IList SearchToList(SearchOp search, + ObjectClass oclass, + Filter filter) where T : class{ + return SearchToList(search,oclass,filter,null); + } + /** + * Performs a raw, unfiltered search at the SPI level, + * eliminating duplicates from the result set. + * @param search The search SPI + * @param oclass The object class - passed through to + * connector so it may be null if the connecor + * allowing it to be null. (This is convenient for + * unit tests, but will not be the case in general) + * @param filter The filter to search on + * @param options The options - may be null - will + * be cast to an empty OperationOptions + * @return The list of results. + */ + public static IList SearchToList(SearchOp search, + ObjectClass oclass, + Filter filter, + OperationOptions options) where T : class{ + ToListResultsHandler handler = new + ToListResultsHandler(); + Search(search,oclass,filter, handler.Handle, options); + return handler.Objects; + } + + /** + * Performs a raw, unfiltered search at the SPI level, + * eliminating duplicates from the result set. + * @param search The search SPI + * @param oclass The object class - passed through to + * connector so it may be null if the connecor + * allowing it to be null. (This is convenient for + * unit tests, but will not be the case in general) + * @param filter The filter to search on + * @param handler The result handler + * @param options The options - may be null - will + * be cast to an empty OperationOptions + */ + public static void Search(SearchOp search, + ObjectClass oclass, + Filter filter, + ResultsHandler handler, + OperationOptions options) where T : class { + GetInstance().SearchImpl(search, oclass, filter, handler, options); + } + + + //At some point we might make this pluggable, but for now, hard-code + private const String IMPL_NAME = + "Org.IdentityConnectors.Framework.Impl.Test.TestHelpersImpl"; + private static readonly object LOCK = new object(); + private static TestHelpers _instance; + + /** + * Returns the instance of this factory. + * @return The instance of this factory + */ + private static TestHelpers GetInstance() { + lock(LOCK) { + if (_instance == null) { + Type type = FrameworkInternalBridge.LoadType(IMPL_NAME); + Object obj = Activator.CreateInstance(type); + _instance = (TestHelpers)obj; + } + return _instance; + } + } + + + abstract protected APIConfiguration CreateTestConfigurationImpl(Type clazz, + Configuration config); + abstract protected void SearchImpl(SearchOp search, + ObjectClass oclass, + Filter filter, + ResultsHandler handler, + OperationOptions options) where T : class; + + + private static IDictionary _properties = null; + public static readonly string GLOBAL_PROPS = "connectors.xml"; + + public static string GetProperty(string key, string def) { + return CollectionUtil.GetValue(GetProperties(), key, def); + } + + private static IDictionary GetProperties() { + lock(LOCK) { + if (_properties == null) { + _properties = LoadProperties(); + } + } + // create a new instance so its not mutable + return CollectionUtil.NewReadOnlyDictionary(_properties); + } + + private static IDictionary LoadProperties() { + const string ERR = "Unable to load optional XML properties file: "; + string fn = null; + IDictionary props = null; + IDictionary ret = new Dictionary(); + try { + // load the local properties file + fn = Path.Combine(Environment.CurrentDirectory, "project.xml"); + props = LoadPropertiesFile(fn); + CollectionUtil.AddAll(ret, props); + } catch (Exception e) { + TraceUtil.TraceException(ERR + fn, e); + } + // global settings are prefixed w/ the project name + string prjName = Environment.GetEnvironmentVariable("project.name"); + if (!StringUtil.IsBlank(prjName)) { + // includes the parent configuration and the specific config. + IList configurations = CollectionUtil.NewList(new string[] {prjName}); + // determine the configuration property + string cfg = Environment.GetEnvironmentVariable("configuration"); + if (!StringUtil.IsBlank(cfg)) { + string name = prjName + "-" + cfg; + configurations.Add(name); + } + // load the user properties file (project specific) + fn = Path.Combine( + Environment.SpecialFolder.LocalApplicationData.ToString(), + GLOBAL_PROPS); + try { + props = LoadPropertiesFile(fn); + foreach (string cfgName in configurations) { + string cmp = cfgName + "."; + foreach (string key in props.Keys) { + if (key.StartsWith(cmp)) { + String newKey = key.Substring(cmp.Length); + ret[newKey] = props[key]; + } + } + } + } catch (IOException e) { + TraceUtil.TraceException(ERR + fn, e); + } + // load the project file then the project + // configuration specific file + foreach (string cfgFn in configurations) { + // load the user project specific file + try { + // load the local properties file + fn = System.IO.Path.Combine( + Environment.SpecialFolder.LocalApplicationData.ToString(), + cfgFn + ".xml"); + props = LoadPropertiesFile(fn); + CollectionUtil.AddAll(ret, props); + } catch (IOException e) { + TraceUtil.TraceException(ERR + fn, e); + } + } + } + // load the environment variables + foreach (DictionaryEntry entry in Environment.GetEnvironmentVariables()) { + ret[entry.Key.ToString()] = entry.Value.ToString(); + } + return ret; + } + + /// + /// Format for the xml file is simple <property name='' value=''/> + /// + /// + /// + public static IDictionary LoadPropertiesFile(string filename) { + IDictionary ret = new Dictionary(); + //Environment. + XmlTextReader reader = null; + try { + // Load the reader with the data file and ignore all white space nodes. + reader = new XmlTextReader(filename); + reader.WhitespaceHandling = WhitespaceHandling.None; + // Parse the file and display each of the nodes. + while (reader.Read()) { + if (reader.NodeType == XmlNodeType.Element && + reader.Name.Equals("property")) { + string name = reader.GetAttribute("name"); + string xmlValue = reader.GetAttribute("value"); + if (!StringUtil.IsBlank(name) && xmlValue != null) { + ret[name] = xmlValue; + } + } + } + } finally { + if (reader!=null) + reader.Close(); + } + return ret; + } + } +} diff --git a/FrameworkInternal/Api.cs b/FrameworkInternal/Api.cs new file mode 100644 index 00000000..8b6124bc --- /dev/null +++ b/FrameworkInternal/Api.cs @@ -0,0 +1,856 @@ +/* + * Copyright 2008 Sun Microsystems, Inc. All rights reserved. + * + * U.S. Government Rights - Commercial software. Government users + * are subject to the Sun Microsystems, Inc. standard license agreement + * and applicable provisions of the FAR and its supplements. + * + * Use is subject to license terms. + * + * This distribution may include materials developed by third parties. + * Sun, Sun Microsystems, the Sun logo, Java and Project Identity + * Connectors are trademarks or registered trademarks of Sun + * Microsystems, Inc. or its subsidiaries in the U.S. and other + * countries. + * + * UNIX is a registered trademark in the U.S. and other countries, + * exclusively licensed through X/Open Company, Ltd. + * + * ----------- + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER. + * + * Copyright 2008 Sun Microsystems, Inc. All rights reserved. + * + * The contents of this file are subject to the terms of the Common Development + * and Distribution License(CDDL) (the License). You may not use this file + * except in compliance with the License. + * + * You can obtain a copy of the License at + * http://identityconnectors.dev.java.net/CDDLv1.0.html + * See the License for the specific language governing permissions and + * limitations under the License. + * + * When distributing the Covered Code, include this CDDL Header Notice in each + * file and include the License file at identityconnectors/legal/license.txt. + * If applicable, add the following below this CDDL Header, with the fields + * enclosed by brackets [] replaced by your own identifying information: + * "Portions Copyrighted [year] [name of copyright owner]" + * ----------- + */ +using System; + +using Org.IdentityConnectors.Framework.Api; +using Org.IdentityConnectors.Framework.Api.Operations; +using Org.IdentityConnectors.Framework.Common; +using Org.IdentityConnectors.Framework.Common.Objects; +using Org.IdentityConnectors.Framework.Common.Objects.Filters; +using Org.IdentityConnectors.Framework.Common.Serializer; +using Org.IdentityConnectors.Framework.Impl.Api.Local; +using Org.IdentityConnectors.Framework.Impl.Api.Remote; +using Org.IdentityConnectors.Framework.Impl.Serializer.Binary; +using Org.IdentityConnectors.Common; +using Org.IdentityConnectors.Common.Pooling; +using Org.IdentityConnectors.Common.Proxy; +using Org.IdentityConnectors.Common.Security; +using System.Linq; +using System.Collections.Generic; +using System.Globalization; +using System.Reflection; +using System.Reflection.Emit; +using System.Diagnostics; +using System.Text; + +namespace Org.IdentityConnectors.Framework.Impl.Api +{ + #region ConfigurationPropertyImpl + /// + /// Internal class, public only for unit tests + /// + public class ConfigurationPropertyImpl : ConfigurationProperty { + + public ConfigurationPropertiesImpl Parent { get; set; } + + public int Order { get; set; } + + public string Name { get; set; } + + public string HelpMessageKey { get; set; } + + public string DisplayMessageKey { get; set; } + + public string GetHelpMessage(string def) { + return FormatMessage(HelpMessageKey,def); + } + + public string GetDisplayName(string def) { + return FormatMessage(DisplayMessageKey,def); + } + + public object Value { get; set; } + + public Type ValueType { get; set; } + + public bool IsConfidential { get; set; } + + public override int GetHashCode() + { + return Name.GetHashCode(); + } + + public override bool Equals(Object o) { + ConfigurationPropertyImpl other = o as ConfigurationPropertyImpl; + if ( other != null ) { + if (!Name.Equals(other.Name)) { + return false; + } + if (!CollectionUtil.Equals(Value,other.Value)) { + return false; + } + if (Order != other.Order) { + return false; + } + if (!CollectionUtil.Equals(HelpMessageKey,other.HelpMessageKey)) { + return false; + } + if (!CollectionUtil.Equals(DisplayMessageKey,other.DisplayMessageKey)) { + return false; + } + if (IsConfidential != other.IsConfidential) { + return false; + } + if (!CollectionUtil.Equals(ValueType,other.ValueType)) { + return false; + } + + return true; + } + return false; + } + private String FormatMessage(String key, String dflt, params object [] args) { + APIConfigurationImpl apiConfig = Parent.Parent; + CultureInfo locale = apiConfig.CultureInfo; + ConnectorMessages messages = + apiConfig.ConnectorInfo.Messages; + return messages.Format(locale, key, dflt, args); + } + } + #endregion + + #region ConfigurationPropertiesImpl + /// + /// Internal class, public only for unit tests + /// + public class ConfigurationPropertiesImpl : ConfigurationProperties { + //properties, sorted by "order" + private IList _properties; + //property names, sorted by "order + private IList _propertyNames; + private IDictionary _propertiesByName; + + public IList Properties { + get { + return _properties; + } + set { + ConfigurationPropertyImpl [] arr = + value == null ? new ConfigurationPropertyImpl[0] : value.ToArray(); + + Array.Sort(arr, + (p1,p2)=> {return p1.Order.CompareTo(p2.Order);}); + _properties = + CollectionUtil.NewReadOnlyList(arr); + IList propertyNames = new List(); + IDictionary propertiesByName = + new Dictionary(); + foreach (ConfigurationPropertyImpl property in _properties) { + propertyNames.Add(property.Name); + propertiesByName[property.Name] = property; + property.Parent = this; + } + _propertyNames = CollectionUtil.AsReadOnlyList(propertyNames); + _propertiesByName = CollectionUtil.AsReadOnlyDictionary(propertiesByName); + } + } + public ConfigurationProperty GetProperty(String name) { + return CollectionUtil.GetValue(_propertiesByName,name,null); + } + public IList PropertyNames { + get { + return _propertyNames; + } + } + public APIConfigurationImpl Parent { get; set; } + public void SetPropertyValue(String name, object val) { + ConfigurationProperty property = GetProperty(name); + if ( property == null ) { + String MSG = "Property '"+name+"' does not exist."; + throw new ArgumentException(MSG); + } + property.Value = val; + } + + public override int GetHashCode() + { + return CollectionUtil.HashCode(_properties,null); + } + + public override bool Equals(object o) { + ConfigurationPropertiesImpl other = o as ConfigurationPropertiesImpl; + if ( other != null ) { + return CollectionUtil.SetsEqual(_properties, + other._properties); + } + return false; + } + } + #endregion + + #region APIConfigurationImpl + /// + /// Internal class, public only for unit tests + /// + public class APIConfigurationImpl : APIConfiguration { + + private ObjectPoolConfiguration _connectorPooling; + + private ConfigurationPropertiesImpl _configurationProperties; + private ICollection _supportedOperations = + CollectionUtil.NewReadOnlySet(new Type[0]); + + private IDictionary _timeoutMap = + new Dictionary(); + private CultureInfo _cultureInfo; + + public ConfigurationProperties ConfigurationProperties { + get { + return _configurationProperties; + } + set { + if (_configurationProperties != null) { + _configurationProperties.Parent = null; + } + _configurationProperties = (ConfigurationPropertiesImpl)value; + if (_configurationProperties != null) { + _configurationProperties.Parent = this; + } + } + } + public IDictionary TimeoutMap { + get { + return _timeoutMap; + } + set { + _timeoutMap = value; + } + } + public bool IsConnectorPoolingSupported { get; set;} + public ObjectPoolConfiguration ConnectorPoolConfiguration + { + get { + if (_connectorPooling == null) { + _connectorPooling = new ObjectPoolConfiguration(); + } + return _connectorPooling; + } + set { + _connectorPooling = value; + } + } + public CultureInfo CultureInfo { + get { + return _cultureInfo == null ? CultureInfo.CurrentCulture : _cultureInfo; + } + set { + _cultureInfo = value; + } + } + public ICollection SupportedOperations { + get { + return _supportedOperations; + } + set { + _supportedOperations = CollectionUtil.NewReadOnlySet(value); + } + } + + public int GetTimeout(Type operation) { + FrameworkUtil.AssertApiOperation(operation); + return CollectionUtil.GetValue(_timeoutMap,operation, + APIConstants.NO_TIMEOUT); + } + public void SetTimeout(Type operation, int timeout) { + FrameworkUtil.AssertApiOperation(operation); + _timeoutMap[operation] = timeout; + } + + public AbstractConnectorInfo ConnectorInfo { get; set; } + + public int ProducerBufferSize { get; set; } + + public APIConfigurationImpl() { + ProducerBufferSize = 100; + } + } + #endregion + + #region AbstractConnectorInfo + /// + /// internal class, public only for unit tests + /// + public class AbstractConnectorInfo : ConnectorInfo { + + private APIConfigurationImpl _defaultAPIConfiguration; + + + public string GetConnectorDisplayName(CultureInfo info) { + return Messages.Format(info, + ConnectorDisplayNameKey, + ConnectorKey.ConnectorName); + } + + public ConnectorKey ConnectorKey { get; set; } + + public string ConnectorDisplayNameKey { get; set; } + + public ConnectorMessagesImpl Messages { get; set; } + + public APIConfigurationImpl DefaultAPIConfiguration { + get { + return _defaultAPIConfiguration; + } + set { + if ( value != null ) { + value.ConnectorInfo = this; + } + _defaultAPIConfiguration = value; + } + } + + public APIConfiguration CreateDefaultAPIConfiguration() { + APIConfigurationImpl rv = + (APIConfigurationImpl) + SerializerUtil.CloneObject(_defaultAPIConfiguration); + rv.ConnectorInfo = this; + return rv; + } + } + #endregion + + #region ConnectorMessagesImpl + /// + /// internal class, public only for unit tests + /// + public class ConnectorMessagesImpl : ConnectorMessages { + + private IDictionary> + _catalogs = new Dictionary>(); + + public String Format(CultureInfo locale, String key, String dflt, params object [] args) { + if ( key == null ) { + return dflt; + } + if ( locale == null ) { + locale = CultureInfo.CurrentCulture; + } + if ( dflt == null ) { + dflt = key; + } + CultureInfo foundCulture = locale; + IDictionary + catalog = CollectionUtil.GetValue(_catalogs,foundCulture,null); + //check neutral culture + if ( catalog == null ) { + foundCulture = foundCulture.Parent; + catalog = CollectionUtil.GetValue(_catalogs,foundCulture,null); + } + //check invariant culture + if ( catalog == null ) { + foundCulture = foundCulture.Parent; + catalog = CollectionUtil.GetValue(_catalogs,foundCulture,null); + } + String message = null; + if ( catalog != null ) { + message = CollectionUtil.GetValue(catalog,key,null); + } + if ( message == null ) { + return dflt; + } + else { + //TODO: think more about this since the formatting + //is slightly different than Java + return String.Format(foundCulture,message,args); + } + } + + public IDictionary> Catalogs { + get { + return _catalogs; + } + set { + if ( value == null ) { + _catalogs = + new Dictionary>(); + } + else { + _catalogs = value; + } + } + } + } + #endregion + + #region ConnectorInfoManagerFactoryImpl + public sealed class ConnectorInfoManagerFactoryImpl : + ConnectorInfoManagerFactory { + private class RemoteManagerKey { + private readonly String _host; + private readonly int _port; + + public RemoteManagerKey(RemoteFrameworkConnectionInfo info) { + _host = info.Host; + _port = info.Port; + } + + public override bool Equals(Object o) { + if ( o is RemoteManagerKey ) { + RemoteManagerKey other = (RemoteManagerKey)o; + if (!_host.Equals(other._host)) { + return false; + } + if (_port != other._port) { + return false; + } + return true; + } + return false; + } + + public override int GetHashCode() { + return _host.GetHashCode()^_port; + } + + } + + private Object LOCAL_LOCK = new Object(); + private Object REMOTE_LOCK = new Object(); + + private ConnectorInfoManager + _localManagerCache; + + private IDictionary + _remoteManagerCache = new Dictionary(); + + public ConnectorInfoManagerFactoryImpl() { + + } + + + + public override void ClearRemoteCache() { + lock (REMOTE_LOCK) { + _remoteManagerCache.Clear(); + } + } + + + public override ConnectorInfoManager GetLocalManager() + { + lock (LOCAL_LOCK) { + ConnectorInfoManager rv = _localManagerCache; + if ( rv == null ) { + rv = new LocalConnectorInfoManagerImpl(); + } + _localManagerCache = rv; + return rv; + } + } + + public override ConnectorInfoManager GetRemoteManager(RemoteFrameworkConnectionInfo info) + { + RemoteManagerKey key = new RemoteManagerKey(info); + lock (REMOTE_LOCK) { + RemoteConnectorInfoManagerImpl rv = CollectionUtil.GetValue(_remoteManagerCache,key,null); + if ( rv == null ) { + rv = new RemoteConnectorInfoManagerImpl(info); + } + _remoteManagerCache[key] = rv; + return rv.Derive(info); + } + } + + } + #endregion + + #region AbstractConnectorFacade + internal abstract class AbstractConnectorFacade : ConnectorFacade { + + private readonly APIConfigurationImpl _configuration; + + + /** + * Builds up the maps of supported operations and calls. + */ + public AbstractConnectorFacade(APIConfigurationImpl configuration) { + Assertions.NullCheck(configuration,"configuration"); + //clone in case application tries to modify + //after the fact. this is necessary to + //ensure thread-safety of a ConnectorFacade + //also, configuration is used as a key in the + //pool, so it is important that it not be modified. + _configuration = (APIConfigurationImpl)SerializerUtil.CloneObject(configuration); + //parent ref not included in the clone + _configuration.ConnectorInfo=(configuration.ConnectorInfo); + } + + /** + * Return an instance of an API operation. + * + * @return null if the operation is not support otherwise + * return an instance of the operation. + * @see com.sun.openconnectors.framework.api.ConnectorFacade#getOperation(java.lang.Class) + */ + public APIOperation GetOperation(Type api) { + if (!SupportedOperations.Contains(api)) { + return null; + } + return GetOperationImplementation(api); + } + + /** + * {@inheritDoc} + */ + public ICollection SupportedOperations { + get { + return _configuration.SupportedOperations; + } + } + + // ======================================================================= + // Operation API Methods + // ======================================================================= + /** + * {@inheritDoc} + */ + public Schema Schema() { + return ((SchemaApiOp) this.GetOperationCheckSupported(typeof(SchemaApiOp))) + .Schema(); + } + + /** + * {@inheritDoc} + */ + public Uid Create(ObjectClass oclass, ICollection attrs, OperationOptions options) { + CreateApiOp op = ((CreateApiOp) GetOperationCheckSupported(typeof(CreateApiOp))); + return op.Create(oclass,attrs,options); + } + + /** + * {@inheritDoc} + */ + public void Delete(ObjectClass objClass, Uid uid, OperationOptions options) { + ((DeleteApiOp) this.GetOperationCheckSupported(typeof(DeleteApiOp))).Delete(objClass, uid, options); + } + + /** + * {@inheritDoc} + */ + public void Search(ObjectClass oclass,Filter filter, ResultsHandler handler, OperationOptions options) { + ((SearchApiOp) this.GetOperationCheckSupported(typeof(SearchApiOp))).Search( + oclass,filter, handler, options); + } + + /** + * {@inheritDoc} + */ + public Uid Update(UpdateApiType type, ObjectClass objclass, ICollection attrs, OperationOptions options) { + return ((UpdateApiOp) this.GetOperationCheckSupported(typeof(UpdateApiOp))) + .Update(type, objclass, attrs, options); + } + + /** + * {@inheritDoc} + */ + public void Authenticate(String username, GuardedString password, OperationOptions options) { + ((AuthenticationApiOp) this + .GetOperationCheckSupported(typeof(AuthenticationApiOp))).Authenticate( + username, password, options); + } + + /** + * {@inheritDoc} + */ + public ConnectorObject GetObject(ObjectClass objClass, Uid uid, OperationOptions options) { + return ((GetApiOp) this.GetOperationCheckSupported(typeof(GetApiOp))) + .GetObject(objClass, uid, options); + } + /** + * {@inheritDoc} + */ + public Object RunScriptOnConnector(ScriptContext request, + OperationOptions options) { + return ((ScriptOnConnectorApiOp) this + .GetOperationCheckSupported(typeof(ScriptOnConnectorApiOp))) + .RunScriptOnConnector(request, options); + } + + /** + * {@inheritDoc} + */ + public Object RunScriptOnResource(ScriptContext request, + OperationOptions options) { + return ((ScriptOnResourceApiOp) this + .GetOperationCheckSupported(typeof(ScriptOnResourceApiOp))) + .RunScriptOnResource(request, options); + } + + /** + * {@inheritDoc} + */ + public void Test() { + ((TestApiOp) this.GetOperationCheckSupported(typeof(TestApiOp))).Test(); + } + + /** + * {@inheritDoc} + */ + public void Validate() { + ((ValidateApiOp) this.GetOperationCheckSupported(typeof(ValidateApiOp))).Validate(); + } + + public void Sync(ObjectClass objClass, SyncToken token, + SyncResultsHandler handler, + OperationOptions options) { + ((SyncApiOp)this.GetOperationCheckSupported(typeof(SyncApiOp))) + .Sync(objClass, token, handler, options); + } + + private APIOperation GetOperationCheckSupported(Type api) { + // check if this operation is supported. + if (!SupportedOperations.Contains(api)) { + String MSG = "Operation ''{0}'' not supported."; + String str = String.Format(MSG, api); + throw new InvalidOperationException(str); + } + return GetOperationImplementation(api); + } + + /** + * Gets the implementation of the given operation + * @param api The operation to implement. + * @return The implementation + */ + protected abstract APIOperation GetOperationImplementation(Type api); + + protected APIConfigurationImpl GetAPIConfiguration() { + return _configuration; + } + + /** + * Creates a new {@link APIOperation} proxy given a handler. + */ + protected APIOperation NewAPIOperationProxy(Type api, InvocationHandler handler) { + return (APIOperation) Proxy.NewProxyInstance(api, handler); + } + + private static bool LOGGINGPROXY_ENABLED; + static AbstractConnectorFacade() { + string enabled = System.Configuration. + ConfigurationManager.AppSettings.Get("logging.proxy"); + LOGGINGPROXY_ENABLED = StringUtil.IsTrue(enabled); + } + + protected APIOperation CreateLoggingProxy(Type api, APIOperation target) { + APIOperation ret = target; + if (LOGGINGPROXY_ENABLED) { + LoggingProxy logging = new LoggingProxy(api, target); + ret = NewAPIOperationProxy(api, logging); + } + return ret; + } + } + #endregion + + #region ConnectorFacadeFactoryImpl + public class ConnectorFacadeFactoryImpl : ConnectorFacadeFactory { + + + + + /** + * {@inheritDoc} + */ + public override ConnectorFacade NewInstance(APIConfiguration config) { + ConnectorFacade ret = null; + APIConfigurationImpl impl = (APIConfigurationImpl) config; + AbstractConnectorInfo connectorInfo = impl.ConnectorInfo; + if ( connectorInfo is LocalConnectorInfoImpl ) { + LocalConnectorInfoImpl localInfo = + (LocalConnectorInfoImpl)connectorInfo; + // create a new Provisioner.. + ret = new LocalConnectorFacadeImpl(localInfo,impl); + } + else { + ret = new RemoteConnectorFacadeImpl(impl); + } + return ret; + } + + + /** + * Dispose of all object pools and other resources associated with this + * class. + */ + public override void Dispose() { + ConnectorPoolManager.Dispose(); + } + + } + #endregion + + #region ObjectStreamHandler + internal interface ObjectStreamHandler { + bool Handle(Object obj); + } + #endregion + + #region StreamHandlerUtil + internal static class StreamHandlerUtil { + + /** + * Adapts from a ObjectStreamHandler to a ResultsHandler + */ + private class ResultsHandlerAdapter { + private readonly ObjectStreamHandler _target; + public ResultsHandlerAdapter(ObjectStreamHandler target) { + _target = target; + } + public bool Handle(ConnectorObject obj) { + return _target.Handle(obj); + } + } + + /** + * Adapts from a ObjectStreamHandler to a SyncResultsHandler + */ + private class SyncResultsHandlerAdapter { + private readonly ObjectStreamHandler _target; + public SyncResultsHandlerAdapter(ObjectStreamHandler target) { + _target = target; + } + public bool Handle(SyncDelta obj) { + return _target.Handle(obj); + } + } + + /** + * Adapts from a ObjectStreamHandler to a SyncResultsHandler + */ + private class ObjectStreamHandlerAdapter : ObjectStreamHandler { + private readonly Type _targetInterface; + private readonly Object _target; + public ObjectStreamHandlerAdapter(Type targetInterface, Object target) { + Assertions.NullCheck(targetInterface, "targetInterface"); + Assertions.NullCheck(target, "target"); + if (!targetInterface.IsAssignableFrom(target.GetType())) { + throw new ArgumentException("Target" +targetInterface+" "+target); + } + if (!IsAdaptableToObjectStreamHandler(targetInterface)) { + throw new ArgumentException("Target interface not supported: "+targetInterface); + } + _targetInterface = targetInterface; + _target = target; + } + public bool Handle(Object obj) { + if (_targetInterface.Equals( typeof(ResultsHandler) )) { + return ((ResultsHandler)_target)((ConnectorObject)obj); + } + else if (_targetInterface.Equals(typeof(SyncResultsHandler))) { + return ((SyncResultsHandler)_target)((SyncDelta)obj); + } + else { + throw new InvalidOperationException("Unhandled case: "+_targetInterface); + } + } + } + + public static bool IsAdaptableToObjectStreamHandler(Type clazz) { + return ( typeof(ResultsHandler).IsAssignableFrom(clazz) || + typeof(SyncResultsHandler).IsAssignableFrom(clazz)); + } + + public static ObjectStreamHandler AdaptToObjectStreamHandler(Type interfaceType, + Object target) { + return new ObjectStreamHandlerAdapter(interfaceType,target); + } + public static Object AdaptFromObjectStreamHandler(Type interfaceType, + ObjectStreamHandler target) { + if (interfaceType.Equals( typeof(ResultsHandler) ) ){ + return new ResultsHandler(new ResultsHandlerAdapter(target).Handle); + } + else if (interfaceType.Equals( typeof(SyncResultsHandler) ) ) { + return new SyncResultsHandler(new SyncResultsHandlerAdapter(target).Handle); + } + else { + throw new InvalidOperationException("Unhandled case: "+interfaceType); + } + } + + } + #endregion + + #region LoggingProxy + public class LoggingProxy : InvocationHandler { + + private readonly Type _op; + private readonly object _target; + + public LoggingProxy(Type api, object target) { + _op = api; + _target = target; + } + /// + /// Log all operations. + /// + public Object Invoke(Object proxy, MethodInfo method, Object [] args) { + //do not proxy equals, hashCode, toString + if (method.DeclaringType.Equals(typeof(object))) { + return method.Invoke(this, args); + } + StringBuilder bld = new StringBuilder(); + bld.Append("Enter: "); + AddMethodName(bld, method); + bld.Append('('); + for (int i=0; args != null && i < args.Length; i++) { + if (i != 0) { + bld.Append(", "); + } + bld.Append('{').Append(i).Append('}'); + } + bld.Append(')'); + // write out trace header + Trace.TraceInformation(bld.ToString(), args); + // invoke the method + try { + object ret = method.Invoke(_target, args); + // clear out buffer. + bld.Length = 0; + bld.Append("Return: "); + AddMethodName(bld, method); + bld.Append("({0})"); + Trace.TraceInformation(bld.ToString(), ret); + return ret; + } catch (TargetInvocationException e) { + Exception root = e.InnerException; + throw root; + } + } + + private void AddMethodName(StringBuilder bld, MethodInfo method) { + bld.Append(_op.Name); + bld.Append('.'); + bld.Append(method.Name); + } + } + #endregion + +} diff --git a/FrameworkInternal/ApiLocal.cs b/FrameworkInternal/ApiLocal.cs new file mode 100644 index 00000000..058d59b5 --- /dev/null +++ b/FrameworkInternal/ApiLocal.cs @@ -0,0 +1,1030 @@ +/* + * Copyright 2008 Sun Microsystems, Inc. All rights reserved. + * + * U.S. Government Rights - Commercial software. Government users + * are subject to the Sun Microsystems, Inc. standard license agreement + * and applicable provisions of the FAR and its supplements. + * + * Use is subject to license terms. + * + * This distribution may include materials developed by third parties. + * Sun, Sun Microsystems, the Sun logo, Java and Project Identity + * Connectors are trademarks or registered trademarks of Sun + * Microsystems, Inc. or its subsidiaries in the U.S. and other + * countries. + * + * UNIX is a registered trademark in the U.S. and other countries, + * exclusively licensed through X/Open Company, Ltd. + * + * ----------- + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER. + * + * Copyright 2008 Sun Microsystems, Inc. All rights reserved. + * + * The contents of this file are subject to the terms of the Common Development + * and Distribution License(CDDL) (the License). You may not use this file + * except in compliance with the License. + * + * You can obtain a copy of the License at + * http://identityconnectors.dev.java.net/CDDLv1.0.html + * See the License for the specific language governing permissions and + * limitations under the License. + * + * When distributing the Covered Code, include this CDDL Header Notice in each + * file and include the License file at identityconnectors/legal/license.txt. + * If applicable, add the following below this CDDL Header, with the fields + * enclosed by brackets [] replaced by your own identifying information: + * "Portions Copyrighted [year] [name of copyright owner]" + * ----------- + */ +using System; +using System.Diagnostics; +using Org.IdentityConnectors.Common; +using Org.IdentityConnectors.Common.Pooling; +using Org.IdentityConnectors.Common.Proxy; +using Org.IdentityConnectors.Framework.Api; +using Org.IdentityConnectors.Framework.Api.Operations; +using Org.IdentityConnectors.Framework.Common; +using Org.IdentityConnectors.Framework.Common.Objects; +using Org.IdentityConnectors.Framework.Common.Exceptions; +using Org.IdentityConnectors.Framework.Common.Serializer; +using Org.IdentityConnectors.Framework.Impl.Api.Remote; +using Org.IdentityConnectors.Framework.Impl.Api.Local.Operations; +using Org.IdentityConnectors.Framework.Spi; +using System.Collections.Generic; +using System.Globalization; +using System.Reflection; +using System.Resources; +using System.Threading; +using System.IO; +using System.Linq; +namespace Org.IdentityConnectors.Framework.Impl.Api.Local +{ + #region ConnectorPoolManager + public class ConnectorPoolManager { + + + private class ConnectorPoolKey { + private readonly ConnectorKey _connectorKey; + private readonly ConfigurationPropertiesImpl _configProperties; + private readonly ObjectPoolConfiguration _poolingConfig; + public ConnectorPoolKey(ConnectorKey connectorKey, + ConfigurationPropertiesImpl configProperties, + ObjectPoolConfiguration poolingConfig) { + _connectorKey = connectorKey; + _configProperties = configProperties; + _poolingConfig = poolingConfig; + } + public override int GetHashCode() { + return _connectorKey.GetHashCode(); + } + public override bool Equals(Object o) { + if ( o is ConnectorPoolKey ) { + ConnectorPoolKey other = (ConnectorPoolKey)o; + if (!_connectorKey.Equals(other._connectorKey)) { + return false; + } + if (!_configProperties.Equals(other._configProperties)) { + return false; + } + if (!_poolingConfig.Equals(other._poolingConfig)) { + return false; + } + return true; + } + return false; + } + } + + private class ConnectorPoolHandler : ObjectPoolHandler { + private readonly APIConfigurationImpl _apiConfiguration; + private readonly LocalConnectorInfoImpl _localInfo; + public ConnectorPoolHandler(APIConfigurationImpl apiConfiguration, + LocalConnectorInfoImpl localInfo) { + _apiConfiguration = apiConfiguration; + _localInfo = localInfo; + } + public PoolableConnector NewObject() { + Configuration config = + CSharpClassProperties.CreateBean((ConfigurationPropertiesImpl)_apiConfiguration.ConfigurationProperties, + _localInfo.ConnectorConfigurationClass); + PoolableConnector connector = + (PoolableConnector)(Activator.CreateInstance(_localInfo.ConnectorClass)); + connector.Init(config); + return connector; + } + public void TestObject(PoolableConnector obj) { + obj.CheckAlive(); + } + public void DisposeObject(PoolableConnector obj) { + obj.Dispose(); + } + } + + /** + * Cache of the various _pools.. + */ + private static readonly IDictionary> + _pools = new Dictionary>(); + + + /** + * Get a object pool for this connector if it supports connector pooling. + */ + public static ObjectPool GetPool(APIConfigurationImpl impl, + LocalConnectorInfoImpl localInfo) { + ObjectPool pool = null; + // determine if this connector wants generic connector pooling.. + if (impl.IsConnectorPoolingSupported) { + ConnectorPoolKey key = + new ConnectorPoolKey( + impl.ConnectorInfo.ConnectorKey, + (ConfigurationPropertiesImpl)impl.ConfigurationProperties, + impl.ConnectorPoolConfiguration); + + lock (_pools) { + // get the pool associated.. + pool = CollectionUtil.GetValue(_pools,key,null); + // create a new pool if it doesn't exist.. + if (pool == null) { + Trace.TraceInformation("Creating new pool: "+ + impl.ConnectorInfo.ConnectorKey); + // this instance is strictly used for the pool.. + pool = new ObjectPool( + new ConnectorPoolHandler(impl,localInfo), + impl.ConnectorPoolConfiguration); + // add back to the map of _pools.. + _pools[key] = pool; + } + } + } + return pool; + } + + public static void Dispose() { + lock (_pools) { + // close each pool.. + foreach (ObjectPool pool in _pools.Values) { + try { + pool.Shutdown(); + } catch (Exception e) { + TraceUtil.TraceException("Failed to close pool", e); + } + } + // clear the map of all _pools.. + _pools.Clear(); + } + } + + } + #endregion + + #region CSharpClassProperties + internal static class CSharpClassProperties + { + public static ConfigurationPropertiesImpl + CreateConfigurationProperties(Configuration defaultObject) + { + Type config = defaultObject.GetType(); + ConfigurationPropertiesImpl properties = + new ConfigurationPropertiesImpl(); + IList temp = + new List(); + IDictionary descs = GetFilteredProperties(config); + + foreach (PropertyInfo desc in descs.Values) { + + String name = desc.Name; + + // get the configuration options.. + ConfigurationPropertyAttribute options = + GetPropertyOptions(desc); + // use the options to set internal properties.. + int order = 0; + String helpKey = name + ".help"; + String displKey = name + ".display"; + bool confidential = false; + if (options != null) { + // determine the display and help keys.. + if (!StringUtil.IsBlank(options.HelpMessageKey)) { + helpKey = options.HelpMessageKey; + } + if (!StringUtil.IsBlank(options.DisplayMessageKey)) { + displKey = options.DisplayMessageKey; + } + // determine the order.. + order = options.Order; + + confidential = options.Confidential; + } + Type type = desc.PropertyType; + if (!FrameworkUtil.IsSupportedConfigurationType(type)) { + const String MSG = "Property type ''{0}'' is not supported."; + throw new ArgumentException(String.Format(MSG, type)); + } + + Object value = desc.GetValue(defaultObject,null); + + ConfigurationPropertyImpl prop = new ConfigurationPropertyImpl(); + prop.IsConfidential=confidential; + prop.DisplayMessageKey=displKey; + prop.HelpMessageKey=helpKey; + prop.Name=name; + prop.Order=order; + prop.Value=value; + prop.ValueType=type; + + temp.Add(prop); + + } + properties.Properties=(temp); + return properties; + } + + public static Configuration + CreateBean(ConfigurationPropertiesImpl properties, + Type config) { + Configuration rv = (Configuration)Activator.CreateInstance(config); + rv.CultureInfo=(properties.Parent.CultureInfo); + rv.ConnectorMessages=properties.Parent.ConnectorInfo.Messages; + IDictionary descriptors = + GetFilteredProperties(config); + foreach (ConfigurationPropertyImpl property in properties.Properties) { + String name = property.Name; + PropertyInfo desc = + CollectionUtil.GetValue(descriptors,name,null); + if ( desc == null ) { + String FMT = + "Class ''{0}'' does not have a property ''{1}''."; + String MSG = String.Format(FMT, + config.Name, + name); + throw new ArgumentException(MSG); + } + object val = property.Value; + //some value types such as arrays + //are mutable. make sure the config object + //has its own copy + val = SerializerUtil.CloneObject(val); + desc.SetValue(rv,val,null); + } + return rv; + } + + private static IDictionary + GetFilteredProperties(Type config) + { + IDictionary rv = + new Dictionary(); + PropertyInfo [] descriptors = config.GetProperties(); + foreach (PropertyInfo descriptor in descriptors) { + String propName = descriptor.Name; + if ( !descriptor.CanWrite ) { + //if there's no setter, ignore it + continue; + } + if ( "CultureInfo".Equals(propName) ) { + // exclude setLocale since its part of the interface.. + continue; + } + if ("ConnectorMessages".Equals(propName)) { + continue; + } + if ( !descriptor.CanRead ) { + const String FMT = + "Found setter ''{0}'' but not the corresponding getter."; + String MSG = String.Format(FMT,propName); + throw new ArgumentException(MSG); + } + rv[propName] = descriptor; + } + return rv; + } + + + + /** + * Get the option from the property. + */ + private static ConfigurationPropertyAttribute GetPropertyOptions( + PropertyInfo propertyInfo) { + Object [] objs = + propertyInfo.GetCustomAttributes( + typeof(ConfigurationPropertyAttribute),true); + if ( objs.Length == 0 ) { + return null; + } + else { + return (ConfigurationPropertyAttribute)objs[0]; + } + } + + } + #endregion + + #region LocalConnectorInfoManagerImpl + internal class LocalConnectorInfoManagerImpl : ConnectorInfoManager + { + private IList _connectorInfo; + + public LocalConnectorInfoManagerImpl() + { + _connectorInfo = new List(); + Assembly assembly = Assembly.GetExecutingAssembly(); + FileInfo thisAssemblyFile = + new FileInfo(assembly.Location); + DirectoryInfo directory = + thisAssemblyFile.Directory; + FileInfo [] files = + directory.GetFiles("*.Connector.dll"); + foreach (FileInfo file in files) { + Assembly lib = + Assembly.LoadFrom(file.ToString()); + CollectionUtil.AddAll(_connectorInfo, + ProcessAssembly(lib)); + } + } + + private IList ProcessAssembly(Assembly assembly) { + IList rv = new List(); + + Type [] types = null; + try { + types = assembly.GetTypes(); + } + catch (Exception e) { + TraceUtil.TraceException("Unable to load assembly: "+assembly.FullName+". Assembly will be ignored.",e); + } + + foreach (Type type in types) { + Object [] attributes = type.GetCustomAttributes( + typeof(ConnectorClassAttribute), + false); + if ( attributes.Length > 0 ) { + ConnectorClassAttribute attribute = + (ConnectorClassAttribute)attributes[0]; + LocalConnectorInfoImpl info = + CreateConnectorInfo(assembly,type,attribute); + rv.Add(info); + } + } + return rv; + } + + private LocalConnectorInfoImpl CreateConnectorInfo(Assembly assembly, + Type connectorClass, + ConnectorClassAttribute attribute) { + String fileName = assembly.Location; + if (!typeof(Connector).IsAssignableFrom(connectorClass)) { + String MSG = ( "File "+fileName+ + " declares a connector "+connectorClass+ + " that does not implement Connector."); + throw new ConfigurationException(MSG); + } + Type connectorConfigurationClass = attribute.ConnectorConfigurationType; + if ( connectorConfigurationClass == null ) { + String MSG = ( "File "+fileName+ + " contains a ConnectorInfo attribute "+ + "with no connector configuration class."); + throw new ConfigurationException(MSG); + } + if (!typeof(Configuration).IsAssignableFrom(connectorConfigurationClass)) { + String MSG = ("File "+fileName+" declared a connector "+ + "configuration class "+connectorConfigurationClass+ + " that does not implement IConfiguration."); + throw new ConfigurationException(MSG); + } + String connectorDisplayNameKey = + attribute.ConnectorDisplayNameKey; + if ( connectorDisplayNameKey == null ) { + String MSG = ( "File "+fileName+ + " contains a ConnectorInfo attribute "+ + "with no connector display name."); + throw new ConfigurationException(MSG); + } + ConnectorKey key = + new ConnectorKey(assembly.GetName().Name, + assembly.GetName().Version.ToString(), + connectorClass.Namespace+"."+connectorClass.Name); + LocalConnectorInfoImpl rv = new LocalConnectorInfoImpl(); + rv.ConnectorClass = connectorClass; + rv.ConnectorConfigurationClass = connectorConfigurationClass; + rv.ConnectorDisplayNameKey = connectorDisplayNameKey; + rv.ConnectorKey = key; + rv.DefaultAPIConfiguration = CreateDefaultAPIConfiguration(rv); + rv.Messages = LoadMessages(assembly,rv,attribute.MessageCatalogPath); + return rv; + } + + private APIConfigurationImpl + CreateDefaultAPIConfiguration(LocalConnectorInfoImpl localInfo) { + Type connectorClass = + localInfo.ConnectorClass; + APIConfigurationImpl rv = new APIConfigurationImpl(); + Configuration config = + (Configuration)Activator.CreateInstance(localInfo.ConnectorConfigurationClass); + bool pooling = IsPoolingSupported(connectorClass); + rv.IsConnectorPoolingSupported=pooling; + rv.ConfigurationProperties=(CSharpClassProperties.CreateConfigurationProperties(config)); + rv.ConnectorInfo=(localInfo); + rv.SupportedOperations=(FrameworkUtil.GetDefaultSupportedOperations(connectorClass)); + return rv; + } + + private static bool IsPoolingSupported(Type clazz) { + return ReflectionUtil.IsParentTypeOf(typeof(PoolableConnector),clazz); + } + /// + /// Given an assembly, returns the list of cultures that + /// it is localized for + /// + /// + /// + private CultureInfo [] GetLocalizedCultures(Assembly assembly) { + FileInfo assemblyFile = + new FileInfo(assembly.Location); + DirectoryInfo directory = + assemblyFile.Directory; + IList temp = new List(); + DirectoryInfo [] subdirs = directory.GetDirectories(); + foreach (DirectoryInfo subdir in subdirs) { + String name = subdir.Name; + CultureInfo cultureInfo; + //get the culture if the directory is the name of the + //culture + try { + cultureInfo = new CultureInfo(name); + } + catch (ArgumentException) { + //invalid culture + continue; + } + //see if there's a satellite assembly for this + try { + assembly.GetSatelliteAssembly(cultureInfo); + } + catch (Exception) { + //invalid assembly + continue; + } + temp.Add(cultureInfo); + } + temp.Add(CultureInfo.InvariantCulture); + return temp.ToArray(); + } + + private ConnectorMessagesImpl LoadMessages(Assembly assembly, + LocalConnectorInfoImpl info, + String nameBase) { + if ( StringUtil.IsBlank(nameBase) ) { + String pkage = + info.ConnectorClass.Namespace; + nameBase = pkage+".Messages"; + } + ConnectorMessagesImpl rv = new ConnectorMessagesImpl(); + CultureInfo [] cultures = GetLocalizedCultures(assembly); + ResourceManager manager = new ResourceManager(nameBase,assembly); + foreach (CultureInfo culture in cultures) { + ResourceSet resourceSet = manager.GetResourceSet(culture,true,false); + if ( resourceSet != null ) { + IDictionary temp = new + Dictionary(); + foreach (System.Collections.DictionaryEntry entry in resourceSet) { + String key = ""+entry.Key; + String val = ""+entry.Value; + temp[key] = val; + } + rv.Catalogs[culture] = temp; + } + } + + return rv; + } + + public ConnectorInfo FindConnectorInfo(ConnectorKey key) { + foreach (ConnectorInfo info in _connectorInfo) { + if ( info.ConnectorKey.Equals(key) ) { + return info; + } + } + return null; + } + public IList ConnectorInfos { + get { + return CollectionUtil.AsReadOnlyList(_connectorInfo); + } + } + } + #endregion + + #region LocalConnectorInfoImpl + /// + /// Internal class, public only for unit tests + /// + public class LocalConnectorInfoImpl : AbstractConnectorInfo + { + public RemoteConnectorInfoImpl ToRemote() { + RemoteConnectorInfoImpl rv = new RemoteConnectorInfoImpl(); + rv.ConnectorDisplayNameKey=ConnectorDisplayNameKey; + rv.ConnectorKey=ConnectorKey; + rv.DefaultAPIConfiguration=DefaultAPIConfiguration; + rv.Messages=Messages; + return rv; + } + public Type ConnectorClass {get;set;} + public Type ConnectorConfigurationClass {get;set;} + } + #endregion + + #region LocalConnectorFacadeImpl + internal class LocalConnectorFacadeImpl : AbstractConnectorFacade { + + // ======================================================================= + // Constants + // ======================================================================= + /** + * Map the API interfaces to their implementation counterparts. + */ + private static readonly IDictionary API_TO_IMPL= + new Dictionary(); + + private static void AddImplementation(Type inter, + Type impl) { + ConstructorInfo info = + impl.GetConstructor(new Type[]{typeof(ConnectorOperationalContext), + typeof(Connector)}); + if ( info == null ) { + throw new ArgumentException(impl+" does not define the proper constructor"); + } + API_TO_IMPL[inter]= info; + } + + static LocalConnectorFacadeImpl() { + AddImplementation(typeof(CreateApiOp), typeof(CreateImpl)); + AddImplementation(typeof(DeleteApiOp), typeof(DeleteImpl)); + AddImplementation(typeof(SchemaApiOp), typeof(SchemaImpl)); + AddImplementation(typeof(SearchApiOp), typeof(SearchImpl)); + AddImplementation(typeof(UpdateApiOp), typeof(UpdateImpl)); + AddImplementation(typeof(AuthenticationApiOp), typeof(AuthenticationImpl)); + AddImplementation(typeof(TestApiOp), typeof(TestImpl)); + AddImplementation(typeof(ScriptOnConnectorApiOp), typeof(ScriptOnConnectorImpl)); + AddImplementation(typeof(ScriptOnResourceApiOp), typeof(ScriptOnResourceImpl)); + AddImplementation(typeof(SyncApiOp), typeof(SyncImpl)); + } + + + + // ======================================================================= + // Fields + // ======================================================================= + /** + * Pool used to acquire connection from to use during operations. + */ + + /** + * The connector info + */ + private readonly LocalConnectorInfoImpl connectorInfo; + + /** + * Builds up the maps of supported operations and calls. + */ + public LocalConnectorFacadeImpl(LocalConnectorInfoImpl connectorInfo, + APIConfigurationImpl apiConfiguration) + :base(apiConfiguration) { + this.connectorInfo = connectorInfo; + } + + // ======================================================================= + // ConnectorFacade Interface + // ======================================================================= + + protected override APIOperation GetOperationImplementation(Type api) { + APIOperation ret = null; + // need to figure out if api operation is a get op.. + if (api.Equals(typeof(GetApiOp))) { + APIOperation op = GetAPIOperationRunner(typeof(SearchApiOp)); + ret = new GetImpl((SearchApiOp) op); + } else { + ret = GetAPIOperationRunner(api); + } + return ret; + } + + APIOperation GetAPIOperationRunner(Type api) { + APIOperation proxy; + //first create the inner proxy - this is the proxy that obtaining + //a connection from the pool, etc + //NOTE: we want to skip this part of the proxy for + //validate op, but we will want the timeout proxy + if ( api.Equals(typeof(ValidateApiOp))) { + OperationalContext context = + new OperationalContext(connectorInfo,GetAPIConfiguration()); + proxy = new ValidateImpl(context); + } + else { + ConstructorInfo constructor = + API_TO_IMPL[api]; + ConnectorOperationalContext context = + new ConnectorOperationalContext(connectorInfo, + GetAPIConfiguration(), + GetPool()); + + ConnectorAPIOperationRunnerProxy handler = + new ConnectorAPIOperationRunnerProxy(context,constructor); + proxy = + (APIOperation)Proxy.NewProxyInstance(api, + handler); + } + + //TODO: timeout + + // add logging proxy.. + proxy = CreateLoggingProxy(api, proxy); + return proxy; + } + private ObjectPool GetPool() + { + return ConnectorPoolManager.GetPool(GetAPIConfiguration(),connectorInfo); + } + } + #endregion + + #region ObjectPool + public class ObjectPool where T : class { + + /** + * Statistics bean + */ + public sealed class Statistics { + private readonly int _numIdle; + private readonly int _numActive; + + internal Statistics(int numIdle, int numActive) { + _numIdle = numIdle; + _numActive = numActive; + } + + /** + * Returns the number of idle objects + */ + public int NumIdle { + get { + return _numIdle; + } + } + + /** + * Returns the number of active objects + */ + public int NumActive { + get { + return _numActive; + } + } + } + + /** + * An object plus additional book-keeping + * information about the object + */ + private class PooledObject where T2 : class { + /** + * The underlying object + */ + private readonly T2 _object; + + /** + * True if this is currently active, false if + * it is idle + */ + private bool _isActive; + + /** + * Last state change (change from active to + * idle or vice-versa) + */ + private long _lastStateChangeTimestamp; + + /** + * Is this a freshly created object (never been pooled)? + */ + private bool _isNew; + + public PooledObject(T2 obj) { + _object = obj; + _isNew = true; + Touch(); + } + + public T2 Object { + get { + return _object; + } + } + + public bool IsActive { + get { + return _isActive; + } + set { + if (_isActive != value) { + Touch(); + _isActive = value; + } + } + } + + public bool IsNew { + get { + return _isNew; + } + set { + _isNew = value; + } + } + + + private void Touch() { + _lastStateChangeTimestamp = DateTimeUtil.GetCurrentUtcTimeMillis(); + } + + public long LastStateChangeTimestamp { + get { + return _lastStateChangeTimestamp; + } + } + } + + /** + * The lock object we use for everything + */ + private readonly Object LOCK = new Object(); + + /** + * Map from the object to the + * PooledObject (use IdentityHashMap so it's + * always object equality) + */ + private readonly IDictionary> + _activeObjects = CollectionUtil.NewIdentityDictionary>(); + + /** + * Queue of idle objects. The one that has + * been idle for the longest comes first in the queue + */ + private readonly LinkedList> + _idleObjects = new LinkedList>(); + + /** + * ObjectPoolHandler we use for managing object lifecycle + */ + private readonly ObjectPoolHandler _handler; + + /** + * Configuration for this pool. + */ + private readonly ObjectPoolConfiguration _config; + + /** + * Is the pool shutdown + */ + private bool _isShutdown; + + /** + * Create a new ObjectPool + * @param handler Handler for objects + * @param config Configuration for the pool + */ + public ObjectPool(ObjectPoolHandler handler, + ObjectPoolConfiguration config) { + + Assertions.NullCheck(handler, "handler"); + Assertions.NullCheck(config, "config"); + + _handler = handler; + //clone it + _config = + (ObjectPoolConfiguration)SerializerUtil.CloneObject(config); + //validate it + _config.Validate(); + + } + + /** + * Return an object to the pool + * @param object + */ + public void ReturnObject(T obj) { + Assertions.NullCheck(obj, "object"); + lock (LOCK) { + //remove it from the active list + PooledObject pooled = + CollectionUtil.GetValue(_activeObjects,obj,null); + + //they are attempting to return something + //we haven't allocated (or that they've + //already returned) + if ( pooled == null ) { + throw new InvalidOperationException("Attempt to return an object not in the pool: "+obj); + } + _activeObjects.Remove(obj); + + //set it to idle and add to idle list + //(this might get evicted right away + //by evictIdleObjects if we're over the + //limit or if we're shutdown) + pooled.IsActive=(false); + pooled.IsNew=(false); + _idleObjects.AddLast(pooled); + + //finally evict idle objects + EvictIdleObjects(); + + //wake anyone up who was waiting on a object + Monitor.PulseAll(LOCK); + } + } + + /** + * Borrow an object from the pool. + * @return An object + */ + public T BorrowObject() { + while ( true ) { + PooledObject rv = BorrowObjectNoTest(); + try { + //make sure we are testing it outside + //of synchronization. otherwise this + //can create an IO bottleneck + _handler.TestObject(rv.Object); + return rv.Object; + } + catch (Exception e) { + //it's bad - remove from active objects + lock (LOCK) { + _activeObjects.Remove(rv.Object); + } + DisposeNoException(rv.Object); + //if it's a new object, break out of the loop + //immediately + if ( rv.IsNew ) { + throw e; + } + } + } + } + + /** + * Borrow an object from the pool, but don't test + * it (it gets tested by the caller *outside* of + * synchronization) + * @return the object + */ + private PooledObject BorrowObjectNoTest() { + //time when the call began + long startTime = DateTimeUtil.GetCurrentUtcTimeMillis(); + + lock (LOCK) { + EvictIdleObjects(); + while ( true ) { + if (_isShutdown) { + throw new InvalidOperationException("Object pool already shutdown"); + } + + PooledObject pooledConn = null; + + //first try to recycle an idle object + if (_idleObjects.Count > 0) { + pooledConn = _idleObjects.First(); + _idleObjects.RemoveFirst(); + } + //otherwise, allocate a new object if + //below the limit + else if (_activeObjects.Count < _config.MaxObjects) { + pooledConn = + new PooledObject(_handler.NewObject()); + } + + //if there's an object available, return it + //and break out of the loop + if ( pooledConn != null ) { + pooledConn.IsActive=(true); + _activeObjects[pooledConn.Object] = + pooledConn; + return pooledConn; + } + + //see if we haven't timed-out yet + long elapsed = + DateTimeUtil.GetCurrentUtcTimeMillis() - startTime; + long remaining = _config.MaxWait - elapsed; + + //wait if we haven't timed out + if (remaining > 0) { + Monitor.Wait(LOCK,(int)remaining); + } + else { + //otherwise throw + throw new ConnectorException("Max objects exceeded"); + } + } + } + } + + /** + * Closes any idle objects in the pool. + * Existing active objects will remain alive and + * be allowed to shutdown gracefully, but no more + * objects will be allocated. + */ + public void Shutdown() { + lock(LOCK) { + _isShutdown = true; + //just evict idle objects + //if there are any active objects still + //going, leave them alone so they can return + //gracefully + EvictIdleObjects(); + //wake anyone up who was waiting on an object + Monitor.PulseAll(LOCK); + } + } + + /** + * Gets a snapshot of the pool's stats at a point in time. + * @return The statistics + */ + public Statistics GetStatistics() { + lock(LOCK) { + return new Statistics(_idleObjects.Count, + _activeObjects.Count); + } + } + + /** + * Evicts idle objects as needed (evicts + * all idle objects if we're shutdown) + */ + private void EvictIdleObjects() { + while (TooManyIdleObjects()) { + PooledObject conn = _idleObjects.First(); + _idleObjects.RemoveFirst(); + DisposeNoException(conn.Object); + } + } + + /** + * Returns true if any of the following are true: + *
    + *
  1. We're shutdown and there are idle objects
  2. + *
  3. Max idle objects exceeded
  4. + *
  5. Min idle objects exceeded and there are old objects
  6. + *
+ */ + private bool TooManyIdleObjects() { + + if (_isShutdown && _idleObjects.Count > 0) { + return true; + } + + if (_config.MaxIdle < _idleObjects.Count) { + return true; + } + if (_config.MinIdle >= _idleObjects.Count) { + return false; + } + + PooledObject oldest = + _idleObjects.First(); + + long age = + ( DateTimeUtil.GetCurrentUtcTimeMillis()-oldest.LastStateChangeTimestamp ); + + + return age > _config.MinEvictableIdleTimeMillis; + } + + /** + * Dispose of an object, but don't throw any exceptions + * @param object + */ + private void DisposeNoException(T obj) { + try { + _handler.DisposeObject(obj); + } + catch (Exception e) { + TraceUtil.TraceException("disposeObject() is not supposed to throw",e); + } + } + } + #endregion + + #region ObjectPoolHandler + public interface ObjectPoolHandler where T : class { + T NewObject(); + void TestObject(T obj); + void DisposeObject(T obj); + } + #endregion +} diff --git a/FrameworkInternal/ApiLocalOperations.cs b/FrameworkInternal/ApiLocalOperations.cs new file mode 100644 index 00000000..b61758fc --- /dev/null +++ b/FrameworkInternal/ApiLocalOperations.cs @@ -0,0 +1,1365 @@ +/* + * Copyright 2008 Sun Microsystems, Inc. All rights reserved. + * + * U.S. Government Rights - Commercial software. Government users + * are subject to the Sun Microsystems, Inc. standard license agreement + * and applicable provisions of the FAR and its supplements. + * + * Use is subject to license terms. + * + * This distribution may include materials developed by third parties. + * Sun, Sun Microsystems, the Sun logo, Java and Project Identity + * Connectors are trademarks or registered trademarks of Sun + * Microsystems, Inc. or its subsidiaries in the U.S. and other + * countries. + * + * UNIX is a registered trademark in the U.S. and other countries, + * exclusively licensed through X/Open Company, Ltd. + * + * ----------- + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER. + * + * Copyright 2008 Sun Microsystems, Inc. All rights reserved. + * + * The contents of this file are subject to the terms of the Common Development + * and Distribution License(CDDL) (the License). You may not use this file + * except in compliance with the License. + * + * You can obtain a copy of the License at + * http://identityconnectors.dev.java.net/CDDLv1.0.html + * See the License for the specific language governing permissions and + * limitations under the License. + * + * When distributing the Covered Code, include this CDDL Header Notice in each + * file and include the License file at identityconnectors/legal/license.txt. + * If applicable, add the following below this CDDL Header, with the fields + * enclosed by brackets [] replaced by your own identifying information: + * "Portions Copyrighted [year] [name of copyright owner]" + * ----------- + */ +using System; + +using Org.IdentityConnectors.Common; +using Org.IdentityConnectors.Common.Pooling; +using Org.IdentityConnectors.Common.Proxy; +using Org.IdentityConnectors.Common.Script; +using Org.IdentityConnectors.Common.Security; +using Org.IdentityConnectors.Framework.Api.Operations; +using Org.IdentityConnectors.Framework.Common.Objects; +using Org.IdentityConnectors.Framework.Common.Objects.Filters; +using Org.IdentityConnectors.Framework.Common.Exceptions; +using Org.IdentityConnectors.Framework.Common.Serializer; +using Org.IdentityConnectors.Framework.Spi; +using Org.IdentityConnectors.Framework.Spi.Operations; +using System.Reflection; +using System.Collections.Generic; + +namespace Org.IdentityConnectors.Framework.Impl.Api.Local.Operations +{ + #region APIOperationRunner + /** + * NOTE: internal class, public only for unit tests + * Base class for API operation runners. + */ + public abstract class APIOperationRunner { + + /** + * Context that has all the information required to execute an operation. + */ + private readonly OperationalContext _context; + + + /** + * Creates the API operation so it can called multiple times. + */ + public APIOperationRunner(OperationalContext context) { + _context = context; + //TODO: verify this + // get the APIOperation that this class implements.. + //List> apiOps = getInterfaces(this + //.getClass(), APIOperation.class); + // there should be only one.. + //if (apiOps.size() > 1) { + // final String MSG = "Must only implement one operation."; + // throw new IllegalStateException(MSG); + //} + } + + /** + * Get the current operational context. + */ + public OperationalContext GetOperationalContext() { + return _context; + } + + } + #endregion + + #region ConnectorAPIOperationRunner + /** + * NOTE: internal class, public only for unit tests + * Subclass of APIOperationRunner for operations that require a connector. + */ + public abstract class ConnectorAPIOperationRunner : APIOperationRunner { + + /** + * The connector instance + */ + private readonly Connector _connector; + + /** + * Creates the API operation so it can called multiple times. + */ + public ConnectorAPIOperationRunner(ConnectorOperationalContext context, + Connector connector) : base(context) { + _connector = connector; + } + + public Connector GetConnector() { + return _connector; + } + + public ObjectNormalizerFacade GetNormalizer(ObjectClass oclass) { + AttributeNormalizer norm = null; + Connector connector = GetConnector(); + if ( connector is AttributeNormalizer ) { + norm = (AttributeNormalizer)connector; + } + return new ObjectNormalizerFacade(oclass,norm); + } + } + #endregion + + #region ConnectorAPIOperationRunnerProxy + /** + * Proxy for APIOperationRunner that takes care of setting up underlying + * connector and creating the implementation of APIOperationRunner. + * The implementation of APIOperationRunner gets created whenever the + * actual method is invoked. + */ + internal class ConnectorAPIOperationRunnerProxy : InvocationHandler { + + + /** + * The operational context + */ + private readonly ConnectorOperationalContext _context; + + /** + * The implementation constructor. The instance is lazily created upon + * invocation + */ + private readonly ConstructorInfo _runnerImplConstructor; + + /** + * Create an APIOperationRunnerProxy + * @param context The operational context + * @param runnerImplConstructor The implementation constructor. Implementation + * must define a two-argument constructor(OperationalContext,Connector) + */ + public ConnectorAPIOperationRunnerProxy(ConnectorOperationalContext context, + ConstructorInfo runnerImplConstructor) { + _context = context; + _runnerImplConstructor = runnerImplConstructor; + } + + public Object Invoke(Object proxy, MethodInfo method, object[] args) + { + //do not proxy equals, hashCode, toString + if (method.DeclaringType.Equals(typeof(object))) { + return method.Invoke(this, args); + } + object ret = null; + Connector connector = null; + ObjectPool pool = _context.GetPool(); + // get the connector class.. + Type connectorClazz = _context.GetConnectorClass(); + try { + // pooling is implemented get one.. + if (pool != null) { + connector = pool.BorrowObject(); + } + else { + // get a new instance of the connector.. + connector = (Connector)Activator.CreateInstance(connectorClazz); + // initialize the connector.. + connector.Init(_context.GetConfiguration()); + } + APIOperationRunner runner = + (APIOperationRunner)_runnerImplConstructor.Invoke(new object[]{ + _context, + connector}); + ret = method.Invoke(runner, args); + // call out to the operation.. + } catch (TargetInvocationException e) { + Exception root = e.InnerException; + throw root; + } finally { + // make sure dispose of the connector properly + if (connector != null) { + // determine if there was a pool.. + if (pool != null) { + try { + //try to return it to the pool even though an + //exception may have happened that leaves it in + //a bad state. The contract of checkAlive + //is that it will tell you if the connector is + //still valid and so we leave it up to the pool + //and connector to work it out. + pool.ReturnObject((PoolableConnector)connector); + } catch (Exception e) { + //don't let pool exceptions propogate or mask + //other exceptions. do log it though. + TraceUtil.TraceException(null,e); + } + } + //not pooled - just dispose + else { + //dispose it not supposed to throw, but just in case, + //catch the exception and log it so we know about it + //but don't let the exception prevent additional + //cleanup that needs to happen + try { + connector.Dispose(); + } catch (Exception e) { + //log this though + TraceUtil.TraceException(null,e); + } + } + } + } + return ret; + } + } + #endregion + + #region ConnectorOperationalContext + /** + * NOTE: internal class, public only for unit tests + * Simple structure to pass more variables through the constructor of + * {@link APIOperationRunner}. + */ + public class ConnectorOperationalContext : OperationalContext { + + private readonly ObjectPool _pool; + + public ConnectorOperationalContext(LocalConnectorInfoImpl connectorInfo, + APIConfigurationImpl apiConfiguration, + ObjectPool pool) + :base(connectorInfo,apiConfiguration) { + _pool = pool; + } + + public ObjectPool GetPool() { + return _pool; + } + + + public Type GetConnectorClass() { + return GetConnectorInfo().ConnectorClass; + } + + } + #endregion + + #region AuthenticationImpl + internal class AuthenticationImpl : ConnectorAPIOperationRunner, + AuthenticationApiOp { + /** + * Pass the configuration etc to the abstract class. + */ + public AuthenticationImpl(ConnectorOperationalContext context, + Connector connector) + :base(context,connector) { + } + + /** + * Authenticate using the basic credentials. + * + * @see Authentication#authenticate(String, String) + */ + public void Authenticate(String username, GuardedString password, OperationOptions options) { + Assertions.NullCheck(username, "username"); + Assertions.NullCheck(password, "password"); + //convert null into empty + if ( options == null ) { + options = new OperationOptionsBuilder().Build(); + } + ((AuthenticateOp) GetConnector()).Authenticate(username, password,options); + } + } + #endregion + + #region CreateImpl + internal class CreateImpl : ConnectorAPIOperationRunner, + CreateApiOp { + + /** + * Initializes the operation works. + */ + public CreateImpl(ConnectorOperationalContext context, + Connector connector) + :base(context,connector) { + } + + /** + * Calls the create method on the Connector side. + * + * @see CreateApiOp#create(Set) + */ + public Uid Create(ObjectClass oclass, ICollection attributes, OperationOptions options) { + Assertions.NullCheck(oclass, "oclass"); + Assertions.NullCheck(attributes, "attributes"); + //convert null into empty + if ( options == null ) { + options = new OperationOptionsBuilder().Build(); + } + HashSet dups = new HashSet(); + foreach (ConnectorAttribute attr in attributes) { + if (dups.Contains(attr.Name)) { + throw new ArgumentException("Duplicate attribute name exists: " + attr.Name); + } + dups.Add(attr.Name); + } + if (oclass == null) { + throw new ArgumentException("Required attribute ObjectClass not found!"); + } + Connector connector = GetConnector(); + ObjectNormalizerFacade normalizer = GetNormalizer(oclass); + ICollection normalizedAttributes = + normalizer.NormalizeAttributes(attributes); + // create the object.. + Uid ret = ((CreateOp) connector).Create(oclass,attributes,options); + return (Uid)normalizer.NormalizeAttribute(ret); + } + } + #endregion + + #region DeleteImpl + internal class DeleteImpl : ConnectorAPIOperationRunner , + DeleteApiOp { + + /** + * Initializes the operation works. + */ + public DeleteImpl(ConnectorOperationalContext context, + Connector connector) + :base(context,connector) { + } + /** + * Calls the delete method on the Connector side. + * + * @see com.sun.openconnectors.framework.api.operations.CreateApiOp#create(java.util.Set) + */ + public void Delete(ObjectClass objClass, Uid uid, OperationOptions options) { + Assertions.NullCheck(objClass, "objClass"); + Assertions.NullCheck(uid, "uid"); + //convert null into empty + if ( options == null ) { + options = new OperationOptionsBuilder().Build(); + } + Connector connector = GetConnector(); + ObjectNormalizerFacade normalizer = GetNormalizer(objClass); + // delete the object.. + ((DeleteOp) connector).Delete(objClass, + (Uid)normalizer.NormalizeAttribute(uid), + options); + } + } + #endregion + + #region AttributesToGetResultsHandler + public abstract class AttributesToGetResultsHandler { + // ======================================================================= + // Fields + // ======================================================================= + private readonly string[] _attrsToGet; + + // ======================================================================= + // Constructors + // ======================================================================= + /** + * Keep the attribute to get.. + */ + public AttributesToGetResultsHandler(string[] attrsToGet) { + Assertions.NullCheck(attrsToGet, "attrsToGet"); + _attrsToGet = attrsToGet; + } + + /** + * Simple method that clones the object and remove the attribute thats are + * not in the {@link OperationOptions#OP_ATTRIBUTES_TO_GET} set. + * + * @param attrsToGet + * case insensitive set of attribute names. + */ + public ICollection ReduceToAttrsToGet( + ICollection attrs) { + ICollection ret = new HashSet(); + IDictionary map = ConnectorAttributeUtil.ToMap(attrs); + foreach (string attrName in _attrsToGet) { + ConnectorAttribute attr = CollectionUtil.GetValue(map, attrName, null); + // TODO: Should we throw if the attribute is not yet it was + // requested?? Or do we ignore because the API maybe asking + // for what the resource doesn't have?? + if (attr != null) { + ret.Add(attr); + } + } + return ret; + } + } + #endregion + + #region SearchAttributesToGetResultsHandler + public sealed class SearchAttributesToGetResultsHandler : + AttributesToGetResultsHandler { + + // ======================================================================= + // Fields + // ======================================================================= + private readonly ResultsHandler _handler; + + // ======================================================================= + // Constructors + // ======================================================================= + public SearchAttributesToGetResultsHandler( + ResultsHandler handler, string[] attrsToGet) : base(attrsToGet) { + Assertions.NullCheck(handler, "handler"); + this._handler = handler; + } + + public bool Handle(ConnectorObject obj) { + // clone the object and reduce the attributes only the set of + // attributes. + ConnectorObjectBuilder bld = new ConnectorObjectBuilder(); + bld.SetUid(obj.Uid); + bld.SetName(obj.Name); + bld.ObjectClass = obj.ObjectClass; + ICollection objAttrs = obj.GetAttributes(); + ICollection attrs = ReduceToAttrsToGet(objAttrs); + bld.AddAttributes(attrs); + return _handler(bld.Build()); + } + } + #endregion + + #region SearchAttributesToGetResultsHandler + public sealed class SyncAttributesToGetResultsHandler : + AttributesToGetResultsHandler { + + // ======================================================================= + // Fields + // ======================================================================= + private readonly SyncResultsHandler _handler; + + // ======================================================================= + // Constructors + // ======================================================================= + public SyncAttributesToGetResultsHandler( + SyncResultsHandler handler, string[] attrsToGet) : base(attrsToGet) { + Assertions.NullCheck(handler, "handler"); + this._handler = handler; + } + + public bool Handle(SyncDelta delta) { + SyncDeltaBuilder bld = new SyncDeltaBuilder(); + bld.Uid = delta.Uid; + bld.Token = delta.Token; + bld.DeltaType = delta.DeltaType; + ICollection deltaAttrs = delta.Attributes; + ICollection attrs = ReduceToAttrsToGet(deltaAttrs); + bld.Attributes = attrs; + return _handler(bld.Build()); + } + } + #endregion + + #region DuplicateFilteringResultsHandler + public sealed class DuplicateFilteringResultsHandler { + + // ======================================================================= + // Fields + // ======================================================================= + private readonly ResultsHandler _handler; + private readonly HashSet _visitedUIDs = new HashSet(); + + private bool _stillHandling; + + // ======================================================================= + // Constructors + // ======================================================================= + /** + * Filter chain for producers. + * + * @param producer + * Producer to filter. + * + */ + public DuplicateFilteringResultsHandler(ResultsHandler handler) { + // there must be a producer.. + if (handler == null) { + throw new ArgumentException("Handler must not be null!"); + } + this._handler = handler; + } + + public bool Handle(ConnectorObject obj) { + String uid = + obj.Uid.GetUidValue(); + if (!_visitedUIDs.Add(uid)) { + //we've already seen this - don't pass it + //throw + return true; + } + _stillHandling = _handler(obj); + return _stillHandling; + } + + public bool IsStillHandling { + get { + return _stillHandling; + } + } + + } + #endregion + + #region FilteredResultsHandler + public sealed class FilteredResultsHandler { + + // ======================================================================= + // Fields + // ======================================================================= + readonly ResultsHandler handler; + readonly Filter filter; + + // ======================================================================= + // Constructors + // ======================================================================= + /** + * Filter chain for producers. + * + * @param producer + * Producer to filter. + * @param filter + * Filter to use to accept objects. + */ + public FilteredResultsHandler(ResultsHandler handler, Filter filter) { + // there must be a producer.. + if (handler == null) { + throw new ArgumentException("Producer must not be null!"); + } + this.handler = handler; + // use a default pass through filter.. + this.filter = filter == null ? new PassThruFilter() : filter; + } + + public bool Handle(ConnectorObject obj) { + if ( filter.Accept(obj) ) { + return handler(obj); + } + else { + return true; + } + } + + /** + * Use a pass thru filter to use if a null filter is provided. + */ + class PassThruFilter : Filter { + public bool Accept(ConnectorObject obj) { + return true; + } + } + + } + #endregion + + #region GetImpl + /** + * Uses {@link SearchOp} to find the object that is referenced by the + * {@link Uid} provided. + */ + public class GetImpl : GetApiOp { + + readonly SearchApiOp op; + + private class ResultAdapter { + private IList _list = new List(); + public bool Handle(ConnectorObject obj) { + _list.Add(obj); + return false; + } + public ConnectorObject GetResult() { + return _list.Count == 0 ? null : _list[0]; + } + } + + public GetImpl(SearchApiOp search) { + this.op = search; + } + + public ConnectorObject GetObject(ObjectClass objClass, Uid uid, OperationOptions options) { + Assertions.NullCheck(objClass, "objClass"); + Assertions.NullCheck(uid, "uid"); + //convert null into empty + if ( options == null ) { + options = new OperationOptionsBuilder().Build(); + } + Filter filter = FilterBuilder.EqualTo(uid); + ResultAdapter adapter = new ResultAdapter(); + op.Search(objClass,filter,new ResultsHandler(adapter.Handle),options); + return adapter.GetResult(); + } + } + #endregion + + #region OperationalContext + /** + * NOTE: internal class, public only for unit tests + * OperationalContext - base class for operations that do not + * require a connection. + */ + public class OperationalContext { + + /** + * ConnectorInfo + */ + private readonly LocalConnectorInfoImpl connectorInfo; + + /** + * Contains the {@link Connector} {@link Configuration}. + */ + private readonly APIConfigurationImpl apiConfiguration; + + + public OperationalContext(LocalConnectorInfoImpl connectorInfo, + APIConfigurationImpl apiConfiguration) { + this.connectorInfo = connectorInfo; + this.apiConfiguration = apiConfiguration; + } + + public Configuration GetConfiguration() { + return CSharpClassProperties.CreateBean((ConfigurationPropertiesImpl)this.apiConfiguration.ConfigurationProperties, + connectorInfo.ConnectorConfigurationClass); + } + + protected LocalConnectorInfoImpl GetConnectorInfo() { + return connectorInfo; + } + } + #endregion + + #region NormalizingResultsHandler + public class NormalizingResultsHandler { + + private readonly ResultsHandler _target; + private readonly ObjectNormalizerFacade _normalizer; + + public NormalizingResultsHandler(ResultsHandler target, + ObjectNormalizerFacade normalizer) { + Assertions.NullCheck(target, "target"); + Assertions.NullCheck(normalizer, "normalizer"); + _target = target; + _normalizer = normalizer; + } + + + public bool Handle(ConnectorObject obj) { + ConnectorObject normalized = _normalizer.NormalizeObject(obj); + return _target(normalized); + } + + } + #endregion + + #region NormalizingSyncResultsHandler + public class NormalizingSyncResultsHandler { + + private readonly SyncResultsHandler _target; + private readonly ObjectNormalizerFacade _normalizer; + + public NormalizingSyncResultsHandler(SyncResultsHandler target, + ObjectNormalizerFacade normalizer) { + Assertions.NullCheck(target, "target"); + Assertions.NullCheck(normalizer, "normalizer"); + _target = target; + _normalizer = normalizer; + } + + + public bool Handle(SyncDelta delta) { + SyncDelta normalized = _normalizer.NormalizeSyncDelta(delta); + return _target(normalized); + } + + } + #endregion + + #region ObjectNormalizerFacade + public sealed class ObjectNormalizerFacade { + /** + * The (non-null) object class + */ + private readonly ObjectClass _objectClass; + /** + * The (possibly null) attribute normalizer + */ + private readonly AttributeNormalizer _normalizer; + + /** + * Create a new ObjectNormalizer + * @param objectClass The object class + * @param normalizer The normalizer. May be null. + */ + public ObjectNormalizerFacade(ObjectClass objectClass, + AttributeNormalizer normalizer) { + Assertions.NullCheck(objectClass, "objectClass"); + _objectClass = objectClass; + _normalizer = normalizer; + } + + /** + * Returns the normalized value of the attribute. + * If no normalizer is specified, returns the original + * attribute. + * @param attribute The attribute to normalize. + * @return The normalized attribute + */ + public ConnectorAttribute NormalizeAttribute(ConnectorAttribute attribute) { + if ( attribute == null ) { + return null; + } + else if (_normalizer != null) { + return _normalizer.NormalizeAttribute(_objectClass, attribute); + } + else { + return attribute; + } + } + + /** + * Returns the normalized set of attributes or null + * if the original set is null. + * @param attributes The original attributes. + * @return The normalized attributes or null if + * the original set is null. + */ + public ICollection NormalizeAttributes(ICollection attributes) { + if ( attributes == null ) { + return null; + } + ICollection temp = new HashSet(); + foreach (ConnectorAttribute attribute in attributes ) { + temp.Add(NormalizeAttribute(attribute)); + } + return CollectionUtil.AsReadOnlySet(temp); + } + + /** + * Returns the normalized object. + * @param orig The original object + * @return The normalized object. + */ + public ConnectorObject NormalizeObject(ConnectorObject orig) { + return new ConnectorObject(orig.ObjectClass, + NormalizeAttributes(orig.GetAttributes())); + } + + /** + * Returns the normalized sync delta. + * @param delta The original delta. + * @return The normalized delta. + */ + public SyncDelta NormalizeSyncDelta(SyncDelta delta) { + SyncDeltaBuilder builder = new + SyncDeltaBuilder(delta); + builder.Attributes = NormalizeAttributes(delta.Attributes); + return builder.Build(); + } + + /** + * Returns a filter consisting of the original with + * all attributes normalized. + * @param filter The original. + * @return The normalized filter. + */ + public Filter NormalizeFilter(Filter filter) { + if ( filter is ContainsFilter ) { + AttributeFilter afilter = + (AttributeFilter)filter; + return new ContainsFilter(NormalizeAttribute(afilter.GetAttribute())); + } + else if (filter is EndsWithFilter) { + AttributeFilter afilter = + (AttributeFilter)filter; + return new EndsWithFilter(NormalizeAttribute(afilter.GetAttribute())); + } + else if ( filter is EqualsFilter ) { + AttributeFilter afilter = + (AttributeFilter)filter; + return new EqualsFilter(NormalizeAttribute(afilter.GetAttribute())); + } + else if ( filter is GreaterThanFilter ) { + AttributeFilter afilter = + (AttributeFilter)filter; + return new GreaterThanFilter(NormalizeAttribute(afilter.GetAttribute())); + } + else if ( filter is GreaterThanOrEqualFilter ) { + AttributeFilter afilter = + (AttributeFilter)filter; + return new GreaterThanOrEqualFilter(NormalizeAttribute(afilter.GetAttribute())); + } + else if ( filter is LessThanFilter ) { + AttributeFilter afilter = + (AttributeFilter)filter; + return new LessThanFilter(NormalizeAttribute(afilter.GetAttribute())); + } + else if ( filter is LessThanOrEqualFilter ) { + AttributeFilter afilter = + (AttributeFilter)filter; + return new LessThanOrEqualFilter(NormalizeAttribute(afilter.GetAttribute())); + } + else if (filter is StartsWithFilter) { + AttributeFilter afilter = + (AttributeFilter)filter; + return new StartsWithFilter(NormalizeAttribute(afilter.GetAttribute())); + } + else if (filter is ContainsAllValuesFilter) { + AttributeFilter afilter = + (AttributeFilter)filter; + return new ContainsAllValuesFilter(NormalizeAttribute(afilter.GetAttribute())); + } + else if ( filter is NotFilter ) { + NotFilter notFilter = + (NotFilter)filter; + return new NotFilter(NormalizeFilter(notFilter.Filter)); + } + else if ( filter is AndFilter ) { + AndFilter andFilter = + (AndFilter)filter; + return new AndFilter(NormalizeFilter(andFilter.Left), + NormalizeFilter(andFilter.Right)); + } + else if ( filter is OrFilter ) { + OrFilter orFilter = + (OrFilter)filter; + return new OrFilter(NormalizeFilter(orFilter.Left), + NormalizeFilter(orFilter.Right)); + } + else { + return filter; + } + } + + + } + #endregion + + #region SchemaImpl + internal class SchemaImpl : ConnectorAPIOperationRunner , SchemaApiOp { + /** + * Initializes the operation works. + */ + public SchemaImpl(ConnectorOperationalContext context, + Connector connector) + :base(context,connector) { + } + + /** + * Retrieve the schema from the {@link Connector}. + * + * @see com.sun.openconnectors.framework.api.operations.SchemaApiOp#schema() + */ + public Schema Schema() { + return ((SchemaOp)GetConnector()).Schema(); + } + } + #endregion + + #region ScriptOnConnectorImpl + public class ScriptOnConnectorImpl : ConnectorAPIOperationRunner, + ScriptOnConnectorApiOp { + + public ScriptOnConnectorImpl(ConnectorOperationalContext context, + Connector connector) : + base(context,connector) { + } + + public Object RunScriptOnConnector(ScriptContext request, + OperationOptions options) { + Assertions.NullCheck(request, "request"); + //convert null into empty + if ( options == null ) { + options = new OperationOptionsBuilder().Build(); + } + Object rv; + if ( GetConnector() is ScriptOnConnectorOp ) { + rv = ((ScriptOnConnectorOp)GetConnector()).RunScriptOnConnector(request, options); + } + else { + String language = request.ScriptLanguage; + Assembly assembly = GetConnector().GetType().Assembly; + + ScriptExecutor executor = + ScriptExecutorFactory.NewInstance(language).NewScriptExecutor( + BuildReferenceList(assembly), + request.ScriptText, + false); + IDictionary scriptArgs = + new Dictionary(request.ScriptArguments); + scriptArgs["connector"]=GetConnector(); //add the connector instance itself + rv = executor.Execute(scriptArgs); + } + return SerializerUtil.CloneObject(rv); + } + + private Assembly [] BuildReferenceList(Assembly assembly) + { + List list = new List(); + BuildReferenceList2(assembly,list,new HashSet()); + return list.ToArray(); + } + + private void BuildReferenceList2(Assembly assembly, + List list, + HashSet visited) { + bool notThere = visited.Add(assembly.GetName().FullName); + if (notThere) + { + list.Add(assembly); + foreach (AssemblyName referenced in assembly.GetReferencedAssemblies()) { + Assembly assembly2 = Assembly.Load(referenced); + BuildReferenceList2(assembly2,list,visited); + } + } + } + + } + #endregion + + #region ScriptOnResourceImpl + public class ScriptOnResourceImpl : ConnectorAPIOperationRunner, + ScriptOnResourceApiOp { + + public ScriptOnResourceImpl(ConnectorOperationalContext context, + Connector connector) : + base(context,connector) { + } + + public Object RunScriptOnResource(ScriptContext request, + OperationOptions options) { + Assertions.NullCheck(request, "request"); + //convert null into empty + if ( options == null ) { + options = new OperationOptionsBuilder().Build(); + } + Object rv + = ((ScriptOnResourceOp)GetConnector()).RunScriptOnResource(request, options); + return SerializerUtil.CloneObject(rv); + } + + } + #endregion + + #region SearchImpl + internal class SearchImpl : ConnectorAPIOperationRunner , SearchApiOp { + + /** + * Initializes the operation works. + */ + public SearchImpl(ConnectorOperationalContext context, + Connector connector) + :base(context,connector) { + } + + /** + * Call the SPI search routines to return the results to the + * {@link ResultsHandler}. + * + * @see SearchApiOp#search(Filter, long, long, SearchApiOp.ResultsHandler) + */ + public void Search(ObjectClass oclass, Filter originalFilter, ResultsHandler handler, OperationOptions options) { + Assertions.NullCheck(oclass, "oclass"); + Assertions.NullCheck(handler, "handler"); + //convert null into empty + if ( options == null ) { + options = new OperationOptionsBuilder().Build(); + } + ObjectNormalizerFacade normalizer = + GetNormalizer(oclass); + //chain a normalizing handler (must come before + //filter handler) + handler = + new NormalizingResultsHandler(handler,normalizer).Handle; + Filter normalizedFilter = + normalizer.NormalizeFilter(originalFilter); + + //get the IList interface that this type implements + Type interfaceType = ReflectionUtil.FindInHierarchyOf + (typeof(SearchOp<>),GetConnector().GetType()); + Type [] val = interfaceType.GetGenericArguments(); + if (val.Length != 1) { + throw new Exception("Unexpected type: "+interfaceType); + } + Type queryType = val[0]; + Type searcherRawType = typeof(RawSearcherImpl<>); + Type searcherType = + searcherRawType.MakeGenericType(queryType); + RawSearcher searcher = (RawSearcher)Activator.CreateInstance(searcherType); + // add filtering handler + handler = new FilteredResultsHandler(handler, normalizedFilter).Handle; + // add attributes to get handler + string[] attrsToGet = options.AttributesToGet; + if (attrsToGet != null && attrsToGet.Length > 0) { + handler = new SearchAttributesToGetResultsHandler( + handler, attrsToGet).Handle; + } + searcher.RawSearch(GetConnector(),oclass,normalizedFilter,handler,options); + } + } + #endregion + + #region RawSearcher + internal interface RawSearcher { + /** + * Public because it is used by TestHelpers. Raw, + * SPI-level search. + * @param search The underlying implementation of search + * (generally the connector itself) + * @param oclass The object class + * @param filter The filter + * @param handler The handler + * @param options The options + */ + void RawSearch(Object search, + ObjectClass oclass, + Filter filter, + ResultsHandler handler, + OperationOptions options); + } + #endregion + + #region RawSearcherImpl + internal class RawSearcherImpl : RawSearcher where T : class { + public void RawSearch(Object search, + ObjectClass oclass, + Filter filter, + ResultsHandler handler, + OperationOptions options) { + RawSearch((SearchOp)search,oclass,filter,handler,options); + } + + /** + * Public because it is used by TestHelpers. Raw, + * SPI-level search. + * @param search The underlying implementation of search + * (generally the connector itself) + * @param oclass The object class + * @param filter The filter + * @param handler The handler + * @param options The options + */ + public static void RawSearch(SearchOp search, + ObjectClass oclass, + Filter filter, + ResultsHandler handler, + OperationOptions options) { + + FilterTranslator translator = + search.CreateFilterTranslator(oclass, options); + IList queries = + (IList)translator.Translate(filter); + if ( queries.Count == 0) { + search.ExecuteQuery(oclass, + null, handler, options); + } + else { + //eliminate dups if more than one + bool eliminateDups = + queries.Count > 1; + DuplicateFilteringResultsHandler dups = null; + if (eliminateDups) { + dups = new DuplicateFilteringResultsHandler(handler); + handler = dups.Handle; + } + foreach( T query in queries ) { + search.ExecuteQuery(oclass, + query, handler, options); + //don't run any more queries if the consumer + //has stopped + if (dups != null ) { + if (!dups.IsStillHandling) { + break; + } + } + } + } + } + + } + #endregion + + #region SyncImpl + public class SyncImpl : ConnectorAPIOperationRunner, + SyncApiOp { + + public SyncImpl(ConnectorOperationalContext context, + Connector connector) + : base(context,connector) { + } + + public void Sync(ObjectClass objClass, SyncToken token, + SyncResultsHandler handler, + OperationOptions options) { + //token is allowed to be null, objClass and handler must not be null + Assertions.NullCheck(objClass, "objClass"); + Assertions.NullCheck(handler, "handler"); + //convert null into empty + if ( options == null ) { + options = new OperationOptionsBuilder().Build(); + } + // add a handler in the chain to remove attributes + string[] attrsToGet = options.AttributesToGet; + if (attrsToGet != null && attrsToGet.Length > 0) { + handler = new SyncAttributesToGetResultsHandler( + handler, attrsToGet).Handle; + } + //chain a normalizing results handler + ObjectNormalizerFacade normalizer = + GetNormalizer(objClass); + handler = new NormalizingSyncResultsHandler(handler,normalizer).Handle; + ((SyncOp)GetConnector()).Sync(objClass, token, handler, options); + } + + } + #endregion + + #region TestImpl + /** + * Provides a method for the API to call the SPI's test method on the + * connector. The test method is intended to determine if the {@link Connector} + * is ready to perform the various operations it supports. + * + * @author Will Droste + * + */ + internal class TestImpl : ConnectorAPIOperationRunner , TestApiOp { + + public TestImpl(ConnectorOperationalContext context, Connector connector) + :base(context,connector) { + } + + /** + * {@inheritDoc} + */ + public void Test() { + ((TestOp) GetConnector()).Test(); + } + + } + #endregion + + #region UpdateImpl + /** + * NOTE: internal class, public only for unit tests + * Handles both version of update this include simple replace and the advance + * update. + */ + public class UpdateImpl : ConnectorAPIOperationRunner , UpdateApiOp { + /** + * Static map between API/SPI update types. + */ + private static readonly IDictionary CONV_TYPE = + new Dictionary(); + /** + * All the operational attributes that can not be added or deleted. + */ + static readonly HashSet OPERATIONAL_ATTRIBUTE_NAMES = new HashSet(); + + const String OPERATIONAL_ATTRIBUTE_ERR = + "Operational attribute '{0}' can not be added or deleted only replaced."; + + static UpdateImpl() { + CONV_TYPE[UpdateApiType.ADD]= UpdateType.ADD; + CONV_TYPE[UpdateApiType.DELETE]= UpdateType.DELETE; + CONV_TYPE[UpdateApiType.REPLACE]= UpdateType.REPLACE; + OPERATIONAL_ATTRIBUTE_NAMES.Add(Name.NAME); + CollectionUtil.AddAll(OPERATIONAL_ATTRIBUTE_NAMES, + OperationalAttributes.OPERATIONAL_ATTRIBUTE_NAMES); + } + + /** + * Determines which type of update a connector supports and then uses that + * handler. + */ + public UpdateImpl(ConnectorOperationalContext context, + Connector connector) + :base(context,connector){ + } + /** + * Create a new instance of the handler for the type of update the connector + * can support and run it. + * + * @see UpdateApiOp#update(UpdateApiOp.Type, ConnectorObject) + */ + public Uid Update(UpdateApiType type, ObjectClass objclass, + ICollection attributes, + OperationOptions options) { + // validate all the parameters.. + ValidateInput(type, objclass, attributes); + //convert null into empty + if ( options == null ) { + options = new OperationOptionsBuilder().Build(); + } + + Uid ret = null; + Connector c = GetConnector(); + ObjectNormalizerFacade normalizer = + GetNormalizer(objclass); + ICollection normalizedAttributes = + normalizer.NormalizeAttributes(attributes); + if (c is AdvancedUpdateOp) { + // easy way its an advance update + ret = ((AdvancedUpdateOp) c).Update(CONV_TYPE[type], objclass, normalizedAttributes, options); + } else if (c is UpdateOp) { + // check that this connector supports Search.. + if (ReflectionUtil.FindInHierarchyOf(typeof(SearchOp<>),c.GetType()) == null) { + string MSG = "Connector must support: " + typeof(SearchOp<>); + throw new ConfigurationException(MSG); + } + // get the connector object from the resource... + Uid uid = ConnectorAttributeUtil.GetUidAttribute(normalizedAttributes); + ConnectorObject o = GetConnectorObject(objclass, uid, options); + if (o == null) { + string MSG = "Object with Uid '{0}' and ObjectClass '{1}' does not exist!"; + string ERR = String.Format(MSG, uid, objclass); + throw new UnknownUidException(ERR); + } + // merge the update data.. + ICollection mergeAttrs = Merge(type, normalizedAttributes, o.GetAttributes()); + // update the object.. + ret = ((UpdateOp) c).Update(objclass, mergeAttrs, options); + } + return (Uid)normalizer.NormalizeAttribute(ret); + } + + /** + * Merges two connector objects into a single updated object. + */ + public ICollection Merge(UpdateApiType type, + ICollection updateAttrs, + ICollection baseAttrs) { + // return the merged attributes + ICollection ret = new HashSet(); + ret.Add(ConnectorAttributeUtil.GetUidAttribute(baseAttrs)); + IDictionary baseAttrMap = ConnectorAttributeUtil.ToMap(baseAttrs); + // run through attributes of the current object.. + foreach (ConnectorAttribute updateAttr in updateAttrs) { + // ignore uid because its immutable from this layer.. + if (updateAttr is Uid) { + continue; + } + // get the name of the update attributes + string name = updateAttr.Name; + ConnectorAttribute baseAttr = CollectionUtil.GetValue(baseAttrMap, name, null); + // if this is a delete and the base attribute doesn't exist + if (UpdateApiType.DELETE.Equals(type) && baseAttr == null) { + continue; + } + // exclude attributes that are the same on replace + if (UpdateApiType.REPLACE.Equals(type) && updateAttr.Equals(baseAttr)) { + continue; + } + ICollection values; + ConnectorAttribute modifiedAttr; + if (UpdateApiType.ADD.Equals(type) && baseAttr != null) { + // create a new list with the base attribute to add to.. + values = CollectionUtil.NewList(baseAttr.Value); + CollectionUtil.AddAll(values,updateAttr.Value); + modifiedAttr = ConnectorAttributeBuilder.Build(name, values); + } else if (UpdateApiType.DELETE.Equals(type)) { + // create a list with the base attribute to remove from.. + values = CollectionUtil.NewList(baseAttr.Value); + foreach (Object val in updateAttr.Value) { + values.Remove(val); + } + // if the values are empty send a null to the connector.. + if (values.Count == 0) { + modifiedAttr = ConnectorAttributeBuilder.Build(name); + } else { + modifiedAttr = ConnectorAttributeBuilder.Build(name, values); + } + } else { + // replace the attribute w/ the change set value. + values = updateAttr.Value; + modifiedAttr = ConnectorAttributeBuilder.Build(name, values); + } + ret.Add(modifiedAttr); + } + return ret; + } + + + /// + /// Get the ConnectorObject that is merged to create the change set + /// for the SPI simple update. + /// + ConnectorObject GetConnectorObject(ObjectClass oclass, Uid uid, OperationOptions options) { + // attempt to get the connector object.. + GetApiOp get = new GetImpl(new SearchImpl((ConnectorOperationalContext)GetOperationalContext(),GetConnector())); + return get.GetObject(oclass, uid, options); + } + /// + /// Validate all the input to determine if request can be handled. + /// + public static void ValidateInput(UpdateApiType type, ObjectClass objclass, + ICollection attrs) { + Assertions.NullCheck(type, "type"); + Assertions.NullCheck(objclass, "objclass"); + Assertions.NullCheck(attrs, "attrs"); + // check to make sure there's a uid.. + if (ConnectorAttributeUtil.GetUidAttribute(attrs) == null) { + throw new ArgumentException( + "Parameter 'attrs' must contain a 'Uid'!"); + } + // check for things only valid during ADD/DELETE + if (UpdateApiType.ADD.Equals(type) || UpdateApiType.DELETE.Equals(type)) { + foreach (ConnectorAttribute attr in attrs) { + Assertions.NullCheck(attr, "attr"); + // make sure that none of the values are null.. + if (attr.Value == null) { + throw new ArgumentException( + "Can not ADD or DELETE 'null' value."); + } + // make sure that if this an delete/add that it doesn't include + // certain attributes because it doesn't make any since.. + string name = attr.Name; + if (OPERATIONAL_ATTRIBUTE_NAMES.Contains(name)) { + String msg = String.Format(OPERATIONAL_ATTRIBUTE_ERR, name); + throw new ArgumentException(msg); + } + } + } + } + } + #endregion + + #region ValidateImpl + internal class ValidateImpl : APIOperationRunner, ValidateApiOp { + + public ValidateImpl(OperationalContext context) + :base(context) { + } + + /** + * {@inheritDoc} + */ + public void Validate() { + GetOperationalContext().GetConfiguration().Validate(); + } + } + #endregion + +} diff --git a/FrameworkInternal/ApiRemote.cs b/FrameworkInternal/ApiRemote.cs new file mode 100644 index 00000000..40c28dd1 --- /dev/null +++ b/FrameworkInternal/ApiRemote.cs @@ -0,0 +1,396 @@ +/* + * Copyright 2008 Sun Microsystems, Inc. All rights reserved. + * + * U.S. Government Rights - Commercial software. Government users + * are subject to the Sun Microsystems, Inc. standard license agreement + * and applicable provisions of the FAR and its supplements. + * + * Use is subject to license terms. + * + * This distribution may include materials developed by third parties. + * Sun, Sun Microsystems, the Sun logo, Java and Project Identity + * Connectors are trademarks or registered trademarks of Sun + * Microsystems, Inc. or its subsidiaries in the U.S. and other + * countries. + * + * UNIX is a registered trademark in the U.S. and other countries, + * exclusively licensed through X/Open Company, Ltd. + * + * ----------- + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER. + * + * Copyright 2008 Sun Microsystems, Inc. All rights reserved. + * + * The contents of this file are subject to the terms of the Common Development + * and Distribution License(CDDL) (the License). You may not use this file + * except in compliance with the License. + * + * You can obtain a copy of the License at + * http://identityconnectors.dev.java.net/CDDLv1.0.html + * See the License for the specific language governing permissions and + * limitations under the License. + * + * When distributing the Covered Code, include this CDDL Header Notice in each + * file and include the License file at identityconnectors/legal/license.txt. + * If applicable, add the following below this CDDL Header, with the fields + * enclosed by brackets [] replaced by your own identifying information: + * "Portions Copyrighted [year] [name of copyright owner]" + * ----------- + */ +using System; +using System.Collections.Generic; +using System.IO; +using System.Net; +using System.Reflection; +using System.Security.Authentication; +using System.Net.Security; +using System.Net.Sockets; +using System.Security.Cryptography.X509Certificates; +using System.Threading; +using Org.IdentityConnectors.Common; +using Org.IdentityConnectors.Common.Proxy; +using Org.IdentityConnectors.Framework.Api; +using Org.IdentityConnectors.Framework.Api.Operations; +using Org.IdentityConnectors.Framework.Common.Exceptions; +using Org.IdentityConnectors.Framework.Common.Objects; +using Org.IdentityConnectors.Framework.Common.Objects.Filters; +using Org.IdentityConnectors.Framework.Common.Serializer; +using Org.IdentityConnectors.Framework.Impl.Api; +using Org.IdentityConnectors.Framework.Impl.Api.Local.Operations; +using Org.IdentityConnectors.Framework.Impl.Api.Remote.Messages; +using System.Diagnostics; +namespace Org.IdentityConnectors.Framework.Impl.Api.Remote +{ + public class RemoteFrameworkConnection : IDisposable { + + private TcpClient _socket; + private Stream _stream; + + private BinaryObjectSerializer _encoder; + private BinaryObjectDeserializer _decoder; + + public RemoteFrameworkConnection(RemoteFrameworkConnectionInfo info) { + Init(info); + } + + public RemoteFrameworkConnection(TcpClient socket, Stream stream) { + Init(socket,stream); + } + + private void Init(RemoteFrameworkConnectionInfo connectionInfo) + { + IPAddress [] addresses = + Dns.GetHostAddresses(connectionInfo.Host); + TcpClient client = new TcpClient(addresses[0].AddressFamily); + client.SendTimeout = connectionInfo.Timeout; + client.ReceiveTimeout = connectionInfo.Timeout; + client.Connect(addresses[0],connectionInfo.Port); + Stream stream; + try { + stream = client.GetStream(); + } + catch (Exception) { + try { client.Close(); } catch (Exception) {} + throw; + } + try { + if (connectionInfo.UseSSL) { + if (connectionInfo.CertificateValidationCallback != null) { + RemoteCertificateValidationCallback callback = + connectionInfo.CertificateValidationCallback; + + stream = new SslStream( + stream,false,callback); + } + else { + stream = new SslStream(stream, + false); + } + ((SslStream)stream).AuthenticateAsClient(connectionInfo.Host, + new X509CertificateCollection(new X509Certificate[0]), + SslProtocols.Tls, + false); + } + } + catch (Exception) { + try { stream.Close(); } catch (Exception) {} + try { client.Close(); } catch (Exception) {} + throw; + } + Init(client,stream); + } + + private void Init(TcpClient socket, Stream stream) + { + _socket = socket; + _stream = stream; + ObjectSerializerFactory fact = + ObjectSerializerFactory.GetInstance(); + _encoder = fact.NewBinarySerializer(_stream); + _decoder = fact.NewBinaryDeserializer(_stream); + } + + public void Dispose() { + Flush(); + _stream.Close(); + _socket.Close(); + } + + public void Flush() { + _encoder.Flush(); + } + + public void WriteObject(object obj) { + _encoder.WriteObject(obj); + } + + public object ReadObject() { + //flush first in case there is any data in the + //output buffer + Flush(); + return _decoder.ReadObject(); + } + } + + + /// + /// internal class, public only for unit tests + /// + public sealed class RemoteConnectorInfoImpl : AbstractConnectorInfo { + + + public RemoteConnectorInfoImpl() { + + } + + //transient field, not serialized + public RemoteFrameworkConnectionInfo RemoteConnectionInfo {get;set;} + + } + + public class RemoteConnectorInfoManagerImpl : ConnectorInfoManager { + + + private IList _connectorInfo; + + private RemoteConnectorInfoManagerImpl() { + + } + + public RemoteConnectorInfoManagerImpl(RemoteFrameworkConnectionInfo info) + { + using (RemoteFrameworkConnection connection = + new RemoteFrameworkConnection(info)) { + connection.WriteObject(info.Key); + connection.WriteObject(new HelloRequest()); + HelloResponse response = (HelloResponse)connection.ReadObject(); + if ( response.Exception != null ) { + throw response.Exception; + } + IList remoteInfos = + response.ConnectorInfos; + //populate transient fields not serialized + foreach (RemoteConnectorInfoImpl remoteInfo in remoteInfos ) { + remoteInfo.RemoteConnectionInfo=info; + } + _connectorInfo = + CollectionUtil.NewReadOnlyList(remoteInfos); + } + + } + + /** + * Derives another RemoteConnectorInfoManagerImpl with + * a different RemoteFrameworkConnectionInfo but with the + * same metadata + * @param info + * @return + */ + public RemoteConnectorInfoManagerImpl Derive(RemoteFrameworkConnectionInfo info) { + RemoteConnectorInfoManagerImpl rv = new RemoteConnectorInfoManagerImpl(); + IList remoteInfosObj = + (IList)SerializerUtil.CloneObject(_connectorInfo); + IList remoteInfos = + CollectionUtil.NewList(remoteInfosObj); + foreach (ConnectorInfo remoteInfo in remoteInfos) { + ((RemoteConnectorInfoImpl)remoteInfo).RemoteConnectionInfo=(info); + } + rv._connectorInfo = + CollectionUtil.AsReadOnlyList(remoteInfos); + return rv; + } + + + public ConnectorInfo FindConnectorInfo(ConnectorKey key) { + foreach (ConnectorInfo info in _connectorInfo) { + if ( info.ConnectorKey.Equals(key) ) { + return info; + } + } + return null; + } + + public IList ConnectorInfos { + get { + return _connectorInfo; + } + } + + } + + internal class RemoteConnectorFacadeImpl : AbstractConnectorFacade { + + /** + * Builds up the maps of supported operations and calls. + */ + public RemoteConnectorFacadeImpl(APIConfigurationImpl configuration) + :base(configuration) { + } + + protected override APIOperation GetOperationImplementation(Type api) { + InvocationHandler handler = new RemoteOperationInvocationHandler( + GetAPIConfiguration(), + api); + APIOperation proxy = NewAPIOperationProxy(api, handler); + // add logging.. + proxy = CreateLoggingProxy(api, proxy); + return proxy; + } + } + + + /** + * Invocation handler for all of our operations + */ + public class RemoteOperationInvocationHandler : InvocationHandler { + private readonly APIConfigurationImpl _configuration; + private readonly Type _operation; + + public RemoteOperationInvocationHandler(APIConfigurationImpl configuration, + Type operation) { + _configuration = configuration; + _operation = operation; + } + + + public Object Invoke(Object proxy, MethodInfo method, Object[] args) + { + //don't proxy toString, hashCode, or equals + if ( method.DeclaringType.Equals(typeof(object))) { + return method.Invoke(this, args); + } + + //partition arguments into arguments that can + //be simply marshalled as part of the request and + //those that are response handlers + IList simpleMarshallArgs = + CollectionUtil.NewList(args); + ObjectStreamHandler streamHandlerArg = + ExtractStreamHandler(ReflectionUtil.GetParameterTypes(method),simpleMarshallArgs); + + //build the request object + RemoteConnectorInfoImpl connectorInfo = + (RemoteConnectorInfoImpl)_configuration.ConnectorInfo; + RemoteFrameworkConnectionInfo connectionInfo = + connectorInfo.RemoteConnectionInfo; + OperationRequest request = new OperationRequest( + connectorInfo.ConnectorKey, + _configuration, + _operation, + simpleMarshallArgs); + + //create the connection + RemoteFrameworkConnection connection = + new RemoteFrameworkConnection(connectionInfo); + try { + connection.WriteObject(connectionInfo.Key); + //send the request + connection.WriteObject(request); + + //now process each response stream (if any) + if ( streamHandlerArg != null ) { + HandleStreamResponse(connection,streamHandlerArg); + } + + //finally return the actual return value + OperationResponsePart response = + (OperationResponsePart)connection.ReadObject(); + if ( response.Exception != null ) { + throw response.Exception; + } + return response.Result; + } + finally { + connection.Dispose(); + } + } + /** + * Handles a stream response until the end of the stream + */ + private static void HandleStreamResponse(RemoteFrameworkConnection connection, ObjectStreamHandler streamHandler) { + Object response; + bool handleMore = true; + while ( true ) { + response = connection.ReadObject(); + if ( response is OperationResponsePart ) { + OperationResponsePart part = (OperationResponsePart)response; + if ( part.Exception != null ) { + throw part.Exception; + } + object obj = + part.Result; + if ( handleMore ) { + handleMore = streamHandler.Handle(obj); + } + } + else if ( response is OperationResponsePause ) { + if ( handleMore ) { + connection.WriteObject(new OperationRequestMoreData()); + } + else { + connection.WriteObject(new OperationRequestStopData()); + } + } + else if ( response is OperationResponseEnd ) { + break; + } + else { + throw new ConnectorException("Unexpected response: "+response); + } + } + } + + /** + * Partitions arguments into regular arguments and + * stream arguments. + * @param paramTypes The param types of the method + * @param arguments The passed-in arguments. As a + * side-effect will be set to just the regular arguments. + * @return The stream handler arguments. + */ + private static ObjectStreamHandler ExtractStreamHandler(Type [] paramTypes, + IList arguments) { + ObjectStreamHandler rv = null; + IList filteredArguments = new List(); + for ( int i = 0; i < paramTypes.Length; i++ ) { + Type paramType = paramTypes[i]; + object arg = arguments[i]; + if (StreamHandlerUtil.IsAdaptableToObjectStreamHandler(paramType)) { + ObjectStreamHandler handler = StreamHandlerUtil.AdaptToObjectStreamHandler(paramType, arg); + if ( rv != null ) { + throw new InvalidOperationException("Multiple stream handlers not supported"); + } + rv = handler; + } + else { + filteredArguments.Add(arg); + } + } + arguments.Clear(); + CollectionUtil.AddAll(arguments,filteredArguments); + return rv; + } + } + + +} diff --git a/FrameworkInternal/ApiRemoteMessages.cs b/FrameworkInternal/ApiRemoteMessages.cs new file mode 100644 index 00000000..ec74563c --- /dev/null +++ b/FrameworkInternal/ApiRemoteMessages.cs @@ -0,0 +1,244 @@ +/* + * Copyright 2008 Sun Microsystems, Inc. All rights reserved. + * + * U.S. Government Rights - Commercial software. Government users + * are subject to the Sun Microsystems, Inc. standard license agreement + * and applicable provisions of the FAR and its supplements. + * + * Use is subject to license terms. + * + * This distribution may include materials developed by third parties. + * Sun, Sun Microsystems, the Sun logo, Java and Project Identity + * Connectors are trademarks or registered trademarks of Sun + * Microsystems, Inc. or its subsidiaries in the U.S. and other + * countries. + * + * UNIX is a registered trademark in the U.S. and other countries, + * exclusively licensed through X/Open Company, Ltd. + * + * ----------- + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER. + * + * Copyright 2008 Sun Microsystems, Inc. All rights reserved. + * + * The contents of this file are subject to the terms of the Common Development + * and Distribution License(CDDL) (the License). You may not use this file + * except in compliance with the License. + * + * You can obtain a copy of the License at + * http://identityconnectors.dev.java.net/CDDLv1.0.html + * See the License for the specific language governing permissions and + * limitations under the License. + * + * When distributing the Covered Code, include this CDDL Header Notice in each + * file and include the License file at identityconnectors/legal/license.txt. + * If applicable, add the following below this CDDL Header, with the fields + * enclosed by brackets [] replaced by your own identifying information: + * "Portions Copyrighted [year] [name of copyright owner]" + * ----------- + */ +using System; +using System.Collections.Generic; +using Org.IdentityConnectors.Common; +using Org.IdentityConnectors.Framework.Api; +namespace Org.IdentityConnectors.Framework.Impl.Api.Remote.Messages +{ + /// + /// internal class, public only for unit tests + /// + public class HelloRequest : Message { + + public HelloRequest() { + } + + } + + /// + /// internal class, public only for unit tests + /// + public class HelloResponse : Message { + + /** + * The exception + */ + private Exception _exception; + + /** + * List of connector infos, containing infos for all the connectors + * on the server. + */ + private IList _connectorInfos; + + public HelloResponse(Exception exception, + IList connectorInfos) { + _exception = exception; + _connectorInfos = CollectionUtil.NewReadOnlyList(connectorInfos); + } + + public Exception Exception { + get { + return _exception; + } + } + + public IList ConnectorInfos { + get { + return _connectorInfos; + } + } + + } + + /// + /// internal class, public only for unit tests + /// + + public interface Message { + + } + + /// + /// internal class, public only for unit tests + /// + public class OperationRequest : Message { + /** + * The key of the connector to operate on. + */ + private readonly ConnectorKey _connectorKey; + + /** + * The configuration information to use. + */ + private readonly APIConfigurationImpl _configuration; + + /** + * The operation to perform. + */ + private readonly Type _operation; + + /** + * The arguments to the operation. In general, these correspond + * to the actual arguments of the method. The one exception is + * search - in this case, the callback is not passed. + */ + private readonly IList _arguments; + + public OperationRequest(ConnectorKey key, + APIConfigurationImpl apiConfiguration, + Type operation, + IList arguments) { + _connectorKey = key; + _configuration = apiConfiguration; + _operation = operation; + _arguments = CollectionUtil.NewReadOnlyList(arguments); + } + + public ConnectorKey ConnectorKey { + get { + return _connectorKey; + } + } + + public APIConfigurationImpl Configuration { + get { + return _configuration; + } + } + + public Type Operation { + get { + return _operation; + } + } + + public IList Arguments { + get { + return _arguments; + } + } + } + + /// + /// internal class, public only for unit tests + /// + public class OperationRequestMoreData : Message { + + public OperationRequestMoreData() { + } + + } + + /// + /// internal class, public only for unit tests + /// + public class OperationRequestStopData : Message { + + public OperationRequestStopData() { + } + + } + + /// + /// internal class, public only for unit tests + /// + public class OperationResponseEnd : Message { + + public OperationResponseEnd() { + } + + } + + /// + /// internal class, public only for unit tests + /// + public class OperationResponsePart : Message { + private Exception _exception; + private Object _result; + + public OperationResponsePart(Exception ex, Object result) { + _exception = ex; + _result = result; + } + + public Exception Exception { + get { + return _exception; + } + } + + public Object Result { + get { + return _result; + } + } + } + + /// + /// internal class, public only for unit tests + /// + public class OperationResponsePause : Message { + + public OperationResponsePause() { + } + + } + + public class EchoMessage : Message { + private object _object; + private string _objectXml; + public EchoMessage(object obj, string xml) { + _object = obj; + _objectXml = xml; + } + public object Object { + get { + return _object; + } + } + public string ObjectXml { + get { + return _objectXml; + } + } + } +} diff --git a/FrameworkInternal/AssemblyInfo.cs b/FrameworkInternal/AssemblyInfo.cs new file mode 100644 index 00000000..b304c494 --- /dev/null +++ b/FrameworkInternal/AssemblyInfo.cs @@ -0,0 +1,70 @@ +/* + * Copyright 2008 Sun Microsystems, Inc. All rights reserved. + * + * U.S. Government Rights - Commercial software. Government users + * are subject to the Sun Microsystems, Inc. standard license agreement + * and applicable provisions of the FAR and its supplements. + * + * Use is subject to license terms. + * + * This distribution may include materials developed by third parties. + * Sun, Sun Microsystems, the Sun logo, Java and Project Identity + * Connectors are trademarks or registered trademarks of Sun + * Microsystems, Inc. or its subsidiaries in the U.S. and other + * countries. + * + * UNIX is a registered trademark in the U.S. and other countries, + * exclusively licensed through X/Open Company, Ltd. + * + * ----------- + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER. + * + * Copyright 2008 Sun Microsystems, Inc. All rights reserved. + * + * The contents of this file are subject to the terms of the Common Development + * and Distribution License(CDDL) (the License). You may not use this file + * except in compliance with the License. + * + * You can obtain a copy of the License at + * http://identityconnectors.dev.java.net/CDDLv1.0.html + * See the License for the specific language governing permissions and + * limitations under the License. + * + * When distributing the Covered Code, include this CDDL Header Notice in each + * file and include the License file at identityconnectors/legal/license.txt. + * If applicable, add the following below this CDDL Header, with the fields + * enclosed by brackets [] replaced by your own identifying information: + * "Portions Copyrighted [year] [name of copyright owner]" + * ----------- + */ +#region Using directives + +using System; +using System.Reflection; +using System.Runtime.InteropServices; + +#endregion + +// General Information about an assembly is controlled through the following +// set of attributes. Change these attribute values to modify the information +// associated with an assembly. +[assembly: AssemblyTitle("FrameworkInternal")] +[assembly: AssemblyDescription("")] +[assembly: AssemblyConfiguration("")] +[assembly: AssemblyCompany("")] +[assembly: AssemblyProduct("FrameworkInternal")] +[assembly: AssemblyCopyright("")] +[assembly: AssemblyTrademark("")] +[assembly: AssemblyCulture("")] + +// This sets the default COM visibility of types in the assembly to invisible. +// If you need to expose a type to COM, use [ComVisible(true)] on that type. +[assembly: ComVisible(false)] + +// The assembly version has following format : +// +// Major.Minor.Build.Revision +// +// You can specify all the values or you can use the default the Revision and +// Build Numbers by using the '*' as shown below: +[assembly: AssemblyVersion("1.0.0.0")] diff --git a/FrameworkInternal/FrameworkInternal.csproj b/FrameworkInternal/FrameworkInternal.csproj new file mode 100644 index 00000000..fef50b0e --- /dev/null +++ b/FrameworkInternal/FrameworkInternal.csproj @@ -0,0 +1,112 @@ + + + + {5B011775-B121-4EEE-A410-BA2D2F5BFB8B} + Debug + AnyCPU + Library + Org.IdentityConnectors + FrameworkInternal + v3.5 + True + False + 4 + false + + + bin\Debug\ + true + Full + False + True + DEBUG;TRACE + + + bin\Release\ + False + None + True + False + TRACE + + + False + Auto + 4194304 + AnyCPU + 4096 + + + + + + + 3.5 + + + + + + + + + + + + + + + + + + + + + + + + {F140E8DA-52B4-4159-992A-9DA10EA8EEFB} + Common + + + {8B24461B-456A-4032-89A1-CD418F7B5B62} + Framework + + + \ No newline at end of file diff --git a/FrameworkInternal/Resources.resx b/FrameworkInternal/Resources.resx new file mode 100644 index 00000000..8556b4db --- /dev/null +++ b/FrameworkInternal/Resources.resx @@ -0,0 +1,461 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + text/microsoft-resx + + + 2.0 + + + System.Resources.ResXResourceReader, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + System.Resources.ResXResourceWriter, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + <?xml version='1.0' encoding='UTF-8'?> + + +<!--=======================================================--> +<!--= =--> +<!--= DTD for Connector Objects =--> +<!--= =--> +<!--=======================================================--> + +<!--=======================================================--> +<!--= =--> +<!--= All XML Objects =--> +<!--= =--> +<!--=======================================================--> + +<!ENTITY % exceptionTypes + "AlreadyExistsException | ConfigurationException | ConnectionBrokenException + | ConnectionFailedException | ConnectorIOException | InvalidPasswordException + | UnknownUidException | InvalidCredentialException | PermissionDeniedException + | ConnectorSecurityException | OperationTimeoutException | ConnectorException + | RuntimeException | Exception | Throwable + "> + +<!ENTITY % messageTypes + "HelloRequest | HelloResponse | OperationRequest | OperationResponseEnd | + OperationResponsePart | OperationRequestMoreData | OperationRequestStopData | + OperationResponsePause | EchoMessage + "> + +<!ENTITY % filterTypes + "AndFilter | ContainsFilter | EndsWithFilter | EqualsFilter | + GreaterThanFilter | GreaterThanOrEqualFilter | LessThanFilter | + LessThanOrEqualFilter | NotFilter | OrFilter | StartsWithFilter | + ContainsAllValuesFilter + "> + +<!ENTITY % attributeTypes + "Attribute | Uid | Name"> + +<!ENTITY % primitiveTypes + "null | Array | Boolean | boolean | Character | char | Integer | + int | Long | long | Float | float | Double | double | String | + URI | File | BigDecimal | BigInteger | ByteArray | Class | + Map | List | Set | Locale | GuardedString + "> + +<!ENTITY % xmlObject + "%primitiveTypes; | %exceptionTypes; | %messageTypes; | %filterTypes; | %attributeTypes; | +ObjectPoolConfiguration | ConfigurationProperty | ConfigurationProperties | +APIConfiguration | ConnectorMessages | ConnectorKey | ConnectorInfo | +UpdateApiOpType | AttributeInfo | ConnectorObject | ObjectClass | +ObjectClassInfo | Schema | ScriptContext | OperationOptions | +OperationOptionInfo | SyncDeltaType | SyncToken | SyncDelta +"> + + + +<!--=======================================================--> +<!--= =--> +<!--= Top Level Element for object streams =--> +<!--= =--> +<!--=======================================================--> + + +<!ELEMENT MultiObject (( + %xmlObject; +)*)> + + +<!--=======================================================--> +<!--= =--> +<!--= Primitives =--> +<!--= =--> +<!--=======================================================--> + + + +<!ELEMENT null EMPTY> +<!ELEMENT Array ((%xmlObject;)*)> +<!ATTLIST Array + componentType CDATA #REQUIRED +> +<!ELEMENT Boolean (#PCDATA)> +<!ELEMENT boolean (#PCDATA)> +<!ELEMENT Character (#PCDATA)> +<!ELEMENT char (#PCDATA)> +<!ELEMENT Integer (#PCDATA)> +<!ELEMENT int (#PCDATA)> +<!ELEMENT Long (#PCDATA)> +<!ELEMENT long (#PCDATA)> +<!ELEMENT Float (#PCDATA)> +<!ELEMENT float (#PCDATA)> +<!ELEMENT Double (#PCDATA)> +<!ELEMENT double (#PCDATA)> +<!ELEMENT String (#PCDATA)> +<!ELEMENT URI (#PCDATA)> +<!ELEMENT File (#PCDATA)> +<!ELEMENT BigDecimal EMPTY> +<!ATTLIST BigDecimal + unscaled CDATA #REQUIRED + scale CDATA #REQUIRED +> +<!ELEMENT BigInteger (#PCDATA)> +<!ELEMENT ByteArray (#PCDATA)> +<!ELEMENT Class (#PCDATA)> +<!ELEMENT Map ((MapEntry)*)> +<!ELEMENT MapEntry ((%xmlObject;),(%xmlObject;))> +<!ELEMENT Keys ((%xmlObject;)*)> +<!ELEMENT List ((%xmlObject;)*)> +<!ELEMENT Set ((%xmlObject;)*)> +<!ELEMENT Locale EMPTY> +<!ATTLIST Locale + language CDATA #IMPLIED + country CDATA #IMPLIED + variant CDATA #IMPLIED +> +<!ELEMENT GuardedString (#PCDATA)> + +<!--=======================================================--> +<!--= =--> +<!--= APIConfiguration =--> +<!--= =--> +<!--=======================================================--> + +<!ELEMENT ObjectPoolConfiguration EMPTY> +<!ATTLIST ObjectPoolConfiguration + maxObjects CDATA #IMPLIED + maxIdle CDATA #IMPLIED + maxWait CDATA #IMPLIED + minEvictableIdleTimeMillis CDATA #IMPLIED + minIdle CDATA #IMPLIED +> + +<!ELEMENT ConfigurationProperty (value)> +<!ATTLIST ConfigurationProperty + order CDATA #IMPLIED + confidential CDATA #IMPLIED + name CDATA #REQUIRED + helpMessageKey CDATA #REQUIRED + displayMessageKey CDATA #REQUIRED + type CDATA #REQUIRED +> +<!ELEMENT value (%xmlObject;)> +<!ELEMENT ConfigurationProperties ((ConfigurationProperty)*)> + +<!ELEMENT APIConfiguration (connectorPoolConfiguration,ConfigurationProperties,timeoutMap,SupportedOperations,Locale)> +<!ATTLIST APIConfiguration + connectorPoolingSupported CDATA #REQUIRED + producerBufferSize CDATA #REQUIRED +> +<!ELEMENT connectorPoolConfiguration ((ObjectPoolConfiguration))> +<!ELEMENT timeoutMap (Map)> +<!ELEMENT SupportedOperations ((Class)*)> +<!ELEMENT ConnectorMessages (catalogs)> +<!ELEMENT catalogs (Map)> +<!ELEMENT ConnectorKey EMPTY> +<!ATTLIST ConnectorKey + bundleName CDATA #REQUIRED + bundleVersion CDATA #REQUIRED + connectorName CDATA #REQUIRED +> +<!ELEMENT ConnectorInfo (ConnectorKey,ConnectorMessages,APIConfiguration)> +<!ATTLIST ConnectorInfo + connectorDisplayNameKey CDATA #REQUIRED +> + +<!--=======================================================--> +<!--= =--> +<!--= Common Objects =--> +<!--= =--> +<!--=======================================================--> +<!ELEMENT Attribute (Values)> +<!ELEMENT Values ((%xmlObject;)*)> +<!ATTLIST Attribute + name CDATA #REQUIRED +> + +<!ELEMENT Uid (#PCDATA)> +<!ELEMENT Name (#PCDATA)> + + + +<!ELEMENT UpdateApiOpType (#PCDATA)> + + +<!ELEMENT AlreadyExistsException EMPTY> +<!ATTLIST AlreadyExistsException + message CDATA #IMPLIED +> +<!ELEMENT ConfigurationException EMPTY> +<!ATTLIST ConfigurationException + message CDATA #IMPLIED +> +<!ELEMENT ConnectionBrokenException EMPTY> +<!ATTLIST ConnectionBrokenException + message CDATA #IMPLIED +> +<!ELEMENT ConnectionFailedException EMPTY> +<!ATTLIST ConnectionFailedException + message CDATA #IMPLIED +> +<!ELEMENT ConnectorIOException EMPTY> +<!ATTLIST ConnectorIOException + message CDATA #IMPLIED +> +<!ELEMENT InvalidPasswordException EMPTY> +<!ATTLIST InvalidPasswordException + message CDATA #IMPLIED +> +<!ELEMENT UnknownUidException EMPTY> +<!ATTLIST UnknownUidException + message CDATA #IMPLIED +> +<!ELEMENT InvalidCredentialException EMPTY> +<!ATTLIST InvalidCredentialException + message CDATA #IMPLIED +> +<!ELEMENT PermissionDeniedException EMPTY> +<!ATTLIST PermissionDeniedException + message CDATA #IMPLIED +> +<!ELEMENT ConnectorSecurityException EMPTY> +<!ATTLIST ConnectorSecurityException + message CDATA #IMPLIED +> +<!ELEMENT OperationTimeoutException EMPTY> +<!ATTLIST OperationTimeoutException + message CDATA #IMPLIED +> +<!ELEMENT ConnectorException EMPTY> +<!ATTLIST ConnectorException + message CDATA #IMPLIED +> +<!ELEMENT RuntimeException EMPTY> +<!ATTLIST RuntimeException + message CDATA #IMPLIED +> +<!ELEMENT Exception EMPTY> +<!ATTLIST Exception + message CDATA #IMPLIED +> +<!ELEMENT Throwable EMPTY> +<!ATTLIST Throwable + message CDATA #IMPLIED +> +<!ELEMENT AttributeInfo EMPTY> +<!ATTLIST AttributeInfo + name CDATA #REQUIRED + type CDATA #REQUIRED + required CDATA #IMPLIED + readable CDATA #IMPLIED + writeable CDATA #IMPLIED + multivalue CDATA #IMPLIED + returnedbydefault CDATA #IMPLIED +> + +<!ELEMENT ConnectorObject (ObjectClass,Attributes)> +<!ELEMENT Attributes ((%attributeTypes;)*)> + +<!ELEMENT ObjectClass EMPTY> +<!ATTLIST ObjectClass + type CDATA #REQUIRED +> + +<!ELEMENT ObjectClassInfo (AttributeInfos)> +<!ATTLIST ObjectClassInfo + type CDATA #REQUIRED +> +<!ELEMENT AttributeInfos ((AttributeInfo)*)> + +<!ELEMENT Schema (ObjectClassInfos,OperationOptionInfos,objectClassesByOperation,optionsByOperation)> +<!ELEMENT ObjectClassInfos ((ObjectClassInfo)*)> +<!ELEMENT OperationOptionInfos ((OperationOptionInfo)*)> +<!ELEMENT objectClassesByOperation (Map)> +<!ELEMENT optionsByOperation (Map)> + +<!ELEMENT ScriptContext (scriptArguments,scriptText)> +<!ATTLIST ScriptContext + scriptLanguage CDATA #REQUIRED +> +<!ELEMENT scriptText (#PCDATA)> +<!ELEMENT scriptArguments (Map)> + +<!ELEMENT OperationOptions (options)> +<!ELEMENT options (Map)> +<!ELEMENT OperationOptionInfo EMPTY> +<!ATTLIST OperationOptionInfo + name CDATA #REQUIRED + type CDATA #REQUIRED +> +<!ELEMENT SyncDeltaType (#PCDATA)> +<!ELEMENT SyncToken (value)> +<!ELEMENT SyncDelta (Uid,SyncDeltaType,SyncToken,Attributes)> + + + +<!--=======================================================--> +<!--= =--> +<!--= Filters =--> +<!--= =--> +<!--=======================================================--> + + +<!ELEMENT attribute (%attributeTypes;)> +<!ELEMENT AndFilter ((%filterTypes;),(%filterTypes;))> +<!ELEMENT ContainsFilter (attribute)> +<!ELEMENT EndsWithFilter (attribute)> +<!ELEMENT EqualsFilter (attribute)> +<!ELEMENT GreaterThanFilter (attribute)> +<!ELEMENT GreaterThanOrEqualFilter (attribute)> +<!ELEMENT LessThanFilter (attribute)> +<!ELEMENT LessThanOrEqualFilter (attribute)> +<!ELEMENT NotFilter (%filterTypes;)> +<!ELEMENT OrFilter ((%filterTypes;),(%filterTypes;))> +<!ELEMENT StartsWithFilter (attribute)> +<!ELEMENT ContainsAllValuesFilter (attribute)> + +<!--=======================================================--> +<!--= =--> +<!--= Messages =--> +<!--= =--> +<!--=======================================================--> +<!ELEMENT HelloRequest EMPTY> +<!ELEMENT ConnectorInfos ((ConnectorInfo)*)> +<!ELEMENT exception (%exceptionTypes;)> +<!ELEMENT HelloResponse (exception,ConnectorInfos)> +<!ELEMENT OperationRequest (ConnectorKey,APIConfiguration,Arguments)> +<!ATTLIST OperationRequest + operation CDATA #REQUIRED +> +<!ELEMENT Arguments ((%xmlObject;)*)> +<!ELEMENT OperationResponseEnd EMPTY> +<!ELEMENT OperationResponsePart (exception,result)> +<!ELEMENT result ((%xmlObject;)*)> +<!ELEMENT OperationRequestMoreData EMPTY> +<!ELEMENT OperationRequestStopData EMPTY> +<!ELEMENT OperationResponsePause EMPTY> +<!ELEMENT EchoMessage (value,objectXml?)> +<!ELEMENT objectXml (#PCDATA)> + + \ No newline at end of file diff --git a/FrameworkInternal/Security.cs b/FrameworkInternal/Security.cs new file mode 100644 index 00000000..25b92852 --- /dev/null +++ b/FrameworkInternal/Security.cs @@ -0,0 +1,141 @@ +/* + * Copyright 2008 Sun Microsystems, Inc. All rights reserved. + * + * U.S. Government Rights - Commercial software. Government users + * are subject to the Sun Microsystems, Inc. standard license agreement + * and applicable provisions of the FAR and its supplements. + * + * Use is subject to license terms. + * + * This distribution may include materials developed by third parties. + * Sun, Sun Microsystems, the Sun logo, Java and Project Identity + * Connectors are trademarks or registered trademarks of Sun + * Microsystems, Inc. or its subsidiaries in the U.S. and other + * countries. + * + * UNIX is a registered trademark in the U.S. and other countries, + * exclusively licensed through X/Open Company, Ltd. + * + * ----------- + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER. + * + * Copyright 2008 Sun Microsystems, Inc. All rights reserved. + * + * The contents of this file are subject to the terms of the Common Development + * and Distribution License(CDDL) (the License). You may not use this file + * except in compliance with the License. + * + * You can obtain a copy of the License at + * http://identityconnectors.dev.java.net/CDDLv1.0.html + * See the License for the specific language governing permissions and + * limitations under the License. + * + * When distributing the Covered Code, include this CDDL Header Notice in each + * file and include the License file at identityconnectors/legal/license.txt. + * If applicable, add the following below this CDDL Header, with the fields + * enclosed by brackets [] replaced by your own identifying information: + * "Portions Copyrighted [year] [name of copyright owner]" + * ----------- + */ +using System; + +using Org.IdentityConnectors.Common.Security; +using System.Text; +using System.Security.Cryptography; + +namespace Org.IdentityConnectors.Common.Security.Impl +{ + public class EncryptorFactoryImpl : EncryptorFactory { + + private readonly Encryptor _defaultEncryptor; + + public EncryptorFactoryImpl() { + _defaultEncryptor = new EncryptorImpl(); + } + + public override Encryptor GetDefaultEncryptor() { + return _defaultEncryptor; + } + } + + public class EncryptorImpl : Encryptor { + + private readonly static byte [] _defaultKeyBytes = + { + (byte) 0x23,(byte) 0x65,(byte) 0x87,(byte) 0x22, + (byte) 0x59,(byte) 0x78,(byte) 0x54,(byte) 0x43, + (byte) 0x64,(byte) 0x05,(byte) 0x6A,(byte) 0xBD, + (byte) 0x34,(byte) 0xA2,(byte) 0x34,(byte) 0x57, + }; + private readonly static byte [] _defaultIvBytes = + { + (byte) 0x51,(byte) 0x65,(byte) 0x22,(byte) 0x23, + (byte) 0x64,(byte) 0x05,(byte) 0x6A,(byte) 0xBE, + (byte) 0x51,(byte) 0x65,(byte) 0x22,(byte) 0x23, + (byte) 0x64,(byte) 0x05,(byte) 0x6A,(byte) 0xBE, + }; + + + + + public EncryptorImpl() { + } + + public UnmanagedArray Decrypt(byte[] bytes) { + using (SymmetricAlgorithm algo = Aes.Create()) { + algo.Padding = PaddingMode.PKCS7; + algo.Mode = CipherMode.CBC; + algo.Key = _defaultKeyBytes; + algo.IV = _defaultIvBytes; + using (ICryptoTransform transform = algo.CreateDecryptor()) { + return Decrypt2(bytes,transform); + } + } + } + + private unsafe UnmanagedArray Decrypt2(byte [] bytes, ICryptoTransform transform) { + byte [] managedBytes = transform.TransformFinalBlock(bytes,0,bytes.Length); + //pin it ASAP. this is a small race condition that is unavoidable due + //to the way the crypto API's return byte[]. + fixed(byte*dummy = managedBytes) { + try { + UnmanagedByteArray rv = new UnmanagedByteArray(managedBytes.Length); + for ( int i = 0; i < rv.Length; i++ ) { + rv[i] = managedBytes[i]; + } + return rv; + } + finally { + SecurityUtil.Clear(managedBytes); + } + } + } + + public byte[] Encrypt(UnmanagedArray bytes) { + using (SymmetricAlgorithm algo = Aes.Create()) { + algo.Padding = PaddingMode.PKCS7; + algo.Mode = CipherMode.CBC; + algo.Key = _defaultKeyBytes; + algo.IV = _defaultIvBytes; + using (ICryptoTransform transform = algo.CreateEncryptor()) { + return Encrypt2(bytes,transform); + } + } + } + + private unsafe byte[] Encrypt2(UnmanagedArray bytes, ICryptoTransform transform) { + byte [] managedBytes = new byte[bytes.Length]; + fixed(byte*dummy = managedBytes) { + try { + SecurityUtil.UnmanagedBytesToManagedBytes(bytes,managedBytes); + byte [] rv = transform.TransformFinalBlock(managedBytes,0,managedBytes.Length); + return rv; + } + finally { + SecurityUtil.Clear(managedBytes); + } + } + } + + } +} diff --git a/FrameworkInternal/Serializer.cs b/FrameworkInternal/Serializer.cs new file mode 100644 index 00000000..729fd95a --- /dev/null +++ b/FrameworkInternal/Serializer.cs @@ -0,0 +1,2294 @@ +/* + * Copyright 2008 Sun Microsystems, Inc. All rights reserved. + * + * U.S. Government Rights - Commercial software. Government users + * are subject to the Sun Microsystems, Inc. standard license agreement + * and applicable provisions of the FAR and its supplements. + * + * Use is subject to license terms. + * + * This distribution may include materials developed by third parties. + * Sun, Sun Microsystems, the Sun logo, Java and Project Identity + * Connectors are trademarks or registered trademarks of Sun + * Microsystems, Inc. or its subsidiaries in the U.S. and other + * countries. + * + * UNIX is a registered trademark in the U.S. and other countries, + * exclusively licensed through X/Open Company, Ltd. + * + * ----------- + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER. + * + * Copyright 2008 Sun Microsystems, Inc. All rights reserved. + * + * The contents of this file are subject to the terms of the Common Development + * and Distribution License(CDDL) (the License). You may not use this file + * except in compliance with the License. + * + * You can obtain a copy of the License at + * http://identityconnectors.dev.java.net/CDDLv1.0.html + * See the License for the specific language governing permissions and + * limitations under the License. + * + * When distributing the Covered Code, include this CDDL Header Notice in each + * file and include the License file at identityconnectors/legal/license.txt. + * If applicable, add the following below this CDDL Header, with the fields + * enclosed by brackets [] replaced by your own identifying information: + * "Portions Copyrighted [year] [name of copyright owner]" + * ----------- + */ +using System; +using System.IO; +using System.Collections.Generic; +using System.Security; +using Org.IdentityConnectors.Common; +using Org.IdentityConnectors.Common.Pooling; +using Org.IdentityConnectors.Common.Security; +using Org.IdentityConnectors.Framework.Api; +using Org.IdentityConnectors.Framework.Api.Operations; +using Org.IdentityConnectors.Framework.Common.Objects; +using Org.IdentityConnectors.Framework.Common.Objects.Filters; +using Org.IdentityConnectors.Framework.Common.Exceptions; +using Org.IdentityConnectors.Framework.Common.Serializer; +using Org.IdentityConnectors.Framework.Impl.Api; +using Org.IdentityConnectors.Framework.Impl.Api.Remote; +using Org.IdentityConnectors.Framework.Impl.Api.Remote.Messages; +using Org.IdentityConnectors.Framework.Impl.Serializer.Binary; +using Org.IdentityConnectors.Framework.Impl.Serializer.Xml; +using System.Linq; +using System.Globalization; +namespace Org.IdentityConnectors.Framework.Impl.Serializer +{ + #region Serialization Framework + internal abstract class AbstractObjectSerializationHandler + : ObjectTypeMapperImpl, ObjectSerializationHandler { + + protected AbstractObjectSerializationHandler(Type handledClass, + String type) + :base(handledClass,type) { + } + /** + * Called to serialize the object. + */ + abstract public void Serialize(Object obj, ObjectEncoder encoder) ; + + /** + * Called to deserialize the object. + */ + abstract public Object Deserialize(ObjectDecoder decoder) ; + } + + + internal class EnumSerializationHandler : + AbstractObjectSerializationHandler { + + public EnumSerializationHandler (Type clazz, String name) + :base(clazz,name) { + } + + + public override object Deserialize(ObjectDecoder decoder) { + String val = decoder.ReadStringContents(); + Type enumClass = HandledObjectType; + Object rv = Enum.Parse(enumClass, val); + return rv; + } + + public override void Serialize(object obj, ObjectEncoder encoder) + { + Enum e = (Enum)obj; + encoder.WriteStringContents(Enum.GetName(e.GetType(),e)); + } + + } + + /** + * Interface to abstract away the difference between deserializing + * xml and binary + */ + internal interface ObjectDecoder { + /** + * Reads an object using the appropriate serializer for that object + * @param fieldName. A hint of the field name. Ignored for binary + * serialization. The subelement name for xml serialization + */ + Object ReadObjectField(String fieldName, + Type expectedType, + Object dflt); + + /** + * Reads a bool. + * @param fieldName. A hint of the field name. Ignored for binary + * serialization. The subelement name for xml serialization + */ + bool ReadBooleanField(String fieldName, bool dflt) ; + + /** + * Reads an int. + * @param fieldName. A hint of the field name. Ignored for binary + * serialization. The subelement name for xml serialization + */ + int ReadIntField(String fieldName, int dflt) ; + + /** + * Reads a long. + * @param fieldName. A hint of the field name. Ignored for binary + * serialization. The subelement name for xml serialization + */ + long ReadLongField(String fieldName, long dflt) ; + + /** + * Reads a float. + * @param fieldName. A hint of the field name. Ignored for binary + * serialization. The subelement name for xml serialization + */ + float ReadFloatField(String fieldName, float dflt) ; + + /** + * Reads a double. + * @param fieldName. A hint of the field name. Ignored for binary + * serialization. The subelement name for xml serialization + * @param v. The value to serialize + */ + double ReadDoubleField(String fieldName, double dflt) ; + + /** + * Reads a double. + * @param fieldName. A hint of the field name. Ignored for binary + * serialization. The subelement name for xml serialization + * @param v. The value to serialize + */ + string ReadStringField(String fieldName, string dflt) ; + + /** + * Reads a double. + * @param fieldName. A hint of the field name. Ignored for binary + * serialization. The subelement name for xml serialization + * @param v. The value to serialize + */ + Type ReadClassField(String fieldName, Type dflt) ; + + /** + * Reads the value in-line. + */ + String ReadStringContents() ; + + /** + * Reads the value in-line. + */ + bool ReadBooleanContents() ; + + /** + * Reads the value in-line. + */ + int ReadIntContents() ; + + /** + * reads the value in-line. + */ + long ReadLongContents() ; + + /** + * Reads the value in-line. + */ + float ReadFloatContents() ; + + /** + * reads the value in-line. + */ + double ReadDoubleContents() ; + + /** + * reads the value in-line. + */ + byte [] ReadByteArrayContents() ; + + /** + * reads the value in-line. + */ + Type ReadClassContents() ; + + /** + * Returns the number of anonymous sub-objects. + * @return + */ + int GetNumSubObjects(); + + /** + * Reads a sub-object + */ + Object ReadObjectContents(int index) ; + + } + + /** + * Interface to abstract away the difference between serializing + * xml and binary + */ + internal interface ObjectEncoder { + /** + * Writes an object using the appropriate serializer for that object + * @param fieldName. A hint of the field name. Ignored for binary + * serialization. Becomes the subelement name for xml serialization + * @param object. The object to serialize + */ + void WriteObjectField(String fieldName, Object obj, bool inline) ; + + /** + * Writes a boolean. + * @param fieldName. A hint of the field name. Ignored for binary + * serialization. Becomes the subelement name for xml serialization + * @param v. The value to serialize + */ + void WriteBooleanField(String fieldName, bool v) ; + + /** + * Writes an int. + * @param fieldName. A hint of the field name. Ignored for binary + * serialization. Becomes the subelement name for xml serialization + * @param v. The value to serialize + */ + void WriteIntField(String fieldName, int v) ; + + /** + * Writes a long. + * @param fieldName. A hint of the field name. Ignored for binary + * serialization. Becomes the subelement name for xml serialization + * @param v. The value to serialize + */ + void WriteLongField(String fieldName, long v) ; + + /** + * Writes a float. + * @param fieldName. A hint of the field name. Ignored for binary + * serialization. Becomes the subelement name for xml serialization + * @param v. The value to serialize + */ + void WriteFloatField(String fieldName, float v) ; + + /** + * Writes a double. + * @param fieldName. A hint of the field name. Ignored for binary + * serialization. Becomes the subelement name for xml serialization + * @param v. The value to serialize + */ + void WriteDoubleField(String fieldName, double v) ; + + /** + * Writes a double. + * @param fieldName. A hint of the field name. Ignored for binary + * serialization. Becomes the subelement name for xml serialization + * @param v. The value to serialize + */ + void WriteStringField(String fieldName, string v) ; + + /** + * Writes a double. + * @param fieldName. A hint of the field name. Ignored for binary + * serialization. Becomes the subelement name for xml serialization + * @param v. The value to serialize + */ + void WriteClassField(String fieldName, Type v) ; + + /** + * Writes the value in-line. + */ + void WriteStringContents(String str) ; + + /** + * Writes the value in-line. + */ + void WriteBooleanContents(bool v) ; + + /** + * Writes the value in-line. + */ + void WriteIntContents(int v) ; + + /** + * Writes the value in-line. + */ + void WriteLongContents(long v) ; + + /** + * Writes the value in-line. + */ + void WriteFloatContents(float v) ; + + /** + * Writes the value in-line. + */ + void WriteDoubleContents(double v) ; + + /** + * Special case for byte [] that uses base64 encoding for XML + */ + void WriteByteArrayContents(byte [] v) ; + + /** + * Writes the value in-line. + */ + void WriteClassContents(Type v) ; + + /** + * Writes a sub-object + */ + void WriteObjectContents(object o) ; + } + + /** + * Interface to be implemented to handle the serialization/ + * deserialization of an object. + */ + internal interface ObjectSerializationHandler : ObjectTypeMapper { + + /** + * Called to serialize the object. + */ + void Serialize(Object obj, ObjectEncoder encoder) ; + + /** + * Called to deserialize the object. + */ + Object Deserialize(ObjectDecoder decoder) ; + } + + internal static class ObjectSerializerRegistry { + + private static readonly IList HANDLERS = + new List(); + + + + + private static readonly IDictionary + HANDLERS_BY_SERIAL_TYPE = new Dictionary(); + + static ObjectSerializerRegistry() { + CollectionUtil.AddAll(HANDLERS,Primitives.HANDLERS); + CollectionUtil.AddAll(HANDLERS,OperationMappings.MAPPINGS); + CollectionUtil.AddAll(HANDLERS,APIConfigurationHandlers.HANDLERS); + CollectionUtil.AddAll(HANDLERS,FilterHandlers.HANDLERS); + CollectionUtil.AddAll(HANDLERS,CommonObjectHandlers.HANDLERS); + CollectionUtil.AddAll(HANDLERS,MessageHandlers.HANDLERS); + //object is special - just map the type, but don't actually + //serialize + HANDLERS.Add(new ObjectTypeMapperImpl(typeof(object),"Object")); + + foreach (ObjectTypeMapper handler in HANDLERS) { + if (HANDLERS_BY_SERIAL_TYPE.ContainsKey(handler.HandledSerialType)) { + throw new Exception("More than one handler of the" + + " same type: "+handler.HandledSerialType); + } + HANDLERS_BY_SERIAL_TYPE[handler.HandledSerialType] = + handler; + } + } + + /** + * Mapping by class. Dynamically built since actual class may be + * a subclass. + */ + private static readonly IDictionary + HANDLERS_BY_OBJECT_TYPE = + new Dictionary(); + + + + public static ObjectTypeMapper GetMapperBySerialType(String type) { + return CollectionUtil.GetValue(HANDLERS_BY_SERIAL_TYPE,type,null); + } + + public static ObjectTypeMapper GetMapperByObjectType(Type clazz) { + lock(HANDLERS_BY_OBJECT_TYPE) { + ObjectTypeMapper rv = + CollectionUtil.GetValue(HANDLERS_BY_OBJECT_TYPE,clazz,null); + if ( rv == null ) { + foreach (ObjectTypeMapper handler in HANDLERS) { + IDelegatingObjectTypeMapper delegator = + handler as IDelegatingObjectTypeMapper; + ObjectTypeMapper effectiveHandler; + if ( delegator != null ) { + effectiveHandler = delegator.FindMapperDelegate(clazz); + } + else { + effectiveHandler = handler; + } + if ( effectiveHandler != null ) { + Type handledClass = + effectiveHandler.HandledObjectType; + if ( effectiveHandler.MatchSubclasses ) { + if ( handledClass.IsAssignableFrom(clazz) ) { + rv = effectiveHandler; + break; + } + } + else if ( handledClass.Equals(clazz) ) { + rv = effectiveHandler; + break; + } + } + } + HANDLERS_BY_OBJECT_TYPE[clazz] = rv; + } + return rv; + } + } + public static ObjectSerializationHandler GetHandlerBySerialType(String type) { + ObjectTypeMapper rv = GetMapperBySerialType(type); + return rv as ObjectSerializationHandler; + } + + public static ObjectSerializationHandler GetHandlerByObjectType(Type clazz) { + ObjectTypeMapper rv = GetMapperByObjectType(clazz); + return rv as ObjectSerializationHandler; + } + } + + /// + /// ObjectTypeMappers can implement this interface as well. If + /// they do, they can handle specific generic types as well. + /// + internal interface IDelegatingObjectTypeMapper { + ObjectTypeMapper FindMapperDelegate(Type type); + } + + /** + * Interface to be implemented to handle the serialization/ + * deserialization of an object. + */ + internal interface ObjectTypeMapper { + + /** + * Returns the type of object being serialized. This is + * an abstract type name that is intended to be language + * neutral. + */ + String HandledSerialType {get;} + + /** + * Returns the java class handled by this handler. + */ + Type HandledObjectType {get;} + + /** + * Should we match subclasses of the given class or only + * the exact class? + */ + bool MatchSubclasses {get;} + + } + + internal class ObjectTypeMapperImpl : ObjectTypeMapper { + + private Type _handledClass; + private String _handledType; + + public ObjectTypeMapperImpl(Type handledClass, String handledType) { + _handledClass = handledClass; + _handledType = handledType; + } + + public Type HandledObjectType { + get { + return _handledClass; + } + } + + public String HandledSerialType { + get { + return _handledType; + } + } + + public virtual bool MatchSubclasses { + get { + return false; + } + } + + } + + internal abstract class AbstractExceptionHandler : AbstractObjectSerializationHandler + where T : Exception + { + + protected AbstractExceptionHandler(String typeName) + : base(typeof(T),typeName) { + } + + + public override sealed Object Deserialize(ObjectDecoder decoder) { + String message = decoder.ReadStringField("message",null); + return CreateException(message); + } + + public override sealed void Serialize(Object obj, ObjectEncoder encoder) + { + Exception val = (Exception)obj; + encoder.WriteStringField("message", val.Message); + } + public override bool MatchSubclasses { + get { return true; } + } + protected abstract T CreateException(String message); + } + + + #endregion + + #region Primitives + + internal static class Primitives { + public static readonly IList HANDLERS = + new List(); + static Primitives() { + HANDLERS.Add(new BooleanHandler(typeof(bool?),"Boolean")); + HANDLERS.Add(new BooleanHandler(typeof(bool),"boolean")); + HANDLERS.Add(new CharacterHandler(typeof(char?),"Character")); + HANDLERS.Add(new CharacterHandler(typeof(char),"char")); + HANDLERS.Add(new IntegerHandler(typeof(int?),"Integer")); + HANDLERS.Add(new IntegerHandler(typeof(int),"int")); + HANDLERS.Add(new LongHandler(typeof(long?),"Long")); + HANDLERS.Add(new LongHandler(typeof(long),"long")); + HANDLERS.Add(new FloatHandler(typeof(float?),"Float")); + HANDLERS.Add(new FloatHandler(typeof(float),"float")); + HANDLERS.Add(new DoubleHandler(typeof(double?),"Double")); + HANDLERS.Add(new DoubleHandler(typeof(double),"double")); + HANDLERS.Add(new StringHandler()); + HANDLERS.Add(new URIHandler()); + HANDLERS.Add(new FileHandler()); + HANDLERS.Add(new BigIntegerHandler()); + HANDLERS.Add(new BigDecimalHandler()); + HANDLERS.Add(new ByteArrayHandler()); + HANDLERS.Add(new ClassHandler()); + HANDLERS.Add(new MapEntryHandler()); + HANDLERS.Add(new MapHandler()); + HANDLERS.Add(new ListHandler()); + HANDLERS.Add(new SetHandler()); + HANDLERS.Add(new LocaleHandler()); + HANDLERS.Add(new GuardedStringHandler()); + } + private class BooleanHandler : AbstractObjectSerializationHandler { + public BooleanHandler(Type objectType, String serialType) + : base(objectType,serialType) { + + } + public override Object Deserialize(ObjectDecoder decoder) { + bool val = decoder.ReadBooleanContents(); + return val; + } + + public override void Serialize(Object obj, ObjectEncoder encoder) { + bool val = (bool)obj; + encoder.WriteBooleanContents(val); + } + } + private class CharacterHandler : AbstractObjectSerializationHandler { + public CharacterHandler(Type objectType, String serialType) + : base(objectType,serialType) { + + } + public override Object Deserialize(ObjectDecoder decoder) { + String val = decoder.ReadStringContents(); + return val.ToCharArray()[0]; + } + + public override void Serialize(Object obj, ObjectEncoder encoder) { + char val = (char)obj; + encoder.WriteStringContents(val.ToString()); + } + } + private class IntegerHandler : AbstractObjectSerializationHandler { + public IntegerHandler(Type objectType, String serialType) + : base(objectType,serialType) { + + } + public override Object Deserialize(ObjectDecoder decoder) { + int val = decoder.ReadIntContents(); + return val; + } + + public override void Serialize(Object obj, ObjectEncoder encoder) { + int val = (int)obj; + encoder.WriteIntContents(val); + } + } + private class LongHandler : AbstractObjectSerializationHandler { + public LongHandler(Type objectType, String serialType) + : base(objectType,serialType) { + + } + public override Object Deserialize(ObjectDecoder decoder) { + long val = decoder.ReadLongContents(); + return val; + } + + public override void Serialize(Object obj, ObjectEncoder encoder) { + long val = (long)obj; + encoder.WriteLongContents(val); + } + } + private class FloatHandler : AbstractObjectSerializationHandler { + public FloatHandler(Type objectType, String serialType) + : base(objectType,serialType) { + + } + public override Object Deserialize(ObjectDecoder decoder) { + float val = decoder.ReadFloatContents(); + return val; + } + + public override void Serialize(Object obj, ObjectEncoder encoder) { + float val = (float)obj; + encoder.WriteFloatContents(val); + } + } + private class DoubleHandler : AbstractObjectSerializationHandler { + public DoubleHandler(Type objectType, String serialType) + : base(objectType,serialType) { + + } + public override Object Deserialize(ObjectDecoder decoder) { + double val = decoder.ReadDoubleContents(); + return val; + } + + public override void Serialize(Object obj, ObjectEncoder encoder) { + double val = (double)obj; + encoder.WriteDoubleContents(val); + } + } + private class StringHandler : AbstractObjectSerializationHandler { + public StringHandler() + : base(typeof(string),"String") { + + } + public override Object Deserialize(ObjectDecoder decoder) { + string val = decoder.ReadStringContents(); + return val; + } + + public override void Serialize(Object obj, ObjectEncoder encoder) { + string val = (string)obj; + encoder.WriteStringContents(val); + } + } + private class URIHandler : AbstractObjectSerializationHandler { + public URIHandler() + : base(typeof(Uri),"URI") { + + } + public override Object Deserialize(ObjectDecoder decoder) { + string val = decoder.ReadStringContents(); + return new Uri(val); + } + + public override void Serialize(Object obj, ObjectEncoder encoder) { + Uri val = (Uri)obj; + encoder.WriteStringContents(val.ToString()); + } + } + private class FileHandler : AbstractObjectSerializationHandler { + public FileHandler() + : base(typeof(FileName),"File") { + + } + public override Object Deserialize(ObjectDecoder decoder) { + string val = decoder.ReadStringContents(); + return new FileName(val); + } + + public override void Serialize(Object obj, ObjectEncoder encoder) { + FileName val = (FileName)obj; + encoder.WriteStringContents(val.Path); + } + } + private class BigDecimalHandler : AbstractObjectSerializationHandler { + public BigDecimalHandler() + : base(typeof(BigDecimal),"BigDecimal") { + + } + public override Object Deserialize(ObjectDecoder decoder) { + BigInteger unscaled = + new BigInteger(decoder.ReadStringField("unscaled",null)); + int scale = decoder.ReadIntField("scale",0); + return new BigDecimal(unscaled,scale); + } + + public override void Serialize(Object obj, ObjectEncoder encoder) { + BigDecimal val = (BigDecimal)obj; + encoder.WriteStringField("unscaled", val.UnscaledValue.Value); + encoder.WriteIntField("scale", val.Scale); + } + } + private class BigIntegerHandler : AbstractObjectSerializationHandler { + public BigIntegerHandler() + : base(typeof(BigInteger),"BigInteger") { + + } + public override Object Deserialize(ObjectDecoder decoder) { + string val = decoder.ReadStringContents(); + return new BigInteger(val); + } + + public override void Serialize(Object obj, ObjectEncoder encoder) { + BigInteger val = (BigInteger)obj; + encoder.WriteStringContents(val.Value); + } + } + private class ByteArrayHandler : AbstractObjectSerializationHandler { + public ByteArrayHandler() + : base(typeof(byte[]),"ByteArray") { + + } + public override Object Deserialize(ObjectDecoder decoder) { + return decoder.ReadByteArrayContents(); + } + + public override void Serialize(Object obj, ObjectEncoder encoder) { + byte [] val = (byte[])obj; + encoder.WriteByteArrayContents(val); + } + } + private class ClassHandler : AbstractObjectSerializationHandler { + public ClassHandler() + : base(typeof(Type),"Class") { + + } + public override Object Deserialize(ObjectDecoder decoder) { + return decoder.ReadClassContents(); + } + + public override void Serialize(Object obj, ObjectEncoder encoder) { + Type val = (Type)obj; + encoder.WriteClassContents(val); + } + + /// + /// In C#, the actual Type of Type is RuntimeType + /// + public override bool MatchSubclasses { + get { return true; } + } + + } + + private class MapEntry { + internal object key; + internal object val; + public MapEntry(object key, object val) { + this.key = key; + this.val = val; + } + } + private class MapEntryHandler : + AbstractObjectSerializationHandler { + + public MapEntryHandler() + : base(typeof(MapEntry),"MapEntry") { + } + public override Object Deserialize(ObjectDecoder decoder) { + Object key = decoder.ReadObjectContents(0); + Object val = decoder.ReadObjectContents(1); + return new MapEntry(key,val); + } + + public override void Serialize(Object obj, ObjectEncoder encoder) + { + MapEntry entry = (MapEntry)obj; + encoder.WriteObjectContents(entry.key); + encoder.WriteObjectContents(entry.val); + } + } + private class MapHandler : + AbstractObjectSerializationHandler, + IDelegatingObjectTypeMapper { + + public MapHandler() + : base(typeof(IDictionary),"Map") { + } + public ObjectTypeMapper FindMapperDelegate(Type type) { + //get the IDictionary interface that this type implements + Type interfaceType = ReflectionUtil.FindInHierarchyOf + (typeof(IDictionary<,>),type); + if ( interfaceType != null ) { + Type [] keyAndValue = interfaceType.GetGenericArguments(); + if (keyAndValue.Length != 2) { + throw new Exception("Cannot serialize type: "+type); + } + Type mapHandlerRawType = typeof(MapHandler<,>); + Type mapHandlerType = + mapHandlerRawType.MakeGenericType(keyAndValue); + return (ObjectTypeMapper)Activator.CreateInstance(mapHandlerType); + } + return null; + } + public override Object Deserialize(ObjectDecoder decoder) { + IDictionary rv = + new Dictionary(); + int count = decoder.GetNumSubObjects(); + for (int i = 0; i < count; i++) { + MapEntry entry = (MapEntry)decoder.ReadObjectContents(i); + rv[entry.key] = entry.val; + } + return rv; + } + + public override void Serialize(Object obj, ObjectEncoder encoder) + { + IDictionary map = (IDictionary)obj; + if ( map is SortedDictionary ) { + throw new Exception("Serialization of SortedDictionary not supported"); + } + foreach (KeyValuePair entry in map) { + MapEntry myEntry = new MapEntry(entry.Key,entry.Value); + encoder.WriteObjectContents(myEntry); + } + } + + public override bool MatchSubclasses { + get { return true; } + } + } + private class ListHandler : + AbstractObjectSerializationHandler, + IDelegatingObjectTypeMapper { + + public ListHandler() + : base(typeof(IList),"List") { + } + public ObjectTypeMapper FindMapperDelegate(Type type) { + //in C#, arrays implement IList + if (type.IsArray) { + return null; + } + //get the IList interface that this type implements + Type interfaceType = ReflectionUtil.FindInHierarchyOf + (typeof(IList<>),type); + if ( interfaceType != null ) { + Type [] val = interfaceType.GetGenericArguments(); + if (val.Length != 1) { + throw new Exception("Cannot serialize type: "+type); + } + Type listHandlerRawType = typeof(ListHandler<>); + Type listHandlerType = + listHandlerRawType.MakeGenericType(val); + return (ObjectTypeMapper)Activator.CreateInstance(listHandlerType); + } + return null; + } + public override Object Deserialize(ObjectDecoder decoder) { + IList rv = + new List(); + int count = decoder.GetNumSubObjects(); + for ( int i = 0; i < count; i++ ){ + rv.Add(decoder.ReadObjectContents(i)); + } + return rv; + } + + public override void Serialize(Object obj, ObjectEncoder encoder) + { + IList list = (IList)obj; + foreach (T o in list) { + encoder.WriteObjectContents(o); + } + } + + public override bool MatchSubclasses { + get { return true; } + } + } + private class SetHandler : + AbstractObjectSerializationHandler, + IDelegatingObjectTypeMapper { + + public SetHandler() + : base(typeof(ICollection),"Set") { + } + public ObjectTypeMapper FindMapperDelegate(Type type) { + //in C#, arrays implement IList + if (type.IsArray) { + return null; + } + + //get the IList interface that this type implements + Type interfaceType = ReflectionUtil.FindInHierarchyOf + (typeof(ICollection<>),type); + if ( interfaceType != null ) { + Type [] val = interfaceType.GetGenericArguments(); + if (val.Length != 1) { + throw new Exception("Cannot serialize type: "+type); + } + Type setHandlerRawType = typeof(SetHandler<>); + Type setHandlerType = + setHandlerRawType.MakeGenericType(val); + return (ObjectTypeMapper)Activator.CreateInstance(setHandlerType); + } + return null; + } + public override Object Deserialize(ObjectDecoder decoder) { + ICollection rv = + new HashSet(); + int count = decoder.GetNumSubObjects(); + for (int i = 0; i < count; i++) { + rv.Add(decoder.ReadObjectContents(i)); + } + return rv; + } + + public override void Serialize(Object obj, ObjectEncoder encoder) + { + ICollection list = (ICollection)obj; + foreach (T o in list) { + encoder.WriteObjectContents(o); + } + } + + public override bool MatchSubclasses { + get { return true; } + } + } + private class LocaleHandler : AbstractObjectSerializationHandler { + public LocaleHandler() + : base(typeof(CultureInfo),"Locale") { + + } + public override Object Deserialize(ObjectDecoder decoder) { + string language = decoder.ReadStringField("language",""); + string country = decoder.ReadStringField("country",""); + string variant = decoder.ReadStringField("variant",""); + Locale locale = new Locale(language,country,variant); + return locale.ToCultureInfo(); + } + + public override void Serialize(Object obj, ObjectEncoder encoder) { + CultureInfo cultureInfo = (CultureInfo)obj; + Locale locale = Locale.FindLocale(cultureInfo); + encoder.WriteStringField("language", locale.Language); + encoder.WriteStringField("country", locale.Country); + encoder.WriteStringField("variant", locale.Variant); + + } + } + + private class GuardedStringHandler : AbstractObjectSerializationHandler { + public GuardedStringHandler() + : base(typeof(GuardedString),"GuardedString") { + + } + public override Object Deserialize(ObjectDecoder decoder) { + byte [] encryptedBytes = null; + UnmanagedArray clearBytes = null; + UnmanagedArray clearChars = null; + try { + encryptedBytes = decoder.ReadByteArrayContents(); + clearBytes = EncryptorFactory.GetInstance().GetDefaultEncryptor().Decrypt(encryptedBytes); + clearChars = SecurityUtil.BytesToChars(clearBytes); + GuardedString rv = new GuardedString(); + for ( int i = 0; i < clearChars.Length; i++ ) { + rv.AppendChar(clearChars[i]); + } + return rv; + } + finally { + if ( clearBytes != null ) { + clearBytes.Dispose(); + } + if ( clearChars != null ) { + clearChars.Dispose(); + } + SecurityUtil.Clear(encryptedBytes); + } + } + + public override void Serialize(Object obj, ObjectEncoder encoder) { + GuardedString str = (GuardedString)obj; + str.Access( + clearChars=> + { + UnmanagedArray clearBytes = null; + byte [] encryptedBytes = null; + try { + clearBytes = SecurityUtil.CharsToBytes(clearChars); + encryptedBytes = EncryptorFactory.GetInstance().GetDefaultEncryptor().Encrypt(clearBytes); + encoder.WriteByteArrayContents(encryptedBytes); + } + finally { + if ( clearBytes != null ) { + clearBytes.Dispose(); + } + SecurityUtil.Clear(encryptedBytes); + } + }); + } + } + } + #endregion + + #region APIConfigurationHandlers + internal static class APIConfigurationHandlers { + public static readonly IList HANDLERS = + new List(); + static APIConfigurationHandlers() { + HANDLERS.Add( new ConnectionPoolingConfigurationHandler() ); + HANDLERS.Add( new ConfigurationPropertyHandler() ); + HANDLERS.Add( new ConfigurationPropertiesHandler() ); + HANDLERS.Add( new APIConfigurationHandler() ); + HANDLERS.Add( new ConnectorMessagesHandler() ); + HANDLERS.Add( new ConnectorKeyHandler() ); + HANDLERS.Add( new ConnectorInfoHandler() ); + } + private class ConnectionPoolingConfigurationHandler : AbstractObjectSerializationHandler { + public ConnectionPoolingConfigurationHandler() + : base(typeof(ObjectPoolConfiguration),"ObjectPoolConfiguration") { + + } + public override Object Deserialize(ObjectDecoder decoder) { + ObjectPoolConfiguration rv = + new ObjectPoolConfiguration(); + rv.MaxObjects=(decoder.ReadIntField("maxObjects",rv.MaxObjects)); + rv.MaxIdle=(decoder.ReadIntField("maxIdle",rv.MaxIdle)); + rv.MaxWait=(decoder.ReadLongField("maxWait",rv.MaxWait)); + rv.MinEvictableIdleTimeMillis=( + decoder.ReadLongField("minEvictableIdleTimeMillis",rv.MinEvictableIdleTimeMillis)); + rv.MinIdle=( + decoder.ReadIntField("minIdle",rv.MinIdle)); + return rv; + } + + public override void Serialize(Object obj, ObjectEncoder encoder) { + ObjectPoolConfiguration val = + (ObjectPoolConfiguration)obj; + encoder.WriteIntField("maxObjects", + val.MaxObjects); + encoder.WriteIntField("maxIdle", + val.MaxIdle); + encoder.WriteLongField("maxWait", + val.MaxWait); + encoder.WriteLongField("minEvictableIdleTimeMillis", + val.MinEvictableIdleTimeMillis); + encoder.WriteIntField("minIdle", + val.MinIdle); + } + } + private class ConfigurationPropertyHandler : AbstractObjectSerializationHandler { + public ConfigurationPropertyHandler() + : base(typeof(ConfigurationPropertyImpl),"ConfigurationProperty") { + + } + public override Object Deserialize(ObjectDecoder decoder) { + ConfigurationPropertyImpl rv = new ConfigurationPropertyImpl(); + rv.Order=(decoder.ReadIntField("order",0)); + rv.IsConfidential=(decoder.ReadBooleanField("confidential",false)); + rv.Name=(decoder.ReadStringField("name",null)); + rv.HelpMessageKey=( + decoder.ReadStringField("helpMessageKey",null)); + rv.DisplayMessageKey=( + decoder.ReadStringField("displayMessageKey",null)); + rv.ValueType=( + decoder.ReadClassField("type",null)); + rv.Value=( + decoder.ReadObjectField("value",null,null)); + return rv; + } + + public override void Serialize(Object obj, ObjectEncoder encoder) { + ConfigurationPropertyImpl val = + (ConfigurationPropertyImpl)obj; + encoder.WriteIntField("order", + val.Order); + encoder.WriteBooleanField("confidential", + val.IsConfidential); + encoder.WriteStringField("name", + val.Name); + encoder.WriteStringField("helpMessageKey", + val.HelpMessageKey); + encoder.WriteStringField("displayMessageKey", + val.DisplayMessageKey); + encoder.WriteClassField("type", + val.ValueType); + encoder.WriteObjectField("value", + val.Value, + false); + } + } + private class ConfigurationPropertiesHandler : AbstractObjectSerializationHandler { + public ConfigurationPropertiesHandler() + : base(typeof(ConfigurationPropertiesImpl),"ConfigurationProperties") { + + } + public override Object Deserialize(ObjectDecoder decoder) { + ConfigurationPropertiesImpl rv = + new ConfigurationPropertiesImpl(); + IList props = + new List + (); + int count = decoder.GetNumSubObjects(); + for ( int i = 0; i < count; i++ ) { + ConfigurationPropertyImpl prop = + (ConfigurationPropertyImpl)decoder.ReadObjectContents(i); + props.Add(prop); + } + rv.Properties = props; + return rv; + } + + public override void Serialize(Object obj, ObjectEncoder encoder) { + ConfigurationPropertiesImpl val = + (ConfigurationPropertiesImpl)obj; + IList props = + val.Properties; + foreach (ConfigurationPropertyImpl prop in props) { + encoder.WriteObjectContents(prop); + } + } + } + private class APIConfigurationHandler : AbstractObjectSerializationHandler { + public APIConfigurationHandler() + : base(typeof(APIConfigurationImpl),"APIConfiguration") { + + } + public override Object Deserialize(ObjectDecoder decoder) { + APIConfigurationImpl rv = new APIConfigurationImpl(); + rv.IsConnectorPoolingSupported=( + decoder.ReadBooleanField("connectorPoolingSupported",false)); + rv.ConnectorPoolConfiguration=( + (ObjectPoolConfiguration) + decoder.ReadObjectField("connectorPoolConfiguration",null,null)); + rv.ConfigurationProperties=((ConfigurationPropertiesImpl) + decoder.ReadObjectField("ConfigurationProperties",typeof(ConfigurationPropertiesImpl),null)); + IDictionary map = + (IDictionary)decoder.ReadObjectField("timeoutMap",null,null); + rv.TimeoutMap= + CollectionUtil.NewDictionary(map); + ICollection setObj = + (ICollection)decoder.ReadObjectField("SupportedOperations",typeof(ICollection),null); + ICollection set = + CollectionUtil.NewSet(setObj); + rv.SupportedOperations=(set); + rv.ProducerBufferSize=(decoder.ReadIntField("producerBufferSize",0)); + rv.CultureInfo=((CultureInfo)decoder.ReadObjectField("Locale",typeof(CultureInfo),null)); + return rv; + } + + public override void Serialize(Object obj, ObjectEncoder encoder) { + APIConfigurationImpl val = + (APIConfigurationImpl)obj; + encoder.WriteIntField("producerBufferSize", + val.ProducerBufferSize); + encoder.WriteBooleanField("connectorPoolingSupported", + val.IsConnectorPoolingSupported); + encoder.WriteObjectField("connectorPoolConfiguration", + val.ConnectorPoolConfiguration,false); + encoder.WriteObjectField("ConfigurationProperties", + val.ConfigurationProperties,true); + encoder.WriteObjectField("timeoutMap", + val.TimeoutMap,false); + encoder.WriteObjectField("SupportedOperations", + val.SupportedOperations,true); + encoder.WriteObjectField("Locale", + val.CultureInfo,true); + } + } + private class ConnectorMessagesHandler : AbstractObjectSerializationHandler { + public ConnectorMessagesHandler() + : base(typeof(ConnectorMessagesImpl),"ConnectorMessages") { + + } + public override Object Deserialize(ObjectDecoder decoder) { + ConnectorMessagesImpl rv = new ConnectorMessagesImpl(); + IDictionary catalogsObj = + (IDictionary)decoder.ReadObjectField("catalogs",null,null); + IDictionary> + catalogs = new Dictionary>(); + foreach (KeyValuePair entry in catalogsObj) { + CultureInfo key = (CultureInfo)entry.Key; + IDictionary valObj = (IDictionary)entry.Value; + IDictionary val = + CollectionUtil.NewDictionary(valObj); + catalogs[key] = val; + } + rv.Catalogs=(catalogs); + return rv; + + } + + public override void Serialize(Object obj, ObjectEncoder encoder) { + ConnectorMessagesImpl val = + (ConnectorMessagesImpl)obj; + encoder.WriteObjectField("catalogs", + val.Catalogs,false); + } + } + + private class ConnectorKeyHandler : AbstractObjectSerializationHandler { + public ConnectorKeyHandler() + : base(typeof(ConnectorKey),"ConnectorKey") { + + } + public override Object Deserialize(ObjectDecoder decoder) { + String bundleName = + decoder.ReadStringField("bundleName",null); + String bundleVersion = + decoder.ReadStringField("bundleVersion",null); + String connectorName = + decoder.ReadStringField("connectorName",null); + return new ConnectorKey(bundleName,bundleVersion,connectorName); + } + + public override void Serialize(Object obj, ObjectEncoder encoder) { + ConnectorKey val = (ConnectorKey)obj; + encoder.WriteStringField("bundleName", + val.BundleName); + encoder.WriteStringField("bundleVersion", + val.BundleVersion); + encoder.WriteStringField("connectorName", + val.ConnectorName); + } + } + + private class ConnectorInfoHandler : AbstractObjectSerializationHandler { + public ConnectorInfoHandler() + : base(typeof(RemoteConnectorInfoImpl),"ConnectorInfo") { + + } + public override Object Deserialize(ObjectDecoder decoder) { + RemoteConnectorInfoImpl rv = new RemoteConnectorInfoImpl(); + rv.ConnectorDisplayNameKey=( + decoder.ReadStringField("connectorDisplayNameKey",null)); + rv.ConnectorKey=((ConnectorKey) + decoder.ReadObjectField("ConnectorKey",typeof(ConnectorKey),null)); + rv.Messages=((ConnectorMessagesImpl) + decoder.ReadObjectField("ConnectorMessages",typeof(ConnectorMessagesImpl),null)); + rv.DefaultAPIConfiguration=((APIConfigurationImpl) + decoder.ReadObjectField("APIConfiguration",typeof(APIConfigurationImpl),null)); + return rv; + } + + public override void Serialize(Object obj, ObjectEncoder encoder) { + RemoteConnectorInfoImpl val = + (RemoteConnectorInfoImpl)obj; + encoder.WriteStringField("connectorDisplayNameKey", + val.ConnectorDisplayNameKey); + encoder.WriteObjectField("ConnectorKey", + val.ConnectorKey,true); + encoder.WriteObjectField("ConnectorMessages", + val.Messages,true); + encoder.WriteObjectField("APIConfiguration", + val.DefaultAPIConfiguration,true); + } + } + + } + #endregion + + #region ObjectSerializerFactoryImpl + public class ObjectSerializerFactoryImpl : ObjectSerializerFactory { + + + public override BinaryObjectDeserializer NewBinaryDeserializer(Stream i) { + return new BinaryObjectDecoder(i); + } + + public override BinaryObjectSerializer NewBinarySerializer(Stream os) { + return new BinaryObjectEncoder(os); + } + public override XmlObjectSerializer NewXmlSerializer(TextWriter w, + bool includeHeader, + bool multiObject) { + return new XmlObjectSerializerImpl(w,includeHeader,multiObject); + } + + public override void DeserializeXmlStream(TextReader reader, + XmlObjectResultsHandler handler, + bool validate) { + XmlObjectParser.parse(reader, handler, validate); + } + + } + #endregion + + #region OperationMappings + internal static class OperationMappings { + + public static readonly IList MAPPINGS = + new List(); + + static OperationMappings() { + MAPPINGS.Add(new ObjectTypeMapperImpl(typeof(AuthenticationApiOp), + "AuthenticationApiOp")); + MAPPINGS.Add(new ObjectTypeMapperImpl(typeof(SearchApiOp), + "SearchApiOp")); + MAPPINGS.Add(new ObjectTypeMapperImpl(typeof(ValidateApiOp), + "ValidateApiOp")); + MAPPINGS.Add(new ObjectTypeMapperImpl(typeof(CreateApiOp), + "CreateApiOp")); + MAPPINGS.Add(new ObjectTypeMapperImpl(typeof(SchemaApiOp), + "SchemaApiOp")); + MAPPINGS.Add(new ObjectTypeMapperImpl(typeof(UpdateApiOp), + "UpdateApiOp")); + MAPPINGS.Add(new ObjectTypeMapperImpl(typeof(DeleteApiOp), + "DeleteApiOp")); + MAPPINGS.Add(new ObjectTypeMapperImpl(typeof(GetApiOp), + "GetApiOp")); + MAPPINGS.Add(new ObjectTypeMapperImpl(typeof(TestApiOp), + "TestApiOp")); + MAPPINGS.Add(new ObjectTypeMapperImpl(typeof(ScriptOnConnectorApiOp), + "ScriptOnConnectorApiOp")); + MAPPINGS.Add(new ObjectTypeMapperImpl(typeof(ScriptOnResourceApiOp), + "ScriptOnResourceApiOp")); + MAPPINGS.Add(new ObjectTypeMapperImpl(typeof(SyncApiOp), + "SyncApiOp")); + } + + } + #endregion + + #region CommonObjectHandlers + internal static class CommonObjectHandlers { + public static readonly IList HANDLERS = + new List(); + static CommonObjectHandlers() { + + HANDLERS.Add( new EnumSerializationHandler(typeof(UpdateApiType), + "UpdateApiOpType") ); + HANDLERS.Add( new AlreadyExistsExceptionHandler() ); + HANDLERS.Add( new ConfigurationExceptionHandler() ); + HANDLERS.Add( new ConnectionBrokenExceptionHandler() ); + HANDLERS.Add( new ConnectionFailedExceptionHandler() ); + HANDLERS.Add( new ConnectorIOExceptionHandler() ); + HANDLERS.Add( new InvalidPasswordExceptionHandler() ); + HANDLERS.Add( new UnknownUidExceptionHandler() ); + HANDLERS.Add( new InvalidCredentialExceptionHandler() ); + HANDLERS.Add( new PermissionDeniedExceptionHandler() ); + HANDLERS.Add( new ConnectorSecurityExceptionHandler() ); + HANDLERS.Add( new OperationTimeoutExceptionHandler() ); + HANDLERS.Add( new ConnectorExceptionHandler() ); + HANDLERS.Add( new RuntimeExceptionHandler() ); + HANDLERS.Add( new ExceptionHandler() ); + HANDLERS.Add( new ThrowableHandler() ); + HANDLERS.Add( new AttributeHandler() ); + HANDLERS.Add( new AttributeInfoHandler() ); + HANDLERS.Add( new ConnectorObjectHandler() ); + HANDLERS.Add( new NameHandler() ); + HANDLERS.Add( new ObjectClassHandler() ); + HANDLERS.Add( new ObjectClassInfoHandler() ); + HANDLERS.Add( new SchemaHandler() ); + HANDLERS.Add( new UidHandler() ); + HANDLERS.Add( new ScriptContextHandler() ); + HANDLERS.Add( new OperationOptionsHandler() ); + HANDLERS.Add( new OperationOptionInfoHandler() ); + HANDLERS.Add( new EnumSerializationHandler(typeof(SyncDeltaType), + "SyncDeltaType") ); + HANDLERS.Add( new SyncTokenHandler() ); + HANDLERS.Add( new SyncDeltaHandler() ); + } + + private class AlreadyExistsExceptionHandler : AbstractExceptionHandler { + public AlreadyExistsExceptionHandler() + : base("AlreadyExistsException") { + + } + protected override AlreadyExistsException CreateException(String msg) { + return new AlreadyExistsException(msg); + } + } + private class ConfigurationExceptionHandler : AbstractExceptionHandler { + public ConfigurationExceptionHandler() + : base("ConfigurationException") { + + } + protected override ConfigurationException CreateException(String msg) { + return new ConfigurationException(msg); + } + } + private class ConnectionBrokenExceptionHandler : AbstractExceptionHandler { + public ConnectionBrokenExceptionHandler() + : base("ConnectionBrokenException") { + + } + protected override ConnectionBrokenException CreateException(String msg) { + return new ConnectionBrokenException(msg); + } + } + private class ConnectionFailedExceptionHandler : AbstractExceptionHandler { + public ConnectionFailedExceptionHandler() + : base("ConnectionFailedException") { + + } + protected override ConnectionFailedException CreateException(String msg) { + return new ConnectionFailedException(msg); + } + } + private class ConnectorIOExceptionHandler : AbstractExceptionHandler { + public ConnectorIOExceptionHandler() + : base("ConnectorIOException") { + + } + protected override ConnectorIOException CreateException(String msg) { + return new ConnectorIOException(msg); + } + } + private class InvalidPasswordExceptionHandler : AbstractExceptionHandler { + public InvalidPasswordExceptionHandler() + : base("InvalidPasswordException") { + + } + protected override InvalidPasswordException CreateException(String msg) { + return new InvalidPasswordException(msg); + } + } + private class UnknownUidExceptionHandler : AbstractExceptionHandler { + public UnknownUidExceptionHandler() + : base("UnknownUidException") { + + } + protected override UnknownUidException CreateException(String msg) { + return new UnknownUidException(msg); + } + } + private class InvalidCredentialExceptionHandler : AbstractExceptionHandler { + public InvalidCredentialExceptionHandler() + : base("InvalidCredentialException") { + + } + protected override InvalidCredentialException CreateException(String msg) { + return new InvalidCredentialException(msg); + } + } + private class PermissionDeniedExceptionHandler : AbstractExceptionHandler { + public PermissionDeniedExceptionHandler() + : base("PermissionDeniedException") { + + } + protected override PermissionDeniedException CreateException(String msg) { + return new PermissionDeniedException(msg); + } + } + private class ConnectorSecurityExceptionHandler : AbstractExceptionHandler { + public ConnectorSecurityExceptionHandler() + : base("ConnectorSecurityException") { + + } + protected override ConnectorSecurityException CreateException(String msg) { + return new ConnectorSecurityException(msg); + } + } + private class OperationTimeoutExceptionHandler : AbstractExceptionHandler { + public OperationTimeoutExceptionHandler() + : base("OperationTimeoutException") { + + } + protected override OperationTimeoutException CreateException(String msg) { + return new OperationTimeoutException(msg); + } + } + private class ConnectorExceptionHandler : AbstractExceptionHandler { + public ConnectorExceptionHandler() + : base("ConnectorException") { + + } + protected override ConnectorException CreateException(String msg) { + return new ConnectorException(msg); + } + } + //RuntimeException, Exception, and Throwable + //when going from Java to C#, these always become + //Exception. + //when going from C# to Java, these always become + //RuntimeException + private class RuntimeExceptionHandler : AbstractExceptionHandler { + public RuntimeExceptionHandler() + : base("RuntimeException") { + + } + protected override Exception CreateException(String msg) { + return new Exception(msg); + } + } + private class ExceptionHandler : AbstractExceptionHandler { + public ExceptionHandler() + : base("Exception") { + + } + protected override Exception CreateException(String msg) { + return new Exception(msg); + } + } + private class ThrowableHandler : AbstractExceptionHandler { + public ThrowableHandler() + : base("Throwable") { + + } + protected override Exception CreateException(String msg) { + return new Exception(msg); + } + } + + private abstract class AbstractAttributeHandler + : AbstractObjectSerializationHandler + where T : ConnectorAttribute + { + + protected AbstractAttributeHandler(String typeName) + :base(typeof(T),typeName) { + } + + + public override sealed Object Deserialize(ObjectDecoder decoder) { + String name = decoder.ReadStringField("name",null); + IList val = (IList)decoder.ReadObjectField("Values",typeof(IList),null); + return CreateAttribute(name, val); + } + + public override sealed void Serialize(Object obj, ObjectEncoder encoder) + { + ConnectorAttribute val = (ConnectorAttribute)obj; + encoder.WriteStringField("name", val.Name); + encoder.WriteObjectField("Values", val.Value, true); + } + + protected abstract T CreateAttribute(String name, IList val); + } + + private class AttributeHandler + : AbstractAttributeHandler { + public AttributeHandler() + : base("Attribute") { + + } + protected override ConnectorAttribute CreateAttribute(String name, IList value) { + return ConnectorAttributeBuilder.Build(name, value); + } + } + + private class AttributeInfoHandler : AbstractObjectSerializationHandler { + public AttributeInfoHandler() + : base(typeof(ConnectorAttributeInfo),"AttributeInfo") { + + } + public override Object Deserialize(ObjectDecoder decoder) { + ConnectorAttributeInfoBuilder builder = new ConnectorAttributeInfoBuilder(); + builder.Name=( + decoder.ReadStringField("name",null)); + builder.ValueType=( + decoder.ReadClassField("type",null)); + builder.Required=( + decoder.ReadBooleanField("required",false)); + builder.Readable=( + decoder.ReadBooleanField("readable",false)); + builder.Writeable=( + decoder.ReadBooleanField("writeable",false)); + builder.MultiValue=( + decoder.ReadBooleanField("multivalue",false)); + builder.ReturnedByDefault=( + decoder.ReadBooleanField("returnedbydefault",true)); + return builder.Build(); + } + + public override void Serialize(Object obj, ObjectEncoder encoder) { + ConnectorAttributeInfo val = (ConnectorAttributeInfo)obj; + encoder.WriteStringField("name", val.Name); + encoder.WriteClassField("type", val.ValueType); + encoder.WriteBooleanField("required", val.IsRequired); + encoder.WriteBooleanField("readable", val.IsReadable); + encoder.WriteBooleanField("writeable", val.IsWritable); + encoder.WriteBooleanField("multivalue", val.IsMultiValue); + encoder.WriteBooleanField("returnedbydefault", val.IsReturnedByDefault); + } + } + + private class ConnectorObjectHandler : AbstractObjectSerializationHandler { + public ConnectorObjectHandler() + : base(typeof(ConnectorObject),"ConnectorObject") { + + } + public override Object Deserialize(ObjectDecoder decoder) { + ObjectClass oclass = + (ObjectClass)decoder.ReadObjectField("ObjectClass",typeof(ObjectClass),null); + ICollection attsObj = + (ICollection)decoder.ReadObjectField("Attributes",typeof(ICollection),null); + ICollection atts = + CollectionUtil.NewSet(attsObj); + return new ConnectorObject(oclass,atts); + } + + public override void Serialize(Object obj, ObjectEncoder encoder) { + ConnectorObject val = (ConnectorObject)obj; + encoder.WriteObjectField("ObjectClass",val.ObjectClass,true); + encoder.WriteObjectField("Attributes", val.GetAttributes(),true); + } + } + private class NameHandler + : AbstractObjectSerializationHandler { + public NameHandler() + : base(typeof(Name),"Name") { + + } + public override Object Deserialize(ObjectDecoder decoder) { + String str = decoder.ReadStringContents(); + return new Name(str); + } + + public override void Serialize(Object obj, ObjectEncoder encoder) { + Name val = (Name)obj; + encoder.WriteStringContents(val.GetNameValue()); + } + } + private class ObjectClassHandler : AbstractObjectSerializationHandler { + public ObjectClassHandler() + : base(typeof(ObjectClass),"ObjectClass") { + + } + public override object Deserialize(ObjectDecoder decoder) { + string type = decoder.ReadStringField("type",null); + return new ObjectClass(type); + } + + public override void Serialize(object obj, ObjectEncoder encoder) { + ObjectClass val = (ObjectClass)obj; + encoder.WriteStringField("type",val.GetObjectClassValue()); + } + } + + private class ObjectClassInfoHandler : AbstractObjectSerializationHandler { + public ObjectClassInfoHandler() + : base(typeof(ObjectClassInfo),"ObjectClassInfo") { + + } + public override Object Deserialize(ObjectDecoder decoder) { + string type = + decoder.ReadStringField("type",null); + + ICollection attrInfoObj = + (ICollection)decoder.ReadObjectField("AttributeInfos",typeof(ICollection),null); + + ICollection attrInfo = + CollectionUtil.NewSet(attrInfoObj); + + return new ObjectClassInfo(type,attrInfo); + } + + public override void Serialize(Object obj, ObjectEncoder encoder) { + ObjectClassInfo val = (ObjectClassInfo)obj; + encoder.WriteStringField("type",val.ObjectType); + encoder.WriteObjectField("AttributeInfos", val.ConnectorAttributeInfos,true); + } + } + private class SchemaHandler : AbstractObjectSerializationHandler { + public SchemaHandler() + : base(typeof(Schema),"Schema") { + + } + public override Object Deserialize(ObjectDecoder decoder) { + + ICollection objectClassesObj = + (ICollection)decoder.ReadObjectField("ObjectClassInfos",typeof(ICollection),null); + ICollection objectClasses = + CollectionUtil.NewSet(objectClassesObj); + IDictionary objectClassesByName = new Dictionary(); + foreach (ObjectClassInfo info in objectClasses) { + objectClassesByName[info.ObjectType] = info; + } + ICollection operationOptionsObj = + (ICollection)decoder.ReadObjectField("OperationOptionInfos",typeof(ICollection),null); + ICollection operationOptions = + CollectionUtil.NewSet(operationOptionsObj); + IDictionary optionsByName = new Dictionary(); + foreach (OperationOptionInfo info in operationOptions) { + optionsByName[info.Name] = info; + } + IDictionary objectClassNamesByOperationObj = + (IDictionary)decoder.ReadObjectField("objectClassesByOperation",null,null); + IDictionary> + objectClassesByOperation = + new Dictionary>(); + foreach (KeyValuePair entry in objectClassNamesByOperationObj) { + Type op = (Type)entry.Key; + ICollection namesObj = + (ICollection)entry.Value; + ICollection infos = + new HashSet(); + foreach (object name in namesObj) { + ObjectClassInfo objectClass = CollectionUtil.GetValue(objectClassesByName,(string)name,null); + if ( objectClass != null ) { + infos.Add(objectClass); + } + } + objectClassesByOperation[op] = infos; + } + IDictionary optionsByOperationObj = + (IDictionary)decoder.ReadObjectField("optionsByOperation",null,null); + IDictionary> + optionsByOperation = + new Dictionary>(); + foreach (KeyValuePair entry in optionsByOperationObj) { + Type op = (Type)entry.Key; + ICollection namesObj = + (ICollection)entry.Value; + ICollection infos = + new HashSet(); + foreach (object name in namesObj) { + OperationOptionInfo info = CollectionUtil.GetValue(optionsByName,(string)name,null); + if ( info != null ) { + infos.Add(info); + } + } + optionsByOperation[op] = infos; + } + return new Schema(objectClasses, + operationOptions, + objectClassesByOperation, + optionsByOperation); + } + + public override void Serialize(Object obj, ObjectEncoder encoder) { + Schema val = (Schema)obj; + encoder.WriteObjectField("ObjectClassInfos", val.ObjectClassInfo,true); + encoder.WriteObjectField("OperationOptionInfos", val.OperationOptionInfo,true); + IDictionary> + objectClassNamesByOperation = new Dictionary>(); + IDictionary> + optionNamesByOperation = new Dictionary>(); + + + foreach (KeyValuePair> + entry in val.SupportedObjectClassesByOperation) { + ICollection value = entry.Value; + ICollection names = new HashSet(); + foreach (ObjectClassInfo info in value) { + names.Add(info.ObjectType); + } + objectClassNamesByOperation[entry.Key] = names; + } + foreach (KeyValuePair> + entry in val.SupportedOptionsByOperation) { + ICollection value = entry.Value; + ICollection names = new HashSet(); + foreach (OperationOptionInfo info in value) { + names.Add(info.Name); + } + optionNamesByOperation[entry.Key] = names; + } + encoder.WriteObjectField("objectClassesByOperation",objectClassNamesByOperation,false); + encoder.WriteObjectField("optionsByOperation",optionNamesByOperation,false); + } + } + private class UidHandler + : AbstractObjectSerializationHandler { + public UidHandler() + : base(typeof(Uid),"Uid") { + + } + public override Object Deserialize(ObjectDecoder decoder) { + String str = decoder.ReadStringContents(); + return new Uid(str); + } + + public override void Serialize(Object obj, ObjectEncoder encoder) { + Uid val = (Uid)obj; + encoder.WriteStringContents(val.GetUidValue()); + } + } + private class ScriptContextHandler : AbstractObjectSerializationHandler { + public ScriptContextHandler() + : base(typeof(ScriptContext),"ScriptContext") { + + } + public override Object Deserialize(ObjectDecoder decoder) { + String scriptLanguage = + decoder.ReadStringField("scriptLanguage",null); + IDictionary arguments = + (IDictionary)decoder.ReadObjectField("scriptArguments",null,null); + String scriptText = + (String)decoder.ReadObjectField("scriptText",typeof(string),null); + IDictionary arguments2 = + CollectionUtil.NewDictionary(arguments); + return new ScriptContext(scriptLanguage,scriptText,arguments2); + } + + public override void Serialize(Object obj, ObjectEncoder encoder) { + ScriptContext val = (ScriptContext)obj; + encoder.WriteStringField("scriptLanguage", val.ScriptLanguage); + encoder.WriteObjectField("scriptArguments", val.ScriptArguments,false); + encoder.WriteObjectField("scriptText", val.ScriptText,true); + } + } + private class OperationOptionsHandler : AbstractObjectSerializationHandler { + public OperationOptionsHandler() + : base(typeof(OperationOptions),"OperationOptions") { + + } + public override Object Deserialize(ObjectDecoder decoder) { + IDictionary options = + (IDictionary)decoder.ReadObjectField("options",null,null); + IDictionary options2 = + CollectionUtil.NewDictionary(options); + return new OperationOptions(options2); + } + + public override void Serialize(Object obj, ObjectEncoder encoder) { + OperationOptions val = (OperationOptions)obj; + encoder.WriteObjectField("options", val.Options,false); + } + } + private class OperationOptionInfoHandler : AbstractObjectSerializationHandler { + public OperationOptionInfoHandler() + : base(typeof(OperationOptionInfo),"OperationOptionInfo") { + + } + public override Object Deserialize(ObjectDecoder decoder) { + String name = decoder.ReadStringField("name",null); + Type type = decoder.ReadClassField("type",null); + return new OperationOptionInfo(name,type); + } + + public override void Serialize(Object obj, ObjectEncoder encoder) { + OperationOptionInfo val = (OperationOptionInfo)obj; + encoder.WriteStringField("name", val.Name); + encoder.WriteClassField("type", val.OptionType); + } + } + private class SyncTokenHandler : AbstractObjectSerializationHandler { + public SyncTokenHandler() + : base(typeof(SyncToken),"SyncToken") { + + } + public override Object Deserialize(ObjectDecoder decoder) { + Object value = decoder.ReadObjectField("value",null,null); + return new SyncToken(value); + } + + public override void Serialize(Object obj, ObjectEncoder encoder) { + SyncToken val = (SyncToken)obj; + encoder.WriteObjectField("value", val.Value,false); + } + } + private class SyncDeltaHandler : AbstractObjectSerializationHandler { + public SyncDeltaHandler() + : base(typeof(SyncDelta),"SyncDelta") { + + } + public override Object Deserialize(ObjectDecoder decoder) { + SyncDeltaBuilder builder = new SyncDeltaBuilder(); + builder.Uid=((Uid)decoder.ReadObjectField("Uid",typeof(Uid),null)); + builder.DeltaType=((SyncDeltaType)decoder.ReadObjectField("SyncDeltaType",typeof(SyncDeltaType),null)); + builder.Token=((SyncToken)decoder.ReadObjectField("SyncToken",typeof(SyncToken),null)); + ICollection attributesObj = (ICollection)decoder.ReadObjectField("Attributes",typeof(ICollection),null); + ICollection attributes = + CollectionUtil.NewSet(attributesObj); + builder.Attributes=(attributes); + return builder.Build(); + } + + public override void Serialize(Object obj, ObjectEncoder encoder) { + SyncDelta val = (SyncDelta)obj; + encoder.WriteObjectField("Uid", val.Uid,true); + encoder.WriteObjectField("SyncDeltaType", val.DeltaType,true); + encoder.WriteObjectField("SyncToken", val.Token,true); + encoder.WriteObjectField("Attributes", val.Attributes,true); + } + } + + } + #endregion + + #region FilterHandlers + internal static class FilterHandlers { + public static readonly IList HANDLERS = + new List(); + static FilterHandlers() { + HANDLERS.Add(new AndFilterHandler()); + HANDLERS.Add(new ContainsFilterHandler()); + HANDLERS.Add(new EndsWithFilterHandler()); + HANDLERS.Add(new EqualsFilterHandler()); + HANDLERS.Add(new GreaterThanFilterHandler()); + HANDLERS.Add(new GreaterThanOrEqualFilterHandler()); + HANDLERS.Add(new LessThanFilterHandler()); + HANDLERS.Add(new LessThanOrEqualFilterHandler()); + HANDLERS.Add(new NotFilterHandler()); + HANDLERS.Add(new OrFilterHandler()); + HANDLERS.Add(new StartsWithFilterHandler()); + HANDLERS.Add(new ContainsAllValuesFilterHandler()); + } + + private abstract class CompositeFilterHandler + : AbstractObjectSerializationHandler + where T : CompositeFilter { + + protected CompositeFilterHandler(String typeName) + : base(typeof(T),typeName) { + } + + + public override sealed Object Deserialize(ObjectDecoder decoder) { + Filter left = (Filter)decoder.ReadObjectContents(0); + Filter right = (Filter)decoder.ReadObjectContents(1); + return CreateFilter(left, right); + } + + public override sealed void Serialize(Object obj, ObjectEncoder encoder) { + CompositeFilter val = (CompositeFilter)obj; + encoder.WriteObjectContents(val.Left); + encoder.WriteObjectContents(val.Right); + } + + protected abstract T CreateFilter(Filter left, Filter right); + } + + private abstract class AttributeFilterHandler + : AbstractObjectSerializationHandler + where T : AttributeFilter { + + protected AttributeFilterHandler(String typeName) + : base(typeof(T),typeName) { + } + + + public override sealed Object Deserialize(ObjectDecoder decoder) { + ConnectorAttribute attribute = (ConnectorAttribute)decoder.ReadObjectField("attribute",null,null); + return CreateFilter(attribute); + } + + public override sealed void Serialize(Object obj, ObjectEncoder encoder) + { + AttributeFilter val = (AttributeFilter)obj; + encoder.WriteObjectField("attribute", val.GetAttribute(), false); + } + + protected abstract T CreateFilter(ConnectorAttribute attribute); + } + + + + + private class AndFilterHandler : CompositeFilterHandler { + public AndFilterHandler() + : base("AndFilter") { + } + protected override AndFilter CreateFilter(Filter left, Filter right) { + return new AndFilter(left,right); + } + } + + private class ContainsFilterHandler : AttributeFilterHandler { + public ContainsFilterHandler() + : base("ContainsFilter") { + } + protected override ContainsFilter CreateFilter(ConnectorAttribute attribute) { + return new ContainsFilter(attribute); + } + } + + private class EndsWithFilterHandler : AttributeFilterHandler { + public EndsWithFilterHandler() + : base("EndsWithFilter") { + } + protected override EndsWithFilter CreateFilter(ConnectorAttribute attribute) { + return new EndsWithFilter(attribute); + } + } + + private class EqualsFilterHandler : AttributeFilterHandler { + public EqualsFilterHandler() + : base("EqualsFilter") { + } + protected override EqualsFilter CreateFilter(ConnectorAttribute attribute) { + return new EqualsFilter(attribute); + } + } + + private class GreaterThanFilterHandler : AttributeFilterHandler { + public GreaterThanFilterHandler() + : base("GreaterThanFilter") { + } + protected override GreaterThanFilter CreateFilter(ConnectorAttribute attribute) { + return new GreaterThanFilter(attribute); + } + } + + private class GreaterThanOrEqualFilterHandler : AttributeFilterHandler { + public GreaterThanOrEqualFilterHandler() + : base("GreaterThanOrEqualFilter") { + } + protected override GreaterThanOrEqualFilter CreateFilter(ConnectorAttribute attribute) { + return new GreaterThanOrEqualFilter(attribute); + } + } + private class LessThanFilterHandler : AttributeFilterHandler { + public LessThanFilterHandler() + : base("LessThanFilter") { + } + protected override LessThanFilter CreateFilter(ConnectorAttribute attribute) { + return new LessThanFilter(attribute); + } + } + private class LessThanOrEqualFilterHandler : AttributeFilterHandler { + public LessThanOrEqualFilterHandler() + : base("LessThanOrEqualFilter") { + } + protected override LessThanOrEqualFilter CreateFilter(ConnectorAttribute attribute) { + return new LessThanOrEqualFilter(attribute); + } + } + private class NotFilterHandler + : AbstractObjectSerializationHandler { + + public NotFilterHandler() + : base(typeof(NotFilter),"NotFilter") { + } + + + public override sealed Object Deserialize(ObjectDecoder decoder) { + Filter filter = + (Filter)decoder.ReadObjectContents(0); + return new NotFilter(filter); + } + + public override sealed void Serialize(Object obj, ObjectEncoder encoder) + { + NotFilter val = (NotFilter)obj; + encoder.WriteObjectContents(val.Filter); + } + + } + private class OrFilterHandler : CompositeFilterHandler { + public OrFilterHandler() + : base("OrFilter") { + } + protected override OrFilter CreateFilter(Filter left, Filter right) { + return new OrFilter(left,right); + } + } + private class StartsWithFilterHandler : AttributeFilterHandler { + public StartsWithFilterHandler() + : base("StartsWithFilter") { + } + protected override StartsWithFilter CreateFilter(ConnectorAttribute attribute) { + return new StartsWithFilter(attribute); + } + } + + private class ContainsAllValuesFilterHandler : AttributeFilterHandler { + public ContainsAllValuesFilterHandler() + : base("ContainsAllValuesFilter") { + } + protected override ContainsAllValuesFilter CreateFilter(ConnectorAttribute attribute) { + return new ContainsAllValuesFilter(attribute); + } + } + + } + #endregion + + #region MessageHandlers + internal static class MessageHandlers { + public static readonly IList HANDLERS = + new List(); + static MessageHandlers() { + HANDLERS.Add(new HelloRequestHandler()); + HANDLERS.Add(new HelloResponseHandler()); + HANDLERS.Add(new OperationRequestHandler()); + HANDLERS.Add(new OperationResponseEndHandler()); + HANDLERS.Add(new OperationResponsePartHandler()); + HANDLERS.Add(new OperationRequestMoreDataHandler()); + HANDLERS.Add(new OperationRequestStopDataHandler()); + HANDLERS.Add(new OperationResponsePauseHandler()); + HANDLERS.Add(new EchoMessageHandler()); + } + private class HelloRequestHandler + : AbstractObjectSerializationHandler { + + public HelloRequestHandler() + : base(typeof(HelloRequest),"HelloRequest") { + } + + + public override sealed Object Deserialize(ObjectDecoder decoder) { + return new HelloRequest(); + } + + public override sealed void Serialize(Object obj, ObjectEncoder encoder) + { + } + + } + private class HelloResponseHandler + : AbstractObjectSerializationHandler { + + public HelloResponseHandler() + : base(typeof(HelloResponse),"HelloResponse") { + } + + + public override sealed Object Deserialize(ObjectDecoder decoder) { + Exception exception = + (Exception)decoder.ReadObjectField("exception",null,null); + IList connectorInfosObj = + (IList)decoder.ReadObjectField("ConnectorInfos",typeof(IList),null); + IList connectorInfos = + CollectionUtil.NewList(connectorInfosObj); + return new HelloResponse(exception,connectorInfos); + } + + public override sealed void Serialize(Object obj, ObjectEncoder encoder) + { + HelloResponse val = (HelloResponse)obj; + encoder.WriteObjectField("exception", val.Exception, false); + encoder.WriteObjectField("ConnectorInfos", val.ConnectorInfos, true); + } + + } + private class OperationRequestHandler + : AbstractObjectSerializationHandler { + + public OperationRequestHandler() + : base(typeof(OperationRequest),"OperationRequest") { + } + + + public override sealed Object Deserialize(ObjectDecoder decoder) { + ConnectorKey connectorKey = + (ConnectorKey)decoder.ReadObjectField("ConnectorKey",typeof(ConnectorKey),null); + APIConfigurationImpl configuration = + (APIConfigurationImpl)decoder.ReadObjectField("APIConfiguration",typeof(APIConfigurationImpl),null); + Type operation = + decoder.ReadClassField("operation",null); + IList arguments = (IList) + decoder.ReadObjectField("Arguments",typeof(IList),null); + return new OperationRequest(connectorKey, + configuration, + operation, + arguments); + } + + public override sealed void Serialize(Object obj, ObjectEncoder encoder) + { + OperationRequest val = + (OperationRequest)obj; + encoder.WriteClassField("operation", + val.Operation); + encoder.WriteObjectField("ConnectorKey", + val.ConnectorKey,true); + encoder.WriteObjectField("APIConfiguration", + val.Configuration,true); + encoder.WriteObjectField("Arguments", + val.Arguments,true); + } + + } + private class OperationResponseEndHandler + : AbstractObjectSerializationHandler { + + public OperationResponseEndHandler() + : base(typeof(OperationResponseEnd),"OperationResponseEnd") { + } + + + public override sealed Object Deserialize(ObjectDecoder decoder) { + return new OperationResponseEnd(); + } + + public override sealed void Serialize(Object obj, ObjectEncoder encoder) + { + } + } + private class OperationResponsePartHandler + : AbstractObjectSerializationHandler { + + public OperationResponsePartHandler() + : base(typeof(OperationResponsePart),"OperationResponsePart") { + } + + + public override sealed Object Deserialize(ObjectDecoder decoder) { + Exception exception = + (Exception)decoder.ReadObjectField("exception",null,null); + Object result = + decoder.ReadObjectField("result",null,null); + + return new OperationResponsePart(exception,result); + } + + public override sealed void Serialize(Object obj, ObjectEncoder encoder) + { + OperationResponsePart val = (OperationResponsePart)obj; + encoder.WriteObjectField("exception", val.Exception, false); + encoder.WriteObjectField("result", val.Result, false); + } + } + private class OperationRequestMoreDataHandler + : AbstractObjectSerializationHandler { + + public OperationRequestMoreDataHandler() + : base(typeof(OperationRequestMoreData),"OperationRequestMoreData") { + } + + + public override sealed Object Deserialize(ObjectDecoder decoder) { + return new OperationRequestMoreData(); + } + + public override sealed void Serialize(Object obj, ObjectEncoder encoder) + { + } + } + private class OperationRequestStopDataHandler + : AbstractObjectSerializationHandler { + + public OperationRequestStopDataHandler() + : base(typeof(OperationRequestStopData),"OperationRequestStopData") { + } + + + public override sealed Object Deserialize(ObjectDecoder decoder) { + return new OperationRequestStopData(); + } + + public override sealed void Serialize(Object obj, ObjectEncoder encoder) + { + } + } + private class OperationResponsePauseHandler + : AbstractObjectSerializationHandler { + + public OperationResponsePauseHandler() + : base(typeof(OperationResponsePause),"OperationResponsePause") { + } + + + public override sealed Object Deserialize(ObjectDecoder decoder) { + return new OperationResponsePause(); + } + + public override sealed void Serialize(Object obj, ObjectEncoder encoder) + { + } + } + private class EchoMessageHandler + : AbstractObjectSerializationHandler { + + public EchoMessageHandler() + : base(typeof(EchoMessage),"EchoMessage") { + } + + + public override sealed Object Deserialize(ObjectDecoder decoder) { + return new EchoMessage(decoder.ReadObjectField("value",null,null), + (String)decoder.ReadObjectField("objectXml",typeof(string),null)); + } + + public override sealed void Serialize(Object obj, ObjectEncoder encoder) + { + EchoMessage val = (EchoMessage)obj; + encoder.WriteObjectField("value",val.Object,false); + encoder.WriteObjectField("objectXml",val.ObjectXml,true); + } + } + + } + #endregion + +} diff --git a/FrameworkInternal/SerializerBinary.cs b/FrameworkInternal/SerializerBinary.cs new file mode 100644 index 00000000..9b9b52f5 --- /dev/null +++ b/FrameworkInternal/SerializerBinary.cs @@ -0,0 +1,792 @@ +/* + * Copyright 2008 Sun Microsystems, Inc. All rights reserved. + * + * U.S. Government Rights - Commercial software. Government users + * are subject to the Sun Microsystems, Inc. standard license agreement + * and applicable provisions of the FAR and its supplements. + * + * Use is subject to license terms. + * + * This distribution may include materials developed by third parties. + * Sun, Sun Microsystems, the Sun logo, Java and Project Identity + * Connectors are trademarks or registered trademarks of Sun + * Microsystems, Inc. or its subsidiaries in the U.S. and other + * countries. + * + * UNIX is a registered trademark in the U.S. and other countries, + * exclusively licensed through X/Open Company, Ltd. + * + * ----------- + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER. + * + * Copyright 2008 Sun Microsystems, Inc. All rights reserved. + * + * The contents of this file are subject to the terms of the Common Development + * and Distribution License(CDDL) (the License). You may not use this file + * except in compliance with the License. + * + * You can obtain a copy of the License at + * http://identityconnectors.dev.java.net/CDDLv1.0.html + * See the License for the specific language governing permissions and + * limitations under the License. + * + * When distributing the Covered Code, include this CDDL Header Notice in each + * file and include the License file at identityconnectors/legal/license.txt. + * If applicable, add the following below this CDDL Header, with the fields + * enclosed by brackets [] replaced by your own identifying information: + * "Portions Copyrighted [year] [name of copyright owner]" + * ----------- + */ +using System; +using System.Collections.Generic; +using Org.IdentityConnectors.Common; +using Org.IdentityConnectors.Framework.Common.Exceptions; +using Org.IdentityConnectors.Framework.Common.Serializer; +using Org.IdentityConnectors.Framework.Impl.Serializer; +using System.IO; +using System.Text; +namespace Org.IdentityConnectors.Framework.Impl.Serializer.Binary +{ + + internal class InternalEncoder { + + + /** + * Mapping from type name to the ID we serialize so we only have to + */ + private IDictionary _constantPool = + new Dictionary(); + + private IList _constantBuffer = new List(); + + private IList _outputBufferStack = new List(); + internal Stream _rootOutput; + private bool _firstObject = true; + + public InternalEncoder(Stream output) { + _rootOutput = output; + } + + public void WriteObject(ObjectEncoder encoder, Object obj) { + + if (_firstObject) { + WriteInt(BinaryObjectEncoder.OBJECT_MAGIC); + WriteInt(BinaryObjectEncoder.ENCODING_VERSION); + _firstObject = false; + } + + MemoryStream objectBuffer = new MemoryStream(); + _outputBufferStack.Add(objectBuffer); + + if ( obj == null ) { + WriteByte(BinaryObjectEncoder.OBJECT_TYPE_NULL); + } + else { + Type clazz = obj.GetType(); + WriteClass(clazz); + ObjectSerializationHandler handler = + ObjectSerializerRegistry.GetHandlerByObjectType(clazz); + if ( handler == null ) { + //we may have special handlers for certain types of arrays + //if handler is null, treat like any other array + if ( obj is Array ) { + Array array = (Array)obj; + int length = array.Length; + for ( int i = 0; i < length; i++ ) { + Object val = array.GetValue(i); + StartAnonymousField(); + WriteObject(encoder,val); + EndField(); + } + } + else { + throw new ConnectorException("No serializer for class: "+clazz); + } + } + else { + handler.Serialize(obj, encoder); + } + } + + //write end-object into the current obj buffer + WriteByte(BinaryObjectEncoder.FIELD_TYPE_END_OBJECT); + + //pop the stack + _outputBufferStack.RemoveAt(_outputBufferStack.Count-1); + + //it's a top-level object, flush the constant pool + if (_outputBufferStack.Count == 0) { + WriteInt(_constantBuffer.Count); + foreach (String constant in _constantBuffer) { + WriteString(constant,false); + WriteInt(_constantPool[constant]); + } + _constantBuffer.Clear(); + } + + //now write the actual object + objectBuffer.Close(); + byte [] bytes = objectBuffer.ToArray(); + WriteBytes(bytes); + } + + public void WriteClass(Type clazz) + { + ObjectSerializationHandler handler = + ObjectSerializerRegistry.GetHandlerByObjectType(clazz); + ObjectTypeMapper mapper = + ObjectSerializerRegistry.GetMapperByObjectType(clazz); + if ( handler == null && clazz.IsArray ) { + //we may have special handlers for certain types of arrays + //if handler is null, treat like any other array + WriteByte(BinaryObjectEncoder.OBJECT_TYPE_ARRAY); + WriteClass(clazz.GetElementType()); + } + else if ( mapper == null ) { + throw new ConnectorException("No serializer for class: "+clazz); + } + else { + String typeName = mapper.HandledSerialType; + WriteByte(BinaryObjectEncoder.OBJECT_TYPE_CLASS); + WriteString(typeName,true); + } + } + + public void StartAnonymousField() { + WriteByte(BinaryObjectEncoder.FIELD_TYPE_ANONYMOUS_FIELD); + MemoryStream buf = new MemoryStream(); + _outputBufferStack.Add(buf); + } + + public void StartField(String name) { + WriteByte(BinaryObjectEncoder.FIELD_TYPE_NAMED_FIELD); + WriteString(name,true); + MemoryStream buf = new MemoryStream(); + _outputBufferStack.Add(buf); + } + + public void EndField() { + MemoryStream buf = _outputBufferStack[_outputBufferStack.Count-1]; + _outputBufferStack.RemoveAt(_outputBufferStack.Count-1); + buf.Close(); + byte [] bytes = buf.ToArray(); + WriteByteArray(bytes); + } + + public void WriteInt(int v) + { + Stream output = GetCurrentOutput(); + output.WriteByte((byte)(0xff & (v >> 24))); + output.WriteByte((byte)(0xff & (v >> 16))); + output.WriteByte((byte)(0xff & (v >> 8))); + output.WriteByte((byte)(0xff & v)); + } + + public void WriteLong(long v) + { + Stream output = GetCurrentOutput(); + output.WriteByte((byte)(0xff & (v >> 56))); + output.WriteByte((byte)(0xff & (v >> 48))); + output.WriteByte((byte)(0xff & (v >> 40))); + output.WriteByte((byte)(0xff & (v >> 32))); + output.WriteByte((byte)(0xff & (v >> 24))); + output.WriteByte((byte)(0xff & (v >> 16))); + output.WriteByte((byte)(0xff & (v >> 8))); + output.WriteByte((byte)(0xff & v)); + } + + public void WriteDouble(double l) + { + long val = BitConverter.DoubleToInt64Bits(l); + WriteLong(val); + } + + public void WriteByteArray(byte[] v) + { + WriteInt(v.Length); + WriteBytes(v); + } + + public void WriteByte(byte b) + { + GetCurrentOutput().WriteByte(b); + } + + public void WriteBoolean(bool b) + { + WriteByte(b ? (byte)1 : (byte)0); + } + + public void WriteString(String str, bool intern) + { + if ( intern ) { + int code = InternIdentifier(str); + WriteInt(code); + return; + } + byte [] bytes = Encoding.UTF8.GetBytes(str); + WriteByteArray(bytes); + } + + private int InternIdentifier(String name) { + int code = CollectionUtil.GetValue(_constantPool,name,-1); + if ( code == -1 ) { + code = _constantPool.Count; + _constantPool[name] = code; + _constantBuffer.Add(name); + } + return code; + } + + private void WriteBytes(byte [] v) + { + //only write if length > 0 - C# seems to have a problem with + //zero-length byte arrays + if ( v.Length > 0 ) { + GetCurrentOutput().Write(v,0,v.Length); + } + } + + private Stream GetCurrentOutput() { + if (_outputBufferStack.Count == 0) { + return _rootOutput; + } + else { + MemoryStream buf = _outputBufferStack[_outputBufferStack.Count-1]; + return buf; + } + } + + } + + + + internal class BinaryObjectEncoder : ObjectEncoder,BinaryObjectSerializer { + + + + public const int ENCODING_VERSION = 1; + + public const int OBJECT_MAGIC = 0xFAFB; + + public const byte OBJECT_TYPE_NULL = 60; + public const byte OBJECT_TYPE_CLASS = 61; + public const byte OBJECT_TYPE_ARRAY = 62; + + public const byte FIELD_TYPE_ANONYMOUS_FIELD = 70; + public const byte FIELD_TYPE_NAMED_FIELD = 71; + public const byte FIELD_TYPE_END_OBJECT = 72; + + + private InternalEncoder _internalEncoder; + + + + + public BinaryObjectEncoder(Stream output) { + _internalEncoder = new InternalEncoder(new BufferedStream(output,4096)); + } + + public void Flush() { + _internalEncoder._rootOutput.Flush(); + } + + public void Close() { + _internalEncoder._rootOutput.Close(); + } + + public void WriteObject(Object o) { + _internalEncoder.WriteObject(this,o); + } + + public void WriteBooleanContents(bool v) { + _internalEncoder.StartAnonymousField(); + _internalEncoder.WriteBoolean(v); + _internalEncoder.EndField(); + } + + public void WriteBooleanField(String fieldName, bool v) { + _internalEncoder.StartField(fieldName); + _internalEncoder.WriteBoolean(v); + _internalEncoder.EndField(); + } + + public void WriteByteArrayContents(byte[] v) { + _internalEncoder.StartAnonymousField(); + _internalEncoder.WriteByteArray(v); + _internalEncoder.EndField(); + } + + public void WriteClassContents(Type v) { + _internalEncoder.StartAnonymousField(); + _internalEncoder.WriteClass(v); + _internalEncoder.EndField(); + } + + public void WriteClassField(string fieldName, Type v) { + if ( v != null ) { + _internalEncoder.StartField(fieldName); + _internalEncoder.WriteClass(v); + _internalEncoder.EndField(); + } + } + + public void WriteDoubleContents(double v) { + _internalEncoder.StartAnonymousField(); + _internalEncoder.WriteDouble(v); + _internalEncoder.EndField(); + } + + public void WriteDoubleField(String fieldName, double v) { + _internalEncoder.StartField(fieldName); + _internalEncoder.WriteDouble(v); + _internalEncoder.EndField(); + } + + public void WriteFloatContents(float v) { + _internalEncoder.StartAnonymousField(); + //write as double since C# only knows how to deal with that + _internalEncoder.WriteDouble((double)v); + _internalEncoder.EndField(); + } + + public void WriteFloatField(String fieldName, float v) { + _internalEncoder.StartField(fieldName); + //write as double since C# only knows how to deal with that + _internalEncoder.WriteDouble((double)v); + _internalEncoder.EndField(); + } + + public void WriteIntContents(int v) { + _internalEncoder.StartAnonymousField(); + _internalEncoder.WriteInt(v); + _internalEncoder.EndField(); + } + + public void WriteIntField(String fieldName, int v) { + _internalEncoder.StartField(fieldName); + _internalEncoder.WriteInt(v); + _internalEncoder.EndField(); + } + + public void WriteLongContents(long v) { + _internalEncoder.StartAnonymousField(); + _internalEncoder.WriteLong(v); + _internalEncoder.EndField(); + } + + public void WriteLongField(String fieldName, long v) { + _internalEncoder.StartField(fieldName); + _internalEncoder.WriteLong(v); + _internalEncoder.EndField(); + } + + public void WriteObjectContents(Object obj) { + _internalEncoder.StartAnonymousField(); + _internalEncoder.WriteObject(this,obj); + _internalEncoder.EndField(); + } + + public void WriteObjectField(String fieldName, Object obj, bool inline) { + _internalEncoder.StartField(fieldName); + _internalEncoder.WriteObject(this,obj); + _internalEncoder.EndField(); + } + + public void WriteStringContents(String str) { + _internalEncoder.StartAnonymousField(); + _internalEncoder.WriteString(str,false); + _internalEncoder.EndField(); + } + + public void WriteStringField(String fieldName, String val) { + if ( val != null ) { + _internalEncoder.StartField(fieldName); + _internalEncoder.WriteString(val,false); + _internalEncoder.EndField(); + } + } + } + + internal class ReadState { + public IDictionary objectFields = new Dictionary(); + public IList anonymousFields = new List(); + public Stream currentInput; + public ReadState() { + } + public bool StartField(String name) { + currentInput = null; + byte [] content = CollectionUtil.GetValue(objectFields,name,null); + if ( content == null ) { + return false; + } + else { + currentInput = new MemoryStream(content); + return true; + } + } + public void StartAnonymousField(int index) { + if ( index >= anonymousFields.Count ) { + throw new ConnectorException("Anonymous content not found"); + } + currentInput = new MemoryStream(anonymousFields[index]); + } + } + + internal class InternalDecoder { + private readonly byte [] _int_buf = new byte[4]; + private readonly byte [] _long_buf = new byte[8]; + private readonly byte [] _byte_buf = new byte[1]; + + private bool _firstObject = true; + + private readonly IDictionary _constantPool = + new Dictionary(); + + private readonly IList _readStateStack = new List(); + internal readonly Stream _rootInput; + + public InternalDecoder(Stream input) { + _rootInput = input; + } + + public Object ReadObject(ObjectDecoder decoder) { + + if (_firstObject) { + int magic = ReadInt(); + if ( magic != BinaryObjectEncoder.OBJECT_MAGIC) { + throw new ConnectorException("Bad magic number: "+magic); + } + int version = ReadInt(); + if ( version != BinaryObjectEncoder.ENCODING_VERSION ) { + throw new ConnectorException("Unexpected version: "+version); + } + _firstObject = false; + } + + //if it's a top-level object, it's proceeded by a constant pool + if (_readStateStack.Count == 0) + { + int size = ReadInt(); + for ( int i = 0; i < size; i++ ) { + String constant = ReadString(false); + int code = ReadInt(); + _constantPool[code] = constant; + } + } + + Type clazz = ReadClass(); + + ReadState state = new ReadState(); + while(true) { + byte type = ReadByte(); + if ( type == BinaryObjectEncoder.FIELD_TYPE_END_OBJECT ) { + break; + } + else if ( type == BinaryObjectEncoder.FIELD_TYPE_ANONYMOUS_FIELD ) { + byte [] bytes = ReadByteArray(); + state.anonymousFields.Add(bytes); + } + else if ( type == BinaryObjectEncoder.FIELD_TYPE_NAMED_FIELD ) { + String fieldName = ReadString(true); + byte [] bytes = ReadByteArray(); + state.objectFields[fieldName] = bytes; + } + else { + throw new ConnectorException("Unknown type: "+type); + } + } + _readStateStack.Add(state); + + Object rv; + if ( clazz == null ) { + rv = null; + } + else { + ObjectSerializationHandler handler = + ObjectSerializerRegistry.GetHandlerByObjectType(clazz); + if ( handler == null ) { + //we may have special handlers for certain types of arrays + //if handler is null, treat like any other array + if ( clazz.IsArray ) { + int length = GetNumAnonymousFields(); + Array array = Array.CreateInstance(clazz.GetElementType(), + length); + for ( int i = 0; i < length; i++) { + StartAnonymousField(i); + Object element = ReadObject(decoder); + array.SetValue( element, i ); + } + rv = array; + } + else { + throw new ConnectorException("No deserializer for type: "+clazz); + } + } + else { + rv = handler.Deserialize(decoder); + } + } + _readStateStack.RemoveAt(_readStateStack.Count-1); + return rv; + } + + public Type ReadClass() { + int type = ReadByte(); + if ( type == BinaryObjectEncoder.OBJECT_TYPE_NULL ) { + return null; + } + else if ( type == BinaryObjectEncoder.OBJECT_TYPE_ARRAY ) { + Type componentClass = ReadClass(); + return componentClass.MakeArrayType(); + } + else if ( type == BinaryObjectEncoder.OBJECT_TYPE_CLASS ) { + String typeName = ReadString(true); + ObjectTypeMapper mapper = + ObjectSerializerRegistry.GetMapperBySerialType(typeName); + if ( mapper == null ) { + throw new ConnectorException("No deserializer for type: "+typeName); + } + return mapper.HandledObjectType; + } + else { + throw new ConnectorException("Bad type value: "+type); + } + } + + public int GetNumAnonymousFields() { + ReadState readState = _readStateStack[_readStateStack.Count-1]; + return readState.anonymousFields.Count; + } + + public void StartAnonymousField(int index) { + ReadState readState = _readStateStack[_readStateStack.Count-1]; + readState.StartAnonymousField(index); + } + + public bool StartField(String name) { + ReadState readState = _readStateStack[_readStateStack.Count-1]; + return readState.StartField(name); + } + + public int ReadInt() { + ReadByteArrayFully(_int_buf); + return (((_int_buf[0] & 0xff) << 24) | ((_int_buf[1] & 0xff) << 16) | + ((_int_buf[2] & 0xff) << 8) | (_int_buf[3] & 0xff)); + } + + public long ReadLong() { + ReadByteArrayFully(_long_buf); + return (((long)(_long_buf[0] & 0xff) << 56) | + ((long)(_long_buf[1] & 0xff) << 48) | + ((long)(_long_buf[2] & 0xff) << 40) | + ((long)(_long_buf[3] & 0xff) << 32) | + ((long)(_long_buf[4] & 0xff) << 24) | + ((long)(_long_buf[5] & 0xff) << 16) | + ((long)(_long_buf[6] & 0xff) << 8) | + ((long)(_long_buf[7] & 0xff))); + } + + public double ReadDouble() { + long v = ReadLong(); + return BitConverter.Int64BitsToDouble(v); + } + + public byte [] ReadByteArray() { + int len = ReadInt(); + byte [] bytes = new byte[len]; + ReadByteArrayFully(bytes); + return bytes; + } + + public byte ReadByte() + { + ReadByteArrayFully(_byte_buf); + return _byte_buf[0]; + } + + public bool ReadBoolean() { + byte b = ReadByte(); + return b != 0; + } + + public String ReadString(bool interned) { + if ( interned ) { + int code = ReadInt(); + String name = CollectionUtil.GetValue(_constantPool,code,null); + if ( name == null ) { + throw new ConnectorException("Undeclared code: "+code); + } + return name; + } + byte [] bytes = ReadByteArray(); + return Encoding.UTF8.GetString(bytes); + } + + private Stream GetCurrentInput() { + if (_readStateStack.Count > 0) { + ReadState state = _readStateStack[_readStateStack.Count-1]; + return state.currentInput; + } + else { + return _rootInput; + } + } + + private void ReadByteArrayFully(byte [] bytes) + { + int pos = 0; + while ( pos < bytes.Length ) { + int count = GetCurrentInput().Read(bytes,pos,bytes.Length-pos); + if ( count <= 0 ) { + throw new EndOfStreamException(); + } + pos+=count; + } + } + } + + internal class BinaryObjectDecoder : ObjectDecoder,BinaryObjectDeserializer { + + private InternalDecoder _internalDecoder; + + public BinaryObjectDecoder(Stream inp) { + _internalDecoder = new InternalDecoder(new BufferedStream(inp,4096)); + } + + public void Close() { + _internalDecoder._rootInput.Close(); + } + + public Object ReadObject() { + return _internalDecoder.ReadObject(this); + } + + public bool ReadBooleanContents() { + _internalDecoder.StartAnonymousField(0); + return _internalDecoder.ReadBoolean(); + } + + public bool ReadBooleanField(String fieldName, bool dflt) { + if (_internalDecoder.StartField(fieldName)) { + return _internalDecoder.ReadBoolean(); + } + else { + return dflt; + } + } + + public byte[] ReadByteArrayContents() { + _internalDecoder.StartAnonymousField(0); + return _internalDecoder.ReadByteArray(); + } + + public Type ReadClassContents() { + _internalDecoder.StartAnonymousField(0); + return _internalDecoder.ReadClass(); + } + + public Type ReadClassField(string fieldName, Type dflt) { + if (_internalDecoder.StartField(fieldName)) { + return _internalDecoder.ReadClass(); + } + else { + return dflt; + } + } + + public double ReadDoubleContents() { + _internalDecoder.StartAnonymousField(0); + return _internalDecoder.ReadDouble(); + } + + public double ReadDoubleField(String fieldName, double dflt) { + if (_internalDecoder.StartField(fieldName)) { + return ReadDoubleContents(); + } + else { + return dflt; + } + } + + public float ReadFloatContents() { + _internalDecoder.StartAnonymousField(0); + //read as double since C# only knows how to deal with that + return (float)_internalDecoder.ReadDouble(); + } + + public float ReadFloatField(String fieldName, float dflt) { + if (_internalDecoder.StartField(fieldName)) { + //read as double since C# only knows how to deal with that + return (float)_internalDecoder.ReadDouble(); + } + else { + return dflt; + } + } + + public int ReadIntContents() { + _internalDecoder.StartAnonymousField(0); + return _internalDecoder.ReadInt(); + } + + public int ReadIntField(String fieldName, int dflt) { + if (_internalDecoder.StartField(fieldName)) { + return _internalDecoder.ReadInt(); + } + else { + return dflt; + } + } + + public long ReadLongContents() { + _internalDecoder.StartAnonymousField(0); + return _internalDecoder.ReadLong(); + } + + public long ReadLongField(String fieldName, long dflt) { + if (_internalDecoder.StartField(fieldName)) { + return _internalDecoder.ReadLong(); + } + else { + return dflt; + } + } + + public int GetNumSubObjects() + { + return _internalDecoder.GetNumAnonymousFields(); + } + + public Object ReadObjectContents(int index) { + _internalDecoder.StartAnonymousField(index); + return _internalDecoder.ReadObject(this); + } + + public Object ReadObjectField(String fieldName, Type expected, Object dflt) { + if (_internalDecoder.StartField(fieldName)) { + return _internalDecoder.ReadObject(this); + } + else { + return dflt; + } + } + + public String ReadStringContents() { + _internalDecoder.StartAnonymousField(0); + return _internalDecoder.ReadString(false); + } + + public String ReadStringField(String fieldName, String dflt) { + if (_internalDecoder.StartField(fieldName)) { + return _internalDecoder.ReadString(false); + } + else { + return dflt; + } + } + + } + + +} diff --git a/FrameworkInternal/SerializerXml.cs b/FrameworkInternal/SerializerXml.cs new file mode 100644 index 00000000..e780625c --- /dev/null +++ b/FrameworkInternal/SerializerXml.cs @@ -0,0 +1,811 @@ +/* + * Copyright 2008 Sun Microsystems, Inc. All rights reserved. + * + * U.S. Government Rights - Commercial software. Government users + * are subject to the Sun Microsystems, Inc. standard license agreement + * and applicable provisions of the FAR and its supplements. + * + * Use is subject to license terms. + * + * This distribution may include materials developed by third parties. + * Sun, Sun Microsystems, the Sun logo, Java and Project Identity + * Connectors are trademarks or registered trademarks of Sun + * Microsystems, Inc. or its subsidiaries in the U.S. and other + * countries. + * + * UNIX is a registered trademark in the U.S. and other countries, + * exclusively licensed through X/Open Company, Ltd. + * + * ----------- + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER. + * + * Copyright 2008 Sun Microsystems, Inc. All rights reserved. + * + * The contents of this file are subject to the terms of the Common Development + * and Distribution License(CDDL) (the License). You may not use this file + * except in compliance with the License. + * + * You can obtain a copy of the License at + * http://identityconnectors.dev.java.net/CDDLv1.0.html + * See the License for the specific language governing permissions and + * limitations under the License. + * + * When distributing the Covered Code, include this CDDL Header Notice in each + * file and include the License file at identityconnectors/legal/license.txt. + * If applicable, add the following below this CDDL Header, with the fields + * enclosed by brackets [] replaced by your own identifying information: + * "Portions Copyrighted [year] [name of copyright owner]" + * ----------- + */ +using System; +using System.Collections.Generic; +using System.IO; +using System.Resources; +using System.Text; +using System.Net; +using System.Xml; +using Org.IdentityConnectors.Common; +using Org.IdentityConnectors.Framework.Common.Exceptions; +using Org.IdentityConnectors.Framework.Common.Serializer; +namespace Org.IdentityConnectors.Framework.Impl.Serializer.Xml +{ + public class XmlObjectEncoder : ObjectEncoder { + + private StringBuilder _rootBuilder; + private XmlWriter _writer; + + public XmlObjectEncoder(StringBuilder builder) { + Assertions.NullCheck(builder, "builder"); + _rootBuilder = builder; + } + + public String WriteObject(Object o) { + XmlWriterSettings settings = new XmlWriterSettings(); + settings.Indent = true; + settings.OmitXmlDeclaration = true; + _writer = XmlWriter.Create(_rootBuilder, settings); + String rv = WriteObjectInternal(o,false); + _writer.Close(); + return rv; + } + + public void WriteBooleanContents(bool v) { + WriteStringContentsInternal(EncodeBoolean(v)); + } + + public void WriteBooleanField(String fieldName, bool v) { + WriteAttributeInternal(fieldName,EncodeBoolean(v)); + } + + public void WriteByteArrayContents(byte[] v) { + WriteStringContentsInternal(EncodeByteArray(v)); + } + + public void WriteClassContents(Type v) { + WriteStringContentsInternal(EncodeClass(v)); + } + + public void WriteClassField(String name, Type v) { + if ( v != null ) { + WriteAttributeInternal(name, EncodeClass(v)); + } + } + + public void WriteDoubleContents(double v) { + WriteStringContentsInternal(EncodeDouble(v)); + } + + public void WriteDoubleField(String fieldName, double v) { + WriteAttributeInternal(fieldName,EncodeDouble(v)); + } + + public void WriteFloatContents(float v) { + WriteStringContentsInternal(EncodeFloat(v)); + } + + public void WriteFloatField(String fieldName, float v) { + WriteAttributeInternal(fieldName,EncodeFloat(v)); + } + + public void WriteIntContents(int v) { + WriteStringContentsInternal(EncodeInt(v)); + } + + public void WriteIntField(String fieldName, int v) { + WriteAttributeInternal(fieldName,EncodeInt(v)); + } + + public void WriteLongContents(long v) { + WriteStringContentsInternal(EncodeLong(v)); + } + + public void WriteLongField(String fieldName, long v) { + WriteAttributeInternal(fieldName,EncodeLong(v)); + } + + public void WriteObjectContents(Object o) { + WriteObjectInternal(o,false); + } + + public void WriteObjectField(String fieldName, Object obj, bool inline) { + if (inline && obj == null) + { + return; //don't write anything + } + BeginElement(fieldName); + WriteObjectInternal(obj,inline); + EndElement(); + } + + public void WriteStringContents(String str) { + WriteStringContentsInternal(str); + } + + public void WriteStringField(String fieldName, String str) { + if ( str != null ) { + WriteAttributeInternal(fieldName, str); + } + } + + internal static String EncodeBoolean(bool b) { + return b.ToString(); + } + + private static String EncodeByteArray(byte [] bytes) { + return Convert.ToBase64String(bytes); + } + + private static String EncodeClass(Type clazz) { + ObjectSerializationHandler handler = + ObjectSerializerRegistry.GetHandlerByObjectType(clazz); + ObjectTypeMapper mapper = + ObjectSerializerRegistry.GetMapperByObjectType(clazz); + if ( handler == null && clazz.IsArray ) { + //we may have special handlers for certain types of arrays + //if handler is null, treat like any other array + return EncodeClass(clazz.GetElementType())+"[]"; + } + else if ( mapper == null ) { + throw new ConnectorException("No serializer for class: "+clazz); + } + else { + String typeName = mapper.HandledSerialType; + return typeName; + } + } + + internal static String EncodeDouble(double d) { + return d.ToString("R"); + } + + internal static String EncodeFloat(float d) { + return d.ToString("R"); + } + + internal static String EncodeInt(int d) { + return d.ToString(); + } + + internal static String EncodeLong(long d) { + return d.ToString(); + } + + /** + * Writes the object + * @param object + * @param inline + * @return The type name (regardless of whether it was inlined) + */ + String WriteObjectInternal(Object obj, bool inline) { + if ( obj == null ) { + if ( inline ) { + throw new ArgumentException("null cannot be inlined"); + } + BeginElement("null"); + EndElement(); + return "null"; + } + else { + Type clazz = obj.GetType(); + ObjectSerializationHandler handler = + ObjectSerializerRegistry.GetHandlerByObjectType(clazz); + if ( handler == null ) { + //we may have special handlers for certain types of arrays + //if handler is null, treat like any other array + if ( clazz.IsArray ) { + if (!inline) { + String componentTypeName = EncodeClass(clazz.GetElementType()); + BeginElement("Array"); + WriteAttributeInternal("componentType", componentTypeName); + } + Array array = (Array)obj; + int length = array.Length; + for ( int i = 0; i < length; i++ ) { + Object val = array.GetValue(i); + WriteObjectInternal(val,false); + } + if (!inline) { + EndElement(); + } + return "Array"; + } + else { + throw new ConnectorException("No serializer for class: "+clazz); + } + } + else { + String typeName = EncodeClass(clazz); + if (!inline) { + BeginElement(typeName); + } + handler.Serialize(obj, this); + if (!inline) { + EndElement(); + } + return typeName; + } + } + + } + + ////////////////////////////////////////////////////////////////// + // + // xml encoding + // + ///////////////////////////////////////////////////////////////// + + + + private void BeginElement(String name) { + _writer.WriteStartElement(name); + } + + private void EndElement() { + _writer.WriteEndElement(); + } + private void WriteAttributeInternal(String fieldName, String str) { + _writer.WriteAttributeString(fieldName,str); + } + private void WriteStringContentsInternal(String str) { + _writer.WriteString(str); + } + } + + public class XmlObjectDecoder : ObjectDecoder { + + private readonly XmlElement _node; + private readonly Type _expectedClass; + + public XmlObjectDecoder(XmlElement node, Type expectedClass) { + _node = node; + _expectedClass = expectedClass; + } + + public Object ReadObject() { + return ReadObjectInternal(); + } + + public bool ReadBooleanContents() { + return DecodeBoolean(ReadStringContentsInternal()); + } + + public bool ReadBooleanField(String fieldName, bool dflt) { + return DecodeBoolean(ReadStringAttributeInternal(fieldName,XmlObjectEncoder.EncodeBoolean(dflt))); + } + + public byte[] ReadByteArrayContents() { + return DecodeByteArray(ReadStringContentsInternal()); + } + + public Type ReadClassContents() { + return DecodeClass(ReadStringContentsInternal()); + } + + public Type ReadClassField(String name, Type dflt) { + String val = ReadStringAttributeInternal(name, null); + if ( val == null ) { + return dflt; + } + else { + return DecodeClass(val); + } + } + + public double ReadDoubleContents() { + return DecodeDouble(ReadStringContentsInternal()); + } + + public double ReadDoubleField(String fieldName, double dflt) { + return DecodeDouble(ReadStringAttributeInternal(fieldName,XmlObjectEncoder.EncodeDouble(dflt))); + } + + public float ReadFloatContents() { + return DecodeFloat(ReadStringContentsInternal()); + } + + public float ReadFloatField(String fieldName, float dflt) { + return DecodeFloat(ReadStringAttributeInternal(fieldName,XmlObjectEncoder.EncodeFloat(dflt))); + } + + public int ReadIntContents() { + return DecodeInt(ReadStringContentsInternal()); + } + + public int ReadIntField(String fieldName, int dflt) { + return DecodeInt(ReadStringAttributeInternal(fieldName,XmlObjectEncoder.EncodeInt(dflt))); + } + + public long ReadLongContents() { + return DecodeLong(ReadStringContentsInternal()); + } + + public long ReadLongField(String fieldName, long dflt) { + return DecodeLong(ReadStringAttributeInternal(fieldName,XmlObjectEncoder.EncodeLong(dflt))); + } + + public int GetNumSubObjects() { + int count = 0; + for (XmlElement subElement = XmlUtil.GetFirstChildElement(_node); + subElement != null; + subElement = XmlUtil.GetNextElement(subElement)) { + count++; + } + return count; + } + + public Object ReadObjectContents(int index) { + XmlElement subElement = XmlUtil.GetFirstChildElement(_node); + for ( int i = 0; i < index; i++) { + subElement = XmlUtil.GetNextElement(subElement); + } + + if ( subElement == null ) { + throw new ConnectorException("Missing subelement number: "+index); + } + + return new XmlObjectDecoder(subElement,null).ReadObject(); + } + + public Object ReadObjectField(String fieldName, Type expected, Object dflt) { + XmlElement child = XmlUtil.FindImmediateChildElement(_node, fieldName); + if ( child == null ) { + return dflt; + } + if ( expected != null ) { + return new XmlObjectDecoder(child,expected).ReadObject(); + } + XmlElement subElement = XmlUtil.GetFirstChildElement(child); + if ( subElement == null ) { + return dflt; + } + //if they specify null, don't apply defaults + return new XmlObjectDecoder(subElement,null).ReadObject(); + } + + public String ReadStringContents() { + String rv = ReadStringContentsInternal(); + return rv == null ? "" : rv; + } + + public String ReadStringField(String fieldName, String dflt) { + return ReadStringAttributeInternal(fieldName, dflt); + } + + private String ReadStringContentsInternal() { + String xml = XmlUtil.GetContent(_node); + return xml; + } + + private String ReadStringAttributeInternal(String name, String dflt) { + XmlAttribute attr = _node.GetAttributeNode(name); + if ( attr == null ) { + return dflt; + } + return attr.Value; + } + + private bool DecodeBoolean(String v) { + return Boolean.Parse(v); + } + + private byte [] DecodeByteArray(String base64) { + return Convert.FromBase64String(base64); + } + + private Type DecodeClass(String type) { + if ( type.EndsWith("[]") ) { + String componentName = type.Substring(0,type.Length-"[]".Length); + Type componentClass = + DecodeClass(componentName); + Type arrayClass = + componentClass.MakeArrayType(); + return arrayClass; + } + else { + ObjectTypeMapper mapper = + ObjectSerializerRegistry.GetMapperBySerialType(type); + if ( mapper == null ) { + throw new ConnectorException("No deserializer for type: "+type); + } + Type clazz = mapper.HandledObjectType; + return clazz; + } + } + + private double DecodeDouble(String val) { + return Double.Parse(val); + } + + private float DecodeFloat(String val) { + return Single.Parse(val); + } + + private int DecodeInt(String val) { + return Int32.Parse(val); + } + + private long DecodeLong(String val) { + return Int64.Parse(val); + } + + private Object ReadObjectInternal() { + if (_expectedClass != null) { + ObjectSerializationHandler handler = + ObjectSerializerRegistry.GetHandlerByObjectType(_expectedClass); + if ( handler == null ) { + if (_expectedClass.IsArray) { + IList temp = new List(); + for (XmlElement child = XmlUtil.GetFirstChildElement(_node); child != null; + child = XmlUtil.GetNextElement(child)) { + XmlObjectDecoder sub = new XmlObjectDecoder(child,null); + Object obj = sub.ReadObject(); + temp.Add(obj); + } + int length = temp.Count; + Array array = Array.CreateInstance(_expectedClass.GetElementType(),length); + for ( int i = 0; i < length; i++) { + Object element = temp[i]; + array.SetValue(element,i); + } + return array; + } + else { + throw new ConnectorException("No deserializer for type: "+_expectedClass); + } + } + else { + return handler.Deserialize(this); + } + } + else if ( _node.LocalName.Equals("null") ) { + return null; + } + else if (_node.LocalName.Equals("Array")) { + String componentType = XmlUtil.GetAttribute(_node, "componentType"); + if ( componentType == null ) { + componentType = "Object"; + } + Type componentClass = DecodeClass(componentType); + IList temp = new List(); + for (XmlElement child = XmlUtil.GetFirstChildElement(_node); child != null; + child = XmlUtil.GetNextElement(child)) { + XmlObjectDecoder sub = new XmlObjectDecoder(child,null); + Object obj = sub.ReadObject(); + temp.Add(obj); + } + int length = temp.Count; + Array array = Array.CreateInstance(componentClass, + length); + for ( int i = 0; i < length; i++) { + Object element = temp[i]; + array.SetValue(element,i); + } + return array; + } + else { + Type clazz = + DecodeClass(_node.LocalName); + ObjectSerializationHandler handler = + ObjectSerializerRegistry.GetHandlerByObjectType(clazz); + if ( handler == null ) { + throw new ConnectorException("No deserializer for type: "+clazz); + } + else { + return handler.Deserialize(this); + } + } + } + + } + + public class XmlObjectParser { + + public static void parse(TextReader inputSource, + XmlObjectResultsHandler handler, + bool validate) + { + XmlReaderSettings mySettings = + new XmlReaderSettings(); + if ( validate ) { + mySettings.ValidationType = ValidationType.DTD; + } + mySettings.ProhibitDtd = false; + mySettings.XmlResolver = new MyEntityResolver(validate); + XmlReader reader = XmlReader.Create(inputSource,mySettings); + MyParser parser = new MyParser(handler); + parser.Parse(reader); + } + + + private class MyEntityResolver : XmlResolver + { + private readonly bool _validate; + + public MyEntityResolver(bool validate) + { + _validate = validate; + } + + public override Object GetEntity(Uri absoluteUri, string role, Type ofObject) + { + String text = null; + if (absoluteUri.AbsolutePath.EndsWith(XmlObjectSerializerImpl.CONNECTORS_DTD)) { + if (!_validate) { + text = ""; + } + else { + text = GetDTD(); + } + } + if ( text != null ) { + byte [] bytes = Encoding.UTF8.GetBytes(text); + return new MemoryStream(bytes); + } + return null; + } + + public override ICredentials Credentials { + set { + + } + } + private static String GetDTD() + { + ResourceManager manager = + new ResourceManager("Org.IdentityConnectors.Resources", + typeof(XmlObjectParser).Assembly); + String contents = (String)manager.GetObject(XmlObjectSerializerImpl.CONNECTORS_DTD); + return contents; + } + } + + private class MyParser + { + /** + * The document for the current top-level element. with each top-level element, + * we discard the previous to avoid accumulating memory + */ + private XmlDocument _currentTopLevelElementDocument; + + + /** + * Stack of elements we are creating + */ + private IList _elementStack = new List(10); + + /** + * Results handler that we write our objects to + */ + private readonly XmlObjectResultsHandler _handler; + + /** + * Is the handler still handing + */ + private bool _stillHandling = true; + + + public MyParser(XmlObjectResultsHandler handler) + { + _handler = handler; + } + + public void Parse(XmlReader reader) + { + while ( _stillHandling && reader.Read() ) { + XmlNodeType nodeType = reader.NodeType; + switch (nodeType) { + case XmlNodeType.Element: + StartElement(reader.LocalName); + bool empty = reader.IsEmptyElement; + if (reader.MoveToFirstAttribute()) { + AddAttribute(reader.LocalName,reader.Value); + while (reader.MoveToNextAttribute()) { + AddAttribute(reader.LocalName,reader.Value); + } + } + if ( empty ) { + EndElement(); + } + break; + case XmlNodeType.Text: + case XmlNodeType.CDATA: + case XmlNodeType.Whitespace: + case XmlNodeType.SignificantWhitespace: + AddText(reader.Value); + break; + case XmlNodeType.EndElement: + EndElement(); + break; + } + } + } + + private XmlElement GetCurrentElement() + { + if (_elementStack.Count > 0 ) + { + return _elementStack[_elementStack.Count-1]; + } + else + { + return null; + } + } + + private void AddText(String text) + { + XmlElement currentElement = GetCurrentElement(); + if ( currentElement != null ) + { + currentElement.AppendChild(_currentTopLevelElementDocument.CreateTextNode(text)); + } + } + + private void EndElement() + { + if (_elementStack.Count > 0) //we don't push the top-level MULTI_OBJECT_ELEMENT on the stack + { + XmlElement element = _elementStack[_elementStack.Count-1]; + _elementStack.RemoveAt(_elementStack.Count-1); + if (_elementStack.Count == 0) { + _currentTopLevelElementDocument = null; + if (_stillHandling) { + XmlObjectDecoder decoder = new XmlObjectDecoder(element,null); + Object obj = decoder.ReadObject(); + _stillHandling = _handler(obj); + } + } + } + } + + + private void StartElement(String localName) + { + XmlElement element = null; + if (_elementStack.Count == 0) + { + if (!XmlObjectSerializerImpl.MULTI_OBJECT_ELEMENT.Equals(localName)) + { + _currentTopLevelElementDocument = new XmlDocument(); + element = _currentTopLevelElementDocument.CreateElement(localName); + } + } + else + { + element = + _currentTopLevelElementDocument.CreateElement(localName); + GetCurrentElement().AppendChild(element); + } + if (element != null) + { + _elementStack.Add(element); + } + } + + private void AddAttribute(String name, String val) + { + XmlElement element = GetCurrentElement(); + if ( element != null ) + { + element.SetAttribute(name, val); + } + } + } + } + + public class XmlObjectSerializerImpl : XmlObjectSerializer { + + public const String MULTI_OBJECT_ELEMENT = "MultiObject"; + public const String CONNECTORS_DTD = "connectors.dtd"; + + private readonly TextWriter _output; + + private readonly bool _multiObject; + + private readonly bool _includeHeader; + + private bool _firstObjectWritten; + + private bool _documentEnded; + + public XmlObjectSerializerImpl(TextWriter output, bool includeHeader, bool multiObject) { + _output = output; + _includeHeader = includeHeader; + _multiObject = multiObject; + } + + + /** + * Writes the next object to the stream. + * @param object The object to write. + * @see ObjectSerializerFactory for a list of supported types. + * @throws ConnectorException if there is more than one object + * and this is not configured for multi-object document. + */ + public void WriteObject(Object obj) { + if (_documentEnded) { + throw new InvalidOperationException("Attempt to writeObject after the document is already closed"); + } + StringBuilder buf = new StringBuilder(); + XmlObjectEncoder encoder = new XmlObjectEncoder(buf); + String elementName = encoder.WriteObject(obj); + if (!_firstObjectWritten) { + StartDocument(elementName); + } + else { + if (!_multiObject) { + throw new InvalidOperationException("Attempt to write multiple objects on a single-object document"); + } + } + Write(buf.ToString()); + _firstObjectWritten = true; + } + + public void Flush() { + _output.Flush(); + } + + public void Close( bool closeStream ) { + if (!_documentEnded) { + if (!_firstObjectWritten) { + if (!_multiObject) { + throw new InvalidOperationException("Attempt to write zero objects on a single-object document"); + } + StartDocument(null); + } + WriteEndDocument(); + _documentEnded = true; + } + if (closeStream) { + _output.Close(); + } + } + + private void StartDocument(String firstElement) { + if (_includeHeader) { + String docType = _multiObject ? MULTI_OBJECT_ELEMENT : firstElement; + String line1 = "\n"; + String line2 = "\n"; + Write(line1); + Write(line2); + } + if (_multiObject) { + String line3 = "<"+MULTI_OBJECT_ELEMENT+">\n"; + Write(line3); + } + } + + private void WriteEndDocument() { + if (_multiObject) { + String line1 = "\n"; + Write(line1); + } + } + + private void Write(String str) { + _output.Write(str); + } + + } + +} diff --git a/FrameworkInternal/Server.cs b/FrameworkInternal/Server.cs new file mode 100644 index 00000000..01ef76c1 --- /dev/null +++ b/FrameworkInternal/Server.cs @@ -0,0 +1,797 @@ +/* + * Copyright 2008 Sun Microsystems, Inc. All rights reserved. + * + * U.S. Government Rights - Commercial software. Government users + * are subject to the Sun Microsystems, Inc. standard license agreement + * and applicable provisions of the FAR and its supplements. + * + * Use is subject to license terms. + * + * This distribution may include materials developed by third parties. + * Sun, Sun Microsystems, the Sun logo, Java and Project Identity + * Connectors are trademarks or registered trademarks of Sun + * Microsystems, Inc. or its subsidiaries in the U.S. and other + * countries. + * + * UNIX is a registered trademark in the U.S. and other countries, + * exclusively licensed through X/Open Company, Ltd. + * + * ----------- + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER. + * + * Copyright 2008 Sun Microsystems, Inc. All rights reserved. + * + * The contents of this file are subject to the terms of the Common Development + * and Distribution License(CDDL) (the License). You may not use this file + * except in compliance with the License. + * + * You can obtain a copy of the License at + * http://identityconnectors.dev.java.net/CDDLv1.0.html + * See the License for the specific language governing permissions and + * limitations under the License. + * + * When distributing the Covered Code, include this CDDL Header Notice in each + * file and include the License file at identityconnectors/legal/license.txt. + * If applicable, add the following below this CDDL Header, with the fields + * enclosed by brackets [] replaced by your own identifying information: + * "Portions Copyrighted [year] [name of copyright owner]" + * ----------- + */ +using System; +using System.Collections.Generic; +using System.Diagnostics; +using System.Net; +using System.Net.Security; +using System.Security; +using System.Security.Cryptography.X509Certificates; +using System.Net.Sockets; +using System.IO; +using System.Linq; +using System.Threading; +using System.Reflection; +using System.Security.Authentication; +using Org.IdentityConnectors.Common; +using Org.IdentityConnectors.Common.Security; +using Org.IdentityConnectors.Framework.Api; +using Org.IdentityConnectors.Framework.Api.Operations; +using Org.IdentityConnectors.Framework.Common.Objects; +using Org.IdentityConnectors.Framework.Common.Objects.Filters; +using Org.IdentityConnectors.Framework.Common.Serializer; +using Org.IdentityConnectors.Framework.Server; +using Org.IdentityConnectors.Framework.Impl.Api; +using Org.IdentityConnectors.Framework.Impl.Api.Remote.Messages; +using Org.IdentityConnectors.Framework.Impl.Api.Local; +using Org.IdentityConnectors.Framework.Impl.Api.Local.Operations; +using Org.IdentityConnectors.Framework.Impl.Api.Remote; +namespace Org.IdentityConnectors.Framework.Server +{ + /** + * Connector server interface. + */ + public abstract class ConnectorServer { + + // At some point we might make this pluggable, but for now, hard-code + private const String IMPL_NAME + = "Org.IdentityConnectors.Framework.Impl.Server.ConnectorServerImpl"; + + + /** + * The port to listen on; + */ + private int _port = 0; + + /** + * Base 64 sha1 hash of the gateway key + */ + private String _keyHash; + + /** + * The number of connections to queue + */ + private int _maxConnections = 300; + + /** + * The minimum number of worker threads + */ + private int _minWorkers = 10; + + /** + * The maximum number of worker threads + */ + private int _maxWorkers = 100; + + /** + * The network interface address to use. + */ + private IPAddress _ifAddress = null; + + /** + * Listen on SSL + */ + private bool _useSSL = false; + + /** + * The server certificate to use + */ + private X509Certificate _serverCertificate = null; + + /** + * Get the singleton instance of the {@link ConnectorServer}. + */ + public static ConnectorServer NewInstance() { + Type type = Type.GetType(IMPL_NAME,true); + return (ConnectorServer)Activator.CreateInstance(type); + } + + private void AssertNotStarted() { + if ( IsStarted() ) { + throw new InvalidOperationException("Operation cannot be performed " + + "while server is running"); + } + } + + /** + * Returns the port to listen on. + * @return The port to listen on. + */ + public int Port { + get { + return _port; + } + set { + AssertNotStarted(); + _port = value; + } + } + + /** + * Returns the max connections to queue + * @return The max connections to queue + */ + public int MaxConnections { + get { + return _maxConnections; + } + set { + AssertNotStarted(); + _maxConnections = value; + } + } + + + /** + * Returns the max worker threads to allow. + * @return The max worker threads to allow. + */ + public int MaxWorkers { + get { + return _maxWorkers; + } + set { + AssertNotStarted(); + _maxWorkers = value; + } + } + + + + /** + * Returns the min worker threads to allow. + * @return The min worker threads to allow. + */ + public int MinWorkers { + get { + return _minWorkers; + } + set { + AssertNotStarted(); + _minWorkers = value; + } + } + + + + /** + * Returns the network interface address to bind to. May be null. + * @return The network interface address to bind to or null. + */ + public IPAddress IfAddress { + get { + return _ifAddress; + } + set { + AssertNotStarted(); + _ifAddress = value; + } + } + + /** + * Returns true iff we are to use SSL. + * @return true iff we are to use SSL. + */ + public bool UseSSL { + get { + return _useSSL; + } + set { + AssertNotStarted(); + _useSSL = value; + } + } + + + /** + * Returns the certificate to use for the SSL connection. + */ + public X509Certificate ServerCertificate { + get { + return _serverCertificate; + } + set { + AssertNotStarted(); + _serverCertificate = value; + } + } + + public String KeyHash { + get { + return _keyHash; + } + set { + AssertNotStarted(); + _keyHash = value; + } + } + + + /** + * Starts the server. All server settings must be configured prior + * to calling. The following methods are required to be called: + *
    + *
  • {@link #Port(int)}
  • + *
  • {@link #KeyHash(String)}
  • + *
+ */ + abstract public void Start(); + + /** + * Stops the server gracefully. Returns when all in-progress connections + * have been serviced. + */ + abstract public void Stop(); + + /** + * Return true iff the server is started. Note that started is a + * logical state (start method has been called). It does not necessarily + * reflect the health of the server + * @return true iff the server is started. + */ + abstract public bool IsStarted(); + } +} + +namespace Org.IdentityConnectors.Framework.Impl.Server +{ + public class ConnectionProcessor { + + private class RemoteResultsHandler : ObjectStreamHandler + { + private const int PAUSE_INTERVAL = 200; + + private readonly RemoteFrameworkConnection _connection; + private long _count = 0; + public RemoteResultsHandler(RemoteFrameworkConnection conn) + { + _connection = conn; + } + + public bool Handle(Object obj) { + try { + OperationResponsePart part = + new OperationResponsePart(null,obj); + _connection.WriteObject(part); + _count++; + if ( _count % PAUSE_INTERVAL == 0 ) { + _connection.WriteObject(new OperationResponsePause()); + Object message = + _connection.ReadObject(); + return message is OperationRequestMoreData; + } + else { + return true; + } + } + catch (IOException e) { + throw new BrokenConnectionException(e); + } + catch (Exception) { + throw; + } + } + } + + private readonly ConnectorServer _server; + private readonly RemoteFrameworkConnection _connection; + + public ConnectionProcessor(ConnectorServer server, + RemoteFrameworkConnection connection) { + _server = server; + _connection = connection; + } + + public void Run() { + try { + try { + while ( true ) { + bool keepGoing = ProcessRequest(); + if (!keepGoing) { + break; + } + } + } + finally { + try { + _connection.Dispose(); + } + catch (Exception e) { + TraceUtil.TraceException(null,e); + } + } + } + catch (Exception e) { + TraceUtil.TraceException(null,e); + } + } + + private bool ProcessRequest() + { + GuardedString key; + try { + key = (GuardedString)_connection.ReadObject(); + } + catch (EndOfStreamException) { + return false; + } + + bool authorized; + try { + authorized = key.VerifyBase64SHA1Hash(_server.KeyHash); + } + finally { + key.Dispose(); + } + Org.IdentityConnectors.Framework.Common.Exceptions.InvalidCredentialException authException = null; + if (!authorized) { + authException = new Org.IdentityConnectors.Framework.Common.Exceptions.InvalidCredentialException("Remote framework key is invalid"); + } + Object requestObject = _connection.ReadObject(); + if ( requestObject is HelloRequest ) { + if ( authException != null ) { + HelloResponse response = + new HelloResponse(authException,null); + _connection.WriteObject(response); + } + else { + HelloResponse response = + ProcessHelloRequest((HelloRequest)requestObject); + _connection.WriteObject(response); + } + } + else if ( requestObject is OperationRequest ) { + if ( authException != null ) { + OperationResponsePart part = + new OperationResponsePart(authException,null); + _connection.WriteObject(part); + } + else { + OperationRequest opRequest = + (OperationRequest)requestObject; + OperationResponsePart part = + ProcessOperationRequest(opRequest); + _connection.WriteObject(part); + } + } + else if (requestObject is EchoMessage) { + if ( authException != null ) { + //echo message probably doesn't need auth, but + //it couldn't hurt - actually it does for test connection + EchoMessage part = + new EchoMessage(authException,null); + _connection.WriteObject(part); + } + else { + EchoMessage message = (EchoMessage)requestObject; + Object obj = message.Object; + String xml = message.ObjectXml; + if ( xml != null ) { + Console.WriteLine("xml: \n"+xml); + Object xmlClone = + SerializerUtil.DeserializeXmlObject(xml,true); + xml = + SerializerUtil.SerializeXmlObject(xmlClone,true); + } + EchoMessage message2 = new EchoMessage(obj,xml); + _connection.WriteObject(message2); + } + } + else { + throw new Exception("Unexpected request: "+requestObject); + } + return true; + } + + private ConnectorInfoManager GetConnectorInfoManager() { + return ConnectorInfoManagerFactory.GetInstance().GetLocalManager(); + } + + private HelloResponse ProcessHelloRequest(HelloRequest request) { + IList connectorInfo; + Exception exception = null; + try { + ConnectorInfoManager manager = + GetConnectorInfoManager(); + IList localInfos = + manager.ConnectorInfos; + connectorInfo = new List(); + foreach (ConnectorInfo localInfo in localInfos) { + LocalConnectorInfoImpl localInfoImpl = + (LocalConnectorInfoImpl)localInfo; + RemoteConnectorInfoImpl remoteInfo = + localInfoImpl.ToRemote(); + connectorInfo.Add(remoteInfo); + } + } + catch (Exception e) { + TraceUtil.TraceException(null,e); + exception = e; + connectorInfo = null; + } + return new HelloResponse(exception,connectorInfo); + } + + private OperationResponsePart + ProcessOperationRequest(OperationRequest request) { + Object result; + Exception exception = null; + try { + MethodInfo [] methods = + request.Operation.GetMethods(); + if ( methods.Length != 1) { + throw new Exception("APIOperations are expected " + +"to have exactly one method: "+request.Operation+" "+methods.Length); + } + MethodInfo method = methods[0]; + APIOperation operation = GetAPIOperation(request); + IList arguments = request.Arguments; + IList argumentsAndStreamHandlers = + PopulateStreamHandlers(ReflectionUtil.GetParameterTypes(method), + arguments); + try { + Object [] args = argumentsAndStreamHandlers.ToArray(); + FixupArguments(method,args); + result = method.Invoke(operation, args); + } + catch (TargetInvocationException e) { + throw e.InnerException; + } + bool anyStreams = + argumentsAndStreamHandlers.Count > arguments.Count; + if ( anyStreams ) { + try { + _connection.WriteObject(new OperationResponseEnd()); + } + catch (IOException e) { + throw new BrokenConnectionException(e); + } + } + } + catch (BrokenConnectionException w) { + //at this point the stream is broken - just give up + throw w.GetIOException(); + } + catch (Exception e) { + TraceUtil.TraceException(null,e); + exception = e; + result = null; + } + return new OperationResponsePart(exception,result); + } + + private IList PopulateStreamHandlers(Type [] paramTypes, IList arguments) { + IList rv = new List(); + bool firstStream = true; + IEnumerator argIt = arguments.GetEnumerator(); + foreach (Type paramType in paramTypes) { + if ( StreamHandlerUtil.IsAdaptableToObjectStreamHandler(paramType) ) { + if (!firstStream) { + throw new InvalidOperationException("At most one stream handler is supported"); + } + ObjectStreamHandler osh = + new RemoteResultsHandler(_connection); + rv.Add(StreamHandlerUtil.AdaptFromObjectStreamHandler(paramType, osh)); + firstStream = false; + } + else { + argIt.MoveNext(); + rv.Add(argIt.Current); + } + } + return rv; + } + + /// + /// When arguments are serialized, we loose the + /// generic-type of collections. We must fix + /// the arguments + /// + /// + /// + private void FixupArguments(MethodInfo method, + object [] args) + { + Type [] paramTypes = + ReflectionUtil.GetParameterTypes(method); + if (paramTypes.Length != args.Length) { + throw new ArgumentException("Number of arguments does not match for method: "+method); + } + for ( int i = 0; i < args.Length; i++ ) { + args[i] = FixupArgument(paramTypes[i], + args[i]); + } + } + + private object FixupArgument(Type expectedType, + object argument) + { + //at some point, we might want this to be more general-purpose + //for now we just handle those cases that we need to + if (typeof(ICollection).Equals(expectedType)) { + ICollection val = + (ICollection)argument; + return CollectionUtil.NewSet(val); + } + else { + return argument; + } + } + + private APIOperation GetAPIOperation(OperationRequest request) + { + ConnectorInfoManager manager = + GetConnectorInfoManager(); + ConnectorInfo info = manager.FindConnectorInfo( + request.ConnectorKey); + if ( info == null ) { + throw new Exception("No such connector: " + +request.ConnectorKey); + } + APIConfigurationImpl config = + request.Configuration; + + //re-wire the configuration with its connector info + config.ConnectorInfo=(AbstractConnectorInfo)info; + + ConnectorFacade facade = + ConnectorFacadeFactory.GetInstance().NewInstance(config); + + return facade.GetOperation(request.Operation); + } + + private class BrokenConnectionException : Exception { + + + public BrokenConnectionException(IOException ex) + : base("",ex) { + } + + public IOException GetIOException() { + return (IOException)InnerException; + } + } + + } + + class ConnectionListener { + + /** + * This is the size of our internal queue. For now I have this + * relatively small because I want the OS to manage the connect + * queue coming in. That way it can properly turn away excessive + * requests + */ + private const int INTERNAL_QUEUE_SIZE = 2; + + + /** + * The server object that we are using + */ + private readonly ConnectorServer _server; + + /** + * The server socket. This must be bound at the time + * of creation. + */ + private readonly TcpListener _socket; + + /** + * Pool of executors + */ + //TODO: add a thread pool + //private readonly ExecutorService _threadPool; + + /** + * Set to indicated we need to start shutting down + */ + private bool _stopped = false; + + private Thread _thisThread; + + private readonly Object MUTEX = new Object(); + + /** + * Creates the listener thread + * @param server The server object + * @param socket The socket (should already be bound) + */ + public ConnectionListener(ConnectorServer server, + TcpListener socket) { + _server = server; + _socket = socket; + //TODO: thread pool +/* _threadPool = + new ThreadPoolExecutor + (server.getMinWorkers(), + server.getMaxWorkers(), + 30, //idle time timeout + TimeUnit.SECONDS, + new ArrayBlockingQueue( + INTERNAL_QUEUE_SIZE, + true)); //fair*/ + } + + + + public void Run(Object o) { + Trace.TraceInformation("Server started on port: "+_server.Port); + _thisThread = (Thread)o; + while (!IsStopped()) { + try { + TcpClient connection = null; + Stream stream = null; + try { + connection = _socket.AcceptTcpClient(); + stream = connection.GetStream(); + if ( _server.UseSSL ) + { + SslStream sslStream = new SslStream(stream,false); + stream = sslStream; + sslStream.AuthenticateAsServer(_server.ServerCertificate, + false, + SslProtocols.Tls, + false); + } + + ConnectionProcessor processor = + new ConnectionProcessor(_server, + new RemoteFrameworkConnection(connection,stream)); + Thread thread = new Thread(processor.Run); + thread.IsBackground = false; + thread.Start(); + } + catch (Exception) { + if ( stream != null ) + { + try { stream.Close(); } catch (Exception) {} + } + if ( connection != null ) + { + try { connection.Close(); } catch (Exception) {} + } + throw; + } + } + catch (Exception e) { + //log the error unless it's because we've stopped + if (!IsStopped() || !(e is SocketException)) { + TraceUtil.TraceException("Error processing request",e); + } + //wait a second before trying again + if (!IsStopped()) { + Thread.Sleep(1000); + } + } + } + } + + private void MarkStopped() { + lock(MUTEX) { + _stopped = true; + } + } + + private bool IsStopped() { + lock(MUTEX) { + return _stopped; + } + } + + public void Shutdown() { + if (Object.ReferenceEquals(Thread.CurrentThread,_thisThread)) { + throw new ArgumentException("Shutdown may not be called from this thread"); + } + if (!IsStopped()) { + //set the stopped flag so we no its a normal + //shutdown and don't log the SocketException + MarkStopped(); + //close the socket - this causes accept to throw an exception + _socket.Stop(); + //wait for the main listener thread to die so we don't + //get any new requests + _thisThread.Join(); + //TODO: shutdown thread pool + //wait for all in-progress requests to finish + //_threadPool.shutdown(); + } + } + } + + public class ConnectorServerImpl : ConnectorServer { + + private ConnectionListener _listener; + + public override bool IsStarted() { + return _listener != null; + } + + + public override void Start() { + if ( IsStarted() ) { + throw new InvalidOperationException("Server is already running."); + } + if ( Port == 0 ) { + throw new InvalidOperationException("Port must be set prior to starting server."); + } + if (KeyHash == null) { + throw new InvalidOperationException("Key hash must be set prior to starting server."); + } + if ( UseSSL && ServerCertificate == null ) { + throw new InvalidOperationException("ServerCertificate must be set if using SSL."); + } + //make sure we are configured properly + ConnectorInfoManagerFactory.GetInstance().GetLocalManager(); + + TcpListener socket = + CreateServerSocket(); + ConnectionListener listener = new ConnectionListener(this,socket); + Thread thread = new Thread(listener.Run); + thread.Name="ConnectionListener"; + thread.Start(thread); + thread.IsBackground = false; + _listener = listener; + } + + private TcpListener CreateServerSocket() { + IPAddress addr = IfAddress; + + if ( addr == null ) { + addr = IOUtil.GetIPAddress("0.0.0.0"); + } + TcpListener rv = new TcpListener(addr,Port); + //TODO: specify accept count + rv.Start(); + return rv; + } + + + public override void Stop() { + if (_listener != null) { + _listener.Shutdown(); + _listener = null; + } + ConnectorFacadeFactory.GetInstance().Dispose(); + } + + } + + +} diff --git a/FrameworkInternal/Test.cs b/FrameworkInternal/Test.cs new file mode 100644 index 00000000..1c1bb25a --- /dev/null +++ b/FrameworkInternal/Test.cs @@ -0,0 +1,118 @@ +/* + * Copyright 2008 Sun Microsystems, Inc. All rights reserved. + * + * U.S. Government Rights - Commercial software. Government users + * are subject to the Sun Microsystems, Inc. standard license agreement + * and applicable provisions of the FAR and its supplements. + * + * Use is subject to license terms. + * + * This distribution may include materials developed by third parties. + * Sun, Sun Microsystems, the Sun logo, Java and Project Identity + * Connectors are trademarks or registered trademarks of Sun + * Microsystems, Inc. or its subsidiaries in the U.S. and other + * countries. + * + * UNIX is a registered trademark in the U.S. and other countries, + * exclusively licensed through X/Open Company, Ltd. + * + * ----------- + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER. + * + * Copyright 2008 Sun Microsystems, Inc. All rights reserved. + * + * The contents of this file are subject to the terms of the Common Development + * and Distribution License(CDDL) (the License). You may not use this file + * except in compliance with the License. + * + * You can obtain a copy of the License at + * http://identityconnectors.dev.java.net/CDDLv1.0.html + * See the License for the specific language governing permissions and + * limitations under the License. + * + * When distributing the Covered Code, include this CDDL Header Notice in each + * file and include the License file at identityconnectors/legal/license.txt. + * If applicable, add the following below this CDDL Header, with the fields + * enclosed by brackets [] replaced by your own identifying information: + * "Portions Copyrighted [year] [name of copyright owner]" + * ----------- + */ +using System; + +using Org.IdentityConnectors.Common; +using Org.IdentityConnectors.Framework.Api; +using Org.IdentityConnectors.Framework.Impl.Api; +using Org.IdentityConnectors.Framework.Impl.Api.Local; +using Org.IdentityConnectors.Framework.Impl.Api.Local.Operations; +using Org.IdentityConnectors.Framework.Common; +using Org.IdentityConnectors.Framework.Common.Objects; +using Org.IdentityConnectors.Framework.Common.Objects.Filters; +using Org.IdentityConnectors.Framework.Spi; +using Org.IdentityConnectors.Framework.Spi.Operations; +using Org.IdentityConnectors.Framework.Test; + +namespace Org.IdentityConnectors.Framework.Impl.Test +{ + public class TestHelpersImpl : TestHelpers { + + /** + * Method for convenient testing of local connectors. + */ + protected override APIConfiguration CreateTestConfigurationImpl(Type clazz, + Configuration config) { + LocalConnectorInfoImpl info = new LocalConnectorInfoImpl(); + info.ConnectorConfigurationClass=(config.GetType()); + info.ConnectorClass=(clazz); + info.ConnectorDisplayNameKey=("DUMMY_DISPLAY_NAME"); + info.ConnectorKey=( + new ConnectorKey(clazz.Name+".bundle", + "1.0", + clazz.Name)); + info.Messages=(new ConnectorMessagesImpl()); + APIConfigurationImpl rv = new APIConfigurationImpl(); + rv.IsConnectorPoolingSupported=( + IsConnectorPoolingSupported(clazz)); + ConfigurationPropertiesImpl properties = + CSharpClassProperties.CreateConfigurationProperties(config); + rv.ConfigurationProperties=(properties); + rv.ConnectorInfo=(info); + rv.SupportedOperations=( + FrameworkUtil.GetDefaultSupportedOperations(clazz)); + info.DefaultAPIConfiguration=( + rv); + return rv; + } + + + private static bool IsConnectorPoolingSupported(Type clazz) { + return ReflectionUtil.IsParentTypeOf(typeof(PoolableConnector),clazz); + } + + /** + * Performs a raw, unfiltered search at the SPI level, + * eliminating duplicates from the result set. + * @param search The search SPI + * @param oclass The object class - passed through to + * connector so it may be null if the connecor + * allowing it to be null. (This is convenient for + * unit tests, but will not be the case in general) + * @param filter The filter to search on + * @param handler The result handler + * @param options The options - may be null - will + * be cast to an empty OperationOptions + */ + protected override void SearchImpl(SearchOp search, + ObjectClass oclass, + Filter filter, + ResultsHandler handler, + OperationOptions options) { + if ( options == null ) { + options = new OperationOptionsBuilder().Build(); + } + RawSearcherImpl.RawSearch( + search, oclass, filter, handler, options); + } + + } + +} diff --git a/FrameworkTests/AssemblyInfo.cs b/FrameworkTests/AssemblyInfo.cs new file mode 100644 index 00000000..3143253c --- /dev/null +++ b/FrameworkTests/AssemblyInfo.cs @@ -0,0 +1,70 @@ +/* + * Copyright 2008 Sun Microsystems, Inc. All rights reserved. + * + * U.S. Government Rights - Commercial software. Government users + * are subject to the Sun Microsystems, Inc. standard license agreement + * and applicable provisions of the FAR and its supplements. + * + * Use is subject to license terms. + * + * This distribution may include materials developed by third parties. + * Sun, Sun Microsystems, the Sun logo, Java and Project Identity + * Connectors are trademarks or registered trademarks of Sun + * Microsystems, Inc. or its subsidiaries in the U.S. and other + * countries. + * + * UNIX is a registered trademark in the U.S. and other countries, + * exclusively licensed through X/Open Company, Ltd. + * + * ----------- + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER. + * + * Copyright 2008 Sun Microsystems, Inc. All rights reserved. + * + * The contents of this file are subject to the terms of the Common Development + * and Distribution License(CDDL) (the License). You may not use this file + * except in compliance with the License. + * + * You can obtain a copy of the License at + * http://identityconnectors.dev.java.net/CDDLv1.0.html + * See the License for the specific language governing permissions and + * limitations under the License. + * + * When distributing the Covered Code, include this CDDL Header Notice in each + * file and include the License file at identityconnectors/legal/license.txt. + * If applicable, add the following below this CDDL Header, with the fields + * enclosed by brackets [] replaced by your own identifying information: + * "Portions Copyrighted [year] [name of copyright owner]" + * ----------- + */ +#region Using directives + +using System; +using System.Reflection; +using System.Runtime.InteropServices; + +#endregion + +// General Information about an assembly is controlled through the following +// set of attributes. Change these attribute values to modify the information +// associated with an assembly. +[assembly: AssemblyTitle("FrameworkTests")] +[assembly: AssemblyDescription("")] +[assembly: AssemblyConfiguration("")] +[assembly: AssemblyCompany("")] +[assembly: AssemblyProduct("FrameworkTests")] +[assembly: AssemblyCopyright("")] +[assembly: AssemblyTrademark("")] +[assembly: AssemblyCulture("")] + +// This sets the default COM visibility of types in the assembly to invisible. +// If you need to expose a type to COM, use [ComVisible(true)] on that type. +[assembly: ComVisible(false)] + +// The assembly version has following format : +// +// Major.Minor.Build.Revision +// +// You can specify all the values or you can use the default the Revision and +// Build Numbers by using the '*' as shown below: +[assembly: AssemblyVersion("1.0.0.0")] diff --git a/FrameworkTests/CollectionUtilTests.cs b/FrameworkTests/CollectionUtilTests.cs new file mode 100644 index 00000000..c86e3bc7 --- /dev/null +++ b/FrameworkTests/CollectionUtilTests.cs @@ -0,0 +1,107 @@ +/* + * Copyright 2008 Sun Microsystems, Inc. All rights reserved. + * + * U.S. Government Rights - Commercial software. Government users + * are subject to the Sun Microsystems, Inc. standard license agreement + * and applicable provisions of the FAR and its supplements. + * + * Use is subject to license terms. + * + * This distribution may include materials developed by third parties. + * Sun, Sun Microsystems, the Sun logo, Java and Project Identity + * Connectors are trademarks or registered trademarks of Sun + * Microsystems, Inc. or its subsidiaries in the U.S. and other + * countries. + * + * UNIX is a registered trademark in the U.S. and other countries, + * exclusively licensed through X/Open Company, Ltd. + * + * ----------- + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER. + * + * Copyright 2008 Sun Microsystems, Inc. All rights reserved. + * + * The contents of this file are subject to the terms of the Common Development + * and Distribution License(CDDL) (the License). You may not use this file + * except in compliance with the License. + * + * You can obtain a copy of the License at + * http://identityconnectors.dev.java.net/CDDLv1.0.html + * See the License for the specific language governing permissions and + * limitations under the License. + * + * When distributing the Covered Code, include this CDDL Header Notice in each + * file and include the License file at identityconnectors/legal/license.txt. + * If applicable, add the following below this CDDL Header, with the fields + * enclosed by brackets [] replaced by your own identifying information: + * "Portions Copyrighted [year] [name of copyright owner]" + * ----------- + */ +using System; +using System.Collections.Generic; +using NUnit.Framework; +using NUnit.Framework.SyntaxHelpers; +using Org.IdentityConnectors.Common; +namespace FrameworkTests +{ + [TestFixture] + public class CollectionUtilTests + { + [Test] + public void TestEquals() { + Assert.IsTrue(CollectionUtil.Equals(null, null)); + Assert.IsFalse(CollectionUtil.Equals(null, "str")); + Assert.IsTrue(CollectionUtil.Equals("str", "str")); + + byte [] arr1 = new byte[] { 1,2,3 }; + byte [] arr2 = new byte[] { 1,2,3 }; + byte [] arr3 = new byte[] { 1,2,4 }; + byte [] arr4 = new byte[] { 1,2 }; + int [] arr5 = new int[] {1,2,3}; + + Assert.IsTrue(CollectionUtil.Equals(arr1, arr2)); + Assert.IsFalse(CollectionUtil.Equals(arr2, arr3)); + Assert.IsFalse(CollectionUtil.Equals(arr2, arr4)); + Assert.IsFalse(CollectionUtil.Equals(arr2, arr5)); + + IList list1 = new List(); + IList list2 = new List(); + list1.Add(arr1); + list2.Add(arr2); + + Assert.IsTrue(CollectionUtil.Equals(list1, list2)); + + list2.Add(arr2); + Assert.IsFalse(CollectionUtil.Equals(list1, list2)); + + list1.Add(arr1); + Assert.IsTrue(CollectionUtil.Equals(list1, list2)); + + list1.Add(arr1); + list2.Add(arr3); + Assert.IsFalse(CollectionUtil.Equals(list1, list2)); + + IDictionary map1 = new Dictionary(); + IDictionary map2 = new Dictionary(); + map1["key1"] = arr1; + map2["key1"] = arr2; + Assert.IsTrue(CollectionUtil.Equals(map1, map2)); + map2["key2"] = arr2; + Assert.IsFalse(CollectionUtil.Equals(map1, map2)); + map1["key2"] = arr1; + Assert.IsTrue(CollectionUtil.Equals(map1, map2)); + map1["key2"] = arr3; + Assert.IsFalse(CollectionUtil.Equals(map1, map2)); + + ICollection set1 = new HashSet(); + ICollection set2 = new HashSet(); + set1.Add("val"); + set2.Add("val"); + Assert.IsTrue(CollectionUtil.Equals(set1, set2)); + set2.Add("val2"); + Assert.IsFalse(CollectionUtil.Equals(set1, set2)); + set1.Add("val2"); + Assert.IsTrue(CollectionUtil.Equals(set1, set2)); + } + } +} diff --git a/FrameworkTests/ConnectorInfoManagerTests.cs b/FrameworkTests/ConnectorInfoManagerTests.cs new file mode 100644 index 00000000..48798fe4 --- /dev/null +++ b/FrameworkTests/ConnectorInfoManagerTests.cs @@ -0,0 +1,590 @@ +/* + * Copyright 2008 Sun Microsystems, Inc. All rights reserved. + * + * U.S. Government Rights - Commercial software. Government users + * are subject to the Sun Microsystems, Inc. standard license agreement + * and applicable provisions of the FAR and its supplements. + * + * Use is subject to license terms. + * + * This distribution may include materials developed by third parties. + * Sun, Sun Microsystems, the Sun logo, Java and Project Identity + * Connectors are trademarks or registered trademarks of Sun + * Microsystems, Inc. or its subsidiaries in the U.S. and other + * countries. + * + * UNIX is a registered trademark in the U.S. and other countries, + * exclusively licensed through X/Open Company, Ltd. + * + * ----------- + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER. + * + * Copyright 2008 Sun Microsystems, Inc. All rights reserved. + * + * The contents of this file are subject to the terms of the Common Development + * and Distribution License(CDDL) (the License). You may not use this file + * except in compliance with the License. + * + * You can obtain a copy of the License at + * http://identityconnectors.dev.java.net/CDDLv1.0.html + * See the License for the specific language governing permissions and + * limitations under the License. + * + * When distributing the Covered Code, include this CDDL Header Notice in each + * file and include the License file at identityconnectors/legal/license.txt. + * If applicable, add the following below this CDDL Header, with the fields + * enclosed by brackets [] replaced by your own identifying information: + * "Portions Copyrighted [year] [name of copyright owner]" + * ----------- + */ +using System; +using NUnit.Framework; +using NUnit.Framework.SyntaxHelpers; +using System.Collections.Generic; +using System.Diagnostics; +using Org.IdentityConnectors.Common; +using Org.IdentityConnectors.Common.Security; +using Org.IdentityConnectors.Framework.Api; +using Org.IdentityConnectors.Framework.Common.Exceptions; +using Org.IdentityConnectors.Framework.Common.Objects; +using Org.IdentityConnectors.Framework.Server; +using Org.IdentityConnectors.Framework.Impl.Api; +using Org.IdentityConnectors.Framework.Impl.Api.Local; +using System.Security; +using System.Threading; +using System.Globalization; +using System.Net; +using System.Net.Security; +using System.Security.Cryptography.X509Certificates; +namespace FrameworkTests +{ + [TestFixture] + public class ConnectorInfoManagerTests + { + private static ConnectorInfo FindConnectorInfo + (ConnectorInfoManager manager, + string version, + string connectorName) { + foreach (ConnectorInfo info in manager.ConnectorInfos) { + ConnectorKey key = info.ConnectorKey; + if ( version.Equals(key.BundleVersion) && + connectorName.Equals(key.ConnectorName) ) { + //intentionally ineffecient to test + //more code + return manager.FindConnectorInfo(key); + } + } + return null; + } + + [Test] + public void TestClassLoading() { + ConnectorInfoManager manager = + GetConnectorInfoManager(); + ConnectorInfo info1 = + FindConnectorInfo(manager, + "1.0.0.0", + "org.identityconnectors.testconnector.TstConnector"); + Assert.IsNotNull(info1); + ConnectorInfo info2 = + FindConnectorInfo(manager, + "2.0.0.0", + "org.identityconnectors.testconnector.TstConnector"); + + Assert.IsNotNull(info2); + + ConnectorFacade facade1 = + ConnectorFacadeFactory.GetInstance().NewInstance(info1.CreateDefaultAPIConfiguration()); + + ConnectorFacade facade2 = + ConnectorFacadeFactory.GetInstance().NewInstance(info2.CreateDefaultAPIConfiguration()); + + ICollection attrs = new HashSet(); + Assert.AreEqual("1.0", facade1.Create(ObjectClass.ACCOUNT,attrs,null).GetUidValue()); + Assert.AreEqual("2.0", facade2.Create(ObjectClass.ACCOUNT,attrs,null).GetUidValue()); + + ShutdownConnnectorInfoManager(); + } + + [Test] + public void TestAPIConfiguration() + { + ConnectorInfoManager manager = + GetConnectorInfoManager(); + ConnectorInfo info = + FindConnectorInfo(manager, + "1.0.0.0", + "org.identityconnectors.testconnector.TstConnector"); + Assert.IsNotNull(info); + APIConfiguration api = info.CreateDefaultAPIConfiguration(); + + ConfigurationProperties props = api.ConfigurationProperties; + ConfigurationProperty property = props.GetProperty("tstField"); + + Assert.IsNotNull(property); + + Assert.AreEqual("Help for test field.",property.GetHelpMessage(null)); + Assert.AreEqual("Display for test field.",property.GetDisplayName(null)); + + CultureInfo eslocale = new CultureInfo("es"); + api.CultureInfo = eslocale; + Assert.AreEqual("tstField.help_es",property.GetHelpMessage(null)); + Assert.AreEqual("tstField.display_es",property.GetDisplayName(null)); + + CultureInfo esESlocale = new CultureInfo("es-ES"); + api.CultureInfo = esESlocale; + Assert.AreEqual("tstField.help_es-ES",property.GetHelpMessage(null)); + Assert.AreEqual("tstField.display_es-ES",property.GetDisplayName(null)); + + CultureInfo esARlocale = new CultureInfo("es-AR"); + api.CultureInfo = esARlocale; + Assert.AreEqual("tstField.help_es",property.GetHelpMessage(null)); + Assert.AreEqual("tstField.display_es",property.GetDisplayName(null)); + + ConnectorFacadeFactory facf = ConnectorFacadeFactory.GetInstance(); + ConnectorFacade facade = facf.NewInstance(api); + // call the various create/update/delete commands.. + facade.Schema(); + ShutdownConnnectorInfoManager(); + } + + [Test] + public void TestValidate() { + ConnectorInfoManager manager = + GetConnectorInfoManager(); + ConnectorInfo info = + FindConnectorInfo(manager, + "1.0.0.0", + "org.identityconnectors.testconnector.TstConnector"); + + APIConfiguration api = info.CreateDefaultAPIConfiguration(); + + ConfigurationProperties props = api.ConfigurationProperties; + ConfigurationProperty property = props.GetProperty("failValidation"); + property.Value=false; + ConnectorFacadeFactory facf = ConnectorFacadeFactory.GetInstance(); + ConnectorFacade facade = facf.NewInstance(api); + facade.Validate(); + property.Value=true; + facade = facf.NewInstance(api); + try { + facade.Validate(); + Assert.Fail("exception expected"); + } + catch (ConnectorException e) { + Assert.AreEqual("validation failed", e.Message); + } + ShutdownConnnectorInfoManager(); + } + + + + /** + * Main purpose of this is to test searching with + * many results and that we can properly handle + * stopping in the middle of this. There's a bunch of + * code in the remote stuff that is there to handle this + * in particular that we want to excercise. + */ + [Test] + public void TestSearchWithManyResults() { + ConnectorInfoManager manager = + GetConnectorInfoManager(); + ConnectorInfo info = + FindConnectorInfo(manager, + "1.0.0.0", + "org.identityconnectors.testconnector.TstConnector"); + + APIConfiguration api = info.CreateDefaultAPIConfiguration(); + + ConfigurationProperties props = api.ConfigurationProperties; + ConfigurationProperty property = props.GetProperty("numResults"); + + //1000 is several times the remote size between pauses + property.Value=1000; + + ConnectorFacadeFactory facf = ConnectorFacadeFactory.GetInstance(); + ConnectorFacade facade = facf.NewInstance(api); + + IList results = new List(); + + facade.Search(ObjectClass.ACCOUNT,null, + obj => { + results.Add(obj); + return true; + },null); + + Assert.AreEqual(1000,results.Count); + for ( int i = 0; i < results.Count; i++ ) { + ConnectorObject obj = results[i]; + Assert.AreEqual(i.ToString(), + obj.Uid.GetUidValue()); + } + + results.Clear(); + + facade.Search(ObjectClass.ACCOUNT,null, + obj => { + if ( results.Count < 500 ) { + results.Add(obj); + return true; + } + else { + return false; + } + },null); + + Assert.AreEqual(500,results.Count); + for ( int i = 0; i < results.Count; i++ ) { + ConnectorObject obj = results[i]; + Assert.AreEqual(i.ToString(), + obj.Uid.GetUidValue()); + } + + ShutdownConnnectorInfoManager(); + } + /** + * Main purpose of this is to test sync with + * many results and that we can properly handle + * stopping in the middle of this. There's a bunch of + * code in the remote stuff that is there to handle this + * in particular that we want to excercise. + */ + [Test] + public void TestSyncWithManyResults() { + ConnectorInfoManager manager = + GetConnectorInfoManager(); + ConnectorInfo info = + FindConnectorInfo(manager, + "1.0.0.0", + "org.identityconnectors.testconnector.TstConnector"); + + APIConfiguration api = info.CreateDefaultAPIConfiguration(); + + ConfigurationProperties props = api.ConfigurationProperties; + ConfigurationProperty property = props.GetProperty("numResults"); + + //1000 is several times the remote size between pauses + property.Value=(1000); + + ConnectorFacadeFactory facf = ConnectorFacadeFactory.GetInstance(); + ConnectorFacade facade = facf.NewInstance(api); + + IList results = new List(); + + facade.Sync(ObjectClass.ACCOUNT, null, obj => { + results.Add(obj); + return true; + },null); + + Assert.AreEqual(1000,results.Count); + for ( int i = 0; i < results.Count; i++ ) { + SyncDelta obj = results[i]; + Assert.AreEqual(i.ToString(), + obj.Uid.GetUidValue()); + } + + results.Clear(); + + facade.Sync(ObjectClass.ACCOUNT, + null, + obj => { + if ( results.Count < 500 ) { + results.Add(obj); + return true; + } + else { + return false; + } + },null); + + Assert.AreEqual(500,results.Count); + for ( int i = 0; i < results.Count; i++ ) { + SyncDelta obj = results[i]; + Assert.AreEqual(i.ToString(), + obj.Uid.GetUidValue()); + } + + ShutdownConnnectorInfoManager(); + } + + [Test] + public void TestConnectionPooling() { + ConnectorPoolManager.Dispose(); + ConnectorInfoManager manager = + GetConnectorInfoManager(); + ConnectorInfo info1 = + FindConnectorInfo(manager, + "1.0.0.0", + "org.identityconnectors.testconnector.TstConnector"); + Assert.IsNotNull(info1); + //reset connection count + { + //trigger TstConnection.init to be called + APIConfiguration config2 = + info1.CreateDefaultAPIConfiguration(); + config2.ConfigurationProperties.GetProperty("resetConnectionCount").Value=(true); + ConnectorFacade facade2 = + ConnectorFacadeFactory.GetInstance().NewInstance(config2); + facade2.Schema(); //force instantiation + } + + APIConfiguration config = + info1.CreateDefaultAPIConfiguration(); + + config.ConnectorPoolConfiguration.MinIdle=(0); + config.ConnectorPoolConfiguration.MaxIdle=(0); + + ConnectorFacade facade1 = + ConnectorFacadeFactory.GetInstance().NewInstance(config); + + OperationOptionsBuilder builder = new OperationOptionsBuilder(); + builder.SetOption("testPooling", "true"); + OperationOptions options = builder.Build(); + ICollection attrs = CollectionUtil.NewReadOnlySet(); + Assert.AreEqual("1", facade1.Create(ObjectClass.ACCOUNT,attrs,options).GetUidValue()); + Assert.AreEqual("2", facade1.Create(ObjectClass.ACCOUNT,attrs,options).GetUidValue()); + Assert.AreEqual("3", facade1.Create(ObjectClass.ACCOUNT,attrs,options).GetUidValue()); + Assert.AreEqual("4", facade1.Create(ObjectClass.ACCOUNT,attrs,options).GetUidValue()); + config = + info1.CreateDefaultAPIConfiguration(); + config.ConnectorPoolConfiguration.MinIdle=(1); + config.ConnectorPoolConfiguration.MaxIdle=(2); + facade1 = + ConnectorFacadeFactory.GetInstance().NewInstance(config); + Assert.AreEqual("5", facade1.Create(ObjectClass.ACCOUNT,attrs,options).GetUidValue()); + Assert.AreEqual("5", facade1.Create(ObjectClass.ACCOUNT,attrs,options).GetUidValue()); + Assert.AreEqual("5", facade1.Create(ObjectClass.ACCOUNT,attrs,options).GetUidValue()); + Assert.AreEqual("5", facade1.Create(ObjectClass.ACCOUNT,attrs,options).GetUidValue()); + ShutdownConnnectorInfoManager(); + } + + [Test] + public void TestScripting() + { + ConnectorInfoManager manager = + GetConnectorInfoManager(); + ConnectorInfo info = + FindConnectorInfo(manager, + "1.0.0.0", + "org.identityconnectors.testconnector.TstConnector"); + APIConfiguration api = info.CreateDefaultAPIConfiguration(); + + + ConnectorFacadeFactory facf = ConnectorFacadeFactory.GetInstance(); + ConnectorFacade facade = facf.NewInstance(api); + + ScriptContextBuilder builder = new ScriptContextBuilder(); + builder.AddScriptArgument("arg1", "value1"); + builder.AddScriptArgument("arg2", "value2"); + builder.ScriptLanguage=("BOO"); + + //test that they can run the script and access the + //connector object + { + String SCRIPT = + "connector.concat(arg1,arg2)"; + builder.ScriptText=(SCRIPT); + String result = (String)facade.RunScriptOnConnector(builder.Build(), + null); + + Assert.AreEqual("value1value2", result); + } + + //test that they can access a class in the class loader + { + String SCRIPT = + "import org.identityconnectors.testconnector\n"+ + "TstConnector.getVersion()"; + builder.ScriptText=(SCRIPT); + String result = (String)facade.RunScriptOnConnector(builder.Build(), + null); + Assert.AreEqual("1.0", result); + } + + //test that they cannot access a class in internal + { + Type clazz = typeof(ConfigurationPropertyImpl); + + String SCRIPT = + "import "+clazz.Namespace+"\n"+ + clazz.Name+"()"; + builder.ScriptText=(SCRIPT); + try { + facade.RunScriptOnConnector(builder.Build(), + null); + Assert.Fail("exception expected"); + } + catch (Exception e) { + String msg = e.Message; + String expectedMessage = + "Namespace '"+clazz.Namespace+"' not found"; + Assert.IsTrue( + msg.Contains(expectedMessage), + "Unexpected message: "+msg); + } + } + + // test that they can access a class in common + { + Type clazz = typeof(ConnectorAttributeBuilder); + String SCRIPT = + "import "+clazz.Namespace+"\n"+ + clazz.Name + ".Build(\"myattr\")"; + builder.ScriptText=(SCRIPT); + ConnectorAttribute attr = (ConnectorAttribute) facade.RunScriptOnConnector(builder.Build(), null); + Assert.AreEqual("myattr", attr.Name); + } + ShutdownConnnectorInfoManager(); + } + + protected virtual ConnectorInfoManager GetConnectorInfoManager() { + ConnectorInfoManagerFactory fact = ConnectorInfoManagerFactory.GetInstance(); + ConnectorInfoManager manager = fact.GetLocalManager(); + return manager; + } + + protected virtual void ShutdownConnnectorInfoManager() { + + } + } + + [TestFixture] + public class RemoteConnectorInfoManagerClearTests : ConnectorInfoManagerTests { + + private ConnectorServer _server; + + protected override ConnectorInfoManager GetConnectorInfoManager() { + TestUtil.InitializeLogging(); + + ShutdownConnnectorInfoManager(); + + GuardedString str = new GuardedString(); + str.AppendChar('c'); + str.AppendChar('h'); + str.AppendChar('a'); + str.AppendChar('n'); + str.AppendChar('g'); + str.AppendChar('e'); + str.AppendChar('i'); + str.AppendChar('t'); + +#if DEBUG + const int PORT = 8758; +#else + const int PORT = 8759; +#endif + _server = ConnectorServer.NewInstance(); + _server.Port=PORT; + _server.IfAddress=(IOUtil.GetIPAddress("127.0.0.1")); + _server.KeyHash=str.GetBase64SHA1Hash(); + _server.Start(); + //while ( true ) { + // Thread.Sleep(1000); + //} + ConnectorInfoManagerFactory fact = ConnectorInfoManagerFactory.GetInstance(); + + RemoteFrameworkConnectionInfo connInfo = new + RemoteFrameworkConnectionInfo("127.0.0.1",PORT,str); + + ConnectorInfoManager manager = fact.GetRemoteManager(connInfo); + + return manager; + } + + protected override void ShutdownConnnectorInfoManager() { + if (_server != null) { + _server.Stop(); + _server = null; + } + } + } + + internal class MyCertificateValidationCallback + { + public bool Validate(Object sender, + X509Certificate certificate, + X509Chain chain, + SslPolicyErrors sslPolicyErrors) { + Trace.TraceInformation("validating: "+certificate.Subject); + return true; + } + } + + [TestFixture] + public class RemoteConnectorInfoManagerSSLTests : ConnectorInfoManagerTests { + + //To generate test certificate do the following: + // + //makecert -r -pe -n "CN=localhost" -ss TestCertificateStore -sr currentuser -sky exchange + // + //In MMC, go to the certificate, export + private ConnectorServer _server; + private const String CERT_PATH = "../../../server.pfx"; + protected override ConnectorInfoManager GetConnectorInfoManager() { + TestUtil.InitializeLogging(); + + ShutdownConnnectorInfoManager(); + GuardedString str = new GuardedString(); + str.AppendChar('c'); + str.AppendChar('h'); + str.AppendChar('a'); + str.AppendChar('n'); + str.AppendChar('g'); + str.AppendChar('e'); + str.AppendChar('i'); + str.AppendChar('t'); + +#if DEBUG + const int PORT = 8762; +#else + const int PORT = 8761; +#endif + + /*X509Store store = new X509Store("TestCertificateStore", + StoreLocation.CurrentUser); + store.Open(OpenFlags.ReadOnly|OpenFlags.OpenExistingOnly); + X509Certificate certificate = store.Certificates[0]; + store.Close();*/ + + X509Certificate2 certificate = new + X509Certificate2(CERT_PATH, + "changeit"); + //Trace.TraceInformation("certificate: "+certificate); + _server = ConnectorServer.NewInstance(); + _server.Port=PORT; + _server.KeyHash=str.GetBase64SHA1Hash(); + _server.IfAddress=(IOUtil.GetIPAddress("localhost")); + _server.UseSSL = true; + _server.ServerCertificate = certificate; + _server.Start(); + //while ( true ) { + // Thread.Sleep(1000); + //} + ConnectorInfoManagerFactory fact = ConnectorInfoManagerFactory.GetInstance(); + MyCertificateValidationCallback + callback = new MyCertificateValidationCallback(); + RemoteFrameworkConnectionInfo connInfo = new + RemoteFrameworkConnectionInfo("localhost", + PORT, + str, + true, + callback.Validate, + 60000); + + ConnectorInfoManager manager = fact.GetRemoteManager(connInfo); + + return manager; + } + + protected override void ShutdownConnnectorInfoManager() { + if (_server != null) { + _server.Stop(); + _server = null; + } + } + } + + + +} diff --git a/FrameworkTests/FilterTranslatorTests.cs b/FrameworkTests/FilterTranslatorTests.cs new file mode 100644 index 00000000..f6b5453f --- /dev/null +++ b/FrameworkTests/FilterTranslatorTests.cs @@ -0,0 +1,626 @@ +/* + * Copyright 2008 Sun Microsystems, Inc. All rights reserved. + * + * U.S. Government Rights - Commercial software. Government users + * are subject to the Sun Microsystems, Inc. standard license agreement + * and applicable provisions of the FAR and its supplements. + * + * Use is subject to license terms. + * + * This distribution may include materials developed by third parties. + * Sun, Sun Microsystems, the Sun logo, Java and Project Identity + * Connectors are trademarks or registered trademarks of Sun + * Microsystems, Inc. or its subsidiaries in the U.S. and other + * countries. + * + * UNIX is a registered trademark in the U.S. and other countries, + * exclusively licensed through X/Open Company, Ltd. + * + * ----------- + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER. + * + * Copyright 2008 Sun Microsystems, Inc. All rights reserved. + * + * The contents of this file are subject to the terms of the Common Development + * and Distribution License(CDDL) (the License). You may not use this file + * except in compliance with the License. + * + * You can obtain a copy of the License at + * http://identityconnectors.dev.java.net/CDDLv1.0.html + * See the License for the specific language governing permissions and + * limitations under the License. + * + * When distributing the Covered Code, include this CDDL Header Notice in each + * file and include the License file at identityconnectors/legal/license.txt. + * If applicable, add the following below this CDDL Header, with the fields + * enclosed by brackets [] replaced by your own identifying information: + * "Portions Copyrighted [year] [name of copyright owner]" + * ----------- + */ +using System; +using System.Collections.Generic; +using NUnit.Framework; +using NUnit.Framework.SyntaxHelpers; +using Org.IdentityConnectors.Common; +using Org.IdentityConnectors.Framework.Common.Objects; +using Org.IdentityConnectors.Framework.Common.Objects.Filters; +namespace FrameworkTests +{ + + internal class AllFiltersTranslator : AbstractFilterTranslator + { + protected override String CreateAndExpression(String leftExpression, String rightExpression) { + return "( & " + leftExpression+" "+rightExpression+" )"; + } + + protected override String CreateOrExpression(String leftExpression, String rightExpression) { + return "( | " + leftExpression+" "+rightExpression+" )"; + } + + protected override String CreateContainsExpression(ContainsFilter filter, bool not) { + String rv = "( CONTAINS "+filter.GetName()+" "+filter.GetValue()+" )"; + return Not(rv,not); + } + + protected override String CreateEndsWithExpression(EndsWithFilter filter, bool not) { + String rv = "( ENDS-WITH "+filter.GetName()+" "+filter.GetValue()+" )"; + return Not(rv,not); + } + + protected override String CreateEqualsExpression(EqualsFilter filter, bool not) { + String rv = "( = "+filter.GetAttribute().Name+" ["+filter.GetAttribute().Value[0]+"] )"; + return Not(rv,not); + } + + protected override String CreateGreaterThanExpression(GreaterThanFilter filter, bool not) { + String rv = "( > "+filter.GetName()+" "+filter.GetValue()+" )"; + return Not(rv,not); + } + + protected override String CreateGreaterThanOrEqualExpression(GreaterThanOrEqualFilter filter, bool not) { + String rv = "( >= "+filter.GetName()+" "+filter.GetValue()+" )"; + return Not(rv,not); + } + + protected override String CreateLessThanExpression(LessThanFilter filter, bool not) { + String rv = "( < "+filter.GetName()+" "+filter.GetValue()+" )"; + return Not(rv,not); + } + + protected override String CreateLessThanOrEqualExpression(LessThanOrEqualFilter filter, bool not) { + String rv = "( <= "+filter.GetName()+" "+filter.GetValue()+" )"; + return Not(rv,not); + } + + protected override String CreateStartsWithExpression(StartsWithFilter filter, bool not) { + String rv = "( STARTS-WITH "+filter.GetName()+" "+filter.GetValue()+" )"; + return Not(rv,not); + } + protected override String CreateContainsAllValuesExpression(ContainsAllValuesFilter filter, bool not) { + String rv = "( CONTAINS-ALL-VALUES "+filter.GetAttribute()+" )"; + return Not(rv,not); + } + private String Not(String orig, bool not) { + if ( not ) { + return "( ! " + orig + " )"; + } + else { + return orig; + } + } + + } + + /** + * Everything but Or + */ + internal class NoOrTranslator : AllFiltersTranslator + { + protected override String CreateOrExpression(String leftExpression, String rightExpression) { + return null; + } + } + /** + * Everything but EndsWith + */ + internal class NoEndsWithTranslator : AllFiltersTranslator + { + protected override String CreateEndsWithExpression(EndsWithFilter filter, bool not) { + return null; + } + } + /** + * Everything but EndsWith,Or + */ + internal class NoEndsWithNoOrTranslator : AllFiltersTranslator + { + protected override String CreateOrExpression(String leftExpression, String rightExpression) { + return null; + } + protected override String CreateEndsWithExpression(EndsWithFilter filter, bool not) { + return null; + } + } + + /** + * Everything but And + */ + internal class NoAndTranslator : AllFiltersTranslator + { + protected override String CreateAndExpression(String leftExpression, String rightExpression) { + return null; + } + } + /** + * Everything but And + */ + internal class NoAndNoEndsWithTranslator : AllFiltersTranslator + { + protected override String CreateAndExpression(String leftExpression, String rightExpression) { + return null; + } + protected override String CreateEndsWithExpression(EndsWithFilter filter, bool not) { + return null; + } + + } + /** + * Everything but And + */ + internal class NoAndNoOrNoEndsWithTranslator : AllFiltersTranslator + { + protected override String CreateAndExpression(String leftExpression, String rightExpression) { + return null; + } + protected override String CreateEndsWithExpression(EndsWithFilter filter, bool not) { + return null; + } + protected override String CreateOrExpression(String leftExpression, String rightExpression) { + return null; + } + + } + [TestFixture] + public class FilterTranslatorTests + { + + /** + * Test all operations when everything is fully implemented. + * Test not normalization as well. + */ + [Test] + public void TestBasics() { + ConnectorAttribute attribute = + ConnectorAttributeBuilder.Build("att-name", "att-value"); + ConnectorAttribute attribute2 = + ConnectorAttributeBuilder.Build("att-name2", "att-value2"); + AllFiltersTranslator translator = new + AllFiltersTranslator(); + + { + Filter filter = + FilterBuilder.Contains(attribute); + String expected = "( CONTAINS att-name att-value )"; + String actual = + TranslateSingle(translator, filter); + Assert.AreEqual(expected, actual); + + filter = FilterBuilder.Not(filter); + expected = "( ! "+expected+" )"; + actual = + TranslateSingle(translator, filter); + Assert.AreEqual(expected, actual); + } + + { + Filter filter = + FilterBuilder.EndsWith(attribute); + String expected = "( ENDS-WITH att-name att-value )"; + String actual = + TranslateSingle(translator, filter); + Assert.AreEqual(expected, actual); + + filter = FilterBuilder.Not(filter); + expected = "( ! "+expected+" )"; + actual = + TranslateSingle(translator, filter); + Assert.AreEqual(expected, actual); + } + + { + Filter filter = + FilterBuilder.EqualTo(attribute); + String expected = "( = att-name [att-value] )"; + String actual = + TranslateSingle(translator, filter); + Assert.AreEqual(expected, actual); + + filter = FilterBuilder.Not(filter); + expected = "( ! "+expected+" )"; + actual = + TranslateSingle(translator, filter); + Assert.AreEqual(expected, actual); + } + + { + Filter filter = + FilterBuilder.GreaterThan(attribute); + String expected = "( > att-name att-value )"; + String actual = + TranslateSingle(translator, filter); + Assert.AreEqual(expected, actual); + + filter = FilterBuilder.Not(filter); + expected = "( ! "+expected+" )"; + actual = + TranslateSingle(translator, filter); + Assert.AreEqual(expected, actual); + } + + { + Filter filter = + FilterBuilder.GreaterThanOrEqualTo(attribute); + String expected = "( >= att-name att-value )"; + String actual = + TranslateSingle(translator, filter); + Assert.AreEqual(expected, actual); + + filter = FilterBuilder.Not(filter); + expected = "( ! "+expected+" )"; + actual = + TranslateSingle(translator, filter); + Assert.AreEqual(expected, actual); + } + + { + Filter filter = + FilterBuilder.LessThan(attribute); + String expected = "( < att-name att-value )"; + String actual = + TranslateSingle(translator, filter); + Assert.AreEqual(expected, actual); + + filter = FilterBuilder.Not(filter); + expected = "( ! "+expected+" )"; + actual = + TranslateSingle(translator, filter); + Assert.AreEqual(expected, actual); + } + + { + Filter filter = + FilterBuilder.LessThanOrEqualTo(attribute); + String expected = "( <= att-name att-value )"; + String actual = + TranslateSingle(translator, filter); + Assert.AreEqual(expected, actual); + + filter = FilterBuilder.Not(filter); + expected = "( ! "+expected+" )"; + actual = + TranslateSingle(translator, filter); + Assert.AreEqual(expected, actual); + } + + { + Filter filter = + FilterBuilder.StartsWith(attribute); + String expected = "( STARTS-WITH att-name att-value )"; + String actual = + TranslateSingle(translator, filter); + Assert.AreEqual(expected, actual); + + filter = FilterBuilder.Not(filter); + expected = "( ! "+expected+" )"; + actual = + TranslateSingle(translator, filter); + Assert.AreEqual(expected, actual); + } + + { + Filter filter = + FilterBuilder.ContainsAllValues(attribute); + String expected = "( CONTAINS-ALL-VALUES "+attribute+" )"; + String actual = + TranslateSingle(translator, filter); + Assert.AreEqual(expected, actual); + + filter = FilterBuilder.Not(filter); + expected = "( ! "+expected+" )"; + actual = + TranslateSingle(translator, filter); + Assert.AreEqual(expected, actual); + } + + //and + { + Filter left = + FilterBuilder.Contains(attribute); + Filter right = + FilterBuilder.Contains(attribute2); + String expectedLeft = "( CONTAINS att-name att-value )"; + String expectedRight = "( CONTAINS att-name2 att-value2 )"; + Filter filter = + FilterBuilder.And(left, right); + String expected = + "( & "+expectedLeft+" "+expectedRight+" )"; + String actual = + TranslateSingle(translator, filter); + Assert.AreEqual(expected, actual); + + filter = FilterBuilder.Not(filter); + expectedLeft = "( ! "+expectedLeft+" )"; + expectedRight = "( ! "+expectedRight+" )"; + expected = + "( | "+expectedLeft+" "+expectedRight+" )"; + actual = + TranslateSingle(translator, filter); + Assert.AreEqual(expected, actual); + + } + + //or + { + Filter left = + FilterBuilder.Contains(attribute); + Filter right = + FilterBuilder.Contains(attribute2); + String expectedLeft = "( CONTAINS att-name att-value )"; + String expectedRight = "( CONTAINS att-name2 att-value2 )"; + Filter filter = + FilterBuilder.Or(left, right); + String expected = + "( | "+expectedLeft+" "+expectedRight+" )"; + String actual = + TranslateSingle(translator, filter); + Assert.AreEqual(expected, actual); + + filter = FilterBuilder.Not(filter); + expectedLeft = "( ! "+expectedLeft+" )"; + expectedRight = "( ! "+expectedRight+" )"; + expected = + "( & "+expectedLeft+" "+expectedRight+" )"; + actual = + TranslateSingle(translator, filter); + Assert.AreEqual(expected, actual); + } + + //double-negative + { + Filter filter = + FilterBuilder.Contains(attribute); + filter = FilterBuilder.Not(filter); + filter = FilterBuilder.Not(filter); + String expected = "( CONTAINS att-name att-value )"; + String actual = + TranslateSingle(translator, filter); + Assert.AreEqual(expected, actual); + } + + } + + /** + * (a OR b) AND ( c OR d) needs to become + * (a AND c) OR ( a AND d) OR (b AND c) OR (b AND d) is + * OR is not implemented. Otherwise it should stay + * as-is. + */ + [Test] + public void TestDistribution() + { + Filter a = + FilterBuilder.Contains(ConnectorAttributeBuilder.Build("a", "a")); + Filter b = + FilterBuilder.Contains(ConnectorAttributeBuilder.Build("b", "b")); + Filter c = + FilterBuilder.Contains(ConnectorAttributeBuilder.Build("c", "c")); + Filter d = + FilterBuilder.Contains(ConnectorAttributeBuilder.Build("d", "d")); + + Filter filter = + FilterBuilder.And( + FilterBuilder.Or(a, b), + FilterBuilder.Or(c, d)); + String expected = "( & ( | ( CONTAINS a a ) ( CONTAINS b b ) ) ( | ( CONTAINS c c ) ( CONTAINS d d ) ) )"; + String actual = + TranslateSingle(new AllFiltersTranslator(),filter); + + Assert.AreEqual(expected, actual); + + IList results = + new NoOrTranslator().Translate(filter); + Assert.AreEqual(4, results.Count); + + Assert.AreEqual("( & ( CONTAINS a a ) ( CONTAINS c c ) )", + results[0]); + Assert.AreEqual("( & ( CONTAINS a a ) ( CONTAINS d d ) )", + results[1]); + Assert.AreEqual("( & ( CONTAINS b b ) ( CONTAINS c c ) )", + results[2]); + Assert.AreEqual("( & ( CONTAINS b b ) ( CONTAINS d d ) )", + results[3]); + } + + //test simplification + //-no leaf + [Test] + public void TestSimplifyNoLeaf() { + Filter a = + FilterBuilder.Contains(ConnectorAttributeBuilder.Build("a", "a")); + Filter b = + FilterBuilder.Contains(ConnectorAttributeBuilder.Build("b", "b")); + Filter c = + FilterBuilder.EndsWith(ConnectorAttributeBuilder.Build("c", "c")); + Filter d = + FilterBuilder.Contains(ConnectorAttributeBuilder.Build("d", "d")); + + Filter filter = + FilterBuilder.And( + FilterBuilder.Or(a, b), + FilterBuilder.Or(c, d)); + String expected = "( | ( CONTAINS a a ) ( CONTAINS b b ) )"; + String actual = + TranslateSingle(new NoEndsWithTranslator(),filter); + Assert.AreEqual(expected, actual); + + } + //-no leaf + no or + [Test] + public void TestSimplifyNoLeafNoOr() { + Filter a = + FilterBuilder.Contains(ConnectorAttributeBuilder.Build("a", "a")); + Filter b = + FilterBuilder.Contains(ConnectorAttributeBuilder.Build("b", "b")); + Filter c = + FilterBuilder.EndsWith(ConnectorAttributeBuilder.Build("c", "c")); + Filter d = + FilterBuilder.Contains(ConnectorAttributeBuilder.Build("d", "d")); + + Filter filter = + FilterBuilder.And( + FilterBuilder.Or(a, b), + FilterBuilder.Or(c, d)); + IList results = + new NoEndsWithNoOrTranslator().Translate(filter); + Assert.AreEqual(2, results.Count); + Assert.AreEqual("( CONTAINS a a )", + results[0]); + Assert.AreEqual("( CONTAINS b b )", + results[1]); + + } + + //-no and + [Test] + public void TestSimplifyNoAnd() { + Filter a = + FilterBuilder.Contains(ConnectorAttributeBuilder.Build("a", "a")); + Filter b = + FilterBuilder.Contains(ConnectorAttributeBuilder.Build("b", "b")); + Filter c = + FilterBuilder.EndsWith(ConnectorAttributeBuilder.Build("c", "c")); + Filter d = + FilterBuilder.Contains(ConnectorAttributeBuilder.Build("d", "d")); + + Filter filter = + FilterBuilder.And( + FilterBuilder.Or(a, b), + FilterBuilder.Or(c, d)); + String expected = "( | ( CONTAINS a a ) ( CONTAINS b b ) )"; + String actual = + TranslateSingle(new NoAndTranslator(),filter); + Assert.AreEqual(expected, actual); + } + + //-no and+no leaf + [Test] + public void TestSimplifyNoAndNoLeaf() { + Filter a = + FilterBuilder.Contains(ConnectorAttributeBuilder.Build("a", "a")); + Filter b = + FilterBuilder.Contains(ConnectorAttributeBuilder.Build("b", "b")); + Filter c = + FilterBuilder.EndsWith(ConnectorAttributeBuilder.Build("c", "c")); + Filter d = + FilterBuilder.Contains(ConnectorAttributeBuilder.Build("d", "d")); + + Filter filter = + FilterBuilder.And( + FilterBuilder.Or(a, b), + FilterBuilder.Or(c, d)); + String expected = "( | ( CONTAINS a a ) ( CONTAINS b b ) )"; + String actual = + TranslateSingle(new NoAndNoEndsWithTranslator(),filter); + Assert.AreEqual(expected, actual); + + a = + FilterBuilder.Contains(ConnectorAttributeBuilder.Build("a", "a")); + b = + FilterBuilder.EndsWith(ConnectorAttributeBuilder.Build("b", "b")); + c = + FilterBuilder.Contains(ConnectorAttributeBuilder.Build("c", "c")); + d = + FilterBuilder.Contains(ConnectorAttributeBuilder.Build("d", "d")); + + filter = + FilterBuilder.And( + FilterBuilder.Or(a, b), + FilterBuilder.Or(c, d)); + expected = "( | ( CONTAINS c c ) ( CONTAINS d d ) )"; + actual = + TranslateSingle(new NoAndNoEndsWithTranslator(),filter); + Assert.AreEqual(expected, actual); + + a = + FilterBuilder.Contains(ConnectorAttributeBuilder.Build("a", "a")); + b = + FilterBuilder.EndsWith(ConnectorAttributeBuilder.Build("b", "b")); + c = + FilterBuilder.Contains(ConnectorAttributeBuilder.Build("c", "c")); + d = + FilterBuilder.EndsWith(ConnectorAttributeBuilder.Build("d", "d")); + + filter = + FilterBuilder.And( + FilterBuilder.Or(a, b), + FilterBuilder.Or(c, d)); + IList results = + new NoAndNoEndsWithTranslator().Translate(filter); + Assert.AreEqual(0, results.Count); + } + + //-no and, no or, no leaf + [Test] + public void TestSimplifyNoAndNoOrNoLeaf() { + Filter a = + FilterBuilder.Contains(ConnectorAttributeBuilder.Build("a", "a")); + Filter b = + FilterBuilder.Contains(ConnectorAttributeBuilder.Build("b", "b")); + Filter c = + FilterBuilder.EndsWith(ConnectorAttributeBuilder.Build("c", "c")); + Filter d = + FilterBuilder.Contains(ConnectorAttributeBuilder.Build("d", "d")); + + Filter filter = + FilterBuilder.And( + FilterBuilder.Or(a, b), + FilterBuilder.Or(c, d)); + IList results = + new NoAndNoOrNoEndsWithTranslator().Translate(filter); + Assert.AreEqual(2, results.Count); + Assert.AreEqual("( CONTAINS a a )", + results[0]); + Assert.AreEqual("( CONTAINS b b )", + results[1]); + + a = + FilterBuilder.Contains(ConnectorAttributeBuilder.Build("a", "a")); + b = + FilterBuilder.EndsWith(ConnectorAttributeBuilder.Build("b", "b")); + c = + FilterBuilder.Contains(ConnectorAttributeBuilder.Build("c", "c")); + d = + FilterBuilder.Contains(ConnectorAttributeBuilder.Build("d", "d")); + filter = + FilterBuilder.And( + FilterBuilder.Or(a, b), + FilterBuilder.Or(c, d)); + results = + new NoAndNoOrNoEndsWithTranslator().Translate(filter); + Assert.AreEqual(2, results.Count); + Assert.AreEqual("( CONTAINS c c )", + results[0]); + Assert.AreEqual("( CONTAINS d d )", + results[1]); + + } + + private static String TranslateSingle(AbstractFilterTranslator translator, + Filter filter) { + IList translated = + translator.Translate(filter); + Assert.AreEqual(1, translated.Count); + return translated[0]; + } + } +} diff --git a/FrameworkTests/FrameworkTests.csproj b/FrameworkTests/FrameworkTests.csproj new file mode 100644 index 00000000..597b4b41 --- /dev/null +++ b/FrameworkTests/FrameworkTests.csproj @@ -0,0 +1,167 @@ + + + + {32804F5A-812C-4FA6-835C-BDAE5B24D355} + Debug + AnyCPU + Library + FrameworkTests + FrameworkTests + v3.5 + + + bin\Debug\ + True + Full + False + True + DEBUG;TRACE + + + bin\Release\ + False + None + True + False + TRACE + + + + + + + 3.5 + + + + 3.5 + + + + 3.5 + + + + + + + + + + + + + + + + + + + Always + + + Always + + + + + {F140E8DA-52B4-4159-992A-9DA10EA8EEFB} + Common + + + {8B24461B-456A-4032-89A1-CD418F7B5B62} + Framework + + + {5B011775-B121-4EEE-A410-BA2D2F5BFB8B} + FrameworkInternal + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/FrameworkTests/GuardedStringTests.cs b/FrameworkTests/GuardedStringTests.cs new file mode 100644 index 00000000..2310bf7b --- /dev/null +++ b/FrameworkTests/GuardedStringTests.cs @@ -0,0 +1,117 @@ +/* + * Copyright 2008 Sun Microsystems, Inc. All rights reserved. + * + * U.S. Government Rights - Commercial software. Government users + * are subject to the Sun Microsystems, Inc. standard license agreement + * and applicable provisions of the FAR and its supplements. + * + * Use is subject to license terms. + * + * This distribution may include materials developed by third parties. + * Sun, Sun Microsystems, the Sun logo, Java and Project Identity + * Connectors are trademarks or registered trademarks of Sun + * Microsystems, Inc. or its subsidiaries in the U.S. and other + * countries. + * + * UNIX is a registered trademark in the U.S. and other countries, + * exclusively licensed through X/Open Company, Ltd. + * + * ----------- + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER. + * + * Copyright 2008 Sun Microsystems, Inc. All rights reserved. + * + * The contents of this file are subject to the terms of the Common Development + * and Distribution License(CDDL) (the License). You may not use this file + * except in compliance with the License. + * + * You can obtain a copy of the License at + * http://identityconnectors.dev.java.net/CDDLv1.0.html + * See the License for the specific language governing permissions and + * limitations under the License. + * + * When distributing the Covered Code, include this CDDL Header Notice in each + * file and include the License file at identityconnectors/legal/license.txt. + * If applicable, add the following below this CDDL Header, with the fields + * enclosed by brackets [] replaced by your own identifying information: + * "Portions Copyrighted [year] [name of copyright owner]" + * ----------- + */ +using System; +using System.Security; +using System.Runtime.InteropServices; +using System.Text; +using Org.IdentityConnectors.Common.Security; +using Org.IdentityConnectors.Framework.Common.Serializer; +using NUnit.Framework; +using NUnit.Framework.SyntaxHelpers; + +namespace FrameworkTests +{ + [TestFixture] + public class GuardedStringTests + { + [Test] + public void TestBasics() + { + GuardedString ss = new GuardedString(); + ss.AppendChar('f'); + ss.AppendChar('o'); + ss.AppendChar('o'); + ss.AppendChar('b'); + ss.AppendChar('a'); + ss.AppendChar('r'); + String decrypted = DecryptToString(ss); + Assert.AreEqual("foobar",decrypted); + String hash = ss.GetBase64SHA1Hash(); + Assert.IsTrue(ss.VerifyBase64SHA1Hash(hash)); + ss.AppendChar('2'); + Assert.IsFalse(ss.VerifyBase64SHA1Hash(hash)); + } + [Test] + public void TestUnicode() { + + for ( int i = 0; i < 0xFFFF; i++) { + int expected = i; + char c = (char)i; + GuardedString gs = new GuardedString(); + gs = (GuardedString)SerializerUtil.CloneObject(gs); + gs.AppendChar(c); + gs.Access(clearChars=> { + int v = (int)clearChars[0]; + Assert.AreEqual(expected, v); + }); + + } + } + + [Test] + public void TestEquals() { + GuardedString str1 = new GuardedString(); + GuardedString str2 = new GuardedString(); + Assert.AreEqual(str1, str2); + str2.AppendChar('2'); + Assert.AreNotEqual(str1,str2); + str1.AppendChar('2'); + Assert.AreEqual(str1, str2); + } + + + /** + * Highly insecure method! Do not do this in production + * code. This is only for test purposes + */ + private String DecryptToString(GuardedString str) { + StringBuilder buf = new StringBuilder(); + str.Access( + array=> + { + for ( int i = 0 ; i < array.Length; i++) + { + buf.Append(array[i]); + } + }); + return buf.ToString(); + } + } +} diff --git a/FrameworkTests/LocaleTests.cs b/FrameworkTests/LocaleTests.cs new file mode 100644 index 00000000..9d8c1a38 --- /dev/null +++ b/FrameworkTests/LocaleTests.cs @@ -0,0 +1,227 @@ +/* + * Copyright 2008 Sun Microsystems, Inc. All rights reserved. + * + * U.S. Government Rights - Commercial software. Government users + * are subject to the Sun Microsystems, Inc. standard license agreement + * and applicable provisions of the FAR and its supplements. + * + * Use is subject to license terms. + * + * This distribution may include materials developed by third parties. + * Sun, Sun Microsystems, the Sun logo, Java and Project Identity + * Connectors are trademarks or registered trademarks of Sun + * Microsystems, Inc. or its subsidiaries in the U.S. and other + * countries. + * + * UNIX is a registered trademark in the U.S. and other countries, + * exclusively licensed through X/Open Company, Ltd. + * + * ----------- + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER. + * + * Copyright 2008 Sun Microsystems, Inc. All rights reserved. + * + * The contents of this file are subject to the terms of the Common Development + * and Distribution License(CDDL) (the License). You may not use this file + * except in compliance with the License. + * + * You can obtain a copy of the License at + * http://identityconnectors.dev.java.net/CDDLv1.0.html + * See the License for the specific language governing permissions and + * limitations under the License. + * + * When distributing the Covered Code, include this CDDL Header Notice in each + * file and include the License file at identityconnectors/legal/license.txt. + * If applicable, add the following below this CDDL Header, with the fields + * enclosed by brackets [] replaced by your own identifying information: + * "Portions Copyrighted [year] [name of copyright owner]" + * ----------- + */ +using System; +using NUnit.Framework; +using NUnit.Framework.SyntaxHelpers; +using System.Globalization; +using System.Collections.Generic; +using Org.IdentityConnectors.Common; +namespace FrameworkTests +{ + [TestFixture] + public class LocaleTests + { + + + [Test] + public void TestJava2CSharp() + { + HashSet + cultures = new HashSet(CultureInfo.GetCultures(CultureTypes.AllCultures)); + + TestJavaLocale(cultures, new Locale("ar", "", ""), "Arabic"); + TestJavaLocale(cultures, new Locale("be", "", ""), "Belarusian"); + TestJavaLocale(cultures, new Locale("bg", "", ""), "Bulgarian"); + TestJavaLocale(cultures, new Locale("ca", "", ""), "Catalan"); + TestJavaLocale(cultures, new Locale("cs", "", ""), "Czech"); + TestJavaLocale(cultures, new Locale("da", "", ""), "Danish"); + TestJavaLocale(cultures, new Locale("de", "", ""), "German"); + TestJavaLocale(cultures, new Locale("el", "", ""), "Greek"); + TestJavaLocale(cultures, new Locale("en", "", ""), "English"); + TestJavaLocale(cultures, new Locale("es", "", ""), "Spanish"); + TestJavaLocale(cultures, new Locale("et", "", ""), "Estonian"); + TestJavaLocale(cultures, new Locale("fi", "", ""), "Finnish"); + TestJavaLocale(cultures, new Locale("fr", "", ""), "French"); + TestJavaLocale(cultures, new Locale("hr", "", ""), "Croatian"); + TestJavaLocale(cultures, new Locale("hu", "", ""), "Hungarian"); + TestJavaLocale(cultures, new Locale("is", "", ""), "Icelandic"); + TestJavaLocale(cultures, new Locale("it", "", ""), "Italian"); + TestJavaLocale(cultures, new Locale("iw", "", ""), "Hebrew"); + TestJavaLocale(cultures, new Locale("ja", "", ""), "Japanese"); + TestJavaLocale(cultures, new Locale("ko", "", ""), "Korean"); + TestJavaLocale(cultures, new Locale("lt", "", ""), "Lithuanian"); + TestJavaLocale(cultures, new Locale("lv", "", ""), "Latvian"); + TestJavaLocale(cultures, new Locale("mk", "", ""), "Macedonian"); + TestJavaLocale(cultures, new Locale("nl", "", ""), "Dutch"); + TestJavaLocale(cultures, new Locale("no", "", ""), "Norwegian"); + TestJavaLocale(cultures, new Locale("pl", "", ""), "Polish"); + TestJavaLocale(cultures, new Locale("pt", "", ""), "Portuguese"); + TestJavaLocale(cultures, new Locale("ro", "", ""), "Romanian"); + TestJavaLocale(cultures, new Locale("ru", "", ""), "Russian"); + TestJavaLocale(cultures, new Locale("sk", "", ""), "Slovak"); + TestJavaLocale(cultures, new Locale("sl", "", ""), "Slovenian"); + TestJavaLocale(cultures, new Locale("sq", "", ""), "Albanian"); + TestJavaLocale(cultures, new Locale("sr", "", ""), "Serbian"); + TestJavaLocale(cultures, new Locale("sv", "", ""), "Swedish"); + TestJavaLocale(cultures, new Locale("th", "", ""), "Thai"); + TestJavaLocale(cultures, new Locale("tr", "", ""), "Turkish"); + TestJavaLocale(cultures, new Locale("uk", "", ""), "Ukrainian"); + TestJavaLocale(cultures, new Locale("vi", "", ""), "Vietnamese"); + TestJavaLocale(cultures, new Locale("zh", "", ""), "Chinese"); + TestJavaLocale(cultures, new Locale("ar", "AE", ""), "Arabic (United Arab Emirates)"); + TestJavaLocale(cultures, new Locale("ar", "BH", ""), "Arabic (Bahrain)"); + TestJavaLocale(cultures, new Locale("ar", "DZ", ""), "Arabic (Algeria)"); + TestJavaLocale(cultures, new Locale("ar", "EG", ""), "Arabic (Egypt)"); + TestJavaLocale(cultures, new Locale("ar", "IQ", ""), "Arabic (Iraq)"); + TestJavaLocale(cultures, new Locale("ar", "JO", ""), "Arabic (Jordan)"); + TestJavaLocale(cultures, new Locale("ar", "KW", ""), "Arabic (Kuwait)"); + TestJavaLocale(cultures, new Locale("ar", "LB", ""), "Arabic (Lebanon)"); + TestJavaLocale(cultures, new Locale("ar", "LY", ""), "Arabic (Libya)"); + TestJavaLocale(cultures, new Locale("ar", "MA", ""), "Arabic (Morocco)"); + TestJavaLocale(cultures, new Locale("ar", "OM", ""), "Arabic (Oman)"); + TestJavaLocale(cultures, new Locale("ar", "QA", ""), "Arabic (Qatar)"); + TestJavaLocale(cultures, new Locale("ar", "SA", ""), "Arabic (Saudi Arabia)"); + TestJavaLocale(cultures, + new Locale("ar", "SD", ""), + "Arabic (Sudan)", + new Locale("ar")); + TestJavaLocale(cultures, new Locale("ar", "SY", ""), "Arabic (Syria)"); + TestJavaLocale(cultures, new Locale("ar", "TN", ""), "Arabic (Tunisia)"); + TestJavaLocale(cultures, new Locale("ar", "YE", ""), "Arabic (Yemen)"); + TestJavaLocale(cultures, new Locale("be", "BY", ""), "Belarusian (Belarus)"); + TestJavaLocale(cultures, new Locale("bg", "BG", ""), "Bulgarian (Bulgaria)"); + TestJavaLocale(cultures, new Locale("ca", "ES", ""), "Catalan (Spain)"); + TestJavaLocale(cultures, new Locale("cs", "CZ", ""), "Czech (Czech Republic)"); + TestJavaLocale(cultures, new Locale("da", "DK", ""), "Danish (Denmark)"); + TestJavaLocale(cultures, new Locale("de", "AT", ""), "German (Austria)"); + TestJavaLocale(cultures, new Locale("de", "CH", ""), "German (Switzerland)"); + TestJavaLocale(cultures, new Locale("de", "DE", ""), "German (Germany)"); + TestJavaLocale(cultures, new Locale("de", "LU", ""), "German (Luxembourg)"); + TestJavaLocale(cultures, new Locale("el", "GR", ""), "Greek (Greece)"); + TestJavaLocale(cultures, new Locale("en", "AU", ""), "English (Australia)"); + TestJavaLocale(cultures, new Locale("en", "CA", ""), "English (Canada)"); + TestJavaLocale(cultures, new Locale("en", "GB", ""), "English (United Kingdom)"); + TestJavaLocale(cultures, new Locale("en", "IE", ""), "English (Ireland)"); + TestJavaLocale(cultures, new Locale("en", "IN", ""), + "English (India)", + new Locale("en")); + TestJavaLocale(cultures, new Locale("en", "NZ", ""), "English (New Zealand)"); + TestJavaLocale(cultures, new Locale("en", "US", ""), "English (United States)"); + TestJavaLocale(cultures, new Locale("en", "ZA", ""), "English (South Africa)"); + TestJavaLocale(cultures, new Locale("es", "AR", ""), "Spanish (Argentina)"); + TestJavaLocale(cultures, new Locale("es", "BO", ""), "Spanish (Bolivia)"); + TestJavaLocale(cultures, new Locale("es", "CL", ""), "Spanish (Chile)"); + TestJavaLocale(cultures, new Locale("es", "CO", ""), "Spanish (Colombia)"); + TestJavaLocale(cultures, new Locale("es", "CR", ""), "Spanish (Costa Rica)"); + TestJavaLocale(cultures, new Locale("es", "DO", ""), "Spanish (Dominican Republic)"); + TestJavaLocale(cultures, new Locale("es", "EC", ""), "Spanish (Ecuador)"); + TestJavaLocale(cultures, new Locale("es", "ES", ""), "Spanish (Spain)"); + TestJavaLocale(cultures, new Locale("es", "GT", ""), "Spanish (Guatemala)"); + TestJavaLocale(cultures, new Locale("es", "HN", ""), "Spanish (Honduras)"); + TestJavaLocale(cultures, new Locale("es", "MX", ""), "Spanish (Mexico)"); + TestJavaLocale(cultures, new Locale("es", "NI", ""), "Spanish (Nicaragua)"); + TestJavaLocale(cultures, new Locale("es", "PA", ""), "Spanish (Panama)"); + TestJavaLocale(cultures, new Locale("es", "PE", ""), "Spanish (Peru)"); + TestJavaLocale(cultures, new Locale("es", "PR", ""), "Spanish (Puerto Rico)"); + TestJavaLocale(cultures, new Locale("es", "PY", ""), "Spanish (Paraguay)"); + TestJavaLocale(cultures, new Locale("es", "SV", ""), "Spanish (El Salvador)"); + TestJavaLocale(cultures, new Locale("es", "UY", ""), "Spanish (Uruguay)"); + TestJavaLocale(cultures, new Locale("es", "VE", ""), "Spanish (Venezuela)"); + TestJavaLocale(cultures, new Locale("et", "EE", ""), "Estonian (Estonia)"); + TestJavaLocale(cultures, new Locale("fi", "FI", ""), "Finnish (Finland)"); + TestJavaLocale(cultures, new Locale("fr", "BE", ""), "French (Belgium)"); + TestJavaLocale(cultures, new Locale("fr", "CA", ""), "French (Canada)"); + TestJavaLocale(cultures, new Locale("fr", "CH", ""), "French (Switzerland)"); + TestJavaLocale(cultures, new Locale("fr", "FR", ""), "French (France)"); + TestJavaLocale(cultures, new Locale("fr", "LU", ""), "French (Luxembourg)"); + TestJavaLocale(cultures, new Locale("hi", "IN", ""), "Hindi (India)"); + TestJavaLocale(cultures, new Locale("hr", "HR", ""), "Croatian (Croatia)"); + TestJavaLocale(cultures, new Locale("hu", "HU", ""), "Hungarian (Hungary)"); + TestJavaLocale(cultures, new Locale("is", "IS", ""), "Icelandic (Iceland)"); + TestJavaLocale(cultures, new Locale("it", "CH", ""), "Italian (Switzerland)"); + TestJavaLocale(cultures, new Locale("it", "IT", ""), "Italian (Italy)"); + TestJavaLocale(cultures, new Locale("iw", "IL", ""), "Hebrew (Israel)"); + TestJavaLocale(cultures, new Locale("ja", "JP", ""), "Japanese (Japan)"); + TestJavaLocale(cultures, new Locale("ko", "KR", ""), "Korean (South Korea)"); + TestJavaLocale(cultures, new Locale("lt", "LT", ""), "Lithuanian (Lithuania)"); + TestJavaLocale(cultures, new Locale("lv", "LV", ""), "Latvian (Latvia)"); + TestJavaLocale(cultures, new Locale("mk", "MK", ""), "Macedonian (Macedonia)"); + TestJavaLocale(cultures, new Locale("nl", "BE", ""), "Dutch (Belgium)"); + TestJavaLocale(cultures, new Locale("nl", "NL", ""), "Dutch (Netherlands)"); + TestJavaLocale(cultures, new Locale("no", "NO", ""), "Norwegian (Norway)"); + TestJavaLocale(cultures, new Locale("no", "NO", "NY"), "Norwegian (Norway,Nynorsk)"); + TestJavaLocale(cultures, new Locale("pl", "PL", ""), "Polish (Poland)"); + TestJavaLocale(cultures, new Locale("pt", "BR", ""), "Portuguese (Brazil)"); + TestJavaLocale(cultures, new Locale("pt", "PT", ""), "Portuguese (Portugal)"); + TestJavaLocale(cultures, new Locale("ro", "RO", ""), "Romanian (Romania)"); + TestJavaLocale(cultures, new Locale("ru", "RU", ""), "Russian (Russia)"); + TestJavaLocale(cultures, new Locale("sk", "SK", ""), "Slovak (Slovakia)"); + TestJavaLocale(cultures, new Locale("sl", "SI", ""), "Slovenian (Slovenia)"); + TestJavaLocale(cultures, new Locale("sq", "AL", ""), "Albanian (Albania)"); + TestJavaLocale(cultures, new Locale("sr", "BA", ""), + "Serbian (Bosnia and Herzegovina)", + new Locale("sr")); + TestJavaLocale(cultures, new Locale("sr", "CS", ""), + "Serbian (Serbia and Montenegro)", + new Locale("sr")); + TestJavaLocale(cultures, new Locale("sv", "SE", ""), "Swedish (Sweden)"); + TestJavaLocale(cultures, new Locale("th", "TH", ""), "Thai (Thailand)"); + TestJavaLocale(cultures, new Locale("th", "TH", "TH"), + "Thai (Thailand,TH)", + new Locale("th","TH")); + TestJavaLocale(cultures, new Locale("tr", "TR", ""), "Turkish (Turkey)"); + TestJavaLocale(cultures, new Locale("uk", "UA", ""), "Ukrainian (Ukraine)"); + TestJavaLocale(cultures, new Locale("vi", "VN", ""), "Vietnamese (Vietnam)"); + TestJavaLocale(cultures, new Locale("zh", "CN", ""), "Chinese (China)"); + TestJavaLocale(cultures, new Locale("zh", "HK", ""), "Chinese (Hong Kong)"); + TestJavaLocale(cultures, new Locale("zh", "TW", ""), "Chinese (Taiwan)"); + + //foreach (CultureInfo info in cultures) { + // Console.WriteLine("remaining: "+info+" "+info.DisplayName+" "+info.TwoLetterISOLanguageName); + //} + } + private void TestJavaLocale(HashSet cultures, + Locale original, + String display) { + TestJavaLocale(cultures,original,display,null); + } + private void TestJavaLocale(HashSet cultures, + Locale original, + String display, + Locale expected) { + if ( expected == null ) { + expected = original; + } + CultureInfo cinfo = original.ToCultureInfo(); + Locale actual = Locale.FindLocale(cinfo); + Assert.AreEqual(expected,actual,display+" ("+original+") "+" didn't map"); + } + } +} diff --git a/FrameworkTests/ObjectNormalizerFacadeTests.cs b/FrameworkTests/ObjectNormalizerFacadeTests.cs new file mode 100644 index 00000000..258540c9 --- /dev/null +++ b/FrameworkTests/ObjectNormalizerFacadeTests.cs @@ -0,0 +1,251 @@ +/* + * Copyright 2008 Sun Microsystems, Inc. All rights reserved. + * + * U.S. Government Rights - Commercial software. Government users + * are subject to the Sun Microsystems, Inc. standard license agreement + * and applicable provisions of the FAR and its supplements. + * + * Use is subject to license terms. + * + * This distribution may include materials developed by third parties. + * Sun, Sun Microsystems, the Sun logo, Java and Project Identity + * Connectors are trademarks or registered trademarks of Sun + * Microsystems, Inc. or its subsidiaries in the U.S. and other + * countries. + * + * UNIX is a registered trademark in the U.S. and other countries, + * exclusively licensed through X/Open Company, Ltd. + * + * ----------- + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER. + * + * Copyright 2008 Sun Microsystems, Inc. All rights reserved. + * + * The contents of this file are subject to the terms of the Common Development + * and Distribution License(CDDL) (the License). You may not use this file + * except in compliance with the License. + * + * You can obtain a copy of the License at + * http://identityconnectors.dev.java.net/CDDLv1.0.html + * See the License for the specific language governing permissions and + * limitations under the License. + * + * When distributing the Covered Code, include this CDDL Header Notice in each + * file and include the License file at identityconnectors/legal/license.txt. + * If applicable, add the following below this CDDL Header, with the fields + * enclosed by brackets [] replaced by your own identifying information: + * "Portions Copyrighted [year] [name of copyright owner]" + * ----------- + */ +using System; +using NUnit.Framework; +using NUnit.Framework.SyntaxHelpers; +using Org.IdentityConnectors.Framework.Common.Objects; +using Org.IdentityConnectors.Framework.Common.Objects.Filters; +using Org.IdentityConnectors.Framework.Common.Serializer; +using Org.IdentityConnectors.Framework.Spi; +using Org.IdentityConnectors.Framework.Impl.Api.Local.Operations; +namespace FrameworkTests +{ + [TestFixture] + public class ObjectNormalizerFacadeTests + { + public class MyAttributeNormalizer : AttributeNormalizer { + public ConnectorAttribute NormalizeAttribute(ObjectClass oclass, ConnectorAttribute attribute) { + if ( attribute.Is("foo")) { + String val = ConnectorAttributeUtil.GetStringValue(attribute); + return ConnectorAttributeBuilder.Build("foo",val.Trim()); + } + else { + return attribute; + } + } + } + + private ConnectorAttribute CreateTestAttribute() + { + return ConnectorAttributeBuilder.Build("foo"," bar "); + } + + private ConnectorAttribute CreateNormalizedTestAttribute() + { + return ConnectorAttributeBuilder.Build("foo","bar"); + } + + private ObjectNormalizerFacade CreateTestNormalizer() + { + ObjectNormalizerFacade facade = new + ObjectNormalizerFacade(ObjectClass.ACCOUNT, + new MyAttributeNormalizer()); + return facade; + } + + private void AssertNormalizedFilter(Filter expectedNormalizedFilter, + Filter filter) { + ObjectNormalizerFacade facade = + CreateTestNormalizer(); + filter = facade.NormalizeFilter(filter); + String expectedXml = SerializerUtil.SerializeXmlObject(expectedNormalizedFilter, false); + String actualXml = SerializerUtil.SerializeXmlObject(filter, false); + Assert.AreEqual(expectedXml, actualXml); + } + + + + [Test] + public void TestEndsWith() + { + Filter expected = + FilterBuilder.EndsWith(CreateNormalizedTestAttribute()); + Filter filter = + FilterBuilder.EndsWith(CreateTestAttribute()); + AssertNormalizedFilter(expected,filter); + } + [Test] + public void TestStartsWith() + { + Filter expected = + FilterBuilder.StartsWith(CreateNormalizedTestAttribute()); + Filter filter = + FilterBuilder.StartsWith(CreateTestAttribute()); + AssertNormalizedFilter(expected,filter); + } + [Test] + public void TestContains() + { + Filter expected = + FilterBuilder.Contains(CreateNormalizedTestAttribute()); + Filter filter = + FilterBuilder.Contains(CreateTestAttribute()); + AssertNormalizedFilter(expected,filter); + } + [Test] + public void TestEqualTo() + { + Filter expected = + FilterBuilder.EqualTo(CreateNormalizedTestAttribute()); + Filter filter = + FilterBuilder.EqualTo(CreateTestAttribute()); + AssertNormalizedFilter(expected,filter); + } + [Test] + public void TestGreaterThanOrEqualTo() + { + Filter expected = + FilterBuilder.GreaterThanOrEqualTo(CreateNormalizedTestAttribute()); + Filter filter = + FilterBuilder.GreaterThanOrEqualTo(CreateTestAttribute()); + AssertNormalizedFilter(expected,filter); + } + [Test] + public void TestLessThanOrEqualTo() + { + Filter expected = + FilterBuilder.LessThanOrEqualTo(CreateNormalizedTestAttribute()); + Filter filter = + FilterBuilder.LessThanOrEqualTo(CreateTestAttribute()); + AssertNormalizedFilter(expected,filter); + } + [Test] + public void TestLessThan() + { + Filter expected = + FilterBuilder.LessThan(CreateNormalizedTestAttribute()); + Filter filter = + FilterBuilder.LessThan(CreateTestAttribute()); + AssertNormalizedFilter(expected,filter); + } + [Test] + public void TestGreaterThan() + { + Filter expected = + FilterBuilder.GreaterThan(CreateNormalizedTestAttribute()); + Filter filter = + FilterBuilder.GreaterThan(CreateTestAttribute()); + AssertNormalizedFilter(expected,filter); + } + [Test] + public void TestAnd() + { + Filter expected = + FilterBuilder.And(FilterBuilder.Contains(CreateNormalizedTestAttribute()), + FilterBuilder.Contains(CreateNormalizedTestAttribute())); + Filter filter = + FilterBuilder.And(FilterBuilder.Contains(CreateTestAttribute()), + FilterBuilder.Contains(CreateTestAttribute())); + AssertNormalizedFilter(expected,filter); + } + [Test] + public void TestOr() + { + Filter expected = + FilterBuilder.Or(FilterBuilder.Contains(CreateNormalizedTestAttribute()), + FilterBuilder.Contains(CreateNormalizedTestAttribute())); + Filter filter = + FilterBuilder.Or(FilterBuilder.Contains(CreateTestAttribute()), + FilterBuilder.Contains(CreateTestAttribute())); + AssertNormalizedFilter(expected,filter); + } + [Test] + public void TestNot() + { + Filter expected = + FilterBuilder.Not(FilterBuilder.Contains(CreateNormalizedTestAttribute())); + Filter filter = + FilterBuilder.Not(FilterBuilder.Contains(CreateTestAttribute())); + AssertNormalizedFilter(expected,filter); + } + [Test] + public void TestContainsAllValues() + { + Filter expected = + FilterBuilder.ContainsAllValues(CreateNormalizedTestAttribute()); + Filter filter = + FilterBuilder.ContainsAllValues(CreateTestAttribute()); + AssertNormalizedFilter(expected,filter); + } + [Test] + public void TestConnectorObject() + { + ConnectorObjectBuilder builder = + new ConnectorObjectBuilder(); + builder.SetName("myname"); + builder.SetUid("myuid"); + builder.AddAttribute(CreateTestAttribute()); + ConnectorObject v1 = builder.Build(); + ConnectorObject v2 = CreateTestNormalizer().NormalizeObject(v1); + builder = + new ConnectorObjectBuilder(); + builder.SetName("myname"); + builder.SetUid("myuid"); + builder.AddAttribute(CreateNormalizedTestAttribute()); + ConnectorObject expected = builder.Build(); + Assert.AreEqual(expected, v2); + Assert.IsFalse(expected.Equals(v1)); + } + + [Test] + public void TestSyncDelta() + { + SyncDeltaBuilder builder = + new SyncDeltaBuilder(); + builder.DeltaType=(SyncDeltaType.DELETE); + builder.Token=(new SyncToken("mytoken")); + builder.Uid=(new Uid("myuid")); + builder.AddAttribute(CreateTestAttribute()); + SyncDelta v1 = builder.Build(); + SyncDelta v2 = CreateTestNormalizer().NormalizeSyncDelta(v1); + builder = + new SyncDeltaBuilder(); + builder.DeltaType=(SyncDeltaType.DELETE); + builder.Token=(new SyncToken("mytoken")); + builder.Uid=(new Uid("myuid")); + builder.AddAttribute(CreateNormalizedTestAttribute()); + SyncDelta expected = builder.Build(); + Assert.AreEqual(expected, v2); + Assert.IsFalse(expected.Equals(v1)); + + } + + } +} diff --git a/FrameworkTests/ObjectPoolTests.cs b/FrameworkTests/ObjectPoolTests.cs new file mode 100644 index 00000000..3251267d --- /dev/null +++ b/FrameworkTests/ObjectPoolTests.cs @@ -0,0 +1,269 @@ +/* + * Copyright 2008 Sun Microsystems, Inc. All rights reserved. + * + * U.S. Government Rights - Commercial software. Government users + * are subject to the Sun Microsystems, Inc. standard license agreement + * and applicable provisions of the FAR and its supplements. + * + * Use is subject to license terms. + * + * This distribution may include materials developed by third parties. + * Sun, Sun Microsystems, the Sun logo, Java and Project Identity + * Connectors are trademarks or registered trademarks of Sun + * Microsystems, Inc. or its subsidiaries in the U.S. and other + * countries. + * + * UNIX is a registered trademark in the U.S. and other countries, + * exclusively licensed through X/Open Company, Ltd. + * + * ----------- + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER. + * + * Copyright 2008 Sun Microsystems, Inc. All rights reserved. + * + * The contents of this file are subject to the terms of the Common Development + * and Distribution License(CDDL) (the License). You may not use this file + * except in compliance with the License. + * + * You can obtain a copy of the License at + * http://identityconnectors.dev.java.net/CDDLv1.0.html + * See the License for the specific language governing permissions and + * limitations under the License. + * + * When distributing the Covered Code, include this CDDL Header Notice in each + * file and include the License file at identityconnectors/legal/license.txt. + * If applicable, add the following below this CDDL Header, with the fields + * enclosed by brackets [] replaced by your own identifying information: + * "Portions Copyrighted [year] [name of copyright owner]" + * ----------- + */ +using System; +using System.Threading; +using NUnit.Framework; +using NUnit.Framework.SyntaxHelpers; + +using Org.IdentityConnectors.Common.Pooling; +using Org.IdentityConnectors.Framework.Common.Exceptions; +using Org.IdentityConnectors.Framework.Common.Objects; +using Org.IdentityConnectors.Framework.Spi; +using Org.IdentityConnectors.Framework.Impl.Api.Local; + +namespace FrameworkTests +{ + [TestFixture] + public class ObjectPoolTests + { + private class MyTestConnection { + private bool _isGood = true; + + public void Test() { + if (!_isGood) { + throw new ConnectorException("Connection is bad"); + } + } + + public void Dispose() { + _isGood = false; + } + + public bool IsGood { + get { + return _isGood; + } + } + } + + private class MyTestConnectionFactory : ObjectPoolHandler { + + private bool _createBadConnection = false; + private int _totalCreatedConnections = 0; + + public MyTestConnection NewObject() { + _totalCreatedConnections++; + MyTestConnection rv = new MyTestConnection(); + if (_createBadConnection) { + rv.Dispose(); + } + return rv; + } + public void TestObject(MyTestConnection obj) { + obj.Test(); + } + public void DisposeObject(MyTestConnection obj) { + obj.Dispose(); + } + + public int TotalCreatedConnections { + get { + return _totalCreatedConnections; + } + } + + public bool CreateBadConnection { + set { + _createBadConnection = value; + } + } + } + + private class MyTestThread { + private readonly ObjectPool _pool; + private readonly int _numIterations; + private Exception _exception; + private Thread _thisThread; + public MyTestThread(ObjectPool pool, + int numIterations) { + _pool = pool; + _numIterations = numIterations; + } + public void Run(object o) { + _thisThread = (Thread)o; + try { + for ( int i = 0; i < _numIterations; i++ ) { + MyTestConnection con = + _pool.BorrowObject(); + Thread.Sleep(300); + _pool.ReturnObject(con); + } + } + catch (Exception e) { + _exception = e; + } + } + public void Shutdown() { + _thisThread.Join(); + if (_exception != null) { + throw _exception; + } + } + + } + + [Test] + public void TestWithManyThreads() + { + int NUM_ITERATIONS = 10; + int NUM_THREADS = 10; + int MAX_CONNECTIONS = NUM_THREADS-3; //make sure we get some waiting + ObjectPoolConfiguration config = new ObjectPoolConfiguration(); + config.MaxObjects=(MAX_CONNECTIONS); + config.MaxIdle=(MAX_CONNECTIONS); + config.MinIdle=(MAX_CONNECTIONS); + config.MinEvictableIdleTimeMillis=(60*1000); + config.MaxWait=(60*1000); + MyTestConnectionFactory fact = new MyTestConnectionFactory(); + + ObjectPool pool = new ObjectPool(fact,config); + + MyTestThread [] threads = new MyTestThread[NUM_THREADS]; + for (int i = 0; i < threads.Length; i++) { + threads[i] = new MyTestThread(pool,NUM_ITERATIONS); + Thread thread = new Thread(threads[i].Run); + thread.Start(thread); + } + + foreach (MyTestThread thread in threads) { + thread.Shutdown(); + } + + //these should be the same since we never + //should have disposed anything + Assert.AreEqual(MAX_CONNECTIONS, fact.TotalCreatedConnections); + ObjectPool.Statistics stats = pool.GetStatistics(); + Assert.AreEqual(0, stats.NumActive); + Assert.AreEqual(MAX_CONNECTIONS, stats.NumIdle); + + pool.Shutdown(); + stats = pool.GetStatistics(); + Assert.AreEqual(0, stats.NumActive); + Assert.AreEqual(0, stats.NumIdle); + + } + + [Test] + public void TestBadConnection() + { + int MAX_CONNECTIONS = 3; + ObjectPoolConfiguration config = new ObjectPoolConfiguration(); + config.MaxObjects=(MAX_CONNECTIONS); + config.MaxIdle=(MAX_CONNECTIONS); + config.MinIdle=(MAX_CONNECTIONS); + config.MinEvictableIdleTimeMillis=(60*1000); + config.MaxWait=(60*1000); + MyTestConnectionFactory fact = new MyTestConnectionFactory(); + + ObjectPool pool = new ObjectPool(fact,config); + + //borrow first connection and return + MyTestConnection conn = pool.BorrowObject(); + Assert.AreEqual(1, fact.TotalCreatedConnections); + pool.ReturnObject(conn); + Assert.AreEqual(1, fact.TotalCreatedConnections); + + //re-borrow same connection and return + conn = pool.BorrowObject(); + Assert.AreEqual(1, fact.TotalCreatedConnections); + pool.ReturnObject(conn); + Assert.AreEqual(1, fact.TotalCreatedConnections); + + //dispose and make sure we get a new connection + conn.Dispose(); + conn = pool.BorrowObject(); + Assert.AreEqual(2, fact.TotalCreatedConnections); + pool.ReturnObject(conn); + Assert.AreEqual(2, fact.TotalCreatedConnections); + } + + [Test] + public void TestIdleCleanup() + { + ObjectPoolConfiguration config = new ObjectPoolConfiguration(); + config.MaxObjects=(3); + config.MaxIdle=(2); + config.MinIdle=(1); + config.MinEvictableIdleTimeMillis=(3000); + config.MaxWait=(60*1000); + MyTestConnectionFactory fact = new MyTestConnectionFactory(); + + ObjectPool pool = new ObjectPool(fact,config); + + MyTestConnection conn1 = (MyTestConnection)pool.BorrowObject(); + MyTestConnection conn2 = (MyTestConnection)pool.BorrowObject(); + MyTestConnection conn3 = (MyTestConnection)pool.BorrowObject(); + + Assert.AreEqual(3, fact.TotalCreatedConnections); + pool.ReturnObject(conn1); + Assert.AreEqual(1, pool.GetStatistics().NumIdle); + pool.ReturnObject(conn2); + Assert.AreEqual(2, pool.GetStatistics().NumIdle); + pool.ReturnObject(conn3); + Assert.AreEqual(2, pool.GetStatistics().NumIdle); + Assert.AreEqual(false, conn1.IsGood); + Assert.AreEqual(true, conn2.IsGood); + Assert.AreEqual(true, conn3.IsGood); + Thread.Sleep(((int)(config.MinEvictableIdleTimeMillis+1000))); + MyTestConnection conn4 = (MyTestConnection)pool.BorrowObject(); + Assert.AreSame(conn3, conn4); + Assert.AreEqual(false, conn1.IsGood); + Assert.AreEqual(false, conn2.IsGood); + Assert.AreEqual(true, conn3.IsGood); + Assert.AreEqual(true, conn4.IsGood); + } + + [Test] + public void TestCreateBadConnection() + { + MyTestConnectionFactory fact = new MyTestConnectionFactory(); + fact.CreateBadConnection=(true); + + ObjectPool pool = new ObjectPool(fact,new ObjectPoolConfiguration()); + try { + pool.BorrowObject(); + Assert.Fail("expected exception"); + } + catch (ConnectorException e) { + Assert.AreEqual("Connection is bad", e.Message); + } + } + } +} diff --git a/FrameworkTests/ObjectSerializationTests.cs b/FrameworkTests/ObjectSerializationTests.cs new file mode 100644 index 00000000..8a554e41 --- /dev/null +++ b/FrameworkTests/ObjectSerializationTests.cs @@ -0,0 +1,1082 @@ +/* + * Copyright 2008 Sun Microsystems, Inc. All rights reserved. + * + * U.S. Government Rights - Commercial software. Government users + * are subject to the Sun Microsystems, Inc. standard license agreement + * and applicable provisions of the FAR and its supplements. + * + * Use is subject to license terms. + * + * This distribution may include materials developed by third parties. + * Sun, Sun Microsystems, the Sun logo, Java and Project Identity + * Connectors are trademarks or registered trademarks of Sun + * Microsystems, Inc. or its subsidiaries in the U.S. and other + * countries. + * + * UNIX is a registered trademark in the U.S. and other countries, + * exclusively licensed through X/Open Company, Ltd. + * + * ----------- + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER. + * + * Copyright 2008 Sun Microsystems, Inc. All rights reserved. + * + * The contents of this file are subject to the terms of the Common Development + * and Distribution License(CDDL) (the License). You may not use this file + * except in compliance with the License. + * + * You can obtain a copy of the License at + * http://identityconnectors.dev.java.net/CDDLv1.0.html + * See the License for the specific language governing permissions and + * limitations under the License. + * + * When distributing the Covered Code, include this CDDL Header Notice in each + * file and include the License file at identityconnectors/legal/license.txt. + * If applicable, add the following below this CDDL Header, with the fields + * enclosed by brackets [] replaced by your own identifying information: + * "Portions Copyrighted [year] [name of copyright owner]" + * ----------- + */ +using System; +using System.IO; +using System.Text; +using NUnit.Framework; +using NUnit.Framework.SyntaxHelpers; +using System.Collections.Generic; +using System.Globalization; +using System.Security; +using System.Linq; +using System.Xml; +using Org.IdentityConnectors.Framework.Impl.Serializer.Binary; +using Org.IdentityConnectors.Common; +using Org.IdentityConnectors.Common.Pooling; +using Org.IdentityConnectors.Common.Security; +using Org.IdentityConnectors.Framework.Api; +using Org.IdentityConnectors.Framework.Api.Operations; +using Org.IdentityConnectors.Framework.Common; +using Org.IdentityConnectors.Framework.Common.Exceptions; +using Org.IdentityConnectors.Framework.Common.Objects; +using Org.IdentityConnectors.Framework.Common.Objects.Filters; +using Org.IdentityConnectors.Framework.Common.Serializer; +using Org.IdentityConnectors.Framework.Impl.Api; +using Org.IdentityConnectors.Framework.Impl.Api.Remote; +using Org.IdentityConnectors.Framework.Impl.Api.Remote.Messages; +using Org.IdentityConnectors.Framework.Impl.Serializer.Xml; +namespace FrameworkTests +{ + [TestFixture] + public class ObjectSerializationTests + { + [Test] + public void TestBoolean() + { + bool v1 = true; + bool v2 = (bool)CloneObject(v1); + Assert.AreEqual(v1, v2); + + v1 = false; + v2 = (bool)CloneObject(v1); + Assert.AreEqual(v1, v2); + } + [Test] + public void TestCharacter() { + char v1 = 'h'; + char v2 = (char)CloneObject(v1); + Assert.AreEqual(v1, v2); + } + [Test] + public void TestInteger() { + int v1 = 12345; + int v2 = (int)CloneObject(v1); + Assert.AreEqual(v1,v2); + + v1 = Int32.MinValue; + v2 = (int)CloneObject(v1); + Assert.AreEqual(v1,v2); + + v1 = Int32.MaxValue; + v2 = (int)CloneObject(v1); + Assert.AreEqual(v1,v2); + + v1 = -1; + v2 = (int)CloneObject(v1); + Assert.AreEqual(v1,v2); + } + + [Test] + public void TestLong() { + long v1 = 12345; + long v2 = (long)CloneObject(v1); + Assert.AreEqual(v1,v2); + + v1 = Int64.MinValue; + v2 = (long)CloneObject(v1); + Assert.AreEqual(v1,v2); + + v1 = Int64.MaxValue; + v2 = (long)CloneObject(v1); + Assert.AreEqual(v1,v2); + + v1 = -1; + v2 = (long)CloneObject(v1); + Assert.AreEqual(v1,v2); + } + + [Test] + public void TestFloat() { + float v1 = 1.1F; + float v2 = (float)CloneObject(v1); + Assert.IsTrue(!Object.ReferenceEquals(v1,v2)); + Assert.AreEqual(v1,v2); + + v1 = Single.Epsilon; + v2 = (float)CloneObject(v1); + Assert.AreEqual(v1,v2); + + v1 = Single.NaN; + v2 = (float)CloneObject(v1); + Assert.AreEqual(v1,v2); + + v1 = Single.NegativeInfinity; + v2 = (float)CloneObject(v1); + Assert.AreEqual(v1,v2); + + v1 = Single.PositiveInfinity; + v2 = (float)CloneObject(v1); + Assert.AreEqual(v1,v2); + + v1 = Single.MinValue; + v2 = (float)CloneObject(v1); + Assert.AreEqual(v1,v2); + + v1 = Single.MaxValue; + v2 = (float)CloneObject(v1); + Assert.AreEqual(v1,v2); + } + + [Test] + public void TestDouble() { + double v1 = 1.1; + double v2 = (double)CloneObject(v1); + Assert.IsTrue(!Object.ReferenceEquals(v1,v2)); + Assert.AreEqual(v1,v2); + + v1 = Double.Epsilon; + v2 = (double)CloneObject(v1); + Assert.AreEqual(v1,v2); + + v1 = Double.NaN; + v2 = (double)CloneObject(v1); + Assert.AreEqual(v1,v2); + + v1 = Double.NegativeInfinity; + v2 = (double)CloneObject(v1); + Assert.AreEqual(v1,v2); + + v1 = Double.PositiveInfinity; + v2 = (double)CloneObject(v1); + Assert.AreEqual(v1,v2); + + v1 = Double.MaxValue; + v2 = (double)CloneObject(v1); + Assert.AreEqual(v1,v2); + + v1 = Double.MinValue; + v2 = (double)CloneObject(v1); + Assert.AreEqual(v1,v2); + } + + [Test] + public void TestString() { + string v1 = "abcd"; + string v2 = (string)CloneObject(v1); + Assert.IsTrue(!Object.ReferenceEquals(v1,v2)); + Assert.AreEqual(v1,v2); + } + + [Test] + public void TestURI() { + Uri v1 = new Uri("mailto:java-net@java.sun.com"); + Uri v2 = (Uri)CloneObject(v1); + Assert.IsTrue(!Object.ReferenceEquals(v1,v2)); + Assert.AreEqual(v1, v2); + } + + [Test] + public void TestFile() { + FileName v1 = new FileName("c:/foo.txt"); + FileName v2 = (FileName)CloneObject(v1); + Assert.IsTrue(!Object.ReferenceEquals(v1,v2)); + Assert.AreEqual(v1, v2); + } + + [Test] + public void TestBigInteger() { + BigInteger v1 = new BigInteger("983423442347324324324"); + BigInteger v2 = (BigInteger)CloneObject(v1); + Assert.AreEqual(v1, v2); + } + + [Test] + public void TestBigDecimal() { + BigDecimal v1 = new BigDecimal(new BigInteger("9847324324324"),45); + BigDecimal v2 = (BigDecimal)CloneObject(v1); + Assert.AreEqual(v1, v2); + } + + [Test] + public void TestByteArray() { + byte [] v1 = {1,2,3}; + byte [] v2 = (byte[])CloneObject(v1); + Assert.AreEqual(3,v2.Length); + Assert.AreEqual(1,v2[0]); + Assert.AreEqual(2,v2[1]); + Assert.AreEqual(3,v2[2]); + Assert.AreEqual(v1,v2); + } + + [Test] + public void TestClasses() { + Assert.AreEqual(typeof(bool), + CloneObject(typeof(bool))); + Assert.AreEqual(typeof(bool?), + CloneObject(typeof(bool?))); + Assert.AreEqual(typeof(bool[][]), + CloneObject(typeof(bool[][]))); + Assert.AreEqual(typeof(bool?[][]), + CloneObject(typeof(bool?[][]))); + Assert.AreEqual(typeof(char), + CloneObject(typeof(char))); + Assert.AreEqual(typeof(char?), + CloneObject(typeof(char?))); + //if this fails, we have added a new type and we need to add + //a serializer for it + Assert.IsTrue( + CollectionUtil.SetsEqual( + CollectionUtil.NewSet(FrameworkUtil.GetAllSupportedConfigTypes()), + (ICollection)CloneObject(FrameworkUtil.GetAllSupportedConfigTypes()))); + //if this fails, we have added a new type and we need to add + //a serializer for it + Assert.IsTrue( + CollectionUtil.SetsEqual( + CollectionUtil.NewSet(FrameworkUtil.GetAllSupportedAttributeTypes()), + (ICollection)CloneObject(FrameworkUtil.GetAllSupportedAttributeTypes()))); + //if this fails, need to add to the OperationMappings class + Assert.IsTrue( + CollectionUtil.SetsEqual( + CollectionUtil.NewSet(FrameworkUtil.AllAPIOperations()), + (ICollection)CloneObject(FrameworkUtil.AllAPIOperations()))); + + } + + [Test] + public void TestArrays() { + int [] v1 = {1,2,3}; + int [] v2 = (int[])CloneObject(v1); + Assert.AreEqual(3, v2.Length); + Assert.AreEqual(1,v2[0]); + Assert.AreEqual(2,v2[1]); + Assert.AreEqual(3,v2[2]); + } + + + [Test] + public void TestObjectArrays() { + object [] v1 = {"1","2","3"}; + object [] v2 = (object[])CloneObject(v1); + Assert.AreEqual(3, v2.Length); + Assert.AreEqual("1",v2[0]); + Assert.AreEqual("2",v2[1]); + Assert.AreEqual("3",v2[2]); + } + + [Test] + public void TestDictionary() { + IDictionary map = new Dictionary(); + map["key1"] = "val1"; + map["key2"] = "val2"; + IDictionary map2 = (IDictionary)CloneObject(map); + Assert.AreEqual("val1",map["key1"]); + Assert.AreEqual("val2",map["key2"]); + + IDictionary map3 = new Dictionary(); + map3["key1"] = "val1"; + map3["key2"] = "val2"; + IDictionary map4 = (IDictionary)CloneObject(map3); + Assert.AreEqual("val1",map4["key1"]); + Assert.AreEqual("val2",map4["key2"]); + } + + [Test] + public void TestList() { + IList v1 = new List(); + v1.Add("val1"); + v1.Add("val2"); + IList v2 = (IList)CloneObject(v1); + Assert.AreEqual(2,v2.Count); + Assert.AreEqual("val1",v2[0]); + Assert.AreEqual("val2",v2[1]); + + IList v3 = new List(); + v3.Add("val1"); + v3.Add("val2"); + + IList v4 = (IList)CloneObject(v3); + Assert.AreEqual(2,v4.Count); + Assert.AreEqual("val1",v4[0]); + Assert.AreEqual("val2",v4[1]); + } + + [Test] + public void TestSet() { + //underneath the covers, this creates an + //ICollection - we need to test that ICollection + //becomes a Set + ICollection v1 = + CollectionUtil.NewReadOnlySet("val1","val2"); + HashSet v2 = (HashSet)CloneObject(v1); + Assert.AreEqual(2,v2.Count); + Assert.IsTrue(v2.Contains("val1")); + Assert.IsTrue(v2.Contains("val2")); + + HashSet v3 = new HashSet(); + v3.Add("val1"); + v3.Add("val2"); + + HashSet v4 = (HashSet)CloneObject(v3); + Assert.AreEqual(2,v4.Count); + Assert.IsTrue(v4.Contains("val1")); + Assert.IsTrue(v4.Contains("val2")); + } + + [Test] + public void TestCultureInfo() { + //use this one since it uses all 3 fields of locale + CultureInfo v1 = new CultureInfo("nn-NO"); + CultureInfo v2 = (CultureInfo)CloneObject(v1); + Assert.AreEqual(v1,v2); + } + + [Test] + public void TestObjectPoolConfiguration() { + ObjectPoolConfiguration v1 = new ObjectPoolConfiguration(); + v1.MaxObjects = 1; + v1.MaxIdle = 2; + v1.MaxWait = 3; + v1.MinEvictableIdleTimeMillis = 4; + v1.MinIdle = 5; + ObjectPoolConfiguration v2 = + (ObjectPoolConfiguration)CloneObject(v1); + Assert.IsTrue(!Object.ReferenceEquals(v1,v2)); + + Assert.AreEqual(v1,v2); + + Assert.AreEqual(1, v2.MaxObjects); + Assert.AreEqual(2, v2.MaxIdle); + Assert.AreEqual(3, v2.MaxWait); + Assert.AreEqual(4, v2.MinEvictableIdleTimeMillis); + Assert.AreEqual(5, v2.MinIdle); + } + [Test] + public void TestConfigurationProperty() { + ConfigurationPropertyImpl v1 = new ConfigurationPropertyImpl(); + v1.Order = (1); + v1.IsConfidential=(true); + v1.Name=("foo"); + v1.HelpMessageKey=("help key"); + v1.DisplayMessageKey=("display key"); + v1.Value=("bar"); + v1.ValueType=(typeof(string)); + + ConfigurationPropertyImpl v2 = (ConfigurationPropertyImpl) + CloneObject(v1); + Assert.AreEqual(1, v2.Order); + Assert.IsTrue(v2.IsConfidential); + Assert.AreEqual("foo", v2.Name); + Assert.AreEqual("help key", v2.HelpMessageKey); + Assert.AreEqual("display key", v2.DisplayMessageKey); + Assert.AreEqual("bar", v2.Value); + Assert.AreEqual(typeof(string), v2.ValueType); + } + + [Test] + public void TestConfigurationProperties() { + ConfigurationPropertyImpl prop1 = new ConfigurationPropertyImpl(); + prop1.Order = (1); + prop1.IsConfidential=(true); + prop1.Name=("foo"); + prop1.HelpMessageKey=("help key"); + prop1.DisplayMessageKey=("display key"); + prop1.Value=("bar"); + prop1.ValueType=(typeof(string)); + + ConfigurationPropertiesImpl v1 = new ConfigurationPropertiesImpl(); + v1.Properties=(CollectionUtil.NewReadOnlyList(prop1)); + v1.SetPropertyValue("foo", "bar"); + + ConfigurationPropertiesImpl v2 = (ConfigurationPropertiesImpl) + CloneObject(v1); + Assert.AreEqual("bar", v2.GetProperty("foo").Value); + } + + [Test] + public void TestAPIConfiguration() { + ConfigurationPropertyImpl prop1 = new ConfigurationPropertyImpl(); + prop1.Order = (1); + prop1.IsConfidential=(true); + prop1.Name=("foo"); + prop1.HelpMessageKey=("help key"); + prop1.DisplayMessageKey=("display key"); + prop1.Value=("bar"); + prop1.ValueType=(typeof(string)); + + ConfigurationPropertiesImpl props1 = new ConfigurationPropertiesImpl(); + props1.Properties=(CollectionUtil.NewReadOnlyList(prop1)); + + APIConfigurationImpl v1 = new APIConfigurationImpl(); + v1.ConnectorPoolConfiguration=(new ObjectPoolConfiguration()); + v1.ConfigurationProperties=(props1); + v1.IsConnectorPoolingSupported=(true); + v1.CultureInfo=(new CultureInfo("en")); + v1.ProducerBufferSize=(200); + v1.SupportedOperations=(FrameworkUtil.AllAPIOperations()); + IDictionary map = + CollectionUtil.NewDictionary(typeof(CreateApiOp),6); + v1.TimeoutMap=(map); + + APIConfigurationImpl v2 = (APIConfigurationImpl) + CloneObject(v1); + Assert.IsTrue(!Object.ReferenceEquals(v1, v2)); + Assert.IsNotNull(v2.ConnectorPoolConfiguration); + Assert.IsNotNull(v2.ConfigurationProperties); + Assert.AreEqual(v1.ConnectorPoolConfiguration,v2.ConnectorPoolConfiguration); + Assert.AreEqual(v1.ConfigurationProperties,v2.ConfigurationProperties); + Assert.IsTrue(v2.IsConnectorPoolingSupported); + Assert.AreEqual(new CultureInfo("en"), v2.CultureInfo); + Assert.AreEqual(200, v2.ProducerBufferSize); + Assert.IsTrue(CollectionUtil.SetsEqual( + FrameworkUtil.AllAPIOperations(), + v2.SupportedOperations)); + Assert.AreEqual(map, v2.TimeoutMap); + } + + [Test] + public void TestConnectorMessages() { + ConnectorMessagesImpl v1 = new ConnectorMessagesImpl(); + IDictionary defaultMap = new Dictionary(); + defaultMap["key1"]="val1"; + IDictionary> messages = + new Dictionary>(); + messages[new CultureInfo("en")]=defaultMap; + messages[new CultureInfo("")]=defaultMap; + v1.Catalogs=(messages); + + ConnectorMessagesImpl v2 = (ConnectorMessagesImpl) + CloneObject(v1); + Assert.IsTrue( + CollectionUtil.SetsEqual(messages[new CultureInfo("")], + v2.Catalogs[new CultureInfo("")])); + Assert.IsTrue( + CollectionUtil.SetsEqual(messages[new CultureInfo("en")], + v2.Catalogs[new CultureInfo("en")])); + } + + [Test] + public void TestRemoteConnectorInfo() { + RemoteConnectorInfoImpl v1 = new RemoteConnectorInfoImpl(); + v1.Messages=(new ConnectorMessagesImpl()); + v1.ConnectorKey=(new ConnectorKey("my bundle", + "my version", + "my connector")); + ConfigurationPropertiesImpl configProperties = new ConfigurationPropertiesImpl(); + configProperties.Properties=(new List()); + APIConfigurationImpl apiImpl = new APIConfigurationImpl(); + apiImpl.ConfigurationProperties=(configProperties); + v1.DefaultAPIConfiguration=(apiImpl); + v1.ConnectorDisplayNameKey=("mykey"); + + RemoteConnectorInfoImpl v2 = (RemoteConnectorInfoImpl) + CloneObject(v1); + + Assert.IsNotNull(v2.Messages); + Assert.AreEqual("my bundle", v2.ConnectorKey.BundleName); + Assert.AreEqual("my version", v2.ConnectorKey.BundleVersion); + Assert.AreEqual("my connector", v2.ConnectorKey.ConnectorName); + Assert.AreEqual("mykey",v2.ConnectorDisplayNameKey); + Assert.IsNotNull(v2.DefaultAPIConfiguration); + } + + [Test] + public void TestAttribute() { + ConnectorAttribute v1 = ConnectorAttributeBuilder.Build("foo", "val1", "val2"); + ConnectorAttribute v2 = (ConnectorAttribute)CloneObject(v1); + Assert.AreEqual(v1,v2); + } + + [Test] + public void TestAttributeInfo() { + + ConnectorAttributeInfoBuilder builder = new ConnectorAttributeInfoBuilder(); + builder.Name=("foo"); + builder.ValueType=(typeof(String)); + builder.Required=(true); + builder.Readable=(true); + builder.Writeable=(true); + builder.MultiValue=(true); + builder.ReturnedByDefault = false; + ConnectorAttributeInfo v1 = builder.Build(); + ConnectorAttributeInfo v2 = (ConnectorAttributeInfo)CloneObject(v1); + Assert.AreEqual(v1,v2); + Assert.AreEqual("foo", v2.Name); + Assert.AreEqual(typeof(String), v2.ValueType); + Assert.IsTrue(v2.IsMultiValue); + Assert.IsTrue(v2.IsReadable); + Assert.IsTrue(v2.IsRequired); + Assert.IsTrue(v2.IsWritable); + Assert.IsFalse(v2.IsReturnedByDefault); + } + + [Test] + public void TestConnectorObject() { + ConnectorObjectBuilder bld = new ConnectorObjectBuilder(); + bld.SetUid("foo"); + bld.SetName("fooToo"); + + ConnectorObject v1 = bld.Build(); + ConnectorObject v2 = (ConnectorObject)CloneObject(v1); + + Assert.AreEqual(v1, v2); + } + + [Test] + public void TestName() { + Name v1 = new Name("Test"); + Name v2 = (Name)CloneObject(v1); + Assert.AreEqual(v1, v2); + } + + [Test] + public void TestObjectClass() { + ObjectClass v1 = new ObjectClass("test"); + ObjectClass v2 = (ObjectClass)CloneObject(v1); + Assert.AreEqual(v1, v2); + } + + [Test] + public void TestObjectClassInfo() { + ConnectorAttributeInfoBuilder builder = new ConnectorAttributeInfoBuilder(); + builder.Name=("foo"); + builder.ValueType=(typeof(String)); + builder.Required=(true); + builder.Readable=(true); + builder.Writeable=(true); + builder.MultiValue=(true); + ObjectClassInfoBuilder obld = new ObjectClassInfoBuilder(); + obld.ObjectType = ObjectClass.ORGANIZATION_NAME; + obld.AddAttributeInfo(builder.Build()); + ObjectClassInfo v1 = obld.Build(); + ObjectClassInfo v2 = (ObjectClassInfo)CloneObject(v1); + Assert.AreEqual(v1, v2); + } + + [Test] + public void TestUid() { + Uid v1 = new Uid("test"); + Uid v2 = (Uid)CloneObject(v1); + Assert.AreEqual(v1, v2); + } + + [Test] + public void testOperationOptionInfo() { + OperationOptionInfo v1 = + new OperationOptionInfo("name",typeof(int?)); + OperationOptionInfo v2 = + (OperationOptionInfo)CloneObject(v1); + Assert.AreEqual(v1, v2); + } + + [Test] + public void TestSchema() { + OperationOptionInfo opInfo = + new OperationOptionInfo("name",typeof(int?)); + ObjectClassInfoBuilder bld = new ObjectClassInfoBuilder(); + bld.ObjectType = ObjectClass.ORGANIZATION_NAME; + ObjectClassInfo info = bld.Build(); + ICollection temp = CollectionUtil.NewSet(info); + IDictionary> map = + new Dictionary>(); + map[typeof(CreateApiOp)] = temp; + ICollection temp2 = CollectionUtil.NewSet(opInfo); + IDictionary> map2 = + new Dictionary>(); + map2[typeof(CreateApiOp)] = temp2; + Schema v1 = new Schema(CollectionUtil.NewSet(info), + CollectionUtil.NewSet(opInfo), + map, + map2); + Schema v2 = (Schema)CloneObject(v1); + Assert.AreEqual(v1, v2); + Assert.AreEqual(info, v2.ObjectClassInfo.First()); + Assert.AreEqual(1, v2.SupportedObjectClassesByOperation.Count); + Assert.AreEqual(1, v2.SupportedOptionsByOperation.Count); + Assert.AreEqual(1, v2.OperationOptionInfo.Count); + } + + [Test] + public void TestContainsFilter() { + ContainsFilter v1 = new ContainsFilter(ConnectorAttributeBuilder.Build("foo", "bar")); + ContainsFilter v2 = (ContainsFilter)CloneObject(v1); + Assert.AreEqual(v1.GetAttribute(), v2.GetAttribute()); + } + + [Test] + public void TestAndFilter() { + ContainsFilter left1 = new ContainsFilter(ConnectorAttributeBuilder.Build("foo", "bar")); + ContainsFilter right1 = new ContainsFilter(ConnectorAttributeBuilder.Build("foo2", "bar2")); + AndFilter v1 = new AndFilter(left1,right1); + AndFilter v2 = (AndFilter)CloneObject(v1); + ContainsFilter left2 = (ContainsFilter)v2.Left; + ContainsFilter right2 = (ContainsFilter)v2.Right; + Assert.AreEqual(left1.GetAttribute(),left2.GetAttribute()); + Assert.AreEqual(right1.GetAttribute(),right2.GetAttribute()); + } + + [Test] + public void TestEndsWithFilter() { + EndsWithFilter v1 = new EndsWithFilter(ConnectorAttributeBuilder.Build("foo", "bar")); + EndsWithFilter v2 = (EndsWithFilter)CloneObject(v1); + Assert.AreEqual(v1.GetAttribute(), v2.GetAttribute()); + } + + [Test] + public void TestEqualsFilter() { + EqualsFilter v1 = new EqualsFilter(ConnectorAttributeBuilder.Build("foo", "bar")); + EqualsFilter v2 = (EqualsFilter)CloneObject(v1); + Assert.AreEqual(v1.GetAttribute(), v2.GetAttribute()); + } + + [Test] + public void TestGreaterThanFilter() { + GreaterThanFilter v1 = new GreaterThanFilter(ConnectorAttributeBuilder.Build("foo", "bar")); + GreaterThanFilter v2 = (GreaterThanFilter)CloneObject(v1); + Assert.AreEqual(v1.GetAttribute(), v2.GetAttribute()); + } + + [Test] + public void TestGreaterThanOrEqualFilter() { + GreaterThanOrEqualFilter v1 = new GreaterThanOrEqualFilter(ConnectorAttributeBuilder.Build("foo", "bar")); + GreaterThanOrEqualFilter v2 = (GreaterThanOrEqualFilter)CloneObject(v1); + Assert.AreEqual(v1.GetAttribute(), v2.GetAttribute()); + } + + [Test] + public void TestLessThanFilter() { + LessThanFilter v1 = new LessThanFilter(ConnectorAttributeBuilder.Build("foo", "bar")); + LessThanFilter v2 = (LessThanFilter)CloneObject(v1); + Assert.AreEqual(v1.GetAttribute(), v2.GetAttribute()); + } + + [Test] + public void TestLessThanOrEqualFilter() { + LessThanOrEqualFilter v1 = new LessThanOrEqualFilter(ConnectorAttributeBuilder.Build("foo", "bar")); + LessThanOrEqualFilter v2 = (LessThanOrEqualFilter)CloneObject(v1); + Assert.AreEqual(v1.GetAttribute(), v2.GetAttribute()); + } + + [Test] + public void TestNotFilter() { + ContainsFilter left1 = new ContainsFilter(ConnectorAttributeBuilder.Build("foo", "bar")); + NotFilter v1 = new NotFilter(left1); + NotFilter v2 = (NotFilter)CloneObject(v1); + ContainsFilter left2 = (ContainsFilter)v2.Filter; + Assert.AreEqual(left1.GetAttribute(),left2.GetAttribute()); + } + + [Test] + public void TestOrFilter() { + ContainsFilter left1 = new ContainsFilter(ConnectorAttributeBuilder.Build("foo", "bar")); + ContainsFilter right1 = new ContainsFilter(ConnectorAttributeBuilder.Build("foo2", "bar2")); + OrFilter v1 = new OrFilter(left1,right1); + OrFilter v2 = (OrFilter)CloneObject(v1); + ContainsFilter left2 = (ContainsFilter)v2.Left; + ContainsFilter right2 = (ContainsFilter)v2.Right; + Assert.AreEqual(left1.GetAttribute(),left2.GetAttribute()); + Assert.AreEqual(right1.GetAttribute(),right2.GetAttribute()); + } + + [Test] + public void TestStartsWithFilter() { + StartsWithFilter v1 = new StartsWithFilter(ConnectorAttributeBuilder.Build("foo", "bar")); + StartsWithFilter v2 = (StartsWithFilter)CloneObject(v1); + Assert.AreEqual(v1.GetAttribute(), v2.GetAttribute()); + } + + [Test] + public void TestContainsAllValuesFilter() { + ContainsAllValuesFilter v1 = new ContainsAllValuesFilter(ConnectorAttributeBuilder.Build("foo", "bar", "foo")); + ContainsAllValuesFilter v2 = (ContainsAllValuesFilter)CloneObject(v1); + Assert.AreEqual(v1.GetAttribute(), v2.GetAttribute()); + } + + [Test] + public void TestExceptions() { + { + AlreadyExistsException v1 = new AlreadyExistsException("ex"); + AlreadyExistsException v2 = (AlreadyExistsException)CloneObject(v1); + Assert.AreEqual("ex",v2.Message); + } + + { + ConfigurationException v1 = new ConfigurationException("ex"); + ConfigurationException v2 = (ConfigurationException)CloneObject(v1); + Assert.AreEqual("ex",v2.Message); + } + + { + ConnectionBrokenException v1 = new ConnectionBrokenException("ex"); + ConnectionBrokenException v2 = (ConnectionBrokenException)CloneObject(v1); + Assert.AreEqual("ex",v2.Message); + } + + { + ConnectionFailedException v1 = new ConnectionFailedException("ex"); + ConnectionFailedException v2 = (ConnectionFailedException)CloneObject(v1); + Assert.AreEqual("ex",v2.Message); + } + + { + ConnectorException v1 = new ConnectorException("ex"); + ConnectorException v2 = (ConnectorException)CloneObject(v1); + Assert.AreEqual("ex",v2.Message); + } + { + ConnectorIOException v1 = new ConnectorIOException("ex"); + ConnectorIOException v2 = (ConnectorIOException)CloneObject(v1); + Assert.AreEqual("ex",v2.Message); + } + { + ConnectorSecurityException v1 = new ConnectorSecurityException("ex"); + ConnectorSecurityException v2 = (ConnectorSecurityException)CloneObject(v1); + Assert.AreEqual("ex",v2.Message); + } + + { + InvalidCredentialException v1 = new InvalidCredentialException("ex"); + InvalidCredentialException v2 = (InvalidCredentialException)CloneObject(v1); + Assert.AreEqual("ex",v2.Message); + } + + { + InvalidPasswordException v1 = new InvalidPasswordException("ex"); + InvalidPasswordException v2 = (InvalidPasswordException)CloneObject(v1); + Assert.AreEqual("ex",v2.Message); + } + + { + OperationTimeoutException v1 = new OperationTimeoutException("ex"); + OperationTimeoutException v2 = (OperationTimeoutException)CloneObject(v1); + Assert.AreEqual("ex",v2.Message); + } + + { + PermissionDeniedException v1 = new PermissionDeniedException("ex"); + PermissionDeniedException v2 = (PermissionDeniedException)CloneObject(v1); + Assert.AreEqual("ex",v2.Message); + } + + { + UnknownUidException v1 = new UnknownUidException("ex"); + UnknownUidException v2 = (UnknownUidException)CloneObject(v1); + Assert.AreEqual("ex",v2.Message); + } + + { + ArgumentException v1 = new ArgumentException("my msg"); + Exception v2 = (Exception)CloneObject(v1); + Assert.AreEqual("my msg", v2.Message); + } + + { + Exception v1 = new Exception("my msg2"); + Exception v2 = (Exception)CloneObject(v1); + Assert.AreEqual("my msg2", v2.Message); + } + + } + + [Test] + public void TestUpdateApiOpType() { + UpdateApiType v1 = UpdateApiType.ADD; + UpdateApiType v2 = (UpdateApiType)CloneObject(v1); + + Assert.AreEqual(v1, v2); + } + + + [Test] + public void TestHelloRequest() { + HelloRequest v1 = new HelloRequest(); + HelloRequest v2 = (HelloRequest)CloneObject(v1); + Assert.IsNotNull(v2); + } + + [Test] + public void TestHelloResponse() { + RemoteConnectorInfoImpl info = new RemoteConnectorInfoImpl(); + info.Messages=(new ConnectorMessagesImpl()); + info.ConnectorKey=(new ConnectorKey("my bundle", + "my version", + "my connector")); + ConfigurationPropertiesImpl configProperties = new ConfigurationPropertiesImpl(); + configProperties.Properties=(new List()); + APIConfigurationImpl apiImpl = new APIConfigurationImpl(); + apiImpl.ConfigurationProperties=(configProperties); + info.DefaultAPIConfiguration=(apiImpl); + info.ConnectorDisplayNameKey=("mykey"); + + Exception ex = new Exception("foo"); + HelloResponse v1 = new HelloResponse(ex,CollectionUtil.NewReadOnlyList(info)); + HelloResponse v2 = (HelloResponse)CloneObject(v1); + Assert.IsNotNull(v2.Exception); + Assert.IsNotNull(v2.ConnectorInfos.First()); + } + + [Test] + public void TestOperationRequest() { + ConfigurationPropertiesImpl configProperties = new ConfigurationPropertiesImpl(); + configProperties.Properties=(new List()); + APIConfigurationImpl apiImpl = new APIConfigurationImpl(); + apiImpl.ConfigurationProperties=(configProperties); + + IList args = new List(); + args.Add("my arg"); + OperationRequest v1 = new + OperationRequest(new ConnectorKey("my bundle", + "my version", + "my connector"), + apiImpl, + typeof(CreateApiOp), + args); + OperationRequest v2 = (OperationRequest)CloneObject(v1); + Assert.AreEqual("my bundle", v2.ConnectorKey.BundleName); + Assert.AreEqual("my version", v2.ConnectorKey.BundleVersion); + Assert.AreEqual("my connector", v2.ConnectorKey.ConnectorName); + Assert.IsNotNull(v2.Configuration); + Assert.AreEqual(typeof(CreateApiOp), v2.Operation); + Assert.IsTrue( + CollectionUtil.Equals( + args, v2.Arguments)); + } + + [Test] + public void TestOperationResponseEnd() { + OperationResponseEnd v1 = new OperationResponseEnd(); + OperationResponseEnd v2 = (OperationResponseEnd)CloneObject(v1); + Assert.IsNotNull(v2); + } + [Test] + public void TestOperationResponsePart() { + Exception ex = new Exception("foo"); + OperationResponsePart v1 = new OperationResponsePart(ex,"bar"); + OperationResponsePart v2 = (OperationResponsePart)CloneObject(v1); + Assert.IsNotNull(v2.Exception); + Assert.AreEqual("bar", v2.Result); + } + + [Test] + public void TestOperationResponsePause() { + OperationResponsePause v1 = new OperationResponsePause(); + OperationResponsePause v2 = (OperationResponsePause)CloneObject(v1); + Assert.IsNotNull(v2); + } + + [Test] + public void TestOperationRequestMoreData() { + OperationRequestMoreData v1 = new OperationRequestMoreData(); + OperationRequestMoreData v2 = (OperationRequestMoreData)CloneObject(v1); + Assert.IsNotNull(v2); + } + + [Test] + public void TestOperationRequestStopData() { + OperationRequestStopData v1 = new OperationRequestStopData(); + OperationRequestStopData v2 = (OperationRequestStopData)CloneObject(v1); + Assert.IsNotNull(v2); + } + + [Test] + public void TestEchoMessage() { + EchoMessage v1 = new EchoMessage("test","xml"); + EchoMessage v2 = (EchoMessage)CloneObject(v1); + Assert.AreEqual("test", v2.Object); + Assert.AreEqual("xml", v2.ObjectXml); + } + + [Test] + public void TestOperationOptions() { + OperationOptionsBuilder builder = new OperationOptionsBuilder(); + builder.SetOption("foo", "bar"); + builder.SetOption("foo2", "bar2"); + OperationOptions v1 = builder.Build(); + OperationOptions v2 = (OperationOptions)CloneObject(v1); + Assert.AreEqual(2,v2.Options.Count); + Assert.AreEqual("bar",v2.Options["foo"]); + Assert.AreEqual("bar2",v2.Options["foo2"]); + } + + [Test] + public void TestScriptContext() { + ScriptContextBuilder builder = new ScriptContextBuilder(); + builder.ScriptLanguage=("language"); + builder.ScriptText=("text"); + builder.AddScriptArgument("foo", "bar"); + builder.AddScriptArgument("foo2", "bar2"); + ScriptContext v1 = builder.Build(); + ScriptContext v2 = (ScriptContext)CloneObject(v1); + Assert.AreEqual(2,v2.ScriptArguments.Count); + Assert.AreEqual("bar",v2.ScriptArguments["foo"]); + Assert.AreEqual("bar2",v2.ScriptArguments["foo2"]); + Assert.AreEqual("language",v2.ScriptLanguage); + Assert.AreEqual("text",v2.ScriptText); + } + [Test] + public void TestSyncDeltaType() { + SyncDeltaType v1 = SyncDeltaType.DELETE; + SyncDeltaType v2 = (SyncDeltaType)CloneObject(v1); + Assert.AreEqual(v1, v2); + } + + [Test] + public void TestSyncToken() { + SyncToken v1 = new SyncToken("mytoken"); + SyncToken v2 = (SyncToken)CloneObject(v1); + Assert.AreEqual(v1.Value,v2.Value); + Assert.AreEqual(v1,v2); + } + + [Test] + public void TestSyncDelta() { + SyncDeltaBuilder builder = new SyncDeltaBuilder(); + builder.Uid=(new Uid("myuid")); + builder.DeltaType=(SyncDeltaType.CREATE); + builder.Token=(new SyncToken("mytoken")); + builder.AddAttribute(ConnectorAttributeBuilder.Build("foo", "bar")); + SyncDelta v1 = builder.Build(); + SyncDelta v2 = (SyncDelta)CloneObject(v1); + Assert.AreEqual(new Uid("myuid"),v2.Uid); + Assert.AreEqual(new SyncToken("mytoken"),v2.Token); + Assert.AreEqual(SyncDeltaType.CREATE,v2.DeltaType); + ICollection attributes = v2.Attributes; + Assert.AreEqual(1,attributes.Count); + ConnectorAttribute attr = attributes.First(); + Assert.AreEqual("foo",attr.Name); + Assert.AreEqual(v1,v2); + } + + [Test] + public void TestNull() { + Object v1 = null; + Object v2 = CloneObject(v1); + Assert.IsNull(v2); + } + + [Test] + public void TestGuardedString() { + GuardedString v1 = new GuardedString(); + v1.AppendChar('f'); + v1.AppendChar('o'); + v1.AppendChar('o'); + v1.AppendChar('b'); + v1.AppendChar('a'); + v1.AppendChar('r'); + GuardedString v2 = (GuardedString)CloneObject(v1); + Assert.AreEqual("foobar",DecryptToString(v2)); + } + + /** + * Highly insecure method! Do not do this in production + * code. This is only for test purposes + */ + private String DecryptToString(GuardedString str) { + StringBuilder buf = new StringBuilder(); + str.Access( + array=> + { + for ( int i = 0 ; i < array.Length; i++) + { + buf.Append(array[i]); + } + }); + return buf.ToString(); + } + + protected virtual Object CloneObject(Object o) { + return SerializerUtil.CloneObject(o); + } + } + [TestFixture] + public class XmlSerializationTests : ObjectSerializationTests + { + protected override Object CloneObject(Object o) + { + String xml = SerializerUtil.SerializeXmlObject(o, true); + Console.WriteLine(xml); + o = SerializerUtil.DeserializeXmlObject(xml, true); + + //pass through a list to make sure dtd correctly defines all xml objects + List list = new List(); + list.Add(o); + xml = SerializerUtil.SerializeXmlObject(list, true); + Console.WriteLine(xml); + IList rv = (IList)SerializerUtil.DeserializeXmlObject(xml, true); + return rv[0]; + } + + [Test] + public void TestMultiObject() { + ObjectSerializerFactory factory = ObjectSerializerFactory.GetInstance(); + StringWriter sw = new StringWriter(); + XmlObjectSerializer ser = factory.NewXmlSerializer(sw,true,true); + ser.WriteObject("foo"); + ser.WriteObject("bar"); + ser.Close(true); + String xml = sw.ToString(); + Console.WriteLine(xml); + IList results = new List(); + factory.DeserializeXmlStream(new StringReader(xml), + o=>{ + results.Add(o); + return true; + }, + true); + Assert.AreEqual(2,results.Count); + Assert.AreEqual("foo",results[0]); + Assert.AreEqual("bar",results[1]); + } + + [Test] + public void TestWriter() + { + XmlWriterSettings settings = new XmlWriterSettings(); + settings.Indent = true; + settings.OmitXmlDeclaration = true; + XmlWriter writer = XmlWriter.Create(Console.Out, settings); + + // Write the book element. + writer.WriteStartElement("book"); + writer.WriteEndElement(); + writer.Close(); + // Write the title element. + //writer.WriteStartElement("title"); + //writer.WriteString("Pride And Prejudice<<"); + //writer.WriteEndElement(); + + // Write the close tag for the root element. + //writer.WriteEndElement(); + + // Write the XML and close the writer. + //writer.Close(); + + } + } +} diff --git a/FrameworkTests/ProxyTests.cs b/FrameworkTests/ProxyTests.cs new file mode 100644 index 00000000..81762048 --- /dev/null +++ b/FrameworkTests/ProxyTests.cs @@ -0,0 +1,117 @@ +/* + * Copyright 2008 Sun Microsystems, Inc. All rights reserved. + * + * U.S. Government Rights - Commercial software. Government users + * are subject to the Sun Microsystems, Inc. standard license agreement + * and applicable provisions of the FAR and its supplements. + * + * Use is subject to license terms. + * + * This distribution may include materials developed by third parties. + * Sun, Sun Microsystems, the Sun logo, Java and Project Identity + * Connectors are trademarks or registered trademarks of Sun + * Microsystems, Inc. or its subsidiaries in the U.S. and other + * countries. + * + * UNIX is a registered trademark in the U.S. and other countries, + * exclusively licensed through X/Open Company, Ltd. + * + * ----------- + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER. + * + * Copyright 2008 Sun Microsystems, Inc. All rights reserved. + * + * The contents of this file are subject to the terms of the Common Development + * and Distribution License(CDDL) (the License). You may not use this file + * except in compliance with the License. + * + * You can obtain a copy of the License at + * http://identityconnectors.dev.java.net/CDDLv1.0.html + * See the License for the specific language governing permissions and + * limitations under the License. + * + * When distributing the Covered Code, include this CDDL Header Notice in each + * file and include the License file at identityconnectors/legal/license.txt. + * If applicable, add the following below this CDDL Header, with the fields + * enclosed by brackets [] replaced by your own identifying information: + * "Portions Copyrighted [year] [name of copyright owner]" + * ----------- + */ +using System; +using NUnit.Framework; +using NUnit.Framework.SyntaxHelpers; +using System.Reflection; +using System.Reflection.Emit; +using Org.IdentityConnectors.Common.Proxy; +using System.Collections.Generic; +using Org.IdentityConnectors.Common; +namespace FrameworkTests +{ + public interface MyTestInterface + { + string TestProperty {get;set;} + + String TestTwoArgs(string val1, string val2); + void TestVoid(IList list); + int TestPrimitive(int arg); + DateTime TestStruct(DateTime arg); + } + + [TestFixture] + public class ProxyTests + { + public class MyHandler : InvocationHandler + { + private string _testProperty; + + public Object Invoke(Object proxy, MethodInfo method, Object [] args) + { + if (method.Name.Equals("TestTwoArgs")) + { + return ""+method.Name+" "+args[0]+" "+args[1]; + } + else if ( method.Name.Equals("TestVoid") ) + { + IList arg = (IList)args[0]; + arg.Add("my void result"); + return null; + } + else if ( method.Name.Equals("get_TestProperty") ) + { + return _testProperty; + } + else if ( method.Name.Equals("set_TestProperty") ) + { + _testProperty = (string)args[0]; + return null; + } + else + { + return args[0]; + } + } + } + + [Test] + public void TestProxy() + { + InvocationHandler handler = new MyHandler(); + + MyTestInterface inter = + (MyTestInterface)Proxy.NewProxyInstance(typeof(MyTestInterface), + handler); + Assert.AreEqual("TestTwoArgs foo bar", + inter.TestTwoArgs("foo","bar")); + IList temp = new List(); + inter.TestVoid(temp); + Assert.AreEqual("my void result",temp[0]); + Assert.AreEqual(10,inter.TestPrimitive(10)); + Assert.AreEqual(1000L,inter.TestStruct(new DateTime(1000)).Ticks); + inter.TestProperty = "my property"; + Assert.AreEqual("my property",inter.TestProperty); + } + + } + + +} diff --git a/FrameworkTests/ScriptTests.cs b/FrameworkTests/ScriptTests.cs new file mode 100644 index 00000000..fdd33cd6 --- /dev/null +++ b/FrameworkTests/ScriptTests.cs @@ -0,0 +1,79 @@ +/* + * Copyright 2008 Sun Microsystems, Inc. All rights reserved. + * + * U.S. Government Rights - Commercial software. Government users + * are subject to the Sun Microsystems, Inc. standard license agreement + * and applicable provisions of the FAR and its supplements. + * + * Use is subject to license terms. + * + * This distribution may include materials developed by third parties. + * Sun, Sun Microsystems, the Sun logo, Java and Project Identity + * Connectors are trademarks or registered trademarks of Sun + * Microsystems, Inc. or its subsidiaries in the U.S. and other + * countries. + * + * UNIX is a registered trademark in the U.S. and other countries, + * exclusively licensed through X/Open Company, Ltd. + * + * ----------- + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER. + * + * Copyright 2008 Sun Microsystems, Inc. All rights reserved. + * + * The contents of this file are subject to the terms of the Common Development + * and Distribution License(CDDL) (the License). You may not use this file + * except in compliance with the License. + * + * You can obtain a copy of the License at + * http://identityconnectors.dev.java.net/CDDLv1.0.html + * See the License for the specific language governing permissions and + * limitations under the License. + * + * When distributing the Covered Code, include this CDDL Header Notice in each + * file and include the License file at identityconnectors/legal/license.txt. + * If applicable, add the following below this CDDL Header, with the fields + * enclosed by brackets [] replaced by your own identifying information: + * "Portions Copyrighted [year] [name of copyright owner]" + * ----------- + */ +using System; +using System.Collections.Generic; +using System.Reflection; +using Org.IdentityConnectors.Common; +using Org.IdentityConnectors.Common.Script; +using NUnit.Framework; +using NUnit.Framework.SyntaxHelpers; + +namespace FrameworkTests +{ + /// + /// Description of ScriptTests. + /// + [TestFixture] + public class ScriptTests + { + [Test] + public void testBooScripting() { + ScriptExecutorFactory factory = ScriptExecutorFactory.NewInstance("BOO"); + ScriptExecutor exe = factory.NewScriptExecutor(new Assembly[0],"x", false); + IDictionary vals = new Dictionary(); + vals["x"] = 1; + Assert.AreEqual(1, exe.Execute(vals)); + vals["x"] = 2; + Assert.AreEqual(2, exe.Execute(vals)); + } + [Test] + public void testShellScripting() { + ScriptExecutorFactory factory = ScriptExecutorFactory.NewInstance("Shell"); + ScriptExecutor exe = factory.NewScriptExecutor(new Assembly[0],"echo bob", false); + IDictionary vals = new Dictionary(); + Assert.AreEqual(0, exe.Execute(vals)); + } + [Test] + [ExpectedException(typeof(ArgumentException))] + public void testUnsupported() { + ScriptExecutorFactory.NewInstance("fadsflkj"); + } + } +} diff --git a/FrameworkTests/TestHelperTests.cs b/FrameworkTests/TestHelperTests.cs new file mode 100644 index 00000000..76da0a73 --- /dev/null +++ b/FrameworkTests/TestHelperTests.cs @@ -0,0 +1,89 @@ +/* + * Copyright 2008 Sun Microsystems, Inc. All rights reserved. + * + * U.S. Government Rights - Commercial software. Government users + * are subject to the Sun Microsystems, Inc. standard license agreement + * and applicable provisions of the FAR and its supplements. + * + * Use is subject to license terms. + * + * This distribution may include materials developed by third parties. + * Sun, Sun Microsystems, the Sun logo, Java and Project Identity + * Connectors are trademarks or registered trademarks of Sun + * Microsystems, Inc. or its subsidiaries in the U.S. and other + * countries. + * + * UNIX is a registered trademark in the U.S. and other countries, + * exclusively licensed through X/Open Company, Ltd. + * + * ----------- + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER. + * + * Copyright 2008 Sun Microsystems, Inc. All rights reserved. + * + * The contents of this file are subject to the terms of the Common Development + * and Distribution License(CDDL) (the License). You may not use this file + * except in compliance with the License. + * + * You can obtain a copy of the License at + * http://identityconnectors.dev.java.net/CDDLv1.0.html + * See the License for the specific language governing permissions and + * limitations under the License. + * + * When distributing the Covered Code, include this CDDL Header Notice in each + * file and include the License file at identityconnectors/legal/license.txt. + * If applicable, add the following below this CDDL Header, with the fields + * enclosed by brackets [] replaced by your own identifying information: + * "Portions Copyrighted [year] [name of copyright owner]" + * ----------- + */ +using System; +using System.IO; +using System.Xml; +using System.Collections.Generic; + +using NUnit.Framework; +using NUnit.Framework.SyntaxHelpers; + +using Org.IdentityConnectors.Framework.Test; + +namespace FrameworkTests +{ + /// + /// Description of TestHelperTests. + /// + [TestFixture] + public class TestHelperTests + { + [Test] + public void testLoadProperties() + { + string tmpFn = Path.GetTempFileName(); + try { + // create some xml text + TextWriter stringWriter = new StringWriter(); + XmlTextWriter w = new XmlTextWriter(stringWriter); + w.WriteStartElement("configuration"); + w.WriteStartElement("property"); + w.WriteAttributeString("name", "bob"); + w.WriteAttributeString("value", "bobsValue"); + w.WriteEndElement(); + w.WriteStartElement("property"); + w.WriteAttributeString("name", "bob2"); + w.WriteAttributeString("value", "bob2sValue"); + w.WriteEndElement(); + w.Close(); + File.WriteAllText(tmpFn, stringWriter.ToString()); + // load the properties files + IDictionary dict =TestHelpers.LoadPropertiesFile(tmpFn); + Assert.AreEqual(dict["bob"], "bobsValue"); + } finally { + File.Delete(tmpFn); + } + } + [Test] + public void testGetProperties() { + Assert.IsTrue(TestHelpers.GetProperty("Help", null).Equals("Me")); + } + } +} diff --git a/FrameworkTests/TestUtil.cs b/FrameworkTests/TestUtil.cs new file mode 100644 index 00000000..586a763b --- /dev/null +++ b/FrameworkTests/TestUtil.cs @@ -0,0 +1,70 @@ +/* + * Copyright 2008 Sun Microsystems, Inc. All rights reserved. + * + * U.S. Government Rights - Commercial software. Government users + * are subject to the Sun Microsystems, Inc. standard license agreement + * and applicable provisions of the FAR and its supplements. + * + * Use is subject to license terms. + * + * This distribution may include materials developed by third parties. + * Sun, Sun Microsystems, the Sun logo, Java and Project Identity + * Connectors are trademarks or registered trademarks of Sun + * Microsystems, Inc. or its subsidiaries in the U.S. and other + * countries. + * + * UNIX is a registered trademark in the U.S. and other countries, + * exclusively licensed through X/Open Company, Ltd. + * + * ----------- + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER. + * + * Copyright 2008 Sun Microsystems, Inc. All rights reserved. + * + * The contents of this file are subject to the terms of the Common Development + * and Distribution License(CDDL) (the License). You may not use this file + * except in compliance with the License. + * + * You can obtain a copy of the License at + * http://identityconnectors.dev.java.net/CDDLv1.0.html + * See the License for the specific language governing permissions and + * limitations under the License. + * + * When distributing the Covered Code, include this CDDL Header Notice in each + * file and include the License file at identityconnectors/legal/license.txt. + * If applicable, add the following below this CDDL Header, with the fields + * enclosed by brackets [] replaced by your own identifying information: + * "Portions Copyrighted [year] [name of copyright owner]" + * ----------- + */ +using System; +using System.Diagnostics; + +namespace FrameworkTests +{ + /// + /// Description of TestUtil. + /// + public static class TestUtil + { + private static readonly object LOCK = new Object(); + private static bool _initialized; + + + public static void InitializeLogging() + { + lock(LOCK) + { + if (!_initialized) + { + ConsoleTraceListener listener = + new ConsoleTraceListener(); + listener.TraceOutputOptions = + TraceOptions.ThreadId|TraceOptions.Timestamp; + Trace.Listeners.Add(listener); + _initialized = true; + } + } + } + } +} diff --git a/FrameworkTests/UpdateImplTests.cs b/FrameworkTests/UpdateImplTests.cs new file mode 100644 index 00000000..1cdcf8ed --- /dev/null +++ b/FrameworkTests/UpdateImplTests.cs @@ -0,0 +1,327 @@ +/* + * Copyright 2008 Sun Microsystems, Inc. All rights reserved. + * + * U.S. Government Rights - Commercial software. Government users + * are subject to the Sun Microsystems, Inc. standard license agreement + * and applicable provisions of the FAR and its supplements. + * + * Use is subject to license terms. + * + * This distribution may include materials developed by third parties. + * Sun, Sun Microsystems, the Sun logo, Java and Project Identity + * Connectors are trademarks or registered trademarks of Sun + * Microsystems, Inc. or its subsidiaries in the U.S. and other + * countries. + * + * UNIX is a registered trademark in the U.S. and other countries, + * exclusively licensed through X/Open Company, Ltd. + * + * ----------- + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER. + * + * Copyright 2008 Sun Microsystems, Inc. All rights reserved. + * + * The contents of this file are subject to the terms of the Common Development + * and Distribution License(CDDL) (the License). You may not use this file + * except in compliance with the License. + * + * You can obtain a copy of the License at + * http://identityconnectors.dev.java.net/CDDLv1.0.html + * See the License for the specific language governing permissions and + * limitations under the License. + * + * When distributing the Covered Code, include this CDDL Header Notice in each + * file and include the License file at identityconnectors/legal/license.txt. + * If applicable, add the following below this CDDL Header, with the fields + * enclosed by brackets [] replaced by your own identifying information: + * "Portions Copyrighted [year] [name of copyright owner]" + * ----------- + */ +using System; +using System.Security; +using NUnit.Framework; +using NUnit.Framework.SyntaxHelpers; +using System.Collections.Generic; +using Org.IdentityConnectors.Common; +using Org.IdentityConnectors.Framework.Api; +using Org.IdentityConnectors.Framework.Api.Operations; +using Org.IdentityConnectors.Framework.Impl.Api.Local.Operations; +using Org.IdentityConnectors.Framework.Common.Objects; +namespace FrameworkTests +{ + /// + /// Description of UpdateImplTests. + /// + [TestFixture] + public class UpdateImplTests + { + [Test] + [ExpectedException(typeof(ArgumentNullException ))] + public void ValidateObjectClassArg() { + UpdateImpl.ValidateInput(UpdateApiType.ADD, null, new HashSet()); + } + + [Test] + [ExpectedException(typeof(ArgumentNullException ))] + public void ValidateAttrsArg() { + UpdateImpl.ValidateInput(UpdateApiType.ADD, ObjectClass.ACCOUNT, null); + } + + [Test] + [ExpectedException(typeof(ArgumentException ))] + public void ValidateNoUidAttribute() { + UpdateImpl.ValidateInput(UpdateApiType.ADD, ObjectClass.ACCOUNT, new HashSet()); + } + + [Test] + [ExpectedException(typeof(ArgumentException ))] + public void ValidateAddWithNullAttribute() { + ICollection attrs = new HashSet(); + attrs.Add(ConnectorAttributeBuilder.Build("something")); + UpdateImpl.ValidateInput(UpdateApiType.ADD, ObjectClass.ACCOUNT, attrs); + } + + [Test] + [ExpectedException(typeof(ArgumentException ))] + public void ValidateDeleteWithNullAttribute() { + ICollection attrs = new HashSet(); + attrs.Add(ConnectorAttributeBuilder.Build("something")); + UpdateImpl.ValidateInput(UpdateApiType.DELETE, ObjectClass.ACCOUNT, attrs); + } + + [Test] + [ExpectedException(typeof(ArgumentException))] + public void ValidateAttemptToAddName() { + ICollection attrs = new HashSet(); + attrs.Add(new Name("fadf")); + attrs.Add(new Uid(1 + "")); + UpdateImpl.ValidateInput(UpdateApiType.ADD, ObjectClass.ACCOUNT, attrs); + } + + [Test] + [ExpectedException(typeof(ArgumentException))] + public void ValidateAttemptToDeleteName() { + ICollection attrs = new HashSet(); + attrs.Add(new Name("fadf")); + attrs.Add(new Uid(1 + "")); + UpdateImpl.ValidateInput(UpdateApiType.DELETE, ObjectClass.ACCOUNT, attrs); + } + + [Test] + public void ValidateAttemptToAddDeleteOperationalAttribute() { + // list of all the operational attributes.. + ICollection list = new List(); + list.Add(ConnectorAttributeBuilder.BuildEnabled(false)); + list.Add(ConnectorAttributeBuilder.BuildLockOut(true)); + list.Add(ConnectorAttributeBuilder.BuildCurrentPassword(newSecureString("fadsf"))); + list.Add(ConnectorAttributeBuilder.BuildPasswordExpirationDate(DateTime.Now)); + list.Add(ConnectorAttributeBuilder.BuildPassword(newSecureString("fadsf"))); + foreach (ConnectorAttribute attr in list) { + ICollection attrs = new HashSet(); + attrs.Add(attr); + attrs.Add(new Uid(1 + "")); + try { + UpdateImpl.ValidateInput(UpdateApiType.DELETE, ObjectClass.ACCOUNT, attrs); + Assert.Fail("Failed: " + attr.Name); + } catch (ArgumentException) { + // this is a good thing.. + } + } + } + + private static SecureString newSecureString(string password) { + SecureString rv = new SecureString(); + foreach (char c in password.ToCharArray()) { + rv.AppendChar(c); + } + return rv; + } + + [Test] + [ExpectedException(typeof(ArgumentException))] + public void ValidateAttemptToAddNull() { + ICollection attrs = new HashSet(); + attrs.Add(ConnectorAttributeBuilder.Build("something w/ null")); + attrs.Add(new Uid(1 + "")); + UpdateImpl.ValidateInput(UpdateApiType.ADD, ObjectClass.ACCOUNT, attrs); + } + + [Test] + [ExpectedException(typeof(ArgumentException))] + public void ValidateAttemptToDeleteNull() { + ICollection attrs = new HashSet(); + attrs.Add(ConnectorAttributeBuilder.Build("something w/ null")); + attrs.Add(new Uid(1 + "")); + UpdateImpl.ValidateInput(UpdateApiType.DELETE, ObjectClass.ACCOUNT, attrs); + } + + /// + /// Validate two collections are equal. (Not fast but effective) + /// + public static bool AreEqual(ICollection arg1, + ICollection arg2) { + if (arg1.Count != arg2.Count) { + return false; + } + foreach (ConnectorAttribute attr in arg1) { + if (!arg2.Contains(attr)) { + return false; + } + } + return true; + } + [Test] + public void MergeAddAttribute() { + UpdateImpl up = new UpdateImpl(null, null); + ICollection actual; + ConnectorAttribute uid = new Uid(1 + ""); + ICollection baseAttrs = CollectionUtil.NewSet(uid); + ICollection expected = CollectionUtil.NewSet(uid); + ICollection changeset = CollectionUtil.NewSet(uid); + // attempt to add a value to an attribute.. + ConnectorAttribute cattr = ConnectorAttributeBuilder.Build("abc", 2); + changeset.Add(cattr); + expected.Add(ConnectorAttributeBuilder.Build("abc", 2)); + actual = up.Merge(UpdateApiType.ADD, changeset, baseAttrs); + Assert.IsTrue(AreEqual(expected, actual)); + } + + [Test] + public void MergeAddToExistingAttribute() { + UpdateImpl up = new UpdateImpl(null, null); + ICollection actual; + ConnectorAttribute uid = new Uid(1 + ""); + ICollection baseAttrs = CollectionUtil.NewSet(uid); + ICollection expected = CollectionUtil.NewSet(uid); + ICollection changeset = CollectionUtil.NewSet(uid); + // attempt to add a value to an attribute.. + ConnectorAttribute battr = ConnectorAttributeBuilder.Build("abc", 1); + ConnectorAttribute cattr = ConnectorAttributeBuilder.Build("abc", 2); + baseAttrs.Add(battr); + changeset.Add(cattr); + expected.Add(ConnectorAttributeBuilder.Build("abc", 1, 2)); + actual = up.Merge(UpdateApiType.ADD, changeset, baseAttrs); + Assert.IsTrue(AreEqual(expected, actual)); + } + + [Test] + public void MergeDeleteNonExistentAttribute() { + UpdateImpl up = new UpdateImpl(null, null); + ICollection actual; + ConnectorAttribute uid = new Uid(1 + ""); + ICollection baseAttrs = CollectionUtil.NewSet(uid); + ICollection expected = CollectionUtil.NewSet(uid); + ICollection changeset = CollectionUtil.NewSet(uid); + // attempt to add a value to an attribute.. + ConnectorAttribute cattr = ConnectorAttributeBuilder.Build("abc", 2); + changeset.Add(cattr); + actual = up.Merge(UpdateApiType.DELETE, changeset, baseAttrs); + Assert.IsTrue(AreEqual(expected, actual)); + } + + [Test] + public void MergeDeleteToExistingAttribute() { + UpdateImpl up = new UpdateImpl(null, null); + ICollection actual; + ConnectorAttribute uid = new Uid(1 + ""); + ICollection baseAttrs = CollectionUtil.NewSet(uid); + ICollection expected = CollectionUtil.NewSet(uid); + ICollection changeset = CollectionUtil.NewSet(uid); + // attempt to add a value to an attribute.. + ConnectorAttribute battr = ConnectorAttributeBuilder.Build("abc", 1, 2); + ConnectorAttribute cattr = ConnectorAttributeBuilder.Build("abc", 2); + baseAttrs.Add(battr); + changeset.Add(cattr); + expected.Add(ConnectorAttributeBuilder.Build("abc", 1)); + actual = up.Merge(UpdateApiType.DELETE, changeset, baseAttrs); + Assert.IsTrue(AreEqual(expected, actual)); + } + + [Test] + public void MergeDeleteToExistingAttributeCompletely() { + UpdateImpl up = new UpdateImpl(null, null); + ICollection actual; + ConnectorAttribute uid = new Uid(1 + ""); + ICollection baseAttrs = CollectionUtil.NewSet(uid); + ICollection expected = CollectionUtil.NewSet(uid); + ICollection changeset = CollectionUtil.NewSet(uid); + // attempt to add a value to an attribute.. + ConnectorAttribute battr = ConnectorAttributeBuilder.Build("abc", 1, 2); + ConnectorAttribute cattr = ConnectorAttributeBuilder.Build("abc", 1, 2); + baseAttrs.Add(battr); + changeset.Add(cattr); + expected.Add(ConnectorAttributeBuilder.Build("abc")); + actual = up.Merge(UpdateApiType.DELETE, changeset, baseAttrs); + Assert.IsTrue(AreEqual(expected, actual)); + } + + [Test] + public void MergeReplaceExistingAttribute() { + UpdateImpl up = new UpdateImpl(null, null); + ICollection actual; + ConnectorAttribute uid = new Uid(1 + ""); + ICollection baseAttrs = CollectionUtil.NewSet(uid); + ICollection expected = CollectionUtil.NewSet(uid); + ICollection changeset = CollectionUtil.NewSet(uid); + // attempt to add a value to an attribute.. + ConnectorAttribute battr = ConnectorAttributeBuilder.Build("abc", 1, 2); + ConnectorAttribute cattr = ConnectorAttributeBuilder.Build("abc", 2); + baseAttrs.Add(battr); + changeset.Add(cattr); + expected.Add(ConnectorAttributeBuilder.Build("abc", 2)); + actual = up.Merge(UpdateApiType.REPLACE, changeset, baseAttrs); + Assert.IsTrue(AreEqual(expected, actual)); + } + + [Test] + public void MergeReplaceNonExistentAttribute() { + UpdateImpl up = new UpdateImpl(null, null); + ICollection actual; + ConnectorAttribute uid = new Uid(1 + ""); + ICollection baseAttrs = CollectionUtil.NewSet(uid); + ICollection expected = CollectionUtil.NewSet(uid); + ICollection changeset = CollectionUtil.NewSet(uid); + // attempt to add a value to an attribute.. + ConnectorAttribute cattr = ConnectorAttributeBuilder.Build("abc", 2); + changeset.Add(cattr); + expected.Add(ConnectorAttributeBuilder.Build("abc", 2)); + actual = up.Merge(UpdateApiType.REPLACE, changeset, baseAttrs); + Assert.IsTrue(AreEqual(expected, actual)); + } + + [Test] + public void MergeReplaceAttributeRemoval() { + UpdateImpl up = new UpdateImpl(null, null); + ICollection actual; + ConnectorAttribute uid = new Uid(1 + ""); + ICollection baseAttrs = CollectionUtil.NewSet(uid); + ICollection expected = CollectionUtil.NewSet(uid); + ICollection changeset = CollectionUtil.NewSet(uid); + // attempt to add a value to an attribute.. + ConnectorAttribute battr = ConnectorAttributeBuilder.Build("abc", 1, 2); + ConnectorAttribute cattr = ConnectorAttributeBuilder.Build("abc"); + baseAttrs.Add(battr); + changeset.Add(cattr); + expected.Add(ConnectorAttributeBuilder.Build("abc")); + actual = up.Merge(UpdateApiType.REPLACE, changeset, baseAttrs); + Assert.IsTrue(AreEqual(expected, actual)); + } + + [Test] + public void MergeReplaceSameAttribute() { + UpdateImpl up = new UpdateImpl(null, null); + ICollection actual; + ConnectorAttribute uid = new Uid(1 + ""); + ICollection baseAttrs = CollectionUtil.NewSet(uid); + ICollection expected = CollectionUtil.NewSet(uid); + ICollection changeset = CollectionUtil.NewSet(uid); + // attempt to add a value to an attribute.. + ConnectorAttribute battr = ConnectorAttributeBuilder.Build("abc", 1); + ConnectorAttribute cattr = ConnectorAttributeBuilder.Build("abc", 1); + baseAttrs.Add(battr); + changeset.Add(cattr); + actual = up.Merge(UpdateApiType.REPLACE, changeset, baseAttrs); + Assert.IsTrue(AreEqual(expected, actual)); + } + } +} diff --git a/FrameworkTests/app.config b/FrameworkTests/app.config new file mode 100644 index 00000000..2a6bf2c2 --- /dev/null +++ b/FrameworkTests/app.config @@ -0,0 +1,24 @@ + + + + + + + + + + + + + + + + + + + + + diff --git a/FrameworkTests/project.xml b/FrameworkTests/project.xml new file mode 100644 index 00000000..6f40613f --- /dev/null +++ b/FrameworkTests/project.xml @@ -0,0 +1,5 @@ + + + + + \ No newline at end of file diff --git a/Service/AssemblyInfo.cs b/Service/AssemblyInfo.cs new file mode 100644 index 00000000..5ba7de71 --- /dev/null +++ b/Service/AssemblyInfo.cs @@ -0,0 +1,70 @@ +/* + * Copyright 2008 Sun Microsystems, Inc. All rights reserved. + * + * U.S. Government Rights - Commercial software. Government users + * are subject to the Sun Microsystems, Inc. standard license agreement + * and applicable provisions of the FAR and its supplements. + * + * Use is subject to license terms. + * + * This distribution may include materials developed by third parties. + * Sun, Sun Microsystems, the Sun logo, Java and Project Identity + * Connectors are trademarks or registered trademarks of Sun + * Microsystems, Inc. or its subsidiaries in the U.S. and other + * countries. + * + * UNIX is a registered trademark in the U.S. and other countries, + * exclusively licensed through X/Open Company, Ltd. + * + * ----------- + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER. + * + * Copyright 2008 Sun Microsystems, Inc. All rights reserved. + * + * The contents of this file are subject to the terms of the Common Development + * and Distribution License(CDDL) (the License). You may not use this file + * except in compliance with the License. + * + * You can obtain a copy of the License at + * http://identityconnectors.dev.java.net/CDDLv1.0.html + * See the License for the specific language governing permissions and + * limitations under the License. + * + * When distributing the Covered Code, include this CDDL Header Notice in each + * file and include the License file at identityconnectors/legal/license.txt. + * If applicable, add the following below this CDDL Header, with the fields + * enclosed by brackets [] replaced by your own identifying information: + * "Portions Copyrighted [year] [name of copyright owner]" + * ----------- + */ +#region Using directives + +using System.Reflection; +using System.Runtime.CompilerServices; +using System.Runtime.InteropServices; + +#endregion + +// General Information about an assembly is controlled through the following +// set of attributes. Change these attribute values to modify the information +// associated with an assembly. +[assembly: AssemblyTitle("Service")] +[assembly: AssemblyDescription("")] +[assembly: AssemblyConfiguration("")] +[assembly: AssemblyCompany("")] +[assembly: AssemblyProduct("Service")] +[assembly: AssemblyCopyright("")] +[assembly: AssemblyTrademark("")] +[assembly: AssemblyCulture("")] + +// This sets the default COM visibility of types in the assembly to invisible. +// If you need to expose a type to COM, use [ComVisible(true)] on that type. +[assembly: ComVisible(false)] + +// The assembly version has following format : +// +// Major.Minor.Build.Revision +// +// You can specify all the values or you can use the default the Revision and +// Build Numbers by using the '*' as shown below: +[assembly: AssemblyVersion("1.0.0.0")] diff --git a/Service/Program.cs b/Service/Program.cs new file mode 100644 index 00000000..784ab29a --- /dev/null +++ b/Service/Program.cs @@ -0,0 +1,238 @@ +/* + * Copyright 2008 Sun Microsystems, Inc. All rights reserved. + * + * U.S. Government Rights - Commercial software. Government users + * are subject to the Sun Microsystems, Inc. standard license agreement + * and applicable provisions of the FAR and its supplements. + * + * Use is subject to license terms. + * + * This distribution may include materials developed by third parties. + * Sun, Sun Microsystems, the Sun logo, Java and Project Identity + * Connectors are trademarks or registered trademarks of Sun + * Microsystems, Inc. or its subsidiaries in the U.S. and other + * countries. + * + * UNIX is a registered trademark in the U.S. and other countries, + * exclusively licensed through X/Open Company, Ltd. + * + * ----------- + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER. + * + * Copyright 2008 Sun Microsystems, Inc. All rights reserved. + * + * The contents of this file are subject to the terms of the Common Development + * and Distribution License(CDDL) (the License). You may not use this file + * except in compliance with the License. + * + * You can obtain a copy of the License at + * http://identityconnectors.dev.java.net/CDDLv1.0.html + * See the License for the specific language governing permissions and + * limitations under the License. + * + * When distributing the Covered Code, include this CDDL Header Notice in each + * file and include the License file at identityconnectors/legal/license.txt. + * If applicable, add the following below this CDDL Header, with the fields + * enclosed by brackets [] replaced by your own identifying information: + * "Portions Copyrighted [year] [name of copyright owner]" + * ----------- + */ +using System; +using System.ComponentModel; +using System.Configuration; +using System.Configuration.Install; +using System.Collections.Generic; +using System.Security; +using System.Reflection; +using System.ServiceProcess; +using System.Text; +using Org.IdentityConnectors.Common.Security; + +namespace Org.IdentityConnectors.Framework.Service +{ + + static class Program + { + private const string OPT_SERVICE_NAME="/serviceName"; + + private static void Usage() + { + Console.WriteLine("Usage: Gateway.exe [option], where command is one of the following: "); + Console.WriteLine(" /install [/serviceName ] - Installs the service."); + Console.WriteLine(" /uninstall [/serviceName ] - Uninstalls the service."); + Console.WriteLine(" /run - Runs the service from the console."); + Console.WriteLine(" /setKey [] - Sets the gateway key."); + } + + private static IDictionary ParseOptions(string [] args) + { + IDictionary rv = new Dictionary(); + String serviceName = null; + for ( int i = 1; i < args.Length; i++) { + String opt = args[i].ToLower(); + if (OPT_SERVICE_NAME.ToLower().Equals(opt)) { + i++; + if (i < args.Length) { + serviceName = args[i]; + } + else { + Usage(); + return null; + } + } + else { + Usage(); + return null; + } + } + rv["/serviceName"] = serviceName; + return rv; + } + + /// + /// This method starts the service. + /// + static void Main(string [] args) + { + if ( args.Length == 0 ) + { + //no args - start the service + ServiceBase.Run(new ServiceBase[] { new Service() }); + } + else + { + String cmd = args[0].ToLower(); + if ( cmd.Equals("/setkey") ) { + if ( args.Length > 2 ) { + Usage(); + return; + } + DoSetKey(args.Length>1?args[1]:null); + return; + } + IDictionary options = + ParseOptions(args); + if ( options == null ) { + //there's a parse error in the options, return + return; + } + if ( "/install".Equals(cmd) ) { + DoInstall(options); + } + else if ("/uninstall".Equals(cmd)) { + DoUninstall(options); + } + else if ("/run".Equals(cmd)) { + DoRun(options); + } + else { + Usage(); + return; + } + } + } + + private static void DoInstall(IDictionary options) + { + String serviceName = options[OPT_SERVICE_NAME]; + if ( serviceName != null ) { + ProjectInstaller.ServiceName = serviceName; + } + TransactedInstaller ti = new TransactedInstaller(); + string [] cmdline = + { + Assembly.GetExecutingAssembly ().Location + }; + AssemblyInstaller ai = new AssemblyInstaller( + cmdline[0], + new string[0]); + ti.Installers.Add (ai); + InstallContext ctx = new InstallContext ("install.log", + cmdline ); + ti.Context = ctx ; + ti.Install ( new System.Collections.Hashtable()); + } + + private static void DoUninstall(IDictionary options) + { + String serviceName = options[OPT_SERVICE_NAME]; + if ( serviceName != null ) { + ProjectInstaller.ServiceName = serviceName; + } + TransactedInstaller ti = new TransactedInstaller(); + string [] cmdline = + { + Assembly.GetExecutingAssembly ().Location + }; + AssemblyInstaller ai = new AssemblyInstaller( + cmdline[0], + new string[0]); + ti.Installers.Add (ai); + InstallContext ctx = new InstallContext ("uninstall.log", + cmdline ); + ti.Context = ctx ; + ti.Uninstall ( null); + } + + private static void DoRun(IDictionary options) + { + Service svc = new Service(); + + svc.StartService(new String[0]); + + Console.WriteLine("Press q to shutdown."); + + while (Console.ReadKey().KeyChar != 'q') ; + + svc.StopService(); + } + + private static GuardedString ReadPassword() + { + GuardedString rv = new GuardedString(); + while (true) { + ConsoleKeyInfo info = Console.ReadKey(true); + if ( info.Key == ConsoleKey.Enter ) { + Console.WriteLine(); + rv.MakeReadOnly(); + return rv; + } + else { + Console.Write("*"); + rv.AppendChar(info.KeyChar); + } + } + } + + private static void DoSetKey(string key) + { + GuardedString str; + if ( key == null ) { + Console.Write("Enter Key: "); + GuardedString v1 = ReadPassword(); + Console.Write("Confirm Key: "); + GuardedString v2 = ReadPassword(); + if (!v1.Equals(v2)) { + Console.WriteLine("Error: Key mismatch."); + return; + } + str = v2; + } + else { + str = new GuardedString(); + foreach (char c in key.ToCharArray()) { + str.AppendChar(c); + } + } + Configuration config = + ConfigurationManager.OpenExeConfiguration(ConfigurationUserLevel.None); + config.AppSettings.Settings.Remove(Service.PROP_KEY); + config.AppSettings.Settings.Add(Service.PROP_KEY,str.GetBase64SHA1Hash()); + config.Save(ConfigurationSaveMode.Modified); + Console.WriteLine("Key Updated."); + } + } + + + +} diff --git a/Service/ProjectInstaller.cs b/Service/ProjectInstaller.cs new file mode 100644 index 00000000..df8d1d19 --- /dev/null +++ b/Service/ProjectInstaller.cs @@ -0,0 +1,73 @@ +/* + * Copyright 2008 Sun Microsystems, Inc. All rights reserved. + * + * U.S. Government Rights - Commercial software. Government users + * are subject to the Sun Microsystems, Inc. standard license agreement + * and applicable provisions of the FAR and its supplements. + * + * Use is subject to license terms. + * + * This distribution may include materials developed by third parties. + * Sun, Sun Microsystems, the Sun logo, Java and Project Identity + * Connectors are trademarks or registered trademarks of Sun + * Microsystems, Inc. or its subsidiaries in the U.S. and other + * countries. + * + * UNIX is a registered trademark in the U.S. and other countries, + * exclusively licensed through X/Open Company, Ltd. + * + * ----------- + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER. + * + * Copyright 2008 Sun Microsystems, Inc. All rights reserved. + * + * The contents of this file are subject to the terms of the Common Development + * and Distribution License(CDDL) (the License). You may not use this file + * except in compliance with the License. + * + * You can obtain a copy of the License at + * http://identityconnectors.dev.java.net/CDDLv1.0.html + * See the License for the specific language governing permissions and + * limitations under the License. + * + * When distributing the Covered Code, include this CDDL Header Notice in each + * file and include the License file at identityconnectors/legal/license.txt. + * If applicable, add the following below this CDDL Header, with the fields + * enclosed by brackets [] replaced by your own identifying information: + * "Portions Copyrighted [year] [name of copyright owner]" + * ----------- + */ +using System; +using System.Collections.Generic; +using System.ComponentModel; +using System.Configuration.Install; +using System.ServiceProcess; + +namespace Org.IdentityConnectors.Framework.Service +{ + [RunInstaller(true)] + public class ProjectInstaller : Installer + { + public static string ServiceName { get; set; } + + static ProjectInstaller() + { + ServiceName = "Connector Gateway"; + } + + private ServiceProcessInstaller serviceProcessInstaller; + private ServiceInstaller serviceInstaller; + + public ProjectInstaller() + { + serviceProcessInstaller = new ServiceProcessInstaller(); + serviceInstaller = new ServiceInstaller(); + + // Here you can set properties on serviceProcessInstaller or register event handlers + serviceProcessInstaller.Account = ServiceAccount.LocalSystem; + + serviceInstaller.ServiceName = ServiceName; + this.Installers.AddRange(new Installer[] { serviceProcessInstaller, serviceInstaller }); + } + } +} diff --git a/Service/Service.cs b/Service/Service.cs new file mode 100644 index 00000000..b4f13c1f --- /dev/null +++ b/Service/Service.cs @@ -0,0 +1,194 @@ +/* + * Copyright 2008 Sun Microsystems, Inc. All rights reserved. + * + * U.S. Government Rights - Commercial software. Government users + * are subject to the Sun Microsystems, Inc. standard license agreement + * and applicable provisions of the FAR and its supplements. + * + * Use is subject to license terms. + * + * This distribution may include materials developed by third parties. + * Sun, Sun Microsystems, the Sun logo, Java and Project Identity + * Connectors are trademarks or registered trademarks of Sun + * Microsystems, Inc. or its subsidiaries in the U.S. and other + * countries. + * + * UNIX is a registered trademark in the U.S. and other countries, + * exclusively licensed through X/Open Company, Ltd. + * + * ----------- + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER. + * + * Copyright 2008 Sun Microsystems, Inc. All rights reserved. + * + * The contents of this file are subject to the terms of the Common Development + * and Distribution License(CDDL) (the License). You may not use this file + * except in compliance with the License. + * + * You can obtain a copy of the License at + * http://identityconnectors.dev.java.net/CDDLv1.0.html + * See the License for the specific language governing permissions and + * limitations under the License. + * + * When distributing the Covered Code, include this CDDL Header Notice in each + * file and include the License file at identityconnectors/legal/license.txt. + * If applicable, add the following below this CDDL Header, with the fields + * enclosed by brackets [] replaced by your own identifying information: + * "Portions Copyrighted [year] [name of copyright owner]" + * ----------- + */ +using System; +using System.Collections.Generic; +using System.Collections.Specialized; +using System.ComponentModel; +using System.Configuration; +using System.Data; +using System.Diagnostics; +using System.IO; +using System.Reflection; +using System.Security.Cryptography.X509Certificates; +using System.ServiceProcess; +using System.Text; +using Org.IdentityConnectors.Common; +using Org.IdentityConnectors.Framework.Server; + +namespace Org.IdentityConnectors.Framework.Service +{ + public class Service : ServiceBase + { + private const string PROP_PORT = "gateway.port"; + private const string PROP_SSL = "gateway.usessl"; + private const string PROP_CERTSTORE = "gateway.certificatestorename"; + private const string PROP_IFADDRESS = "gateway.ifaddress"; + public const string PROP_KEY = "gateway.key"; + + private ConnectorServer _server; + + public Service() + { + } + + + /// + /// Clean up any resources being used. + /// + protected override void Dispose(bool disposing) + { + base.Dispose(disposing); + } + + private void initializeCurrentDirectory() + { + Assembly assembly = + Assembly.GetExecutingAssembly(); + FileInfo thisAssemblyFile = + new FileInfo(assembly.Location); + DirectoryInfo directory = + thisAssemblyFile.Directory; + Environment.CurrentDirectory = + directory.FullName; + + } + + private NameValueCollection GetApplicationSettings() + { + return ConfigurationManager.AppSettings; + } + + private X509Certificate GetCertificate() + { + NameValueCollection settings = + GetApplicationSettings(); + String storeName = settings.Get(PROP_CERTSTORE); + if (storeName == null) { + throw new Org.IdentityConnectors.Framework.Common.Exceptions.ConfigurationException("Missing required configuration setting: "+PROP_CERTSTORE); + } + + X509Store store = new X509Store(storeName, + StoreLocation.LocalMachine); + + store.Open(OpenFlags.ReadOnly|OpenFlags.OpenExistingOnly); + X509CertificateCollection certificates = store.Certificates; + if ( certificates.Count != 1 ) { + throw new Org.IdentityConnectors.Framework.Common.Exceptions.ConfigurationException("There is supported to be exactly one certificate in the store: "+storeName); + } + X509Certificate certificate = store.Certificates[0]; + store.Close(); + return certificate; + } + + public void StartService(string [] args) + { + OnStart(args); + } + + /// + /// Start this service. + /// + protected override void OnStart(string[] args) + { + try { + initializeCurrentDirectory(); + Trace.TraceInformation("Starting connector gateway: "+Environment.CurrentDirectory); + NameValueCollection settings = + GetApplicationSettings(); + String portStr = + settings.Get(PROP_PORT); + if ( portStr == null ) { + throw new Org.IdentityConnectors.Framework.Common.Exceptions.ConfigurationException("Missing required configuration property: "+PROP_PORT); + } + String keyHash = settings.Get(PROP_KEY); + if ( keyHash == null ) { + throw new Org.IdentityConnectors.Framework.Common.Exceptions.ConfigurationException("Missing required configuration property: "+PROP_KEY); + } + + int port = Int32.Parse(portStr); + bool useSSL = Boolean.Parse(settings.Get(PROP_SSL)??"false"); + _server = ConnectorServer.NewInstance(); + _server.Port = port; + _server.UseSSL = useSSL; + _server.KeyHash = keyHash; + if (useSSL) { + _server.ServerCertificate = + GetCertificate(); + } + String ifaddress = settings.Get(PROP_IFADDRESS); + if ( ifaddress != null ) { + _server.IfAddress = + IOUtil.GetIPAddress(ifaddress); + } + _server.Start(); + Trace.TraceInformation("Started connector gateway"); + } + catch (Exception e) { + TraceUtil.TraceException("Exception occured starting gateway", + e); + throw; + } + } + + public void StopService() + { + OnStop(); + } + + + /// + /// Stop this service. + /// + protected override void OnStop() + { + try { + Trace.TraceInformation("Stopping connector gateway"); + if (_server != null) { + _server.Stop(); + } + Trace.TraceInformation("Stopped connector gateway"); + } + catch (Exception e) { + TraceUtil.TraceException("Exception occured stopping gateway", + e); + } + } + } +} diff --git a/Service/Service.csproj b/Service/Service.csproj new file mode 100644 index 00000000..29ff113c --- /dev/null +++ b/Service/Service.csproj @@ -0,0 +1,118 @@ + + + + {A9D6374A-D51F-4FA3-8C02-5B1D23FAA82E} + Debug + AnyCPU + Exe + Sun.OpenConnectors.Framework.Service + Gateway + v3.5 + False + false + + + ./bin/Debug/ + true + Full + False + True + DEBUG;TRACE + Exe + Gateway + False + 4 + + + ./bin/Release/ + False + None + True + False + TRACE + Exe + Gateway + False + 4 + + + False + Auto + 4194304 + AnyCPU + 4096 + + + + + + + + 3.5 + + + + + + + + + + + + + + + + + + + {F140E8DA-52B4-4159-992A-9DA10EA8EEFB} + Common + + + {5B011775-B121-4EEE-A410-BA2D2F5BFB8B} + FrameworkInternal + + + {8B24461B-456A-4032-89A1-CD418F7B5B62} + Framework + + + \ No newline at end of file diff --git a/Service/app.config b/Service/app.config new file mode 100644 index 00000000..2778427c --- /dev/null +++ b/Service/app.config @@ -0,0 +1,32 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/ServiceInstall/ExtBuild.proj b/ServiceInstall/ExtBuild.proj new file mode 100644 index 00000000..f14e633c --- /dev/null +++ b/ServiceInstall/ExtBuild.proj @@ -0,0 +1,78 @@ + + + + Debug + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/ServiceInstall/File.bottom b/ServiceInstall/File.bottom new file mode 100644 index 00000000..8de6ef93 --- /dev/null +++ b/ServiceInstall/File.bottom @@ -0,0 +1,6 @@ + + + + + + \ No newline at end of file diff --git a/ServiceInstall/File.top b/ServiceInstall/File.top new file mode 100644 index 00000000..8a8ce3e6 --- /dev/null +++ b/ServiceInstall/File.top @@ -0,0 +1,21 @@ + + + + + + + + + + + + diff --git a/ServiceInstall/ServiceInstall.wixproj b/ServiceInstall/ServiceInstall.wixproj new file mode 100644 index 00000000..da1ad161 --- /dev/null +++ b/ServiceInstall/ServiceInstall.wixproj @@ -0,0 +1,72 @@ + + + + {1CBA8F74-050C-432B-8437-08BD13BDC684} + Debug + AnyCPU + Package + ServiceInstall + ServiceInstall + $(WIX_HOME) + $(WixToolPath)\wix.targets + $(WixToolPath)\WixTasks.dll + ICE45 + WixUILicenseRtf=license.rtf + + + bin\Debug\ + True + Full + False + + + bin\Release\ + False + None + True + + + + + + + + + + \ No newline at end of file diff --git a/ServiceInstall/Setup.wxs b/ServiceInstall/Setup.wxs new file mode 100644 index 00000000..9343638a --- /dev/null +++ b/ServiceInstall/Setup.wxs @@ -0,0 +1,50 @@ + + + + + + + + + + + + + + + + + + diff --git a/ServiceInstall/license.rtf b/ServiceInstall/license.rtf new file mode 100644 index 00000000..5ecc7cf2 --- /dev/null +++ b/ServiceInstall/license.rtf @@ -0,0 +1,30 @@ +{\rtf1\ansi\deff0\adeflang1025 +{\fonttbl{\f0\froman\fprq2\fcharset0 Thorndale{\*\falt Times New Roman};}{\f1\froman\fprq2\fcharset0 Thorndale{\*\falt Times New Roman};}{\f2\fswiss\fprq2\fcharset0 Albany{\*\falt Arial};}{\f3\fnil\fprq2\fcharset0 Andale Sans UI{\*\falt Arial Unicode MS};}{\f4\fnil\fprq2\fcharset0 Tahoma;}{\f5\fnil\fprq0\fcharset0 Tahoma;}} +{\colortbl;\red0\green0\blue0;\red128\green128\blue128;} +{\stylesheet{\s1\cf0{\*\hyphen2\hyphlead2\hyphtrail2\hyphmax0}\rtlch\af4\afs24\lang255\ltrch\dbch\af3\langfe255\hich\f0\fs24\lang1033\loch\f0\fs24\lang1033\snext1 Normal;} +{\s2\sb240\sa120\keepn\cf0{\*\hyphen2\hyphlead2\hyphtrail2\hyphmax0}\rtlch\afs28\lang255\ltrch\dbch\langfe255\hich\f2\fs28\lang1033\loch\f2\fs28\lang1033\sbasedon1\snext3 Heading;} +{\s3\sa120\cf0{\*\hyphen2\hyphlead2\hyphtrail2\hyphmax0}\rtlch\af4\afs24\lang255\ltrch\dbch\af3\langfe255\hich\f0\fs24\lang1033\loch\f0\fs24\lang1033\sbasedon1\snext3 Body Text;} +{\s4\sa120\cf0{\*\hyphen2\hyphlead2\hyphtrail2\hyphmax0}\rtlch\af5\afs24\lang255\ltrch\dbch\af3\langfe255\hich\f0\fs24\lang1033\loch\f0\fs24\lang1033\sbasedon3\snext4 List;} +{\s5\sb120\sa120\cf0{\*\hyphen2\hyphlead2\hyphtrail2\hyphmax0}\rtlch\af5\afs24\lang255\ai\ltrch\dbch\af3\langfe255\hich\f0\fs24\lang1033\i\loch\f0\fs24\lang1033\i\sbasedon1\snext5 caption;} +{\s6\cf0{\*\hyphen2\hyphlead2\hyphtrail2\hyphmax0}\rtlch\af5\afs24\lang255\ltrch\dbch\af3\langfe255\hich\f0\fs24\lang1033\loch\f0\fs24\lang1033\sbasedon1\snext6 Index;} +} +{\info{\author William Droste}{\creatim\yr2008\mo8\dy7\hr10\min50}{\revtim\yr0\mo0\dy0\hr0\min0}{\printim\yr0\mo0\dy0\hr0\min0}{\comment StarWriter}{\vern6800}}\deftab709 +{\*\pgdsctbl +{\pgdsc0\pgdscuse195\pgwsxn12240\pghsxn15840\marglsxn1134\margrsxn1134\margtsxn1134\margbsxn1134\pgdscnxt0 Standard;}} +\paperh15840\paperw12240\margl1134\margr1134\margt1134\margb1134\sectd\sbknone\pgwsxn12240\pghsxn15840\marglsxn1134\margrsxn1134\margtsxn1134\margbsxn1134\ftnbj\ftnstart1\ftnrstcont\ftnnar\aenddoc\aftnrstcont\aftnstart1\aftnnrlc +\pard\plain \ltrpar\s1\cf0{\*\hyphen2\hyphlead2\hyphtrail2\hyphmax0}\rtlch\af4\afs24\lang255\ltrch\dbch\af3\langfe255\hich\f0\fs24\lang1033\loch\f0\fs24\lang1033 {\rtlch \ltrch\loch\f0\fs24\lang1033\i0\b0 Copyright \'a9 2008 Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, California 95054, U.S.A. All rights reserved.} +\par \pard\plain \ltrpar\s1\cf0{\*\hyphen2\hyphlead2\hyphtrail2\hyphmax0}\rtlch\af4\afs24\lang255\ltrch\dbch\af3\langfe255\hich\f0\fs24\lang1033\loch\f0\fs24\lang1033 +\par \pard\plain \ltrpar\s1\cf0{\*\hyphen2\hyphlead2\hyphtrail2\hyphmax0}\rtlch\af4\afs24\lang255\ltrch\dbch\af3\langfe255\hich\f0\fs24\lang1033\loch\f0\fs24\lang1033 {\rtlch \ltrch\loch }{\rtlch \ltrch\loch\f0\fs24\lang1033\i0\b0 U.S. Government Rights - Commercial software. Government users are subject to the Sun Microsystems, Inc. standard license agreement and applicable provisions of the FAR and its supplements.} +\par \pard\plain \ltrpar\s1\cf0{\*\hyphen2\hyphlead2\hyphtrail2\hyphmax0}\rtlch\af4\afs24\lang255\ltrch\dbch\af3\langfe255\hich\f0\fs24\lang1033\loch\f0\fs24\lang1033 +\par \pard\plain \ltrpar\s1\cf0{\*\hyphen2\hyphlead2\hyphtrail2\hyphmax0}\rtlch\af4\afs24\lang255\ltrch\dbch\af3\langfe255\hich\f0\fs24\lang1033\loch\f0\fs24\lang1033 {\rtlch \ltrch\loch }{\rtlch \ltrch\loch\f0\fs24\lang1033\i0\b0 Use is subject to license terms.} +\par \pard\plain \ltrpar\s1\cf0{\*\hyphen2\hyphlead2\hyphtrail2\hyphmax0}\rtlch\af4\afs24\lang255\ltrch\dbch\af3\langfe255\hich\f0\fs24\lang1033\loch\f0\fs24\lang1033 +\par \pard\plain \ltrpar\s1\cf0{\*\hyphen2\hyphlead2\hyphtrail2\hyphmax0}\rtlch\af4\afs24\lang255\ltrch\dbch\af3\langfe255\hich\f0\fs24\lang1033\loch\f0\fs24\lang1033 {\rtlch \ltrch\loch }{\rtlch \ltrch\loch\f0\fs24\lang1033\i0\b0 This distribution may include materials developed by third parties.} +\par \pard\plain \ltrpar\s1\cf0{\*\hyphen2\hyphlead2\hyphtrail2\hyphmax0}\rtlch\af4\afs24\lang255\ltrch\dbch\af3\langfe255\hich\f0\fs24\lang1033\loch\f0\fs24\lang1033 +\par \pard\plain \ltrpar\s1\cf0{\*\hyphen2\hyphlead2\hyphtrail2\hyphmax0}\rtlch\af4\afs24\lang255\ltrch\dbch\af3\langfe255\hich\f0\fs24\lang1033\loch\f0\fs24\lang1033 {\rtlch \ltrch\loch }{\rtlch \ltrch\loch\f0\fs24\lang1033\i0\b0 Sun, Sun Microsystems, the Sun logo, Java and Project Identity Connectors are trademarks or registered trademarks of Sun Microsystems, Inc. in the U.S. and other countries.} +\par \pard\plain \ltrpar\s1\cf0{\*\hyphen2\hyphlead2\hyphtrail2\hyphmax0}\rtlch\af4\afs24\lang255\ltrch\dbch\af3\langfe255\hich\f0\fs24\lang1033\loch\f0\fs24\lang1033 +\par \pard\plain \ltrpar\s1\cf0{\*\hyphen2\hyphlead2\hyphtrail2\hyphmax0}\rtlch\af4\afs24\lang255\ltrch\dbch\af3\langfe255\hich\f0\fs24\lang1033\loch\f0\fs24\lang1033 {\rtlch \ltrch\loch }{\rtlch \ltrch\loch\f0\fs24\lang1033\i0\b0 UNIX is a registered trademark in the U.S. and other countries, exclusively licensed through X/Open Company, Ltd.} +\par \pard\plain \ltrpar\s1\cf0{\*\hyphen2\hyphlead2\hyphtrail2\hyphmax0}\rtlch\af4\afs24\lang255\ltrch\dbch\af3\langfe255\hich\f0\fs24\lang1033\loch\f0\fs24\lang1033 +\par \pard\plain \ltrpar\s1\cf0{\*\hyphen2\hyphlead2\hyphtrail2\hyphmax0}\rtlch\af4\afs24\lang255\ltrch\dbch\af3\langfe255\hich\f0\fs24\lang1033\loch\f0\fs24\lang1033 {\rtlch \ltrch\loch }{\rtlch \ltrch\loch\f0\fs24\lang1033\i0\b0 This product is covered and controlled by U.S. Export Control laws and may be subject to the export or import laws in other countries. Nuclear, missile, chemical biological weapons or nuclear maritime end uses or end users, whether direct or indirec +t, are strictly prohibited. Export or reexport to countries subject to U.S. embargo or to entities identified on U.S. export exclusion lists, including, but not limited to, the denied persons and specially designated nationals lists is strictly prohibited. + } +\par } \ No newline at end of file diff --git a/ShellScriptExecutorFactory/AssemblyInfo.cs b/ShellScriptExecutorFactory/AssemblyInfo.cs new file mode 100644 index 00000000..7a2dd083 --- /dev/null +++ b/ShellScriptExecutorFactory/AssemblyInfo.cs @@ -0,0 +1,70 @@ +/* + * Copyright 2008 Sun Microsystems, Inc. All rights reserved. + * + * U.S. Government Rights - Commercial software. Government users + * are subject to the Sun Microsystems, Inc. standard license agreement + * and applicable provisions of the FAR and its supplements. + * + * Use is subject to license terms. + * + * This distribution may include materials developed by third parties. + * Sun, Sun Microsystems, the Sun logo, Java and Project Identity + * Connectors are trademarks or registered trademarks of Sun + * Microsystems, Inc. or its subsidiaries in the U.S. and other + * countries. + * + * UNIX is a registered trademark in the U.S. and other countries, + * exclusively licensed through X/Open Company, Ltd. + * + * ----------- + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER. + * + * Copyright 2008 Sun Microsystems, Inc. All rights reserved. + * + * The contents of this file are subject to the terms of the Common Development + * and Distribution License(CDDL) (the License). You may not use this file + * except in compliance with the License. + * + * You can obtain a copy of the License at + * http://identityconnectors.dev.java.net/CDDLv1.0.html + * See the License for the specific language governing permissions and + * limitations under the License. + * + * When distributing the Covered Code, include this CDDL Header Notice in each + * file and include the License file at identityconnectors/legal/license.txt. + * If applicable, add the following below this CDDL Header, with the fields + * enclosed by brackets [] replaced by your own identifying information: + * "Portions Copyrighted [year] [name of copyright owner]" + * ----------- + */ +#region Using directives + +using System; +using System.Reflection; +using System.Runtime.InteropServices; + +#endregion + +// General Information about an assembly is controlled through the following +// set of attributes. Change these attribute values to modify the information +// associated with an assembly. +[assembly: AssemblyTitle("ShellScriptExecutorFactory")] +[assembly: AssemblyDescription("")] +[assembly: AssemblyConfiguration("")] +[assembly: AssemblyCompany("")] +[assembly: AssemblyProduct("ShellScriptExecutorFactory")] +[assembly: AssemblyCopyright("")] +[assembly: AssemblyTrademark("")] +[assembly: AssemblyCulture("")] + +// This sets the default COM visibility of types in the assembly to invisible. +// If you need to expose a type to COM, use [ComVisible(true)] on that type. +[assembly: ComVisible(false)] + +// The assembly version has following format : +// +// Major.Minor.Build.Revision +// +// You can specify all the values or you can use the default the Revision and +// Build Numbers by using the '*' as shown below: +[assembly: AssemblyVersion("1.0.*")] diff --git a/ShellScriptExecutorFactory/ShellScriptExecutorFactory.cs b/ShellScriptExecutorFactory/ShellScriptExecutorFactory.cs new file mode 100644 index 00000000..3c2ea830 --- /dev/null +++ b/ShellScriptExecutorFactory/ShellScriptExecutorFactory.cs @@ -0,0 +1,145 @@ +/* + * Copyright 2008 Sun Microsystems, Inc. All rights reserved. + * + * U.S. Government Rights - Commercial software. Government users + * are subject to the Sun Microsystems, Inc. standard license agreement + * and applicable provisions of the FAR and its supplements. + * + * Use is subject to license terms. + * + * This distribution may include materials developed by third parties. + * Sun, Sun Microsystems, the Sun logo, Java and Project Identity + * Connectors are trademarks or registered trademarks of Sun + * Microsystems, Inc. or its subsidiaries in the U.S. and other + * countries. + * + * UNIX is a registered trademark in the U.S. and other countries, + * exclusively licensed through X/Open Company, Ltd. + * + * ----------- + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER. + * + * Copyright 2008 Sun Microsystems, Inc. All rights reserved. + * + * The contents of this file are subject to the terms of the Common Development + * and Distribution License(CDDL) (the License). You may not use this file + * except in compliance with the License. + * + * You can obtain a copy of the License at + * http://identityconnectors.dev.java.net/CDDLv1.0.html + * See the License for the specific language governing permissions and + * limitations under the License. + * + * When distributing the Covered Code, include this CDDL Header Notice in each + * file and include the License file at identityconnectors/legal/license.txt. + * If applicable, add the following below this CDDL Header, with the fields + * enclosed by brackets [] replaced by your own identifying information: + * "Portions Copyrighted [year] [name of copyright owner]" + * ----------- + */ +using System; +using System.IO; +using System.Security; +using System.Diagnostics; +using System.Reflection; +using System.Collections.Generic; +using System.Text.RegularExpressions; +using Org.IdentityConnectors.Common.Security; + +namespace Org.IdentityConnectors.Common.Script.Shell +{ + + /// + /// Process shell scripts. Valid arguments are below, the rest will be + /// used as environment variables. The return is the exit code of the + /// process. + ///
    + ///
  • USERNAME - name of the user to run this script as..
  • + ///
  • PASSWORD - (GuardedString or SecureString) password for the user to run this script as..
  • + ///
  • WORKINGDIR - working directory run this script in..
  • + ///
  • TIMEOUT - timeout waiting for script to finish in ms (default: 30 secs)
  • + ///
+ ///
+ [ScriptExecutorFactoryClass("Shell")] + public class ShellScriptExecutorFactory : ScriptExecutorFactory + { + + /// + /// Creates a script executor give the Shell script. + /// + override + public ScriptExecutor NewScriptExecutor(Assembly [] refs, string script, bool compile) { + return new ShellScriptExecutor(script); + } + + /// + /// Processes the script. + /// + class ShellScriptExecutor : ScriptExecutor { + private readonly string _script; + + public ShellScriptExecutor(string script) { + _script = script; + } + public object Execute(IDictionary arguments) { + // create the process info.. + Process process = new Process(); + // set the defaults.. + process.StartInfo.CreateNoWindow = true; + process.StartInfo.UseShellExecute = true; + // set the default timeout.. + int timeout = 1000 * 30; // 30 secss + // if there are any environment varibles set to false.. + process.StartInfo.UseShellExecute = arguments.Count == 0; + // take out username and password if they're in the options. + foreach (KeyValuePair kv in arguments) { + if (kv.Key.ToUpper().Equals("USERNAME")) { + string domainUser = kv.Value.ToString(); + string[] split = domainUser.Split(new char[] {'\\'}); + if (split.Length == 1) { + process.StartInfo.UserName = split[0]; + } else { + process.StartInfo.Domain = split[0]; + process.StartInfo.UserName = split[1]; + } + } else if (kv.Key.ToUpper().Equals("PASSWORD")) { + if (kv.Value is SecureString) { + process.StartInfo.Password = (SecureString)kv.Value; + } + else if (kv.Value is GuardedString) { + process.StartInfo.Password = ((GuardedString)kv.Value).ToSecureString(); + } else { + throw new ArgumentException("Invalid type for password."); + } + } else if (kv.Key.ToUpper().Equals("WORKINGDIR")) { + process.StartInfo.WorkingDirectory = kv.Value.ToString(); + } else if (kv.Key.ToUpper().Equals("TIMEOUT")) { + timeout = Int32.Parse(kv.Value.ToString()); + } else { + process.StartInfo.EnvironmentVariables[kv.Key] = kv.Value.ToString(); + } + } + // write out the script.. + string fn = Path.GetTempFileName() + ".cmd"; + using (StreamWriter sw = new StreamWriter(fn)) { + sw.Write(_script); + } + // set temp file.. + process.StartInfo.FileName = fn; + // execute script.. + process.Start(); + // wait for the process to exit.. + if (!process.WaitForExit(timeout)) { + process.Close(); + throw new TimeoutException("Script failed to exit in time!"); + } + int exitCode = process.ExitCode; + // close up the process and return the exit code.. + process.Close(); + // clean up temp file.. + File.Delete(fn); + return exitCode; + } + } + } +} diff --git a/ShellScriptExecutorFactory/ShellScriptExecutorFactory.csproj b/ShellScriptExecutorFactory/ShellScriptExecutorFactory.csproj new file mode 100644 index 00000000..0df4a72a --- /dev/null +++ b/ShellScriptExecutorFactory/ShellScriptExecutorFactory.csproj @@ -0,0 +1,89 @@ + + + + {4700690A-2D29-40A0-86AC-E5A9F71A479A} + Debug + AnyCPU + Library + Sun.OpenConnectors.Common.Script.Shell + Shell.ScriptExecutorFactory + v3.5 + + + bin\Debug\ + True + Full + False + True + DEBUG;TRACE + + + bin\Release\ + False + None + True + False + TRACE + + + + + + 3.5 + + + + 3.5 + + + + + + + + + {F140E8DA-52B4-4159-992A-9DA10EA8EEFB} + Common + + + + + + + \ No newline at end of file diff --git a/TestBundleV1/AssemblyInfo.cs b/TestBundleV1/AssemblyInfo.cs new file mode 100644 index 00000000..33e53d26 --- /dev/null +++ b/TestBundleV1/AssemblyInfo.cs @@ -0,0 +1,74 @@ +/* + * Copyright 2008 Sun Microsystems, Inc. All rights reserved. + * + * U.S. Government Rights - Commercial software. Government users + * are subject to the Sun Microsystems, Inc. standard license agreement + * and applicable provisions of the FAR and its supplements. + * + * Use is subject to license terms. + * + * This distribution may include materials developed by third parties. + * Sun, Sun Microsystems, the Sun logo, Java and Project Identity + * Connectors are trademarks or registered trademarks of Sun + * Microsystems, Inc. or its subsidiaries in the U.S. and other + * countries. + * + * UNIX is a registered trademark in the U.S. and other countries, + * exclusively licensed through X/Open Company, Ltd. + * + * ----------- + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER. + * + * Copyright 2008 Sun Microsystems, Inc. All rights reserved. + * + * The contents of this file are subject to the terms of the Common Development + * and Distribution License(CDDL) (the License). You may not use this file + * except in compliance with the License. + * + * You can obtain a copy of the License at + * http://identityconnectors.dev.java.net/CDDLv1.0.html + * See the License for the specific language governing permissions and + * limitations under the License. + * + * When distributing the Covered Code, include this CDDL Header Notice in each + * file and include the License file at identityconnectors/legal/license.txt. + * If applicable, add the following below this CDDL Header, with the fields + * enclosed by brackets [] replaced by your own identifying information: + * "Portions Copyrighted [year] [name of copyright owner]" + * ----------- + */ +#region Using directives + +using System; +using System.Reflection; +using System.Runtime.InteropServices; +using Org.IdentityConnectors.Framework.Spi; +using System.Resources; + +#endregion + +// General Information about an assembly is controlled through the following +// set of attributes. Change these attribute values to modify the information +// associated with an assembly. +[assembly: AssemblyTitle("TestBundleV1")] +[assembly: AssemblyDescription("")] +[assembly: AssemblyConfiguration("")] +[assembly: AssemblyCompany("")] +[assembly: AssemblyProduct("TestBundleV1")] +[assembly: AssemblyCopyright("")] +[assembly: AssemblyTrademark("")] +[assembly: AssemblyCulture("")] + +// This sets the default COM visibility of types in the assembly to invisible. +// If you need to expose a type to COM, use [ComVisible(true)] on that type. +[assembly: ComVisible(false)] + +// The assembly version has following format : +// +// Major.Minor.Build.Revision +// +// You can specify all the values or you can use the default the Revision and +// Build Numbers by using the '*' as shown below: +[assembly: AssemblyVersion("1.0.0.0")] +[assembly: NeutralResourcesLanguage("en-US")] + diff --git a/TestBundleV1/Messages.es-ES.resx b/TestBundleV1/Messages.es-ES.resx new file mode 100644 index 00000000..ec894ca7 --- /dev/null +++ b/TestBundleV1/Messages.es-ES.resx @@ -0,0 +1,126 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + text/microsoft-resx + + + 2.0 + + + System.Resources.ResXResourceReader, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + System.Resources.ResXResourceWriter, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + tstField.display_es-ES + + + tstField.help_es-ES + + \ No newline at end of file diff --git a/TestBundleV1/Messages.es.resx b/TestBundleV1/Messages.es.resx new file mode 100644 index 00000000..523473e4 --- /dev/null +++ b/TestBundleV1/Messages.es.resx @@ -0,0 +1,126 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + text/microsoft-resx + + + 2.0 + + + System.Resources.ResXResourceReader, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + System.Resources.ResXResourceWriter, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + tstField.help_es + + + tstField.display_es + + \ No newline at end of file diff --git a/TestBundleV1/Messages.resx b/TestBundleV1/Messages.resx new file mode 100644 index 00000000..a988e1e7 --- /dev/null +++ b/TestBundleV1/Messages.resx @@ -0,0 +1,126 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + text/microsoft-resx + + + 2.0 + + + System.Resources.ResXResourceReader, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + System.Resources.ResXResourceWriter, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + Help for test field. + + + Display for test field. + + \ No newline at end of file diff --git a/TestBundleV1/TestBundleV1.csproj b/TestBundleV1/TestBundleV1.csproj new file mode 100644 index 00000000..3ecb068e --- /dev/null +++ b/TestBundleV1/TestBundleV1.csproj @@ -0,0 +1,94 @@ + + + + {0BC2A013-56FE-46DD-90FC-2D2D57748FB6} + Debug + AnyCPU + Library + TestBundleV1 + TestBundleV1.Connector + v3.5 + OnBuildSuccess + + + bin\Debug\ + True + Full + False + True + DEBUG;TRACE + + + bin\Release\ + False + None + True + False + TRACE + + + + + + + + + + + + + + + + + + + {F140E8DA-52B4-4159-992A-9DA10EA8EEFB} + Common + + + {8B24461B-456A-4032-89A1-CD418F7B5B62} + Framework + + + + + + + \ No newline at end of file diff --git a/TestBundleV1/TestConnector.cs b/TestBundleV1/TestConnector.cs new file mode 100644 index 00000000..2c01377b --- /dev/null +++ b/TestBundleV1/TestConnector.cs @@ -0,0 +1,221 @@ +/* + * Copyright 2008 Sun Microsystems, Inc. All rights reserved. + * + * U.S. Government Rights - Commercial software. Government users + * are subject to the Sun Microsystems, Inc. standard license agreement + * and applicable provisions of the FAR and its supplements. + * + * Use is subject to license terms. + * + * This distribution may include materials developed by third parties. + * Sun, Sun Microsystems, the Sun logo, Java and Project Identity + * Connectors are trademarks or registered trademarks of Sun + * Microsystems, Inc. or its subsidiaries in the U.S. and other + * countries. + * + * UNIX is a registered trademark in the U.S. and other countries, + * exclusively licensed through X/Open Company, Ltd. + * + * ----------- + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER. + * + * Copyright 2008 Sun Microsystems, Inc. All rights reserved. + * + * The contents of this file are subject to the terms of the Common Development + * and Distribution License(CDDL) (the License). You may not use this file + * except in compliance with the License. + * + * You can obtain a copy of the License at + * http://identityconnectors.dev.java.net/CDDLv1.0.html + * See the License for the specific language governing permissions and + * limitations under the License. + * + * When distributing the Covered Code, include this CDDL Header Notice in each + * file and include the License file at identityconnectors/legal/license.txt. + * If applicable, add the following below this CDDL Header, with the fields + * enclosed by brackets [] replaced by your own identifying information: + * "Portions Copyrighted [year] [name of copyright owner]" + * ----------- + */ +using System; +using System.Collections.Generic; +using System.Globalization; +using System.Threading; +using Org.IdentityConnectors.Common; +using Org.IdentityConnectors.Common.Pooling; +using Org.IdentityConnectors.Framework.Common.Objects; +using Org.IdentityConnectors.Framework.Common.Objects.Filters; +using Org.IdentityConnectors.Framework.Common.Exceptions; +using Org.IdentityConnectors.Framework.Spi; +using Org.IdentityConnectors.Framework.Spi.Operations; +namespace org.identityconnectors.testconnector +{ + public class TstConnectorConfig : AbstractConfiguration + { + /// + /// keep lower case for consistent unit tests + /// + public string tstField{get;set;} + + /// + /// keep lower case for consistent unit tests + /// + public int numResults{get;set;} + + /// + /// keep lower case for consistent unit tests + /// + public bool failValidation{get;set;} + + /// + /// keep lower case for consistent unit tests + /// + public bool resetConnectionCount{get;set;} + + public override void Validate() { + if (failValidation) { + throw new ConnectorException("validation failed"); + } + } + + } + + public class MyTstConnection { + private readonly int _connectionNumber; + private bool _isGood = true; + + public MyTstConnection(int connectionNumber) { + _connectionNumber = connectionNumber; + } + + public void Test() { + if (!_isGood) { + throw new ConnectorException("Connection is bad"); + } + } + + public void Dispose() { + _isGood = false; + } + + public bool IsGood() { + return _isGood; + } + + public int GetConnectionNumber() { + return _connectionNumber; + } + } + + [ConnectorClass("TestConnector", + typeof(TstConnectorConfig), + MessageCatalogPath="TestBundleV1.Messages" + )] + public class TstConnector : CreateOp, PoolableConnector, SchemaOp, SearchOp, SyncOp + { + private static int _connectionCount = 0; + private MyTstConnection _myConnection; + private TstConnectorConfig _config; + + public Uid Create(ObjectClass oclass, ICollection attrs, OperationOptions options) { + int? delay = (int?)CollectionUtil.GetValue(options.Options,"delay",null); + if ( delay != null ) { + Thread.Sleep((int)delay); + } + + if ( options.Options.ContainsKey("testPooling")) { + return new Uid(_myConnection.GetConnectionNumber().ToString()); + } + else { + String version = "1.0"; + return new Uid(version); + } + } + public void Init(Configuration cfg) { + _config = (TstConnectorConfig)cfg; + if (_config.resetConnectionCount) { + _connectionCount = 0; + } + _myConnection = new MyTstConnection(_connectionCount++); + } + + public static String getVersion() + { + return "1.0"; + } + + public void Dispose() { + if (_myConnection != null) { + _myConnection.Dispose(); + _myConnection = null; + } + } + /** + * Used by the script tests + */ + public String concat(String s1, String s2) { + return s1+s2; + } + + public void CheckAlive() { + _myConnection.Test(); + } + + private class MyTranslator : AbstractFilterTranslator { + + } + public FilterTranslator CreateFilterTranslator(ObjectClass oclass, OperationOptions options) { + return new MyTranslator(); + } + public void ExecuteQuery(ObjectClass oclass, String query, ResultsHandler handler, OperationOptions options) { + + for ( int i = 0; i < _config.numResults; i++ ) { + int? delay = (int?)CollectionUtil.GetValue(options.Options,"delay",null); + if ( delay != null ) { + Thread.Sleep((int)delay); + } + ConnectorObjectBuilder builder = + new ConnectorObjectBuilder(); + builder.SetUid(""+i); + builder.SetName(i.ToString()); + builder.ObjectClass = oclass; + for ( int j = 0; j < 50; j++ ) { + builder.AddAttribute("myattribute"+j,"myvaluevaluevalue"+j); + } + ConnectorObject rv = builder.Build(); + if (!handler(rv)) { + break; + } + } + } + public void Sync(ObjectClass objClass, SyncToken token, + SyncResultsHandler handler, + OperationOptions options) { + for (int i = 0; i < _config.numResults; i++ ) { + SyncDeltaBuilder builder = + new SyncDeltaBuilder(); + builder.Uid=(new Uid(""+i)); + builder.DeltaType=(SyncDeltaType.CREATE); + builder.Token=(new SyncToken("mytoken")); + SyncDelta rv = builder.Build(); + if (!handler(rv)) { + break; + } + } + } + + public Schema Schema() { + SchemaBuilder builder = new SchemaBuilder(typeof(TstConnector)); + for ( int i = 0 ; i < 2; i++ ) { + ObjectClassInfoBuilder classBuilder = new ObjectClassInfoBuilder(); + classBuilder.ObjectType=("class"+i); + for ( int j = 0; j < 200; j++) { + classBuilder.AddAttributeInfo(ConnectorAttributeInfoBuilder.Build("attributename"+j, typeof(String))); + } + builder.DefineObjectClass(classBuilder.Build()); + } + return builder.Build(); + } + + } +} diff --git a/TestBundleV2/AssemblyInfo.cs b/TestBundleV2/AssemblyInfo.cs new file mode 100644 index 00000000..eebe80c0 --- /dev/null +++ b/TestBundleV2/AssemblyInfo.cs @@ -0,0 +1,72 @@ +/* + * Copyright 2008 Sun Microsystems, Inc. All rights reserved. + * + * U.S. Government Rights - Commercial software. Government users + * are subject to the Sun Microsystems, Inc. standard license agreement + * and applicable provisions of the FAR and its supplements. + * + * Use is subject to license terms. + * + * This distribution may include materials developed by third parties. + * Sun, Sun Microsystems, the Sun logo, Java and Project Identity + * Connectors are trademarks or registered trademarks of Sun + * Microsystems, Inc. or its subsidiaries in the U.S. and other + * countries. + * + * UNIX is a registered trademark in the U.S. and other countries, + * exclusively licensed through X/Open Company, Ltd. + * + * ----------- + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER. + * + * Copyright 2008 Sun Microsystems, Inc. All rights reserved. + * + * The contents of this file are subject to the terms of the Common Development + * and Distribution License(CDDL) (the License). You may not use this file + * except in compliance with the License. + * + * You can obtain a copy of the License at + * http://identityconnectors.dev.java.net/CDDLv1.0.html + * See the License for the specific language governing permissions and + * limitations under the License. + * + * When distributing the Covered Code, include this CDDL Header Notice in each + * file and include the License file at identityconnectors/legal/license.txt. + * If applicable, add the following below this CDDL Header, with the fields + * enclosed by brackets [] replaced by your own identifying information: + * "Portions Copyrighted [year] [name of copyright owner]" + * ----------- + */ +#region Using directives + +using System; +using System.Reflection; +using System.Runtime.InteropServices; +using Org.IdentityConnectors.Framework.Spi; + +#endregion + +// General Information about an assembly is controlled through the following +// set of attributes. Change these attribute values to modify the information +// associated with an assembly. +[assembly: AssemblyTitle("TestBundleV2")] +[assembly: AssemblyDescription("")] +[assembly: AssemblyConfiguration("")] +[assembly: AssemblyCompany("")] +[assembly: AssemblyProduct("TestBundleV2")] +[assembly: AssemblyCopyright("")] +[assembly: AssemblyTrademark("")] +[assembly: AssemblyCulture("")] + +// This sets the default COM visibility of types in the assembly to invisible. +// If you need to expose a type to COM, use [ComVisible(true)] on that type. +[assembly: ComVisible(false)] + +// The assembly version has following format : +// +// Major.Minor.Build.Revision +// +// You can specify all the values or you can use the default the Revision and +// Build Numbers by using the '*' as shown below: +[assembly: AssemblyVersion("2.0.0.0")] + diff --git a/TestBundleV2/TestBundleV2.csproj b/TestBundleV2/TestBundleV2.csproj new file mode 100644 index 00000000..fe7c8f4f --- /dev/null +++ b/TestBundleV2/TestBundleV2.csproj @@ -0,0 +1,91 @@ + + + + {3E737796-3A83-4924-9FF1-DC542F21F59E} + Debug + AnyCPU + Library + TestBundleV2 + TestBundleV2.Connector + v3.5 + OnBuildSuccess + + + bin\Debug\ + True + Full + False + True + DEBUG;TRACE + + + bin\Release\ + False + None + True + False + TRACE + + + + + + + + + + + + + + + + {F140E8DA-52B4-4159-992A-9DA10EA8EEFB} + Common + + + {8B24461B-456A-4032-89A1-CD418F7B5B62} + Framework + + + + + + + \ No newline at end of file diff --git a/TestBundleV2/TestConnector.cs b/TestBundleV2/TestConnector.cs new file mode 100644 index 00000000..3c6b05fa --- /dev/null +++ b/TestBundleV2/TestConnector.cs @@ -0,0 +1,78 @@ +/* + * Copyright 2008 Sun Microsystems, Inc. All rights reserved. + * + * U.S. Government Rights - Commercial software. Government users + * are subject to the Sun Microsystems, Inc. standard license agreement + * and applicable provisions of the FAR and its supplements. + * + * Use is subject to license terms. + * + * This distribution may include materials developed by third parties. + * Sun, Sun Microsystems, the Sun logo, Java and Project Identity + * Connectors are trademarks or registered trademarks of Sun + * Microsystems, Inc. or its subsidiaries in the U.S. and other + * countries. + * + * UNIX is a registered trademark in the U.S. and other countries, + * exclusively licensed through X/Open Company, Ltd. + * + * ----------- + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER. + * + * Copyright 2008 Sun Microsystems, Inc. All rights reserved. + * + * The contents of this file are subject to the terms of the Common Development + * and Distribution License(CDDL) (the License). You may not use this file + * except in compliance with the License. + * + * You can obtain a copy of the License at + * http://identityconnectors.dev.java.net/CDDLv1.0.html + * See the License for the specific language governing permissions and + * limitations under the License. + * + * When distributing the Covered Code, include this CDDL Header Notice in each + * file and include the License file at identityconnectors/legal/license.txt. + * If applicable, add the following below this CDDL Header, with the fields + * enclosed by brackets [] replaced by your own identifying information: + * "Portions Copyrighted [year] [name of copyright owner]" + * ----------- + */ +using System; +using System.Collections.Generic; +using System.Globalization; +using Org.IdentityConnectors.Common.Pooling; +using Org.IdentityConnectors.Framework.Common.Objects; +using Org.IdentityConnectors.Framework.Spi; +using Org.IdentityConnectors.Framework.Spi.Operations; +namespace org.identityconnectors.testconnector +{ + public class TstConnectorConfig : AbstractConfiguration + { + public override void Validate() { + } + } + + [ConnectorClass("TestConnector", + typeof(TstConnectorConfig), + MessageCatalogPath="TestBundleV2.Messages" + )] + public class TstConnector : CreateOp, Connector, SchemaOp + { + public Uid Create(ObjectClass oclass, ICollection attrs, OperationOptions options) { + String version = "2.0"; + return new Uid(version); + } + public void Init(Configuration cfg) { + } + + public void Dispose() { + + } + + public Schema Schema() { + return null; + } + + + } +} diff --git a/server.pfx b/server.pfx new file mode 100644 index 0000000000000000000000000000000000000000..63f632a287bcbab06114bdde513c6b95c616be6d GIT binary patch literal 1704 zcmY+Dc|6p47{`Aze*K1Q>@-RyjO5G?zsZ=HZN$hmMvf#`VMnfF4I$Tr#^#7D8sr|i z!V*e!IEzZDSSqvFvTg2;DNWL>{pr>2zV?sr^L?K8b9}#lJRg_`RiaT+Fc10xlG%~W zOcrlIVNe1dv=8J#b_lkCc~UL^OG#CLJgG7S7a=DflKtBC&3Y7Cz(ZewdFU2c8Cw4j zMuy9Q(yVc-fI8yT5E_kn1oF^Xbc#*1_s~A#@Ol4_{1b&gn@69;7rhl%cs$Nj91|Kl z&TeB+#~Ww+*jjI791dETOOBQ%BpL`0izlv+uW}8P0`+5(%UaPj6tfZ!?ScG&=PTJt z4ilYGH@7wP@sv}U^eg%ANA)I!qjVtOKy;3(`;vGab2$=t;J<@V?%Q9`ROFi_(#8t>&7 zoL3VNq~t7X0p}s>Y*Tg6`r|KBrdh+Fa@}bY{)0lp)s=Ow5(8HwGYok~CQoUfkF1DzRsa3I~Ea1Ek110bF;u_mz`&iFgr;+$nIZBc^HY!jcj5?7v@uP$E zjz^!n3+}a>-mSurz(Gsq$vUrm7K3s~?Q|BV*i4R_y4$<<$rNL>xfsAph$YLFg3Y5n z$@_E%bmSDaEo~g@Y3@@_YWAR>CYHJNvQgN##9`{Rk%f|Fr@Yk%*>F1`tfyY z!0~4o8lJ>{nVvC^{`6dxN*bg6YD|Z~Tf-@+YT3fPG2u?f;koDT{7v>@Cx*LC$t9U` zHkZ0ru*7OWqGd&7RqdZJxxrEZ|a{ zmsjQ$D~B3S+FG^lnay2Th%8qis4&?n+gtTdY9Ig5mD>Bw2+d|=g|7@=Y4LOZe!eE@ z__Yx z2>}9-$Q^;Vyp)})%qLo>_lTW-sup11OqYu+0e@2O3XPH)_YcN*zs%jTl> z&SmIaT!|Y#dfR%Yf=)5>n#b6_FRAC+Wd~^xo@IoncI?%0$=UsW^ATG{6mRk}&G)sy zygKvck-{ldNA=6t%T`hBqLQvMpIv)z4i`18f*)_y=iX-ztb{S%Q54|raojntLp6!f z2H@SzwIf$s-M94;tSTdadCoE5+!PwZLOvz%%#Ju|@}TI$L^R=YeG{Sy{P zx3_QMrT=z4HP}&iB&5=b0?XMgblcM&Hk}RBoQ{8bQJB<@7 zNRPa>_?}nf-Vc4X_uaB{et5==kTHuCL`+8Kk$}ig@8(@`62?G&@%0OpF@4rdQNEF_ zmXV`*-{kxFok=R+`c3^lm&CM`xMmomKI|6I$lN7cZQ8oNcu8O&`IF_43J! zrvv1`;DGt|ycVC7Snc|IH4Y%vHB{QHZ6?TWIjf_>9URCMZK)H@&HQyWY#yr>ACFeg zm~UJ-l!?_|6uC8I75=r Date: Mon, 8 Sep 2008 20:08:24 +0000 Subject: [PATCH 002/342] Issue #64 - Incremental checkin. Fixing sync problem where deletes were not handled properly. Also added test for that case. Added logic to create users enabled by default. --- .../ActiveDirectoryConnector.cs | 75 ++++++++++++++----- .../ActiveDirectoryConnectorTest.cs | 67 +++++++++-------- .../ActiveDirectoryConnectorTests.csproj | 12 --- ActiveDirectoryConnectorTests/project.xml | 3 +- 4 files changed, 95 insertions(+), 62 deletions(-) diff --git a/ActiveDirectoryConnector/ActiveDirectoryConnector.cs b/ActiveDirectoryConnector/ActiveDirectoryConnector.cs index 108b3542..a3306a5d 100644 --- a/ActiveDirectoryConnector/ActiveDirectoryConnector.cs +++ b/ActiveDirectoryConnector/ActiveDirectoryConnector.cs @@ -215,6 +215,13 @@ public Uid Create(ObjectClass oclass, ActiveDirectoryUtils.GetADObjectClass(oclass)); newDe.CommitChanges(); + + // default to creating users enabled + if ((ObjectClass.ACCOUNT.Equals(oclass)) && + (ConnectorAttributeUtil.Find(OperationalAttributes.ENABLE_NAME, attributes) == null)) + { + attributes.Add(ConnectorAttributeBuilder.Build(OperationalAttributes.ENABLE_NAME, true)); + } _utils.UpdateADObject(oclass, newDe, attributes, UpdateType.REPLACE, _configuration); Object guidValue = newDe.Properties["objectGUID"].Value; @@ -986,12 +993,17 @@ public class SyncResults SyncResultsHandler _syncResultsHandler; string _serverName; bool _useGlobalCatalog; + long _lastModifiedUsn; + long _lastDeletedUsn; public SyncResults(SyncResultsHandler syncResultsHandler, - bool useGlobalCatalog, string serverName) { + bool useGlobalCatalog, string serverName, + long lastModifiedUsn, long lastDeletedUsn) { _syncResultsHandler = syncResultsHandler; _serverName = serverName; _useGlobalCatalog = false; + _lastModifiedUsn = lastModifiedUsn; + _lastDeletedUsn = lastDeletedUsn; } public bool SyncHandler(ConnectorObject obj) @@ -1020,17 +1032,22 @@ public bool SyncHandler(ConnectorObject obj) long tokenUsnValue = (long)ConnectorAttributeUtil.GetSingleValue(tokenAttr); string tokenServerName = _serverName; - SyncToken token = new SyncToken(String.Format("{0}|{1}|{2}", - tokenUsnValue, _useGlobalCatalog, tokenServerName)); - builder.Token = token; - bool? isDeleted = false; ConnectorAttribute isDeletedAttr = ConnectorAttributeUtil.Find(ATT_IS_DELETED, obj.GetAttributes()); if (isDeletedAttr != null) { isDeleted = (bool?)ConnectorAttributeUtil.GetSingleValue(isDeletedAttr); + _lastDeletedUsn = tokenUsnValue; } + else + { + _lastModifiedUsn = tokenUsnValue; + } + + SyncToken token = new SyncToken(String.Format("{0}|{1}|{2}|{3}", + _lastModifiedUsn, _lastDeletedUsn, _useGlobalCatalog, tokenServerName)); + builder.Token = token; if ((isDeleted != null) && (isDeleted.Equals(true))) { @@ -1050,7 +1067,8 @@ public bool SyncHandler(ConnectorObject obj) public void Sync(ObjectClass objClass, SyncToken token, SyncResultsHandler handler, OperationOptions options) { - string query = null; + string modifiedQuery = null; + string deletedQuery = null; string serverName = null; bool useGlobalCatalog = false; @@ -1092,14 +1110,15 @@ public void Sync(ObjectClass objClass, SyncToken token, } - + long lastModToken = 0; + long lastDelToken = 0; // if the token is not null, we may be able to start from // the usn contained there if (token != null) { string[] tokenParts = ((string)(token.Value)).Split('|'); - bool tokenUseGlobalCatalog = bool.Parse(tokenParts[1]); - string tokenServerName = tokenParts[2]; + bool tokenUseGlobalCatalog = bool.Parse(tokenParts[2]); + string tokenServerName = tokenParts[3]; // If the token server is the same as the configured server, // use the token value (usn) to limit the query. The token is @@ -1111,21 +1130,43 @@ public void Sync(ObjectClass objClass, SyncToken token, if ((serverName == null) || (tokenServerName.Equals(serverName)) || (serverName.Length == 0)) { - // add usnchanged >= token to search ... active directory doesn't support - // > just >=, so add one, and then search for >= - long nextUsn = long.Parse(tokenParts[0]); - nextUsn++; - query = string.Format("({0}>={1})", ATT_USN_CHANGED, nextUsn); + lastModToken = long.Parse(tokenParts[0]); + lastDelToken = long.Parse(tokenParts[1]); + modifiedQuery = string.Format("(!({0}<={1}))", ATT_USN_CHANGED, tokenParts[0]); + deletedQuery = string.Format("(&(!({0}<={1}))(isDeleted=TRUE))", ATT_USN_CHANGED, tokenParts[1]); } } } + else + { + deletedQuery = string.Format("(isDeleted=TRUE)"); + } OperationOptionsBuilder builder = new OperationOptionsBuilder(); - SyncResults syncResults = new SyncResults(handler, useGlobalCatalog, serverName); + SyncResults syncResults = new SyncResults(handler, useGlobalCatalog, + serverName, lastModToken, lastDelToken); - ExecuteQuery(objClass, query, syncResults.SyncHandler, builder.Build(), - true, new SortOption(ATT_USN_CHANGED, SortDirection.Ascending), + // find modified usn's + ExecuteQuery(objClass, modifiedQuery, syncResults.SyncHandler, builder.Build(), + false, new SortOption(ATT_USN_CHANGED, SortDirection.Ascending), serverName, useGlobalCatalog, _configuration.SyncSearchContext); + + // find deleted usn's + DirectoryContext domainContext = new DirectoryContext(DirectoryContextType.DirectoryServer, + serverName, + _configuration.DirectoryAdminName, + _configuration.DirectoryAdminPassword); + Domain domain = Domain.GetDomain(domainContext); + String deleteObjectsSearchRoot = null; + if (domain != null) + { + DirectoryEntry domainDe = domain.GetDirectoryEntry(); + deleteObjectsSearchRoot = ActiveDirectoryUtils.GetDnFromPath(domainDe.Path); + } + ExecuteQuery(objClass, deletedQuery, syncResults.SyncHandler, builder.Build(), + true, new SortOption(ATT_USN_CHANGED, SortDirection.Ascending), + serverName, useGlobalCatalog, deleteObjectsSearchRoot); + } #endregion diff --git a/ActiveDirectoryConnectorTests/ActiveDirectoryConnectorTest.cs b/ActiveDirectoryConnectorTests/ActiveDirectoryConnectorTest.cs index 4bab9221..221cb5a3 100644 --- a/ActiveDirectoryConnectorTests/ActiveDirectoryConnectorTest.cs +++ b/ActiveDirectoryConnectorTests/ActiveDirectoryConnectorTest.cs @@ -71,7 +71,8 @@ public class ActiveDirectoryConnectorTest public static readonly string CONFIG_PROPERTY_SCRIPT_PASSWORD_DOMAIN = "config_script_password_domain"; public static readonly string CONFIG_PROPERTY_LDAPHOSTNAME = "config_ldap_hostname"; public static readonly string CONFIG_PROPERTY_SEARCH_CONTEXT = "config_search_context"; - public static readonly string CONFIG_PROPERTY_SYNC_SEARCH_CONTEXT = "config_sync_search_context"; + public static readonly string CONFIG_PROPERTY_SYNC_SEARCH_CONTEXT_ROOT = "config_sync_search_context_root"; + public static readonly string CONFIG_PROPERTY_SYNC_SEARCH_CONTEXT_CHILD = "config_sync_search_context_child"; public static readonly string CONFIG_PROPERTY_DOMAIN_NAME = "config_domain_name"; public static readonly string CONFIG_PROPERTY_SYNC_DOMAIN_CONTROLLER = "config_sync_domain_controller"; public static readonly string CONFIG_PROPERTY_GC_DOMAIN_CONTROLLER = "config_sync_gc_domain_controller"; @@ -1196,7 +1197,8 @@ public void TestDisableDate() public void TestSyncGC() { // test with searchChildDomain (uses GC) - TestSync(true); + TestSync(true, GetProperty(CONFIG_PROPERTY_SYNC_SEARCH_CONTEXT_ROOT)); + TestSync(true, GetProperty(CONFIG_PROPERTY_SYNC_SEARCH_CONTEXT_CHILD)); } // test sync @@ -1204,7 +1206,8 @@ public void TestSyncGC() public void TestSyncDC() { // test withouth searchChildDomains (uses DC) - TestSync(false); + TestSync(false, GetProperty(CONFIG_PROPERTY_SYNC_SEARCH_CONTEXT_ROOT)); + TestSync(false, GetProperty(CONFIG_PROPERTY_SYNC_SEARCH_CONTEXT_CHILD)); } [Test] @@ -1304,11 +1307,13 @@ public void TestUserPasswordChange() } } - public void TestSync(bool searchChildDomains) { + public void TestSync(bool searchChildDomains, String syncSearchContext) + { //Initialize Connector ActiveDirectoryConnector connector = new ActiveDirectoryConnector(); ActiveDirectoryConfiguration configuration = (ActiveDirectoryConfiguration)GetConfiguration(); + configuration.SyncSearchContext = syncSearchContext; configuration.SearchChildDomains = searchChildDomains; connector.Init(configuration); @@ -1417,23 +1422,28 @@ public bool SyncHandler_Initial(SyncDelta delta) public bool SyncHandler_ModifiedAccounts(SyncDelta delta) { - // just ignore extra ones. they might have come in by other means - if (_mods.ContainsKey(delta.Uid)) - { - ICollection requestedAttrs = _mods[delta.Uid]; - ConnectorObjectBuilder builder = new ConnectorObjectBuilder(); - builder.ObjectClass = ObjectClass.ACCOUNT; - builder.SetUid(delta.Uid); - builder.AddAttributes(delta.Attributes); - ActiveDirectoryConnectorTest.VerifyObject(requestedAttrs, - builder.Build()); - _mods.Remove(delta.Uid); + Token = delta.Token; + if(delta.DeltaType.Equals(SyncDeltaType.UPDATE)) { + // just ignore extra ones. they might have come in by other means + if (_mods.ContainsKey(delta.Uid)) + { + ICollection requestedAttrs = _mods[delta.Uid]; + ConnectorObjectBuilder builder = new ConnectorObjectBuilder(); + builder.ObjectClass = ObjectClass.ACCOUNT; + builder.SetUid(delta.Uid); + builder.AddAttributes(delta.Attributes); + ActiveDirectoryConnectorTest.VerifyObject(requestedAttrs, + builder.Build()); + _mods.Remove(delta.Uid); + } } return true; } public bool SyncHandler_DeletedAccounts(SyncDelta delta) { + Token = delta.Token; + _dels.Remove(delta.Uid); return true; } @@ -1508,23 +1518,7 @@ public String CreateGroupMember(ActiveDirectoryConnector connector) foundObjects.ElementAt(0).GetAttributeByName("distinguishedName")); return dnMember; } - /* - public Uid CreateAndVerifyObject(ActiveDirectoryConnector connector, - ObjectClass oclass, ICollection attributes) - { - // create object - Uid uid = connector.Create(oclass, attributes, null); - Assert.IsNotNull(uid); - // find new object - Filter uidFilter = FilterBuilder.EqualTo(uid); - ICollection foundObjects = TestHelpers.SearchToList( - connector, oclass, uidFilter); - Assert.IsTrue(foundObjects.Count == 1); - VerifyObject(attributes, foundObjects.ElementAt(0)); - return uid; - } - */ public Uid UpdateReplaceAndVerifyObject(ActiveDirectoryConnector connector, ObjectClass oclass, Uid uid, ICollection attributes) { @@ -1707,6 +1701,16 @@ public Uid CreateAndVerifyObject(ActiveDirectoryConnector connector, public Uid CreateObject(ActiveDirectoryConnector connector, ObjectClass oclass, ICollection attributes) { + // if it exists, remove and recreate + Filter nameFilter = FilterBuilder.EqualTo(ConnectorAttributeBuilder.Build( + Name.NAME, ConnectorAttributeUtil.GetNameFromAttributes(attributes).Value)); + ICollection foundObjects = TestHelpers.SearchToList(connector, oclass, nameFilter); + if ((foundObjects != null) && (foundObjects.Count > 0)) + { + Assert.AreEqual(1, foundObjects.Count); + DeleteAndVerifyObject(connector, oclass, foundObjects.ElementAt(0).Uid, false, true); + } + // create object Uid uid = connector.Create(oclass, attributes, null); Assert.IsNotNull(uid); @@ -1736,7 +1740,6 @@ public Configuration GetConfiguration() config.DirectoryAdminName = GetProperty(CONFIG_PROPERTY_USER); config.DirectoryAdminPassword = GetProperty(CONFIG_PROPERTY_PASSWORD); config.SearchContext = GetProperty(CONFIG_PROPERTY_SEARCH_CONTEXT); - config.SyncSearchContext = GetProperty(CONFIG_PROPERTY_SYNC_SEARCH_CONTEXT); config.SyncDomainController = GetProperty(CONFIG_PROPERTY_SYNC_DOMAIN_CONTROLLER); config.SyncGlobalCatalogServer = GetProperty(CONFIG_PROPERTY_GC_DOMAIN_CONTROLLER); return config; diff --git a/ActiveDirectoryConnectorTests/ActiveDirectoryConnectorTests.csproj b/ActiveDirectoryConnectorTests/ActiveDirectoryConnectorTests.csproj index 8b378ae1..dddbcf85 100644 --- a/ActiveDirectoryConnectorTests/ActiveDirectoryConnectorTests.csproj +++ b/ActiveDirectoryConnectorTests/ActiveDirectoryConnectorTests.csproj @@ -109,11 +109,6 @@ - - True - True - TestParams.resx - @@ -129,13 +124,6 @@ Framework - - - ResXFileCodeGenerator - TestParams.Designer.cs - Designer - - Always diff --git a/ActiveDirectoryConnectorTests/project.xml b/ActiveDirectoryConnectorTests/project.xml index 9d99efc1..b1cc342b 100644 --- a/ActiveDirectoryConnectorTests/project.xml +++ b/ActiveDirectoryConnectorTests/project.xml @@ -11,7 +11,8 @@ - + + From 38cebb47ba280b38e58edf76fa9e25dc61fcfe28 Mon Sep 17 00:00:00 2001 From: rcauble Date: Mon, 8 Sep 2008 20:44:07 +0000 Subject: [PATCH 003/342] Issue#247: SyncDelta should included ConnectorObject --- Framework/CommonObjects.cs | 162 +++++++++--------- FrameworkInternal/ApiLocalOperations.cs | 31 ++-- FrameworkInternal/Resources.resx | 2 +- FrameworkInternal/Serializer.cs | 11 +- FrameworkTests/ObjectNormalizerFacadeTests.cs | 16 +- FrameworkTests/ObjectSerializationTests.cs | 11 +- TestBundleV1/TestConnector.cs | 8 +- 7 files changed, 130 insertions(+), 111 deletions(-) diff --git a/Framework/CommonObjects.cs b/Framework/CommonObjects.cs index 356529c0..db13f795 100644 --- a/Framework/CommonObjects.cs +++ b/Framework/CommonObjects.cs @@ -2921,39 +2921,48 @@ public ScriptContext Build() { * @see SyncOp */ public sealed class SyncDelta { - private readonly Uid _uid; private readonly SyncToken _token; private readonly SyncDeltaType _deltaType; - private readonly ICollection _attributes; + private readonly Uid _uid; + private readonly ConnectorObject _object; /** * Creates a SyncDelata - * - * @param uid - * The uid. Must not be null. * @param token * The token. Must not be null. * @param deltaType * The delta. Must not be null. - * @param attributes - * May be null. + * @param uid + * The uid. Must not be null. + * @param object + * The object that has changed. May be null for delete. */ - internal SyncDelta(Uid uid, SyncToken token, SyncDeltaType deltaType, - ICollection attributes) { - Assertions.NullCheck(uid, "uid"); + internal SyncDelta(SyncToken token, SyncDeltaType deltaType, + Uid uid, + ConnectorObject obj) { Assertions.NullCheck(token, "token"); Assertions.NullCheck(deltaType, "deltaType"); + Assertions.NullCheck(uid, "uid"); + + //only allow null object for delete + if ( obj == null && + deltaType != SyncDeltaType.DELETE) { + throw new ArgumentException("ConnectorObject must be specified for anything other than delete."); + } + + //if object not null, make sure its Uid + //matches + if ( obj != null ) { + if (!uid.Equals(obj.Uid)) { + throw new ArgumentException("Uid does not match that of the object."); + } + } - _uid = uid; _token = token; _deltaType = deltaType; - _attributes = CollectionUtil.NewReadOnlySet(attributes); + _uid = uid; + _object = obj; - // make sure attributes don't also contain uid - if (ConnectorAttributeUtil.GetUidAttribute(attributes) != null) { - throw new ArgumentException( - "Attributes must not contain a UID"); - } } /** @@ -2966,6 +2975,18 @@ public Uid Uid { return _uid; } } + + /** + * Returns the connector object that changed. This + * may be null in the case of delete. + * @return The object or possibly null if this + * represents a delete. + */ + public ConnectorObject Object { + get { + return _object; + } + } /** * Returns the SyncToken of the object that changed. @@ -2989,28 +3010,13 @@ public SyncDeltaType DeltaType { } } - /** - * Returns the attributes associated with the change. TODO: Define whether - * this is the whole object or just those that changed. The argument for - * just the changes is that it will be faster. The argument against is that - * the application will need to whole object anyway in most cases and so for - * those cases it will actually be slower. Need some more emperical data - * here... - * - * @return The attributes - */ - public ICollection Attributes { - get { - return _attributes; - } - } public override String ToString() { IDictionary values = new Dictionary(); - values["Uid"] = _uid; values["Token"] = _token; values["DeltaType"] = _deltaType; - values["Attributes"] = _attributes; + values["Uid"] = _uid; + values["Object"] = _object; return values.ToString(); } @@ -3021,16 +3027,21 @@ public override int GetHashCode() { public override bool Equals(Object o) { if ( o is SyncDelta ) { SyncDelta other = (SyncDelta)o; - if (!_uid.Equals(other._uid)) { - return false; - } if (!_token.Equals(other._token)) { return false; } if (!_deltaType.Equals(other._deltaType)) { return false; } - if (!CollectionUtil.SetsEqual(_attributes,other._attributes)) { + if (!_uid.Equals(other._uid)) { + return false; + } + if (_object == null) { + if ( other._object != null ) { + return false; + } + } + else if (!_object.Equals(other._object)) { return false; } return true; @@ -3045,10 +3056,10 @@ public override bool Equals(Object o) { * Builder for {@link SyncDelta}. */ public sealed class SyncDeltaBuilder { - private Uid _uid; private SyncToken _token; private SyncDeltaType _deltaType; - private ICollection _attributes = new HashSet(); + private Uid _uid; + private ConnectorObject _object; /** * Create a new SyncDeltaBuilder @@ -3063,26 +3074,12 @@ public SyncDeltaBuilder() { * @param delta The original delta. */ public SyncDeltaBuilder(SyncDelta delta) { - _uid = delta.Uid; _token = delta.Token; _deltaType = delta.DeltaType; - _attributes = new HashSet(delta.Attributes); - } - - /** - * Returns the Uid of the object that changed. - * - * @return the Uid of the object that changed. - */ - public Uid Uid { - get { - return _uid; - } - set { - _uid = value; - } + _object = delta.Object; + _uid = delta.Uid; } - + /** * Returns the SyncToken of the object that changed. * @@ -3112,50 +3109,51 @@ public SyncDeltaType DeltaType { } /** - * Returns the attributes associated with the change. TODO: Define whether - * this is the whole object or just those that changed. The argument for - * just the changes is that it will be faster. The argument against is that - * the application will need to whole object anyway in most cases and so for - * those cases it will actually be slower. Need some more emperical data - * here... + * Returns the Uid of the object that changed. + * Note that this is implicitly set when you call + * {@link #setObject(ConnectorObject)}. * - * @return The attributes + * @return the Uid of the object that changed. */ - public ICollection Attributes { + public Uid Uid { get { - return _attributes; + return _uid; } set { - _attributes = CollectionUtil.NullAsEmpty(value); + _uid = value; } } /** - * Adds an attribute associated with the change. TODO: Define whether this - * is the whole object or just those that changed. The argument for just the - * changes is that it will be faster. The argument against is that the - * application will need to whole object anyway in most cases and so for - * those cases it will actually be slower. Need some more emperical data - * here... - * - * @param attribute - * The attribute + * Returns the object that changed. + * Sets the object that changed and implicitly + * sets Uid if object is not null. + * @return The object that changed. May be null for + * deletes. */ - public void AddAttribute(ConnectorAttribute attribute) { - Assertions.NullCheck(attribute, "attribute"); - _attributes.Add(attribute); + public ConnectorObject Object { + get { + return _object; + } + set { + _object = value; + if ( value != null ) { + _uid = value.Uid; + } + } } /** - * Creates a SyncDelata. Prior to calling the following must be specified: + * Creates a SyncDelta. Prior to calling the following must be specified: *
    - *
  1. {@link #setUid(Uid) Uid}
  2. + *
  3. {@link #setObject(ConnectorObject) Object} (for anything other than delete)
  4. + *
  5. {@link #setUid(Uid) Uid} (this is implictly set when calling {@link #setObject(ConnectorObject)})
  6. *
  7. {@link #setToken(SyncToken) Token}
  8. *
  9. {@link #setDeltaType(SyncDeltaType) DeltaType}
  10. *
*/ public SyncDelta Build() { - return new SyncDelta(_uid, _token, _deltaType, _attributes); + return new SyncDelta(_token, _deltaType, _uid, _object); } } #endregion diff --git a/FrameworkInternal/ApiLocalOperations.cs b/FrameworkInternal/ApiLocalOperations.cs index b61758fc..e4870487 100644 --- a/FrameworkInternal/ApiLocalOperations.cs +++ b/FrameworkInternal/ApiLocalOperations.cs @@ -408,6 +408,18 @@ public ICollection ReduceToAttrsToGet( } return ret; } + public ConnectorObject ReduceToAttrsToGet(ConnectorObject obj) { + // clone the object and reduce the attributes only the set of + // attributes. + ConnectorObjectBuilder bld = new ConnectorObjectBuilder(); + bld.SetUid(obj.Uid); + bld.SetName(obj.Name); + bld.ObjectClass = obj.ObjectClass; + ICollection objAttrs = obj.GetAttributes(); + ICollection attrs = ReduceToAttrsToGet(objAttrs); + bld.AddAttributes(attrs); + return bld.Build(); + } } #endregion @@ -432,14 +444,7 @@ public SearchAttributesToGetResultsHandler( public bool Handle(ConnectorObject obj) { // clone the object and reduce the attributes only the set of // attributes. - ConnectorObjectBuilder bld = new ConnectorObjectBuilder(); - bld.SetUid(obj.Uid); - bld.SetName(obj.Name); - bld.ObjectClass = obj.ObjectClass; - ICollection objAttrs = obj.GetAttributes(); - ICollection attrs = ReduceToAttrsToGet(objAttrs); - bld.AddAttributes(attrs); - return _handler(bld.Build()); + return _handler(ReduceToAttrsToGet(obj)); } } #endregion @@ -467,9 +472,9 @@ public bool Handle(SyncDelta delta) { bld.Uid = delta.Uid; bld.Token = delta.Token; bld.DeltaType = delta.DeltaType; - ICollection deltaAttrs = delta.Attributes; - ICollection attrs = ReduceToAttrsToGet(deltaAttrs); - bld.Attributes = attrs; + if ( delta.Object != null ) { + bld.Object=ReduceToAttrsToGet(delta.Object); + } return _handler(bld.Build()); } } @@ -775,7 +780,9 @@ public ConnectorObject NormalizeObject(ConnectorObject orig) { public SyncDelta NormalizeSyncDelta(SyncDelta delta) { SyncDeltaBuilder builder = new SyncDeltaBuilder(delta); - builder.Attributes = NormalizeAttributes(delta.Attributes); + if ( delta.Object != null ) { + builder.Object=NormalizeObject(delta.Object); + } return builder.Build(); } diff --git a/FrameworkInternal/Resources.resx b/FrameworkInternal/Resources.resx index 8556b4db..5cfa7407 100644 --- a/FrameworkInternal/Resources.resx +++ b/FrameworkInternal/Resources.resx @@ -410,7 +410,7 @@ OperationOptionInfo | SyncDeltaType | SyncToken | SyncDelta > <!ELEMENT SyncDeltaType (#PCDATA)> <!ELEMENT SyncToken (value)> -<!ELEMENT SyncDelta (Uid,SyncDeltaType,SyncToken,Attributes)> +<!ELEMENT SyncDelta (SyncDeltaType,SyncToken,Uid,ConnectorObject?)> diff --git a/FrameworkInternal/Serializer.cs b/FrameworkInternal/Serializer.cs index 729fd95a..875c9816 100644 --- a/FrameworkInternal/Serializer.cs +++ b/FrameworkInternal/Serializer.cs @@ -1873,22 +1873,19 @@ public SyncDeltaHandler() } public override Object Deserialize(ObjectDecoder decoder) { SyncDeltaBuilder builder = new SyncDeltaBuilder(); - builder.Uid=((Uid)decoder.ReadObjectField("Uid",typeof(Uid),null)); builder.DeltaType=((SyncDeltaType)decoder.ReadObjectField("SyncDeltaType",typeof(SyncDeltaType),null)); builder.Token=((SyncToken)decoder.ReadObjectField("SyncToken",typeof(SyncToken),null)); - ICollection attributesObj = (ICollection)decoder.ReadObjectField("Attributes",typeof(ICollection),null); - ICollection attributes = - CollectionUtil.NewSet(attributesObj); - builder.Attributes=(attributes); + builder.Uid=((Uid)decoder.ReadObjectField("Uid",typeof(Uid),null)); + builder.Object=((ConnectorObject)decoder.ReadObjectField("ConnectorObject",typeof(ConnectorObject),null)); return builder.Build(); } public override void Serialize(Object obj, ObjectEncoder encoder) { SyncDelta val = (SyncDelta)obj; - encoder.WriteObjectField("Uid", val.Uid,true); encoder.WriteObjectField("SyncDeltaType", val.DeltaType,true); encoder.WriteObjectField("SyncToken", val.Token,true); - encoder.WriteObjectField("Attributes", val.Attributes,true); + encoder.WriteObjectField("Uid", val.Uid,true); + encoder.WriteObjectField("ConnectorObject", val.Object, true); } } diff --git a/FrameworkTests/ObjectNormalizerFacadeTests.cs b/FrameworkTests/ObjectNormalizerFacadeTests.cs index 258540c9..2d98b2b1 100644 --- a/FrameworkTests/ObjectNormalizerFacadeTests.cs +++ b/FrameworkTests/ObjectNormalizerFacadeTests.cs @@ -227,12 +227,19 @@ public void TestConnectorObject() [Test] public void TestSyncDelta() { + ConnectorObjectBuilder objbuilder = + new ConnectorObjectBuilder(); + objbuilder.SetName("myname"); + objbuilder.SetUid("myuid"); + objbuilder.AddAttribute(CreateTestAttribute()); + ConnectorObject obj = objbuilder.Build(); + SyncDeltaBuilder builder = new SyncDeltaBuilder(); builder.DeltaType=(SyncDeltaType.DELETE); builder.Token=(new SyncToken("mytoken")); builder.Uid=(new Uid("myuid")); - builder.AddAttribute(CreateTestAttribute()); + builder.Object=(obj); SyncDelta v1 = builder.Build(); SyncDelta v2 = CreateTestNormalizer().NormalizeSyncDelta(v1); builder = @@ -240,7 +247,12 @@ public void TestSyncDelta() builder.DeltaType=(SyncDeltaType.DELETE); builder.Token=(new SyncToken("mytoken")); builder.Uid=(new Uid("myuid")); - builder.AddAttribute(CreateNormalizedTestAttribute()); + objbuilder = + new ConnectorObjectBuilder(); + objbuilder.SetName("myname"); + objbuilder.SetUid("myuid"); + objbuilder.AddAttribute(CreateNormalizedTestAttribute()); + builder.Object = objbuilder.Build(); SyncDelta expected = builder.Build(); Assert.AreEqual(expected, v2); Assert.IsFalse(expected.Equals(v1)); diff --git a/FrameworkTests/ObjectSerializationTests.cs b/FrameworkTests/ObjectSerializationTests.cs index 8a554e41..1b4b280a 100644 --- a/FrameworkTests/ObjectSerializationTests.cs +++ b/FrameworkTests/ObjectSerializationTests.cs @@ -956,20 +956,19 @@ public void TestSyncToken() { [Test] public void TestSyncDelta() { + ConnectorObjectBuilder bld = new ConnectorObjectBuilder(); + bld.SetUid("foo"); + bld.SetName("name"); SyncDeltaBuilder builder = new SyncDeltaBuilder(); builder.Uid=(new Uid("myuid")); builder.DeltaType=(SyncDeltaType.CREATE); builder.Token=(new SyncToken("mytoken")); - builder.AddAttribute(ConnectorAttributeBuilder.Build("foo", "bar")); + builder.Object=(bld.Build()); SyncDelta v1 = builder.Build(); SyncDelta v2 = (SyncDelta)CloneObject(v1); - Assert.AreEqual(new Uid("myuid"),v2.Uid); + Assert.AreEqual(new Uid("foo"),v2.Uid); Assert.AreEqual(new SyncToken("mytoken"),v2.Token); Assert.AreEqual(SyncDeltaType.CREATE,v2.DeltaType); - ICollection attributes = v2.Attributes; - Assert.AreEqual(1,attributes.Count); - ConnectorAttribute attr = attributes.First(); - Assert.AreEqual("foo",attr.Name); Assert.AreEqual(v1,v2); } diff --git a/TestBundleV1/TestConnector.cs b/TestBundleV1/TestConnector.cs index 2c01377b..cdb37cfc 100644 --- a/TestBundleV1/TestConnector.cs +++ b/TestBundleV1/TestConnector.cs @@ -192,9 +192,15 @@ public void Sync(ObjectClass objClass, SyncToken token, SyncResultsHandler handler, OperationOptions options) { for (int i = 0; i < _config.numResults; i++ ) { + ConnectorObjectBuilder obuilder = + new ConnectorObjectBuilder(); + obuilder.SetUid(i.ToString()); + obuilder.SetName(i.ToString()); + obuilder.ObjectClass=(objClass); + SyncDeltaBuilder builder = new SyncDeltaBuilder(); - builder.Uid=(new Uid(""+i)); + builder.Object=(obuilder.Build()); builder.DeltaType=(SyncDeltaType.CREATE); builder.Token=(new SyncToken("mytoken")); SyncDelta rv = builder.Build(); From fcdfe6fab53e6e0b2579fe814aa7dbf21c37f1f6 Mon Sep 17 00:00:00 2001 From: rcauble Date: Tue, 9 Sep 2008 16:08:37 +0000 Subject: [PATCH 004/342] make Schema.toString a bit less verbose --- Framework/CommonObjects.cs | 7 +------ 1 file changed, 1 insertion(+), 6 deletions(-) diff --git a/Framework/CommonObjects.cs b/Framework/CommonObjects.cs index db13f795..5962afad 100644 --- a/Framework/CommonObjects.cs +++ b/Framework/CommonObjects.cs @@ -2415,12 +2415,7 @@ public override bool Equals(object o) { public override string ToString() { - IDictionary map = new Dictionary(); - map["ObjectClasses"] = _declaredObjectClasses; - map["Options"] = _declaredOperationOptions; - map["SupportedClasses"] = _supportedObjectClassesByOperation; - map["SupportedOptions"] = _supportedOptionsByOperation; - return map.ToString(); + return SerializerUtil.SerializeXmlObject(this, false); } } #endregion From 1d4f2343711cf42f4c3c4084c6a351a045f203b9 Mon Sep 17 00:00:00 2001 From: rcauble Date: Tue, 9 Sep 2008 20:13:37 +0000 Subject: [PATCH 005/342] Issue#248: move to thread local locale --- Framework/Api.cs | 3 +-- Framework/CommonObjects.cs | 5 ++--- Framework/Spi.cs | 9 --------- FrameworkInternal/Api.cs | 20 +++++--------------- FrameworkInternal/ApiLocal.cs | 5 ----- FrameworkInternal/ApiRemote.cs | 3 +++ FrameworkInternal/Resources.resx | 2 +- FrameworkInternal/Serializer.cs | 3 --- FrameworkInternal/Server.cs | 14 ++++++++++++-- FrameworkTests/ConnectorInfoManagerTests.cs | 18 ++++++++++++++---- FrameworkTests/ObjectSerializationTests.cs | 2 -- TestBundleV1/TestConnector.cs | 2 +- 12 files changed, 39 insertions(+), 47 deletions(-) diff --git a/Framework/Api.cs b/Framework/Api.cs index 031fe1ff..b37755a2 100644 --- a/Framework/Api.cs +++ b/Framework/Api.cs @@ -61,7 +61,6 @@ public interface APIConfiguration { ConfigurationProperties ConfigurationProperties { get; } bool IsConnectorPoolingSupported { get; } ObjectPoolConfiguration ConnectorPoolConfiguration { get; } - CultureInfo CultureInfo { get; set; } ICollection SupportedOperations { get; } int GetTimeout(Type operation); @@ -217,7 +216,7 @@ public interface ConnectorInfo { * * @return The friendly name */ - string GetConnectorDisplayName(CultureInfo info); + string GetConnectorDisplayName(); ConnectorKey ConnectorKey { get; } diff --git a/Framework/CommonObjects.cs b/Framework/CommonObjects.cs index 5962afad..2104792c 100644 --- a/Framework/CommonObjects.cs +++ b/Framework/CommonObjects.cs @@ -1018,15 +1018,14 @@ public static ConnectorAttribute BuildPasswordChangeInterval(long val) { */ public interface ConnectorMessages { /** - * Formats the given message key. - * @param locale The locale to format in. If null, uses the default locale. + * Formats the given message key in the current UI culture. * @param key The message key to format. * @param dflt The default message if key is not found. If null, defaults * to key. * @param args Parameters with which to format the message. * @return The formatted string. */ - String Format(CultureInfo locale, String key, String dflt, params object [] args); + String Format(String key, String dflt, params object [] args); } #endregion diff --git a/Framework/Spi.cs b/Framework/Spi.cs index 1b246c34..242e2446 100644 --- a/Framework/Spi.cs +++ b/Framework/Spi.cs @@ -78,14 +78,6 @@ public interface AttributeNormalizer /// Configuration information for the Connector. /// public interface Configuration { - /// - /// Called after the instance is created to make - /// sure that the has the proper locale to create messages - /// for Exceptions etc. We don't explicitly require a getter for - /// CultureInfo but we strongly recommend it. - /// - /// current locale the is operating in. - CultureInfo CultureInfo{get;set;} ConnectorMessages ConnectorMessages{get;set;} @@ -98,7 +90,6 @@ public interface Configuration { #region AbstractConfiguration public abstract class AbstractConfiguration : Configuration { - public CultureInfo CultureInfo{get;set;} public ConnectorMessages ConnectorMessages{get;set;} diff --git a/FrameworkInternal/Api.cs b/FrameworkInternal/Api.cs index 8b6124bc..54094d6a 100644 --- a/FrameworkInternal/Api.cs +++ b/FrameworkInternal/Api.cs @@ -128,10 +128,9 @@ public override bool Equals(Object o) { } private String FormatMessage(String key, String dflt, params object [] args) { APIConfigurationImpl apiConfig = Parent.Parent; - CultureInfo locale = apiConfig.CultureInfo; ConnectorMessages messages = apiConfig.ConnectorInfo.Messages; - return messages.Format(locale, key, dflt, args); + return messages.Format(key, dflt, args); } } #endregion @@ -219,7 +218,6 @@ public class APIConfigurationImpl : APIConfiguration { private IDictionary _timeoutMap = new Dictionary(); - private CultureInfo _cultureInfo; public ConfigurationProperties ConfigurationProperties { get { @@ -256,14 +254,6 @@ public ObjectPoolConfiguration ConnectorPoolConfiguration _connectorPooling = value; } } - public CultureInfo CultureInfo { - get { - return _cultureInfo == null ? CultureInfo.CurrentCulture : _cultureInfo; - } - set { - _cultureInfo = value; - } - } public ICollection SupportedOperations { get { return _supportedOperations; @@ -302,9 +292,8 @@ public class AbstractConnectorInfo : ConnectorInfo { private APIConfigurationImpl _defaultAPIConfiguration; - public string GetConnectorDisplayName(CultureInfo info) { - return Messages.Format(info, - ConnectorDisplayNameKey, + public string GetConnectorDisplayName() { + return Messages.Format(ConnectorDisplayNameKey, ConnectorKey.ConnectorName); } @@ -345,10 +334,11 @@ public class ConnectorMessagesImpl : ConnectorMessages { private IDictionary> _catalogs = new Dictionary>(); - public String Format(CultureInfo locale, String key, String dflt, params object [] args) { + public String Format(String key, String dflt, params object [] args) { if ( key == null ) { return dflt; } + CultureInfo locale = CultureInfo.CurrentUICulture; if ( locale == null ) { locale = CultureInfo.CurrentCulture; } diff --git a/FrameworkInternal/ApiLocal.cs b/FrameworkInternal/ApiLocal.cs index 058d59b5..3af4e165 100644 --- a/FrameworkInternal/ApiLocal.cs +++ b/FrameworkInternal/ApiLocal.cs @@ -245,7 +245,6 @@ public static Configuration CreateBean(ConfigurationPropertiesImpl properties, Type config) { Configuration rv = (Configuration)Activator.CreateInstance(config); - rv.CultureInfo=(properties.Parent.CultureInfo); rv.ConnectorMessages=properties.Parent.ConnectorInfo.Messages; IDictionary descriptors = GetFilteredProperties(config); @@ -283,10 +282,6 @@ private static IDictionary //if there's no setter, ignore it continue; } - if ( "CultureInfo".Equals(propName) ) { - // exclude setLocale since its part of the interface.. - continue; - } if ("ConnectorMessages".Equals(propName)) { continue; } diff --git a/FrameworkInternal/ApiRemote.cs b/FrameworkInternal/ApiRemote.cs index 40c28dd1..4b5429bd 100644 --- a/FrameworkInternal/ApiRemote.cs +++ b/FrameworkInternal/ApiRemote.cs @@ -43,6 +43,7 @@ using System.Net; using System.Reflection; using System.Security.Authentication; +using System.Globalization; using System.Net.Security; using System.Net.Sockets; using System.Security.Cryptography.X509Certificates; @@ -181,6 +182,7 @@ public RemoteConnectorInfoManagerImpl(RemoteFrameworkConnectionInfo info) { using (RemoteFrameworkConnection connection = new RemoteFrameworkConnection(info)) { + connection.WriteObject(CultureInfo.CurrentUICulture); connection.WriteObject(info.Key); connection.WriteObject(new HelloRequest()); HelloResponse response = (HelloResponse)connection.ReadObject(); @@ -303,6 +305,7 @@ public Object Invoke(Object proxy, MethodInfo method, Object[] args) RemoteFrameworkConnection connection = new RemoteFrameworkConnection(connectionInfo); try { + connection.WriteObject(CultureInfo.CurrentUICulture); connection.WriteObject(connectionInfo.Key); //send the request connection.WriteObject(request); diff --git a/FrameworkInternal/Resources.resx b/FrameworkInternal/Resources.resx index 5cfa7407..3bd169cb 100644 --- a/FrameworkInternal/Resources.resx +++ b/FrameworkInternal/Resources.resx @@ -263,7 +263,7 @@ OperationOptionInfo | SyncDeltaType | SyncToken | SyncDelta <!ELEMENT value (%xmlObject;)> <!ELEMENT ConfigurationProperties ((ConfigurationProperty)*)> -<!ELEMENT APIConfiguration (connectorPoolConfiguration,ConfigurationProperties,timeoutMap,SupportedOperations,Locale)> +<!ELEMENT APIConfiguration (connectorPoolConfiguration,ConfigurationProperties,timeoutMap,SupportedOperations)> <!ATTLIST APIConfiguration connectorPoolingSupported CDATA #REQUIRED producerBufferSize CDATA #REQUIRED diff --git a/FrameworkInternal/Serializer.cs b/FrameworkInternal/Serializer.cs index 875c9816..2b9c875a 100644 --- a/FrameworkInternal/Serializer.cs +++ b/FrameworkInternal/Serializer.cs @@ -1169,7 +1169,6 @@ public override Object Deserialize(ObjectDecoder decoder) { CollectionUtil.NewSet(setObj); rv.SupportedOperations=(set); rv.ProducerBufferSize=(decoder.ReadIntField("producerBufferSize",0)); - rv.CultureInfo=((CultureInfo)decoder.ReadObjectField("Locale",typeof(CultureInfo),null)); return rv; } @@ -1188,8 +1187,6 @@ public override void Serialize(Object obj, ObjectEncoder encoder) { val.TimeoutMap,false); encoder.WriteObjectField("SupportedOperations", val.SupportedOperations,true); - encoder.WriteObjectField("Locale", - val.CultureInfo,true); } } private class ConnectorMessagesHandler : AbstractObjectSerializationHandler { diff --git a/FrameworkInternal/Server.cs b/FrameworkInternal/Server.cs index 01ef76c1..20ec857a 100644 --- a/FrameworkInternal/Server.cs +++ b/FrameworkInternal/Server.cs @@ -40,6 +40,7 @@ using System; using System.Collections.Generic; using System.Diagnostics; +using System.Globalization; using System.Net; using System.Net.Security; using System.Security; @@ -345,13 +346,22 @@ public void Run() { private bool ProcessRequest() { - GuardedString key; + + CultureInfo locale; try { - key = (GuardedString)_connection.ReadObject(); + locale = (CultureInfo)_connection.ReadObject(); } catch (EndOfStreamException) { return false; } + + //We can't set this because C# does not like language-neutral + //cultures for CurrentCulture - this tends to blow up + //TODO: think more about this... + //Thread.CurrentThread.CurrentCulture = locale; + Thread.CurrentThread.CurrentUICulture = locale; + + GuardedString key = (GuardedString)_connection.ReadObject(); bool authorized; try { diff --git a/FrameworkTests/ConnectorInfoManagerTests.cs b/FrameworkTests/ConnectorInfoManagerTests.cs index 48798fe4..2058d38e 100644 --- a/FrameworkTests/ConnectorInfoManagerTests.cs +++ b/FrameworkTests/ConnectorInfoManagerTests.cs @@ -123,21 +123,22 @@ public void TestAPIConfiguration() Assert.IsNotNull(property); + Thread.CurrentThread.CurrentUICulture = new CultureInfo("en"); Assert.AreEqual("Help for test field.",property.GetHelpMessage(null)); Assert.AreEqual("Display for test field.",property.GetDisplayName(null)); CultureInfo eslocale = new CultureInfo("es"); - api.CultureInfo = eslocale; + Thread.CurrentThread.CurrentUICulture = eslocale; Assert.AreEqual("tstField.help_es",property.GetHelpMessage(null)); Assert.AreEqual("tstField.display_es",property.GetDisplayName(null)); CultureInfo esESlocale = new CultureInfo("es-ES"); - api.CultureInfo = esESlocale; + Thread.CurrentThread.CurrentUICulture = esESlocale; Assert.AreEqual("tstField.help_es-ES",property.GetHelpMessage(null)); Assert.AreEqual("tstField.display_es-ES",property.GetDisplayName(null)); CultureInfo esARlocale = new CultureInfo("es-AR"); - api.CultureInfo = esARlocale; + Thread.CurrentThread.CurrentUICulture = esARlocale; Assert.AreEqual("tstField.help_es",property.GetHelpMessage(null)); Assert.AreEqual("tstField.display_es",property.GetDisplayName(null)); @@ -168,11 +169,20 @@ public void TestValidate() { property.Value=true; facade = facf.NewInstance(api); try { + Thread.CurrentThread.CurrentUICulture = new CultureInfo("en"); facade.Validate(); Assert.Fail("exception expected"); } catch (ConnectorException e) { - Assert.AreEqual("validation failed", e.Message); + Assert.AreEqual("validation failed en", e.Message); + } + try { + Thread.CurrentThread.CurrentUICulture = new CultureInfo("es"); + facade.Validate(); + Assert.Fail("exception expected"); + } + catch (ConnectorException e) { + Assert.AreEqual("validation failed es", e.Message); } ShutdownConnnectorInfoManager(); } diff --git a/FrameworkTests/ObjectSerializationTests.cs b/FrameworkTests/ObjectSerializationTests.cs index 1b4b280a..69eaa9ea 100644 --- a/FrameworkTests/ObjectSerializationTests.cs +++ b/FrameworkTests/ObjectSerializationTests.cs @@ -437,7 +437,6 @@ public void TestAPIConfiguration() { v1.ConnectorPoolConfiguration=(new ObjectPoolConfiguration()); v1.ConfigurationProperties=(props1); v1.IsConnectorPoolingSupported=(true); - v1.CultureInfo=(new CultureInfo("en")); v1.ProducerBufferSize=(200); v1.SupportedOperations=(FrameworkUtil.AllAPIOperations()); IDictionary map = @@ -452,7 +451,6 @@ public void TestAPIConfiguration() { Assert.AreEqual(v1.ConnectorPoolConfiguration,v2.ConnectorPoolConfiguration); Assert.AreEqual(v1.ConfigurationProperties,v2.ConfigurationProperties); Assert.IsTrue(v2.IsConnectorPoolingSupported); - Assert.AreEqual(new CultureInfo("en"), v2.CultureInfo); Assert.AreEqual(200, v2.ProducerBufferSize); Assert.IsTrue(CollectionUtil.SetsEqual( FrameworkUtil.AllAPIOperations(), diff --git a/TestBundleV1/TestConnector.cs b/TestBundleV1/TestConnector.cs index cdb37cfc..e1128eee 100644 --- a/TestBundleV1/TestConnector.cs +++ b/TestBundleV1/TestConnector.cs @@ -74,7 +74,7 @@ public class TstConnectorConfig : AbstractConfiguration public override void Validate() { if (failValidation) { - throw new ConnectorException("validation failed"); + throw new ConnectorException("validation failed "+CultureInfo.CurrentUICulture.TwoLetterISOLanguageName); } } From a0874c0271be3fce006ad6720f07cdf65b856d71 Mon Sep 17 00:00:00 2001 From: dvernon Date: Tue, 9 Sep 2008 22:30:28 +0000 Subject: [PATCH 006/342] Issue #64 - Incremental checkin. Clean up a couple things that Will pointed out with locking, and fix syncdelta to use connector object instead of just attribute list --- .../ActiveDirectoryConnector.cs | 187 ++++++++---------- .../ActiveDirectoryConnectorTest.cs | 7 +- 2 files changed, 83 insertions(+), 111 deletions(-) diff --git a/ActiveDirectoryConnector/ActiveDirectoryConnector.cs b/ActiveDirectoryConnector/ActiveDirectoryConnector.cs index a3306a5d..bb29a2a4 100644 --- a/ActiveDirectoryConnector/ActiveDirectoryConnector.cs +++ b/ActiveDirectoryConnector/ActiveDirectoryConnector.cs @@ -66,7 +66,7 @@ namespace Org.IdentityConnectors.ActiveDirectory /// public class ActiveDirectoryConnector : CreateOp, Connector, SchemaOp, DeleteOp, SearchOp, TestOp, AdvancedUpdateOp, ScriptOnResourceOp, SyncOp, - AuthenticateOp, AttributeNormalizer + AuthenticateOp, AttributeNormalizer, PoolableConnector { // This is the list of attributes returned by default if no attributes are // requested in the options field of ExecuteQuery @@ -142,29 +142,13 @@ public class ActiveDirectoryConnector : CreateOp, Connector, SchemaOp, DeleteOp, ActiveDirectoryConfiguration _configuration = null; ActiveDirectoryUtils _utils = null; - private static Schema _schema = null; - private static object _schema_lock = new object(); - private static object _defaultAttributes_lock = new object(); + private Schema _schema = null; - public ActiveDirectoryConnector() + static ActiveDirectoryConnector() { - // if no one has initialized the default attributes, - // initialize them - if (AttributesReturnedByDefault == null) - { - // lock the default attibutes lock object - lock (_defaultAttributes_lock) - { - // make sure no one populated this while we - // were waiting for the lock - if (AttributesReturnedByDefault == null) - { - // populate default attributes - AttributesReturnedByDefault = new Dictionary>(); - AttributesReturnedByDefault.Add(ObjectClass.ACCOUNT, AccountAttributesReturnedByDefault); - } - } - } + // populate default attributes + AttributesReturnedByDefault = new Dictionary>(); + AttributesReturnedByDefault.Add(ObjectClass.ACCOUNT, AccountAttributesReturnedByDefault); } #region CreateOp Members @@ -287,72 +271,69 @@ public Schema Schema() return _schema; } - lock (_schema_lock) + // could have blocked on the lock while someone else built the + // schema, so check one more time before building + if (_schema == null) { - // could have blocked on the lock while someone else built the - // schema, so check one more time before building - if (_schema == null) - { - String serverName = _configuration.LDAPHostName; - Forest forest = null; + String serverName = _configuration.LDAPHostName; + Forest forest = null; - if ((serverName == null) || (serverName.Length == 0)) - { - // get the active directory schema - DirectoryContext context = new DirectoryContext( - DirectoryContextType.Domain, - _configuration.DomainName, - _configuration.DirectoryAdminName, - _configuration.DirectoryAdminPassword); - DomainController dc = DomainController.FindOne(context); - forest = dc.Forest; - } - else - { - DirectoryContext context = new DirectoryContext( - DirectoryContextType.DirectoryServer, - _configuration.LDAPHostName, - _configuration.DirectoryAdminName, - _configuration.DirectoryAdminPassword); - forest = Forest.GetForest(context); - } - - ActiveDirectorySchema ADSchema = forest.Schema; - - - // get the user attribute infos and operations - ICollection userAttributeInfos = - GetUserAttributeInfos(ADSchema); - ICollection userOperations = GetUserOperations(); - ObjectClassInfoBuilder ociBuilder = new ObjectClassInfoBuilder(); - ociBuilder.ObjectType = ObjectClass.ACCOUNT_NAME; - ociBuilder.AddAllAttributeInfo(userAttributeInfos); - ObjectClassInfo userInfo = ociBuilder.Build(); - - // get the group attribute infos and operations - ICollection groupAttributeInfos = - GetGroupAttributeInfos(ADSchema); - ociBuilder = new ObjectClassInfoBuilder(); - ociBuilder.ObjectType = ObjectClass.GROUP_NAME; - ociBuilder.AddAllAttributeInfo(groupAttributeInfos); - ICollection groupOperations = GetGroupOperations(); - ObjectClassInfo groupInfo = ociBuilder.Build(); - - // get the organizationalUnit attribute infos and operations - ICollection ouAttributeInfos = - GetOuAttributeInfos(ADSchema); - ociBuilder = new ObjectClassInfoBuilder(); - ociBuilder.ObjectType = OBJECTCLASS_OU; - ociBuilder.AddAllAttributeInfo(ouAttributeInfos); - ICollection ouOperations = GetOuOperations(); - ObjectClassInfo ouInfo = ociBuilder.Build(); - - SchemaBuilder schemaBuilder = new SchemaBuilder(typeof(ActiveDirectoryConnector)); - schemaBuilder.DefineObjectClass(userInfo); - schemaBuilder.DefineObjectClass(groupInfo); - schemaBuilder.DefineObjectClass(ouInfo); - _schema = schemaBuilder.Build(); + if ((serverName == null) || (serverName.Length == 0)) + { + // get the active directory schema + DirectoryContext context = new DirectoryContext( + DirectoryContextType.Domain, + _configuration.DomainName, + _configuration.DirectoryAdminName, + _configuration.DirectoryAdminPassword); + DomainController dc = DomainController.FindOne(context); + forest = dc.Forest; } + else + { + DirectoryContext context = new DirectoryContext( + DirectoryContextType.DirectoryServer, + _configuration.LDAPHostName, + _configuration.DirectoryAdminName, + _configuration.DirectoryAdminPassword); + forest = Forest.GetForest(context); + } + + ActiveDirectorySchema ADSchema = forest.Schema; + + + // get the user attribute infos and operations + ICollection userAttributeInfos = + GetUserAttributeInfos(ADSchema); + ICollection userOperations = GetUserOperations(); + ObjectClassInfoBuilder ociBuilder = new ObjectClassInfoBuilder(); + ociBuilder.ObjectType = ObjectClass.ACCOUNT_NAME; + ociBuilder.AddAllAttributeInfo(userAttributeInfos); + ObjectClassInfo userInfo = ociBuilder.Build(); + + // get the group attribute infos and operations + ICollection groupAttributeInfos = + GetGroupAttributeInfos(ADSchema); + ociBuilder = new ObjectClassInfoBuilder(); + ociBuilder.ObjectType = ObjectClass.GROUP_NAME; + ociBuilder.AddAllAttributeInfo(groupAttributeInfos); + ICollection groupOperations = GetGroupOperations(); + ObjectClassInfo groupInfo = ociBuilder.Build(); + + // get the organizationalUnit attribute infos and operations + ICollection ouAttributeInfos = + GetOuAttributeInfos(ADSchema); + ociBuilder = new ObjectClassInfoBuilder(); + ociBuilder.ObjectType = OBJECTCLASS_OU; + ociBuilder.AddAllAttributeInfo(ouAttributeInfos); + ICollection ouOperations = GetOuOperations(); + ObjectClassInfo ouInfo = ociBuilder.Build(); + + SchemaBuilder schemaBuilder = new SchemaBuilder(typeof(ActiveDirectoryConnector)); + schemaBuilder.DefineObjectClass(userInfo); + schemaBuilder.DefineObjectClass(groupInfo); + schemaBuilder.DefineObjectClass(ouInfo); + _schema = schemaBuilder.Build(); } return _schema; @@ -760,23 +741,19 @@ private void ExecuteQuery(ObjectClass oclass, string query, foreach (string attributeName in attributesToReturn) { + SearchResult savedResults = savedDcResult; // if we are using the global catalog, we had to get the // dc's version of the directory entry, but for usnchanged, // we need the gc version of it if (useGlobalCatalog && attributeName.Equals(ATT_USN_CHANGED, StringComparison.CurrentCultureIgnoreCase)) { - AddAttributeIfNotNull(builder, - _utils.GetConnectorAttributeFromADEntry( - oclass, attributeName, savedGcResult)); + savedResults = savedGcResult; } - else - { - AddAttributeIfNotNull(builder, - _utils.GetConnectorAttributeFromADEntry( - oclass, attributeName, savedDcResult)); - } + AddAttributeIfNotNull(builder, + _utils.GetConnectorAttributeFromADEntry( + oclass, attributeName, savedResults)); } } else @@ -1009,18 +986,7 @@ public SyncResults(SyncResultsHandler syncResultsHandler, public bool SyncHandler(ConnectorObject obj) { SyncDeltaBuilder builder = new SyncDeltaBuilder(); - - // for some reason, this cannot contain a uid, - // if it does, the builder throws an exception - ICollection attributes = obj.GetAttributes(); - foreach (ConnectorAttribute attribute in attributes) - { - if (!Uid.NAME.Equals(attribute.Name, StringComparison.CurrentCultureIgnoreCase)) - { - builder.AddAttribute(attribute); - } - } - + builder.Object = obj; ConnectorAttribute tokenAttr = ConnectorAttributeUtil.Find(ATT_USN_CHANGED, obj.GetAttributes()); if(tokenAttr == null) { @@ -1229,5 +1195,14 @@ public ConnectorAttribute NormalizeAttribute(ObjectClass oclass, ConnectorAttrib } #endregion + + #region PoolableConnector Members + + public void CheckAlive() + { + return; + } + + #endregion } } diff --git a/ActiveDirectoryConnectorTests/ActiveDirectoryConnectorTest.cs b/ActiveDirectoryConnectorTests/ActiveDirectoryConnectorTest.cs index 221cb5a3..861ef4ad 100644 --- a/ActiveDirectoryConnectorTests/ActiveDirectoryConnectorTest.cs +++ b/ActiveDirectoryConnectorTests/ActiveDirectoryConnectorTest.cs @@ -1428,12 +1428,9 @@ public bool SyncHandler_ModifiedAccounts(SyncDelta delta) if (_mods.ContainsKey(delta.Uid)) { ICollection requestedAttrs = _mods[delta.Uid]; - ConnectorObjectBuilder builder = new ConnectorObjectBuilder(); - builder.ObjectClass = ObjectClass.ACCOUNT; - builder.SetUid(delta.Uid); - builder.AddAttributes(delta.Attributes); + ActiveDirectoryConnectorTest.VerifyObject(requestedAttrs, - builder.Build()); + delta.Object); _mods.Remove(delta.Uid); } } From 94b42052664b5ce64829608890f406e2af2abc63 Mon Sep 17 00:00:00 2001 From: rcauble Date: Thu, 11 Sep 2008 16:24:19 +0000 Subject: [PATCH 007/342] Issue#250: hashCode incorrect for SyncToken --- Common/CollectionUtil.cs | 77 ++++++++++++++++++++++++--- Framework/CommonObjects.cs | 6 +-- FrameworkInternal/Api.cs | 2 +- FrameworkTests/CollectionUtilTests.cs | 70 ++++++++++++++++++++++++ 4 files changed, 144 insertions(+), 11 deletions(-) diff --git a/Common/CollectionUtil.cs b/Common/CollectionUtil.cs index 8d94f3d8..2c188cc3 100644 --- a/Common/CollectionUtil.cs +++ b/Common/CollectionUtil.cs @@ -197,25 +197,34 @@ public static IDictionary NewIdentityDictionary() /// is suitable for arrays, lists, sets, and dictionaries /// /// The enumerable - /// The optional equality comparer /// The hashcode - public static int HashCode(IEnumerable enum1, - IEqualityComparer comp) { - if ( comp == null ) { - comp = EqualityComparer.Default; - } + public static int GetEnumerableHashCode(IEnumerable enum1) { if ( enum1 == null ) { return 0; } int rv = 0; foreach (T val1 in enum1) { unchecked { - rv+=comp.GetHashCode(val1); + rv+=CollectionUtil.GetHashCode(val1); } } return rv; } + /// + /// Computes a hashCode for a key value pair. + /// + /// The pair + /// The hashcode + public static int GetKeyValuePairHashCode(KeyValuePair pair) { + int rv = 0; + unchecked { + rv+=CollectionUtil.GetHashCode(pair.Key); + rv+=CollectionUtil.GetHashCode(pair.Value); + } + return rv; + } + /// /// Returns true iff the two sets contain the same elements. This is only for /// sets and dictionaries. Does not work for Lists or Arrays. @@ -641,6 +650,60 @@ public static bool ListsEqual(IList v1, return true; } + /** + * hashCode function that properly handles arrays, + * collections, maps, collections of arrays, and maps of arrays. + * @param o The object. May be null. + * @return the hashCode + */ + public static int GetHashCode(Object o) { + if ( o == null ) { + return 0; + } + else if ( o is Array ) { + Array array = (Array)o; + int length = array.Length; + int rv = 0; + for ( int i = 0; i < length; i++ ) { + Object el = array.GetValue(i); + unchecked { + rv += CollectionUtil.GetHashCode(el); + } + } + return rv; + } + else if ( ReflectionUtil.IsParentTypeOf(typeof(KeyValuePair<,>),o.GetType()) ) { + Type parent = ReflectionUtil.FindInHierarchyOf(typeof(KeyValuePair<,>),o.GetType()); + Type [] genericArguments = + parent.GetGenericArguments(); + + Type collectionUtil = typeof(CollectionUtil); + MethodInfo info = collectionUtil.GetMethod("GetKeyValuePairHashCode"); + + info = info.MakeGenericMethod(genericArguments); + + Object rv = info.Invoke(null, new object[]{o}); + return (int)rv; + } + else if ( ReflectionUtil.IsParentTypeOf(typeof(ICollection<>),o.GetType()) ) { + Type parent = ReflectionUtil.FindInHierarchyOf(typeof(ICollection<>),o.GetType()); + + Type [] genericArguments = + parent.GetGenericArguments(); + + + Type collectionUtil = typeof(CollectionUtil); + MethodInfo info = collectionUtil.GetMethod("GetEnumerableHashCode"); + + info = info.MakeGenericMethod(genericArguments); + + Object rv = info.Invoke(null, new object[]{o}); + return (int)rv; + } + else { + return o.GetHashCode(); + } + } /** * Equality function that properly handles arrays, diff --git a/Framework/CommonObjects.cs b/Framework/CommonObjects.cs index 2104792c..d5c54793 100644 --- a/Framework/CommonObjects.cs +++ b/Framework/CommonObjects.cs @@ -1075,7 +1075,7 @@ public ObjectClass ObjectClass { } } public override int GetHashCode() { - return CollectionUtil.HashCode(_attrs,null); + return CollectionUtil.GetHashCode(_attrs); } public override bool Equals(Object o) { ConnectorObject other = o as ConnectorObject; @@ -2387,7 +2387,7 @@ public IDictionary> SupportedOptionsByOper public override int GetHashCode() { - return CollectionUtil.HashCode(_declaredObjectClasses,null); + return CollectionUtil.GetHashCode(_declaredObjectClasses); } public override bool Equals(object o) { @@ -3228,7 +3228,7 @@ public override String ToString() { } public override int GetHashCode() { - return _value.GetHashCode(); + return CollectionUtil.GetHashCode(_value); } public override bool Equals(Object o) { diff --git a/FrameworkInternal/Api.cs b/FrameworkInternal/Api.cs index 54094d6a..c5b476ca 100644 --- a/FrameworkInternal/Api.cs +++ b/FrameworkInternal/Api.cs @@ -190,7 +190,7 @@ public void SetPropertyValue(String name, object val) { public override int GetHashCode() { - return CollectionUtil.HashCode(_properties,null); + return CollectionUtil.GetHashCode(_properties); } public override bool Equals(object o) { diff --git a/FrameworkTests/CollectionUtilTests.cs b/FrameworkTests/CollectionUtilTests.cs index c86e3bc7..bac30fe1 100644 --- a/FrameworkTests/CollectionUtilTests.cs +++ b/FrameworkTests/CollectionUtilTests.cs @@ -103,5 +103,75 @@ public void TestEquals() { set1.Add("val2"); Assert.IsTrue(CollectionUtil.Equals(set1, set2)); } + [Test] + public void TestHashCode() { + Assert.AreEqual(0,CollectionUtil.GetHashCode(null)); + Assert.AreEqual("str".GetHashCode(),CollectionUtil.GetHashCode("str")); + + byte [] arr1 = new byte[] { 1,2,3 }; + byte [] arr2 = new byte[] { 1,2,3 }; + byte [] arr3 = new byte[] { 1,2,4 }; + byte [] arr4 = new byte[] { 1,2 }; + int [] arr5 = new int[] {1,2,3}; + + Assert.AreEqual(CollectionUtil.GetHashCode(arr1), + CollectionUtil.GetHashCode(arr2)); + Assert.IsFalse(CollectionUtil.GetHashCode(arr2) == + CollectionUtil.GetHashCode(arr3)); + Assert.IsFalse(CollectionUtil.GetHashCode(arr2) == + CollectionUtil.GetHashCode(arr4)); + Assert.IsTrue(CollectionUtil.GetHashCode(arr2) == + CollectionUtil.GetHashCode(arr5)); + + List list1 = new List(); + List list2 = new List(); + list1.Add(arr1); + list2.Add(arr2); + + Assert.IsTrue(CollectionUtil.GetHashCode(list1) == + CollectionUtil.GetHashCode(list2)); + + list2.Add(arr2); + Assert.IsFalse(CollectionUtil.GetHashCode(list1) == + CollectionUtil.GetHashCode(list2)); + + list1.Add(arr1); + Assert.IsTrue(CollectionUtil.GetHashCode(list1) == + CollectionUtil.GetHashCode(list2)); + + list1.Add(arr1); + list2.Add(arr3); + Assert.IsFalse(CollectionUtil.GetHashCode(list1) == + CollectionUtil.GetHashCode(list2)); + + Dictionary map1 = new Dictionary(); + Dictionary map2 = new Dictionary(); + map1["key1"] = arr1; + map2["key1"] = arr2; + Assert.IsTrue(CollectionUtil.GetHashCode(map1) == + CollectionUtil.GetHashCode(map2)); + map2["key2"] = arr2; + Assert.IsFalse(CollectionUtil.GetHashCode(map1) == + CollectionUtil.GetHashCode(map2)); + map1["key2"] = arr1; + Assert.IsTrue(CollectionUtil.GetHashCode(map1) == + CollectionUtil.GetHashCode(map2)); + map1["key2"] = arr3; + Assert.IsFalse(CollectionUtil.GetHashCode(map1) == + CollectionUtil.GetHashCode(map2)); + + HashSet set1 = new HashSet(); + HashSet set2 = new HashSet(); + set1.Add("val"); + set2.Add("val"); + Assert.IsTrue(CollectionUtil.GetHashCode(set1) == + CollectionUtil.GetHashCode(set2)); + set2.Add("val2"); + Assert.IsFalse(CollectionUtil.GetHashCode(set1) == + CollectionUtil.GetHashCode(set2)); + set1.Add("val2"); + Assert.IsTrue(CollectionUtil.GetHashCode(set1) == + CollectionUtil.GetHashCode(set2)); + } } } From dfcf890c9d9a977a5bb8faddc9ebaeb91a8a5603 Mon Sep 17 00:00:00 2001 From: rcauble Date: Thu, 11 Sep 2008 19:31:48 +0000 Subject: [PATCH 008/342] Issue#256: support multiple methods per operation --- FrameworkInternal/ApiRemote.cs | 1 + FrameworkInternal/ApiRemoteMessages.cs | 15 +++++++++++ FrameworkInternal/Resources.resx | 1 + FrameworkInternal/Serializer.cs | 5 ++++ FrameworkInternal/Server.cs | 30 +++++++++++++++++----- FrameworkTests/ObjectSerializationTests.cs | 2 ++ 6 files changed, 47 insertions(+), 7 deletions(-) diff --git a/FrameworkInternal/ApiRemote.cs b/FrameworkInternal/ApiRemote.cs index 4b5429bd..a1121530 100644 --- a/FrameworkInternal/ApiRemote.cs +++ b/FrameworkInternal/ApiRemote.cs @@ -299,6 +299,7 @@ public Object Invoke(Object proxy, MethodInfo method, Object[] args) connectorInfo.ConnectorKey, _configuration, _operation, + method.Name, simpleMarshallArgs); //create the connection diff --git a/FrameworkInternal/ApiRemoteMessages.cs b/FrameworkInternal/ApiRemoteMessages.cs index ec74563c..425d632d 100644 --- a/FrameworkInternal/ApiRemoteMessages.cs +++ b/FrameworkInternal/ApiRemoteMessages.cs @@ -116,6 +116,13 @@ public class OperationRequest : Message { */ private readonly Type _operation; + /** + * The name of the method since operations can have more + * than one method. + * NOTE: this is case-insensitive + */ + private readonly String _operationMethodName; + /** * The arguments to the operation. In general, these correspond * to the actual arguments of the method. The one exception is @@ -126,10 +133,12 @@ public class OperationRequest : Message { public OperationRequest(ConnectorKey key, APIConfigurationImpl apiConfiguration, Type operation, + string operationMethodName, IList arguments) { _connectorKey = key; _configuration = apiConfiguration; _operation = operation; + _operationMethodName = operationMethodName; _arguments = CollectionUtil.NewReadOnlyList(arguments); } @@ -151,6 +160,12 @@ public Type Operation { } } + public string OperationMethodName { + get { + return _operationMethodName; + } + } + public IList Arguments { get { return _arguments; diff --git a/FrameworkInternal/Resources.resx b/FrameworkInternal/Resources.resx index 3bd169cb..669f6121 100644 --- a/FrameworkInternal/Resources.resx +++ b/FrameworkInternal/Resources.resx @@ -447,6 +447,7 @@ OperationOptionInfo | SyncDeltaType | SyncToken | SyncDelta <!ELEMENT OperationRequest (ConnectorKey,APIConfiguration,Arguments)> <!ATTLIST OperationRequest operation CDATA #REQUIRED + operationMethodName CDATA #REQUIRED > <!ELEMENT Arguments ((%xmlObject;)*)> <!ELEMENT OperationResponseEnd EMPTY> diff --git a/FrameworkInternal/Serializer.cs b/FrameworkInternal/Serializer.cs index 2b9c875a..af5f7807 100644 --- a/FrameworkInternal/Serializer.cs +++ b/FrameworkInternal/Serializer.cs @@ -2150,11 +2150,14 @@ public override sealed Object Deserialize(ObjectDecoder decoder) { (APIConfigurationImpl)decoder.ReadObjectField("APIConfiguration",typeof(APIConfigurationImpl),null); Type operation = decoder.ReadClassField("operation",null); + string operationMethodName = + decoder.ReadStringField("operationMethodName",null); IList arguments = (IList) decoder.ReadObjectField("Arguments",typeof(IList),null); return new OperationRequest(connectorKey, configuration, operation, + operationMethodName, arguments); } @@ -2164,6 +2167,8 @@ public override sealed void Serialize(Object obj, ObjectEncoder encoder) (OperationRequest)obj; encoder.WriteClassField("operation", val.Operation); + encoder.WriteStringField("operationMethodName", + val.OperationMethodName); encoder.WriteObjectField("ConnectorKey", val.ConnectorKey,true); encoder.WriteObjectField("APIConfiguration", diff --git a/FrameworkInternal/Server.cs b/FrameworkInternal/Server.cs index 20ec857a..6137f054 100644 --- a/FrameworkInternal/Server.cs +++ b/FrameworkInternal/Server.cs @@ -56,6 +56,7 @@ using Org.IdentityConnectors.Framework.Api; using Org.IdentityConnectors.Framework.Api.Operations; using Org.IdentityConnectors.Framework.Common.Objects; +using Org.IdentityConnectors.Framework.Common.Exceptions; using Org.IdentityConnectors.Framework.Common.Objects.Filters; using Org.IdentityConnectors.Framework.Common.Serializer; using Org.IdentityConnectors.Framework.Server; @@ -459,18 +460,33 @@ private HelloResponse ProcessHelloRequest(HelloRequest request) { return new HelloResponse(exception,connectorInfo); } + private MethodInfo GetOperationMethod(OperationRequest request) { + MethodInfo [] methods = + request.Operation.GetMethods(); + MethodInfo found = null; + foreach (MethodInfo m in methods) { + if ( found != null ) { + throw new ConnectorException("APIOperations are expected " + +"to have exactly one method of a given name: "+request.Operation+" "+methods.Length); + } + if ( m.Name.ToUpper().Equals(request.OperationMethodName.ToUpper()) ) { + found = m; + } + } + + if ( found == null ) { + throw new ConnectorException("APIOperations are expected " + +"to have exactly one method of a given name: "+request.Operation+" "+methods.Length); + } + return found; + } + private OperationResponsePart ProcessOperationRequest(OperationRequest request) { Object result; Exception exception = null; try { - MethodInfo [] methods = - request.Operation.GetMethods(); - if ( methods.Length != 1) { - throw new Exception("APIOperations are expected " - +"to have exactly one method: "+request.Operation+" "+methods.Length); - } - MethodInfo method = methods[0]; + MethodInfo method = GetOperationMethod(request); APIOperation operation = GetAPIOperation(request); IList arguments = request.Arguments; IList argumentsAndStreamHandlers = diff --git a/FrameworkTests/ObjectSerializationTests.cs b/FrameworkTests/ObjectSerializationTests.cs index 69eaa9ea..0e6f113c 100644 --- a/FrameworkTests/ObjectSerializationTests.cs +++ b/FrameworkTests/ObjectSerializationTests.cs @@ -854,6 +854,7 @@ public void TestOperationRequest() { "my connector"), apiImpl, typeof(CreateApiOp), + "mymethodName", args); OperationRequest v2 = (OperationRequest)CloneObject(v1); Assert.AreEqual("my bundle", v2.ConnectorKey.BundleName); @@ -861,6 +862,7 @@ public void TestOperationRequest() { Assert.AreEqual("my connector", v2.ConnectorKey.ConnectorName); Assert.IsNotNull(v2.Configuration); Assert.AreEqual(typeof(CreateApiOp), v2.Operation); + Assert.AreEqual("mymethodName",v2.OperationMethodName); Assert.IsTrue( CollectionUtil.Equals( args, v2.Arguments)); From 00543f21d961ce1cbffb63a13d73635ab58837b5 Mon Sep 17 00:00:00 2001 From: dvernon Date: Fri, 12 Sep 2008 19:40:32 +0000 Subject: [PATCH 009/342] Issue #64 - Incremental checkin. Changes to pass more of the contract tests. --- .../ActiveDirectoryConnector.cs | 55 ++++--------------- .../CustomAttributeHandlers.cs | 12 ++-- .../PasswordChangeHandler.cs | 13 ++++- 3 files changed, 30 insertions(+), 50 deletions(-) diff --git a/ActiveDirectoryConnector/ActiveDirectoryConnector.cs b/ActiveDirectoryConnector/ActiveDirectoryConnector.cs index bb29a2a4..02fbe67f 100644 --- a/ActiveDirectoryConnector/ActiveDirectoryConnector.cs +++ b/ActiveDirectoryConnector/ActiveDirectoryConnector.cs @@ -305,7 +305,6 @@ public Schema Schema() // get the user attribute infos and operations ICollection userAttributeInfos = GetUserAttributeInfos(ADSchema); - ICollection userOperations = GetUserOperations(); ObjectClassInfoBuilder ociBuilder = new ObjectClassInfoBuilder(); ociBuilder.ObjectType = ObjectClass.ACCOUNT_NAME; ociBuilder.AddAllAttributeInfo(userAttributeInfos); @@ -317,7 +316,6 @@ public Schema Schema() ociBuilder = new ObjectClassInfoBuilder(); ociBuilder.ObjectType = ObjectClass.GROUP_NAME; ociBuilder.AddAllAttributeInfo(groupAttributeInfos); - ICollection groupOperations = GetGroupOperations(); ObjectClassInfo groupInfo = ociBuilder.Build(); // get the organizationalUnit attribute infos and operations @@ -326,13 +324,19 @@ public Schema Schema() ociBuilder = new ObjectClassInfoBuilder(); ociBuilder.ObjectType = OBJECTCLASS_OU; ociBuilder.AddAllAttributeInfo(ouAttributeInfos); - ICollection ouOperations = GetOuOperations(); ObjectClassInfo ouInfo = ociBuilder.Build(); - SchemaBuilder schemaBuilder = new SchemaBuilder(typeof(ActiveDirectoryConnector)); + SchemaBuilder schemaBuilder = new SchemaBuilder(this.GetType()); + schemaBuilder.DefineObjectClass(userInfo); schemaBuilder.DefineObjectClass(groupInfo); + schemaBuilder.RemoveSupportedObjectClass(typeof(AuthenticateOp), groupInfo); schemaBuilder.DefineObjectClass(ouInfo); + schemaBuilder.RemoveSupportedObjectClass(typeof(AuthenticateOp), ouInfo); + schemaBuilder.RemoveSupportedObjectClass(typeof(CreateOp), ouInfo); + schemaBuilder.RemoveSupportedObjectClass(typeof(DeleteOp), ouInfo); + schemaBuilder.RemoveSupportedObjectClass(typeof(SearchOp), ouInfo); + _schema = schemaBuilder.Build(); } @@ -364,10 +368,6 @@ public ICollection GetUserAttributeInfos( PopulateSchemaFromAD(_configuration.ObjectClass, ADSchema, attributeInfos, attributesToIgnore, ObjectClass.ACCOUNT); - // add in the uid - attributeInfos.Add(GetConnectorAttributeInfo(Uid.NAME, - typeof(string), false, true, true, false, ObjectClass.ACCOUNT)); - // now add in container ... attributeInfos.Add(GetConnectorAttributeInfo(ATT_CONTAINER, typeof(string), true, true, false, false, ObjectClass.ACCOUNT)); @@ -433,10 +433,6 @@ public ICollection GetGroupAttributeInfos( // put in operational attributes //attributeInfos.Add(OperationalAttributeInfos.EXPIRE_PASSWORD); - // add in the uid - attributeInfos.Add(GetConnectorAttributeInfo(Uid.NAME, - typeof(string), false, true, true, false, ObjectClass.GROUP)); - // now add in container ... attributeInfos.Add(GetConnectorAttributeInfo(ATT_CONTAINER, typeof(string), true, true, false, false, ObjectClass.GROUP)); @@ -453,10 +449,6 @@ public ICollection GetOuAttributeInfos( { ICollection attributeInfos = new Collection(); - // add in the uid - attributeInfos.Add(GetConnectorAttributeInfo(Uid.NAME, - typeof(string), false, true, true, false, ouObjectClass)); - // add in container ... attributeInfos.Add(GetConnectorAttributeInfo(ATT_CONTAINER, typeof(string), true, true, false, false, ouObjectClass)); @@ -538,33 +530,6 @@ protected void AddPropertyCollectionToSchema( } } - public ICollection GetUserOperations() - { - ICollection operations = new Collection(); - operations.Add(typeof(CreateApiOp)); - operations.Add(typeof(DeleteApiOp)); - operations.Add(typeof(UpdateApiOp)); - operations.Add(typeof(SearchApiOp)); - operations.Add(typeof(ScriptOnResourceApiOp)); - operations.Add(typeof(SyncApiOp)); - - return operations; - } - - public ICollection GetGroupOperations() - { - ICollection operations = new Collection(); - operations.Add(typeof(CreateApiOp)); - return operations; - } - - public ICollection GetOuOperations() - { - ICollection operations = new Collection(); - operations.Add(typeof(SearchApiOp)); - return operations; - } - private ConnectorAttributeInfo GetConnectorAttributeInfo(string name, Type type, bool writable, bool readable, bool required, bool multivalue, ObjectClass oclass) @@ -928,6 +893,10 @@ public void Delete(ObjectClass objClass, Uid uid, OperationOptions options) // entry and all it's children de.DeleteTree(); } + else + { + throw new ConnectorException(String.Format("Delete is not supported for ObjectClass {0}", objClass.GetObjectClassValue())); + } } #endregion diff --git a/ActiveDirectoryConnector/CustomAttributeHandlers.cs b/ActiveDirectoryConnector/CustomAttributeHandlers.cs index 414dddf3..75b30270 100644 --- a/ActiveDirectoryConnector/CustomAttributeHandlers.cs +++ b/ActiveDirectoryConnector/CustomAttributeHandlers.cs @@ -715,20 +715,20 @@ private ConnectorAttribute GetCaFromDe_Att_Generic( private ConnectorAttribute GetCaFromDe_OpAtt_Name( ObjectClass oclass, string attributeName, SearchResult searchResult) { - ICollection value = new List(); + String value = null; ResultPropertyValueCollection pvc = null; pvc = searchResult.Properties[ActiveDirectoryConnector.ATT_DISTINGUISHED_NAME]; - if ((pvc != null) && (pvc.Count == 1)) + if ((pvc != null) && (pvc.Count == 1) && (pvc[0] is String)) { - value.Add(pvc[0]); + value = (String)pvc[0]; } - else if (pvc.Count > 1) + else { throw new ConnectorException("There should be exactly one value for the name attribute"); - } + } - return ConnectorAttributeBuilder.Build(Name.NAME, value); + return ConnectorAttributeBuilder.Build(Name.NAME, ActiveDirectoryUtils.NormalizeLdapString(value)); } private ConnectorAttribute GetCaFromDe_OpAtt_Uid( diff --git a/ActiveDirectoryConnector/PasswordChangeHandler.cs b/ActiveDirectoryConnector/PasswordChangeHandler.cs index 2e6ef3c7..061f16ad 100644 --- a/ActiveDirectoryConnector/PasswordChangeHandler.cs +++ b/ActiveDirectoryConnector/PasswordChangeHandler.cs @@ -45,6 +45,7 @@ using System.DirectoryServices; using Org.IdentityConnectors.Common.Security; using ActiveDs; +using Org.IdentityConnectors.Framework.Common.Exceptions; namespace Org.IdentityConnectors.ActiveDirectory { @@ -145,7 +146,17 @@ internal void Authenticate(DirectoryEntry directoryEntry, string username, DirectoryEntry userDe = new DirectoryEntry(directoryEntry.Path, sAMAccountName, _currentPassword); - userDe.RefreshCache(); + try + { + // this will cause a bind. Maybe there is a better + // way to do this. If you know of one, please change + // this, as this seems a little weird + userDe.RefreshCache(); + } + catch (Exception e) + { + throw new InvalidCredentialException(); + } } } From 13108900bee3896e0045a1f81587a625e3c02ec6 Mon Sep 17 00:00:00 2001 From: rcauble Date: Mon, 15 Sep 2008 18:58:11 +0000 Subject: [PATCH 010/342] Issue#256: fixed on c# side --- Framework/ApiOperations.cs | 7 +++++++ Framework/SpiOperations.cs | 7 +++++++ FrameworkInternal/Api.cs | 5 +++++ FrameworkInternal/ApiLocalOperations.cs | 5 ++++- FrameworkInternal/Server.cs | 8 ++++---- FrameworkTests/ConnectorInfoManagerTests.cs | 2 ++ TestBundleV1/TestConnector.cs | 5 +++++ 7 files changed, 34 insertions(+), 5 deletions(-) diff --git a/Framework/ApiOperations.cs b/Framework/ApiOperations.cs index c8a78897..22655064 100644 --- a/Framework/ApiOperations.cs +++ b/Framework/ApiOperations.cs @@ -260,6 +260,13 @@ public interface SyncApiOp : APIOperation { void Sync(ObjectClass objClass, SyncToken token, SyncResultsHandler handler, OperationOptions options); + /** + * Returns the token corresponding to the latest sync delta. + * This is to support applications that may wish to sync starting + * "now". + * @return The latest token or null if there is no sync data. + */ + SyncToken GetLatestSyncToken(); } /// diff --git a/Framework/SpiOperations.cs b/Framework/SpiOperations.cs index 43644dd0..2f8ddadf 100644 --- a/Framework/SpiOperations.cs +++ b/Framework/SpiOperations.cs @@ -317,6 +317,13 @@ public interface SyncOp : SPIOperation { void Sync(ObjectClass objClass, SyncToken token, SyncResultsHandler handler, OperationOptions options); + /** + * Returns the token corresponding to the latest sync delta. + * This is to support applications that may wish to sync starting + * "now". + * @return The latest token or null if there is no sync data. + */ + SyncToken GetLatestSyncToken(); } /** diff --git a/FrameworkInternal/Api.cs b/FrameworkInternal/Api.cs index c5b476ca..d20da4d6 100644 --- a/FrameworkInternal/Api.cs +++ b/FrameworkInternal/Api.cs @@ -614,6 +614,11 @@ public void Sync(ObjectClass objClass, SyncToken token, .Sync(objClass, token, handler, options); } + public SyncToken GetLatestSyncToken() { + return ((SyncApiOp)this.GetOperationCheckSupported(typeof(SyncApiOp))) + .GetLatestSyncToken(); + } + private APIOperation GetOperationCheckSupported(Type api) { // check if this operation is supported. if (!SupportedOperations.Contains(api)) { diff --git a/FrameworkInternal/ApiLocalOperations.cs b/FrameworkInternal/ApiLocalOperations.cs index e4870487..ee893eab 100644 --- a/FrameworkInternal/ApiLocalOperations.cs +++ b/FrameworkInternal/ApiLocalOperations.cs @@ -1139,7 +1139,10 @@ public void Sync(ObjectClass objClass, SyncToken token, handler = new NormalizingSyncResultsHandler(handler,normalizer).Handle; ((SyncOp)GetConnector()).Sync(objClass, token, handler, options); } - + public SyncToken GetLatestSyncToken() + { + return ((SyncOp) GetConnector()).GetLatestSyncToken(); + } } #endregion diff --git a/FrameworkInternal/Server.cs b/FrameworkInternal/Server.cs index 6137f054..70c68e64 100644 --- a/FrameworkInternal/Server.cs +++ b/FrameworkInternal/Server.cs @@ -465,11 +465,11 @@ private MethodInfo GetOperationMethod(OperationRequest request) { request.Operation.GetMethods(); MethodInfo found = null; foreach (MethodInfo m in methods) { - if ( found != null ) { - throw new ConnectorException("APIOperations are expected " - +"to have exactly one method of a given name: "+request.Operation+" "+methods.Length); - } if ( m.Name.ToUpper().Equals(request.OperationMethodName.ToUpper()) ) { + if ( found != null ) { + throw new ConnectorException("APIOperations are expected " + +"to have exactly one method of a given name: "+request.Operation); + } found = m; } } diff --git a/FrameworkTests/ConnectorInfoManagerTests.cs b/FrameworkTests/ConnectorInfoManagerTests.cs index 2058d38e..d7e49513 100644 --- a/FrameworkTests/ConnectorInfoManagerTests.cs +++ b/FrameworkTests/ConnectorInfoManagerTests.cs @@ -280,6 +280,8 @@ public void TestSyncWithManyResults() { ConnectorFacadeFactory facf = ConnectorFacadeFactory.GetInstance(); ConnectorFacade facade = facf.NewInstance(api); + SyncToken latest = facade.GetLatestSyncToken(); + Assert.AreEqual("mylatest",latest.Value); IList results = new List(); facade.Sync(ObjectClass.ACCOUNT, null, obj => { diff --git a/TestBundleV1/TestConnector.cs b/TestBundleV1/TestConnector.cs index e1128eee..92864cd8 100644 --- a/TestBundleV1/TestConnector.cs +++ b/TestBundleV1/TestConnector.cs @@ -209,6 +209,11 @@ public void Sync(ObjectClass objClass, SyncToken token, } } } + + public SyncToken GetLatestSyncToken() + { + return new SyncToken("mylatest"); + } public Schema Schema() { SchemaBuilder builder = new SchemaBuilder(typeof(TstConnector)); From 83780b616d360d79fd8a1737f21303e5737a9dec Mon Sep 17 00:00:00 2001 From: rcauble Date: Mon, 15 Sep 2008 20:08:21 +0000 Subject: [PATCH 011/342] Issue#273: started --- Common/Common.csproj | 1 + Common/SafeType.cs | 149 +++++++++++++++++++++++++++ FrameworkTests/FrameworkTests.csproj | 1 + FrameworkTests/SafeTypeTest.cs | 64 ++++++++++++ 4 files changed, 215 insertions(+) create mode 100644 Common/SafeType.cs create mode 100644 FrameworkTests/SafeTypeTest.cs diff --git a/Common/Common.csproj b/Common/Common.csproj index 17c9d69a..3f7f269d 100644 --- a/Common/Common.csproj +++ b/Common/Common.csproj @@ -90,6 +90,7 @@ + diff --git a/Common/SafeType.cs b/Common/SafeType.cs new file mode 100644 index 00000000..17d2ba6f --- /dev/null +++ b/Common/SafeType.cs @@ -0,0 +1,149 @@ +/* + * Copyright 2008 Sun Microsystems, Inc. All rights reserved. + * + * U.S. Government Rights - Commercial software. Government users + * are subject to the Sun Microsystems, Inc. standard license agreement + * and applicable provisions of the FAR and its supplements. + * + * Use is subject to license terms. + * + * This distribution may include materials developed by third parties. + * Sun, Sun Microsystems, the Sun logo, Java and Project Identity + * Connectors are trademarks or registered trademarks of Sun + * Microsystems, Inc. or its subsidiaries in the U.S. and other + * countries. + * + * UNIX is a registered trademark in the U.S. and other countries, + * exclusively licensed through X/Open Company, Ltd. + * + * ----------- + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER. + * + * Copyright 2008 Sun Microsystems, Inc. All rights reserved. + * + * The contents of this file are subject to the terms of the Common Development + * and Distribution License(CDDL) (the License). You may not use this file + * except in compliance with the License. + * + * You can obtain a copy of the License at + * http://identityconnectors.dev.java.net/CDDLv1.0.html + * See the License for the specific language governing permissions and + * limitations under the License. + * + * When distributing the Covered Code, include this CDDL Header Notice in each + * file and include the License file at identityconnectors/legal/license.txt. + * If applicable, add the following below this CDDL Header, with the fields + * enclosed by brackets [] replaced by your own identifying information: + * "Portions Copyrighted [year] [name of copyright owner]" + * ----------- + */ + +using System; + +using System.Reflection; + +namespace Org.IdentityConnectors.Common +{ + /// + /// The equivalent of java's Class<? extends...%gt; syntax. + /// Allows you to restrict a Type to a certain class hierarchy. + /// + public abstract class SafeType + where T : class + { + /// + /// Make private so no one else can subclass this. + /// + private SafeType() + { + } + + /// + /// Returns the member of the group by its C# type + /// + /// + /// + public static SafeType ForRawType(Type type) + { + if (!typeof(T).IsAssignableFrom(type)) { + throw new ArgumentException("Type: "+type+" is not a subclass of"+typeof(T)); + } + Type safeType = typeof(SafeType); + MethodInfo info = safeType.GetMethod("Get"); + info = info.MakeGenericMethod(new Type[]{type}); + Object rv = info.Invoke(null,null); + return (SafeType)rv; + } + + /// + /// Gets an instance of the safe type. + /// + /// The instance of the safe type + public static SafeType Get() + where U : T + { + return new Impl(); + } + + /// + /// Returns the underlying C# type + /// + public abstract Type RawType {get;} + /// + /// Returns true iff these represent the same underlying type + /// and the SafeType has the same parent type. + /// + /// The other + /// true iff these represent the same underylying type + /// and the TypeGroup has the same parent type + public override bool Equals(object o) + { + if ( o is SafeType ) { + SafeType other = (SafeType)o; + return RawType.Equals(other.RawType); + } + return false; + } + /// + /// Returns a hash of the type + /// + /// a hash of the type + public override int GetHashCode() + { + return RawType.GetHashCode(); + } + /// + /// Returns a string representation of the member type + /// + /// a string representation of the member type + public override string ToString() + { + return RawType.ToString(); + } + /// + /// Implementation + /// + private class Impl : SafeType + where U : T + { + /// + /// Creates the implementation + /// + public Impl() + { + } + /// + /// Returns the underlying raw type + /// + public override Type RawType + { + get + { + return typeof(U); + } + } + } + } + + +} diff --git a/FrameworkTests/FrameworkTests.csproj b/FrameworkTests/FrameworkTests.csproj index 597b4b41..ef56ea0f 100644 --- a/FrameworkTests/FrameworkTests.csproj +++ b/FrameworkTests/FrameworkTests.csproj @@ -87,6 +87,7 @@ + diff --git a/FrameworkTests/SafeTypeTest.cs b/FrameworkTests/SafeTypeTest.cs new file mode 100644 index 00000000..0533b2d5 --- /dev/null +++ b/FrameworkTests/SafeTypeTest.cs @@ -0,0 +1,64 @@ +/* + * Copyright 2008 Sun Microsystems, Inc. All rights reserved. + * + * U.S. Government Rights - Commercial software. Government users + * are subject to the Sun Microsystems, Inc. standard license agreement + * and applicable provisions of the FAR and its supplements. + * + * Use is subject to license terms. + * + * This distribution may include materials developed by third parties. + * Sun, Sun Microsystems, the Sun logo, Java and Project Identity + * Connectors are trademarks or registered trademarks of Sun + * Microsystems, Inc. or its subsidiaries in the U.S. and other + * countries. + * + * UNIX is a registered trademark in the U.S. and other countries, + * exclusively licensed through X/Open Company, Ltd. + * + * ----------- + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER. + * + * Copyright 2008 Sun Microsystems, Inc. All rights reserved. + * + * The contents of this file are subject to the terms of the Common Development + * and Distribution License(CDDL) (the License). You may not use this file + * except in compliance with the License. + * + * You can obtain a copy of the License at + * http://identityconnectors.dev.java.net/CDDLv1.0.html + * See the License for the specific language governing permissions and + * limitations under the License. + * + * When distributing the Covered Code, include this CDDL Header Notice in each + * file and include the License file at identityconnectors/legal/license.txt. + * If applicable, add the following below this CDDL Header, with the fields + * enclosed by brackets [] replaced by your own identifying information: + * "Portions Copyrighted [year] [name of copyright owner]" + * ----------- + */ + +using System; +using NUnit.Framework; +using NUnit.Framework.SyntaxHelpers; +using Org.IdentityConnectors.Common; +using Org.IdentityConnectors.Framework.Api.Operations; +namespace FrameworkTests +{ + [TestFixture] + public class SafeTypeTest + { + [Test] + public void TestSafeType() + { + //compile-time type safe + SafeType op = + SafeType.Get(); + Assert.AreEqual(typeof(ScriptOnResourceApiOp),op.RawType); + //runtime type safe. needed for marshalling code, etc + op = + SafeType.ForRawType(typeof(SchemaApiOp)); + Assert.AreEqual(typeof(SchemaApiOp),op.RawType); + } + } +} From deafa6587e6bb5ce625b1c1d54c22c1fa8ecf505 Mon Sep 17 00:00:00 2001 From: dvernon Date: Tue, 16 Sep 2008 19:26:49 +0000 Subject: [PATCH 012/342] Issue #64 - Incremental checkin. Add implementation and test of GetLatestSyncToken(). --- .../ActiveDirectoryConnector.cs | 238 +++++++++++------- .../ActiveDirectoryConnector.csproj | 1 + .../ActiveDirectoryConnectorTest.cs | 47 +++- 3 files changed, 186 insertions(+), 100 deletions(-) diff --git a/ActiveDirectoryConnector/ActiveDirectoryConnector.cs b/ActiveDirectoryConnector/ActiveDirectoryConnector.cs index 02fbe67f..3dd9c4dd 100644 --- a/ActiveDirectoryConnector/ActiveDirectoryConnector.cs +++ b/ActiveDirectoryConnector/ActiveDirectoryConnector.cs @@ -591,7 +591,7 @@ public void ExecuteQuery(ObjectClass oclass, string query, } } - // this is used by the ExecuteQuere method of SearchSpiOp, and + // this is used by the ExecuteQuery method of SearchSpiOp, and // by the SyncSpiOp private void ExecuteQuery(ObjectClass oclass, string query, ResultsHandler handler, OperationOptions options, bool includeDeleted, @@ -840,31 +840,6 @@ public Uid Update(UpdateType type, ObjectClass oclass, _utils.UpdateADObject(oclass, updateEntry, attributes, type, _configuration); - try - { - Object guidValue = updateEntry.Properties["objectGUID"].Value; - if (guidValue != null) - { - // format the uid in the special way required for searching - String searchGuid = - ActiveDirectoryUtils.ConvertUIDBytesToGUIDString( - (Byte[])guidValue); - - Trace.TraceInformation("Created user with uid {0}", searchGuid); - updatedUid = new Uid(searchGuid); - } - else - { - Trace.TraceError("Unable to find uid attribute for newly created object"); - } - } - catch (DirectoryServicesCOMException exception) - { - // have to make sure the new thing gets deleted in - // the case of error - Console.WriteLine("caught exception:" + exception); - return null; - } return updatedUid; } @@ -937,19 +912,12 @@ public object RunScriptOnResource(ScriptContext request, OperationOptions option public class SyncResults { SyncResultsHandler _syncResultsHandler; - string _serverName; - bool _useGlobalCatalog; - long _lastModifiedUsn; - long _lastDeletedUsn; - - public SyncResults(SyncResultsHandler syncResultsHandler, - bool useGlobalCatalog, string serverName, - long lastModifiedUsn, long lastDeletedUsn) { + ActiveDirectorySyncToken _adSyncToken; + + internal SyncResults(SyncResultsHandler syncResultsHandler, + ActiveDirectorySyncToken adSyncToken) { _syncResultsHandler = syncResultsHandler; - _serverName = serverName; - _useGlobalCatalog = false; - _lastModifiedUsn = lastModifiedUsn; - _lastDeletedUsn = lastDeletedUsn; + _adSyncToken = adSyncToken; } public bool SyncHandler(ConnectorObject obj) @@ -965,7 +933,6 @@ public bool SyncHandler(ConnectorObject obj) throw new ConnectorException(msg); } long tokenUsnValue = (long)ConnectorAttributeUtil.GetSingleValue(tokenAttr); - string tokenServerName = _serverName; bool? isDeleted = false; ConnectorAttribute isDeletedAttr = @@ -973,16 +940,14 @@ public bool SyncHandler(ConnectorObject obj) if (isDeletedAttr != null) { isDeleted = (bool?)ConnectorAttributeUtil.GetSingleValue(isDeletedAttr); - _lastDeletedUsn = tokenUsnValue; + _adSyncToken.LastDeleteUsn = tokenUsnValue; } else { - _lastModifiedUsn = tokenUsnValue; + _adSyncToken.LastModifiedUsn = tokenUsnValue; } - SyncToken token = new SyncToken(String.Format("{0}|{1}|{2}|{3}", - _lastModifiedUsn, _lastDeletedUsn, _useGlobalCatalog, tokenServerName)); - builder.Token = token; + builder.Token = _adSyncToken.GetSyncToken(); if ((isDeleted != null) && (isDeleted.Equals(true))) { @@ -1002,15 +967,115 @@ public bool SyncHandler(ConnectorObject obj) public void Sync(ObjectClass objClass, SyncToken token, SyncResultsHandler handler, OperationOptions options) { - string modifiedQuery = null; - string deletedQuery = null; + if (!ObjectClass.ACCOUNT.Equals(objClass)) + { + throw new ConnectorException(String.Format("Sync operation is not available for ObjectClass {0}", objClass.GetObjectClassValue())); + } + + String serverName = GetSyncServerName(); + + ActiveDirectorySyncToken adSyncToken = + new ActiveDirectorySyncToken(token, serverName, UseGlobalCatalog()); + + string modifiedQuery = GetSyncUpdateQuery(adSyncToken); + string deletedQuery = GetSyncDeleteQuery(adSyncToken); + + OperationOptionsBuilder builder = new OperationOptionsBuilder(); + SyncResults syncResults = new SyncResults(handler, adSyncToken); + + // find modified usn's + ExecuteQuery(objClass, modifiedQuery, syncResults.SyncHandler, builder.Build(), + false, new SortOption(ATT_USN_CHANGED, SortDirection.Ascending), + serverName, UseGlobalCatalog(), _configuration.SyncSearchContext); + + // find deleted usn's + DirectoryContext domainContext = new DirectoryContext(DirectoryContextType.DirectoryServer, + serverName, + _configuration.DirectoryAdminName, + _configuration.DirectoryAdminPassword); + Domain domain = Domain.GetDomain(domainContext); + String deleteObjectsSearchRoot = null; + if (domain != null) + { + DirectoryEntry domainDe = domain.GetDirectoryEntry(); + deleteObjectsSearchRoot = ActiveDirectoryUtils.GetDnFromPath(domainDe.Path); + } + ExecuteQuery(objClass, deletedQuery, syncResults.SyncHandler, builder.Build(), + true, new SortOption(ATT_USN_CHANGED, SortDirection.Ascending), + serverName, UseGlobalCatalog(), deleteObjectsSearchRoot); + + } + + public SyncToken GetLatestSyncToken() + { + String serverName = GetSyncServerName(); + + ActiveDirectorySyncToken adSyncToken = + new ActiveDirectorySyncToken((string)null, serverName, UseGlobalCatalog()); + + string updatedQuery = GetSyncUpdateQuery(adSyncToken); + string deletedQuery = GetSyncDeleteQuery(adSyncToken); + + string updateSearchRoot; + string deleteSearchRoot; + + if (UseGlobalCatalog()) + { + updateSearchRoot = ActiveDirectoryUtils.GetGCPath(serverName, + _configuration.SyncSearchContext); + deleteSearchRoot = ActiveDirectoryUtils.GetGCPath(serverName, null); + } + else + { + updateSearchRoot = ActiveDirectoryUtils.GetLDAPPath(serverName, + _configuration.SyncSearchContext); + deleteSearchRoot = ActiveDirectoryUtils.GetGCPath(serverName, null); + } + + DirectoryEntry updateSearchRootEntry = new DirectoryEntry(updateSearchRoot, + _configuration.DirectoryAdminName, _configuration.DirectoryAdminPassword); + DirectoryEntry deleteSearchRootEntry = new DirectoryEntry(deleteSearchRoot, + _configuration.DirectoryAdminName, _configuration.DirectoryAdminPassword); + + + DirectorySearcher updateSearcher = + new DirectorySearcher(updateSearchRootEntry, updatedQuery); + DirectorySearcher deleteSearcher = + new DirectorySearcher(deleteSearchRootEntry, deletedQuery); + + // according to the docs, I would think that this would make the sort order + // to be descending. It does not, so setting the direction below. + updateSearcher.Sort = new SortOption(ATT_USN_CHANGED, SortDirection.Descending); + updateSearcher.Sort.Direction = SortDirection.Descending; + updateSearcher.PageSize = 10; + SearchResultCollection updateResults = updateSearcher.FindAll(); + if ((updateResults != null) && (updateResults.Count > 0)) + { + adSyncToken.LastModifiedUsn = (long)updateResults[0].Properties[ATT_USN_CHANGED][0]; + } + + // according to the docs, I would think that this would make the sort order + // to be descending. It does not, so setting the direction below. + deleteSearcher.Sort = new SortOption(ATT_USN_CHANGED, SortDirection.Descending); + deleteSearcher.Sort.Direction = SortDirection.Descending; + deleteSearcher.PageSize = 10; + deleteSearcher.Tombstone = true; + SearchResultCollection deleteResults = deleteSearcher.FindAll(); + if ((deleteResults != null) && (deleteResults.Count > 0)) + { + adSyncToken.LastDeleteUsn = (long)deleteResults[0].Properties[ATT_USN_CHANGED][0]; + } + + return adSyncToken.GetSyncToken(); + } + + string GetSyncServerName() + { string serverName = null; - bool useGlobalCatalog = false; - if (_configuration.SearchChildDomains) + if (UseGlobalCatalog()) { serverName = _configuration.SyncGlobalCatalogServer; - useGlobalCatalog = true; } else { @@ -1022,7 +1087,7 @@ public void Sync(ObjectClass objClass, SyncToken token, Trace.TraceWarning("No server was configured for synchronization, so picking one. You should configure a server for best performance."); // we have to know which server we are working against, // so find one. - if (useGlobalCatalog) + if (UseGlobalCatalog()) { DirectoryContext context = new DirectoryContext( DirectoryContextType.Forest, _configuration.DomainName, @@ -1043,65 +1108,44 @@ public void Sync(ObjectClass objClass, SyncToken token, serverName = _configuration.SyncDomainController; } } + return serverName; + } + + bool UseGlobalCatalog() + { + return (_configuration.SearchChildDomains); + } + String GetSyncUpdateQuery(ActiveDirectorySyncToken adSyncToken) + { + string modifiedQuery = null; - long lastModToken = 0; - long lastDelToken = 0; // if the token is not null, we may be able to start from // the usn contained there - if (token != null) - { - string[] tokenParts = ((string)(token.Value)).Split('|'); - bool tokenUseGlobalCatalog = bool.Parse(tokenParts[2]); - string tokenServerName = tokenParts[3]; - - // If the token server is the same as the configured server, - // use the token value (usn) to limit the query. The token is - // server specific though, so we cant use the usn if it didn't come - // from this server. - // If no server is configured, just try to use what we used last time. - if (tokenServerName != null) - { - if ((serverName == null) || - (tokenServerName.Equals(serverName)) || (serverName.Length == 0)) - { - lastModToken = long.Parse(tokenParts[0]); - lastDelToken = long.Parse(tokenParts[1]); - modifiedQuery = string.Format("(!({0}<={1}))", ATT_USN_CHANGED, tokenParts[0]); - deletedQuery = string.Format("(&(!({0}<={1}))(isDeleted=TRUE))", ATT_USN_CHANGED, tokenParts[1]); - } - } - } - else + if (adSyncToken != null) { - deletedQuery = string.Format("(isDeleted=TRUE)"); + modifiedQuery = string.Format("(!({0}<={1}))", ATT_USN_CHANGED, adSyncToken.LastModifiedUsn); } - OperationOptionsBuilder builder = new OperationOptionsBuilder(); - SyncResults syncResults = new SyncResults(handler, useGlobalCatalog, - serverName, lastModToken, lastDelToken); + return modifiedQuery; + } - // find modified usn's - ExecuteQuery(objClass, modifiedQuery, syncResults.SyncHandler, builder.Build(), - false, new SortOption(ATT_USN_CHANGED, SortDirection.Ascending), - serverName, useGlobalCatalog, _configuration.SyncSearchContext); + String GetSyncDeleteQuery(ActiveDirectorySyncToken adSyncToken) + { + string deletedQuery = null; - // find deleted usn's - DirectoryContext domainContext = new DirectoryContext(DirectoryContextType.DirectoryServer, - serverName, - _configuration.DirectoryAdminName, - _configuration.DirectoryAdminPassword); - Domain domain = Domain.GetDomain(domainContext); - String deleteObjectsSearchRoot = null; - if (domain != null) + // if the token is not null, we may be able to start from + // the usn contained there + if (adSyncToken != null) { - DirectoryEntry domainDe = domain.GetDirectoryEntry(); - deleteObjectsSearchRoot = ActiveDirectoryUtils.GetDnFromPath(domainDe.Path); + deletedQuery = string.Format("(&(!({0}<={1}))(isDeleted=TRUE))", ATT_USN_CHANGED, adSyncToken.LastDeleteUsn); + } + else + { + deletedQuery = string.Format("(isDeleted=TRUE)"); } - ExecuteQuery(objClass, deletedQuery, syncResults.SyncHandler, builder.Build(), - true, new SortOption(ATT_USN_CHANGED, SortDirection.Ascending), - serverName, useGlobalCatalog, deleteObjectsSearchRoot); + return deletedQuery; } #endregion diff --git a/ActiveDirectoryConnector/ActiveDirectoryConnector.csproj b/ActiveDirectoryConnector/ActiveDirectoryConnector.csproj index 6565cfaf..1ccf09e7 100644 --- a/ActiveDirectoryConnector/ActiveDirectoryConnector.csproj +++ b/ActiveDirectoryConnector/ActiveDirectoryConnector.csproj @@ -80,6 +80,7 @@ Code + diff --git a/ActiveDirectoryConnectorTests/ActiveDirectoryConnectorTest.cs b/ActiveDirectoryConnectorTests/ActiveDirectoryConnectorTest.cs index 861ef4ad..2d5c3796 100644 --- a/ActiveDirectoryConnectorTests/ActiveDirectoryConnectorTest.cs +++ b/ActiveDirectoryConnectorTests/ActiveDirectoryConnectorTest.cs @@ -1319,6 +1319,7 @@ public void TestSync(bool searchChildDomains, String syncSearchContext) Uid createUid = null; + ICollection createdUids = new List(); try { SyncTestHelper syncHelper = new SyncTestHelper(); @@ -1328,7 +1329,6 @@ public void TestSync(bool searchChildDomains, String syncSearchContext) syncHelper.Init(); ICollection attributes = null; - ICollection createdUids = new List(); // create some users for (int i = 0; i < 10; i++) @@ -1384,12 +1384,40 @@ public void TestSync(bool searchChildDomains, String syncSearchContext) // sync and verify connector.Sync(ObjectClass.ACCOUNT, syncHelper.Token, syncHelper.SyncHandler_DeletedAccounts, null); syncHelper.CheckAllSyncsProcessed(); + + createUid = CreateAndVerifyObject(connector, ObjectClass.ACCOUNT, + GetNormalAttributes_Account()); + syncHelper.AddModUid(createUid, attributes); + createdUids.Add(createUid); + + // now get the latest sync token, and it + // should be greater or equal to the last one we saw + SyncToken latestToken = connector.GetLatestSyncToken(); + Assert.Greater(GetUpdateUsnFromToken(latestToken), GetUpdateUsnFromToken(syncHelper.Token)); + Assert.GreaterOrEqual(GetDeleteUsnFromToken(latestToken), GetDeleteUsnFromToken(syncHelper.Token)); } finally { + foreach (Uid uid in createdUids) + { + DeleteAndVerifyObject(connector, ObjectClass.ACCOUNT, uid, + false, false); + } } } + public long GetUpdateUsnFromToken(SyncToken token) + { + string[] tokenParts = ((string)token.Value).Split('|'); + return long.Parse(tokenParts[0]); + } + + public long GetDeleteUsnFromToken(SyncToken token) + { + string[] tokenParts = ((string)token.Value).Split('|'); + return long.Parse(tokenParts[1]); + } + class SyncTestHelper { IDictionary> _mods = null; @@ -1622,7 +1650,18 @@ public void DeleteAndVerifyObject(ActiveDirectoryConnector connector, } // delete - connector.Delete(oclass, uid, null); + try + { + connector.Delete(oclass, uid, null); + } + catch + { + if (verifyDeleted) + { + throw; + } + } + if (verifyDeleted) { // verify that object was deleted @@ -1994,7 +2033,9 @@ private static void VerifyObject(ICollection requestedAttrib ICollection ldapStringAttributes = new List(); ldapStringAttributes.Add("AD_CONTAINER"); - ldapStringAttributes.Add("@@NAME@@"); + ldapStringAttributes.Add(Name.NAME); + ldapStringAttributes.Add(PredefinedAttributes.GROUPS_NAME); + ldapStringAttributes.Add(PredefinedAttributes.ACCOUNTS_NAME); // for each attribute in the connector object ... foreach (ConnectorAttribute attribute in requestedAttributes) From d61a74c0b102fcd4d32f7b88fa2d66b5cb922fbc Mon Sep 17 00:00:00 2001 From: rcauble Date: Tue, 16 Sep 2008 20:00:19 +0000 Subject: [PATCH 013/342] Issue#273: use SafeType --- Common/SafeType.cs | 89 +++++++++-------- Framework/Api.cs | 21 ++-- Framework/Common.cs | 106 ++++++++++----------- Framework/CommonObjects.cs | 90 ++++++++--------- Framework/CommonSerializer.cs | 9 +- Framework/Spi.cs | 7 +- Framework/Test.cs | 9 +- FrameworkInternal/Api.cs | 68 ++++++------- FrameworkInternal/ApiLocal.cs | 99 ++++++++++--------- FrameworkInternal/ApiLocalOperations.cs | 6 +- FrameworkInternal/ApiRemote.cs | 6 +- FrameworkInternal/ApiRemoteMessages.cs | 7 +- FrameworkInternal/Serializer.cs | 68 +++++++++---- FrameworkInternal/Server.cs | 7 +- FrameworkInternal/Test.cs | 12 +-- FrameworkTests/ObjectSerializationTests.cs | 29 +++--- TestBundleV1/TestConnector.cs | 4 +- 17 files changed, 341 insertions(+), 296 deletions(-) diff --git a/Common/SafeType.cs b/Common/SafeType.cs index 17d2ba6f..690e2b1f 100644 --- a/Common/SafeType.cs +++ b/Common/SafeType.cs @@ -48,47 +48,81 @@ namespace Org.IdentityConnectors.Common /// The equivalent of java's Class<? extends...%gt; syntax. /// Allows you to restrict a Type to a certain class hierarchy. /// - public abstract class SafeType + public sealed class SafeType where T : class { + private readonly Type _rawType; + /// - /// Make private so no one else can subclass this. + /// Make private so no one can create directly /// - private SafeType() - { + private SafeType(Type rawType) + { + if (!ReflectionUtil.IsParentTypeOf(typeof(T),rawType)) { + throw new ArgumentException("Type: "+rawType+" is not a subclass of"+typeof(T)); + } + _rawType = rawType; } /// - /// Returns the member of the group by its C# type + /// Returns the SafeType for a given raw type. + /// NOTE: If possible use Get() instead since it is statically + /// checked at compile time. /// /// /// public static SafeType ForRawType(Type type) { - if (!typeof(T).IsAssignableFrom(type)) { - throw new ArgumentException("Type: "+type+" is not a subclass of"+typeof(T)); - } - Type safeType = typeof(SafeType); - MethodInfo info = safeType.GetMethod("Get"); - info = info.MakeGenericMethod(new Type[]{type}); - Object rv = info.Invoke(null,null); - return (SafeType)rv; + return new SafeType(type); } /// - /// Gets an instance of the safe type. + /// Gets an instance of the safe type in a type-safe fashion. /// /// The instance of the safe type public static SafeType Get() where U : T { - return new Impl(); + return new SafeType(typeof(U)); + } + + /// + /// Gets an instance of the safe type in a type-safe fashion from an object. + /// + /// The instance of the safe type + public static SafeType Get(T obj) + { + return new SafeType(obj.GetType()); + } + + /// + /// Returns the generic type definition corresponding to this type. + /// Will return the same type if this is already a generic type. + /// + /// + public SafeType GetTypeErasure() + { + return SafeType.ForRawType(ReflectionUtil.GetTypeErasure(RawType)); } /// /// Returns the underlying C# type /// - public abstract Type RawType {get;} + public Type RawType { + get { + return _rawType; + } + } + + /// + /// Creates a new instance of the given type + /// + /// The new instance + public T CreateInstance() + { + return (T)Activator.CreateInstance(RawType); + } + /// /// Returns true iff these represent the same underlying type /// and the SafeType has the same parent type. @@ -120,29 +154,6 @@ public override string ToString() { return RawType.ToString(); } - /// - /// Implementation - /// - private class Impl : SafeType - where U : T - { - /// - /// Creates the implementation - /// - public Impl() - { - } - /// - /// Returns the underlying raw type - /// - public override Type RawType - { - get - { - return typeof(U); - } - } - } } diff --git a/Framework/Api.cs b/Framework/Api.cs index b37755a2..f8bcd2b2 100644 --- a/Framework/Api.cs +++ b/Framework/Api.cs @@ -45,6 +45,7 @@ using System.Security; using System.Security.Cryptography.X509Certificates; using System.Text; +using Org.IdentityConnectors.Common; using Org.IdentityConnectors.Common.Pooling; using Org.IdentityConnectors.Common.Security; using Org.IdentityConnectors.Framework.Api.Operations; @@ -61,10 +62,10 @@ public interface APIConfiguration { ConfigurationProperties ConfigurationProperties { get; } bool IsConnectorPoolingSupported { get; } ObjectPoolConfiguration ConnectorPoolConfiguration { get; } - ICollection SupportedOperations { get; } + ICollection> SupportedOperations { get; } - int GetTimeout(Type operation); - void SetTimeout(Type operation, int timeout); + int GetTimeout(SafeType operation); + void SetTimeout(SafeType operation, int timeout); int ProducerBufferSize { get; set; } } @@ -155,12 +156,12 @@ public interface ConnectorFacade : CreateApiOp, DeleteApiOp, /** * Get the set of operations that this {@link ConnectorFacade} will support. */ - ICollection SupportedOperations { get; } + ICollection> SupportedOperations { get; } /** * Get an instance of an operation that this facade supports. */ - APIOperation GetOperation(Type type); + APIOperation GetOperation(SafeType type); } @@ -182,8 +183,8 @@ public abstract class ConnectorFacadeFactory { public static ConnectorFacadeFactory GetInstance() { lock(LOCK) { if (_instance == null) { - Type t = FrameworkInternalBridge.LoadType(IMPL_NAME); - _instance = (ConnectorFacadeFactory)Activator.CreateInstance(t); + SafeType t = FrameworkInternalBridge.LoadType(IMPL_NAME); + _instance = t.CreateInstance(); } } return _instance; @@ -264,9 +265,9 @@ public abstract class ConnectorInfoManagerFactory { public static ConnectorInfoManagerFactory GetInstance() { lock(LOCK) { if (_instance == null) { - Type t = - FrameworkInternalBridge.LoadType(IMPL_NAME); - _instance = (ConnectorInfoManagerFactory)Activator.CreateInstance(t); + SafeType t = + FrameworkInternalBridge.LoadType(IMPL_NAME); + _instance = t.CreateInstance(); } } return _instance; diff --git a/Framework/Common.cs b/Framework/Common.cs index 21d57a86..414e6cc2 100644 --- a/Framework/Common.cs +++ b/Framework/Common.cs @@ -56,7 +56,7 @@ internal static class FrameworkInternalBridge { /// /// /// - public static Type LoadType(String typeName) { + public static SafeType LoadType(String typeName) where T : class { Assembly assembly; lock(LOCK) { @@ -68,30 +68,41 @@ public static Type LoadType(String typeName) { assembly = _assembly; } - return assembly.GetType(typeName,true); + return SafeType.ForRawType(assembly.GetType(typeName,true)); } } public static class FrameworkUtil { - private static readonly IDictionary SPI_TO_API; + private static readonly IDictionary,SafeType> SPI_TO_API; private static readonly ICollection CONFIG_SUPPORTED_TYPES; private static readonly ICollection ATTR_SUPPORTED_TYPES; static FrameworkUtil() { - IDictionary temp = - new Dictionary(); - temp[typeof(AuthenticateOp)]=typeof(AuthenticationApiOp); - temp[typeof(CreateOp)]=typeof(CreateApiOp); - temp[typeof(DeleteOp)]=typeof(DeleteApiOp); - temp[typeof(SearchOp<>)]=typeof(SearchApiOp); - temp[typeof(UpdateOp)]=typeof(UpdateApiOp); - temp[typeof(AdvancedUpdateOp)]=typeof(UpdateApiOp); - temp[typeof(SchemaOp)]=typeof(SchemaApiOp); - temp[typeof(TestOp)]=typeof(TestApiOp); - temp[typeof(ScriptOnConnectorOp)]=typeof(ScriptOnConnectorApiOp); - temp[typeof(ScriptOnResourceOp)]=typeof(ScriptOnResourceApiOp); - temp[typeof(SyncOp)]=typeof(SyncApiOp); + IDictionary,SafeType> temp = + new Dictionary,SafeType>(); + temp[SafeType.Get()]= + SafeType.Get(); + temp[SafeType.Get()]= + SafeType.Get(); + temp[SafeType.Get()]= + SafeType.Get(); + temp[SafeType.ForRawType(typeof(SearchOp<>))]= + SafeType.Get(); + temp[SafeType.Get()]= + SafeType.Get(); + temp[SafeType.Get()]= + SafeType.Get(); + temp[SafeType.Get()]= + SafeType.Get(); + temp[SafeType.Get()]= + SafeType.Get(); + temp[SafeType.Get()]= + SafeType.Get(); + temp[SafeType.Get()]= + SafeType.Get(); + temp[SafeType.Get()]= + SafeType.Get(); SPI_TO_API = CollectionUtil.NewReadOnlyDictionary(temp); CONFIG_SUPPORTED_TYPES = CollectionUtil.NewReadOnlySet @@ -176,36 +187,37 @@ public static void CheckAttributeValue(Object value) { CheckAttributeType(value.GetType()); } } - public static ICollection Spi2Apis(Type type) { - type = ReflectionUtil.GetTypeErasure(type); - AssertSpiOperation(type); - HashSet set = new HashSet(); + public static ICollection> Spi2Apis(SafeType type) { + type = type.GetTypeErasure(); + HashSet> set = new HashSet>(); set.Add(SPI_TO_API[type]); // add GetApiOp if search is available.. - if (type.Equals(typeof(SearchOp<>))) { - set.Add(typeof(GetApiOp)); + if (type.RawType.Equals(typeof(SearchOp<>))) { + set.Add(SafeType.Get()); } return set; } - public static ICollection AllSPIOperations() { + public static ICollection> AllSPIOperations() { return SPI_TO_API.Keys; } - public static ICollection AllAPIOperations() { - ICollection set = new HashSet(); + public static ICollection> AllAPIOperations() { + ICollection> set = + new HashSet>(); CollectionUtil.AddAll(set, SPI_TO_API.Values); // add Get because it doesn't have a corresponding SPI. - set.Add(typeof(GetApiOp)); - set.Add(typeof(ValidateApiOp)); + set.Add(SafeType.Get()); + set.Add(SafeType.Get()); return CollectionUtil.AsReadOnlySet(set); } - public static ICollection GetDefaultSupportedOperations(Type connector) { - AssertConnectorType(connector); - ICollection rv = new HashSet(); - ICollection interfaces = ReflectionUtil.GetTypeErasure(ReflectionUtil.GetAllInterfaces(connector)); - foreach (Type spi in AllSPIOperations()) { - if (interfaces.Contains(spi)) { + public static ICollection> GetDefaultSupportedOperations(SafeType connector) { + ICollection> rv = + new HashSet>(); + ICollection interfaces = + ReflectionUtil.GetTypeErasure(ReflectionUtil.GetAllInterfaces(connector.RawType)); + foreach (SafeType spi in AllSPIOperations()) { + if (interfaces.Contains(spi.RawType)) { CollectionUtil.AddAll(rv,Spi2Apis(spi)); } } @@ -213,13 +225,13 @@ public static ICollection GetDefaultSupportedOperations(Type connector) { CollectionUtil.AddAll(rv,GetUnconditionallySupportedOperations()); return CollectionUtil.AsReadOnlySet(rv); } - public static ICollection GetUnconditionallySupportedOperations() { - HashSet ret; - ret = new HashSet(); + public static ICollection> GetUnconditionallySupportedOperations() { + HashSet> ret; + ret = new HashSet>(); //add validate api op always - ret.Add(typeof(ValidateApiOp)); + ret.Add(SafeType.Get()); //add ScriptOnConnectorApiOp always - ret.Add(typeof(ScriptOnConnectorApiOp)); + ret.Add(SafeType.Get()); return ret; } public static ICollection GetAllSupportedConfigTypes() { @@ -240,24 +252,6 @@ public static bool IsSupportedAttributeType(Type clazz) { return ATTR_SUPPORTED_TYPES.Contains(clazz); } - public static void AssertConnectorType(Type connector) { - Type connectorInter = typeof(Connector); - if (!connectorInter.IsAssignableFrom(connector)) { - throw new ArgumentException(connector+" does not implement IConnector"); - } - } - public static void AssertSpiOperation(Type operation) { - Type spiInter = typeof(SPIOperation); - if (!spiInter.IsAssignableFrom(operation)) { - throw new ArgumentException(operation+" does not implement ISPIOperation"); - } - } - public static void AssertApiOperation(Type operation) { - Type spiInter = typeof(APIOperation); - if (!spiInter.IsAssignableFrom(operation)) { - throw new ArgumentException(operation+" does not implement APIOperation"); - } - } /** * Determines if the class is a supported type for an OperationOption. If not it throws * an {@link IllegalArgumentException}. diff --git a/Framework/CommonObjects.cs b/Framework/CommonObjects.cs index d5c54793..059b475a 100644 --- a/Framework/CommonObjects.cs +++ b/Framework/CommonObjects.cs @@ -47,6 +47,7 @@ using Org.IdentityConnectors.Common; using Org.IdentityConnectors.Common.Security; +using Org.IdentityConnectors.Framework.Spi; using Org.IdentityConnectors.Framework.Spi.Operations; using Org.IdentityConnectors.Framework.Api.Operations; using Org.IdentityConnectors.Framework.Common.Serializer; @@ -2230,9 +2231,9 @@ public static OperationOptionInfo BuildRunAsUser() { public sealed class Schema { private readonly ICollection _declaredObjectClasses; private readonly ICollection _declaredOperationOptions; - private readonly IDictionary> + private readonly IDictionary,ICollection> _supportedObjectClassesByOperation; - private readonly IDictionary> + private readonly IDictionary,ICollection> _supportedOptionsByOperation; /** @@ -2243,18 +2244,18 @@ private readonly IDictionary> */ public Schema(ICollection info, ICollection options, - IDictionary> supportedObjectClassesByOperation, - IDictionary> supportedOptionsByOperation) { + IDictionary,ICollection> supportedObjectClassesByOperation, + IDictionary,ICollection> supportedOptionsByOperation) { _declaredObjectClasses = CollectionUtil.NewReadOnlySet(info); _declaredOperationOptions = CollectionUtil.NewReadOnlySet(options); //make read-only { - IDictionary> temp = - new Dictionary>(); - foreach (KeyValuePair> entry in + IDictionary,ICollection> temp = + new Dictionary,ICollection>(); + foreach (KeyValuePair,ICollection> entry in supportedObjectClassesByOperation) { - Type op = + SafeType op = entry.Key; ICollection resolvedClasses = CollectionUtil.NewReadOnlySet(entry.Value); @@ -2264,11 +2265,11 @@ public Schema(ICollection info, } //make read-only { - IDictionary> temp = - new Dictionary>(); - foreach (KeyValuePair> entry in + IDictionary,ICollection> temp = + new Dictionary,ICollection>(); + foreach (KeyValuePair,ICollection> entry in supportedOptionsByOperation) { - Type op = + SafeType op = entry.Key; ICollection resolvedClasses = CollectionUtil.NewReadOnlySet(entry.Value); @@ -2333,7 +2334,7 @@ public OperationOptionInfo FindOperationOptionInfo(String name) { * @param apiop The operation. * @return the supported object classes for the given operation. */ - public ICollection GetSupportedObjectClassesByOperation(Type apiop) { + public ICollection GetSupportedObjectClassesByOperation(SafeType apiop) { ICollection rv = CollectionUtil.GetValue(_supportedObjectClassesByOperation,apiop,null); if ( rv == null ) { @@ -2352,7 +2353,7 @@ public ICollection GetSupportedObjectClassesByOperation(Type ap * @param apiop The operation. * @return the supported options for the given operation. */ - public ICollection GetSupportedOptionsByOperation(Type apiop) { + public ICollection GetSupportedOptionsByOperation(SafeType apiop) { ICollection rv = CollectionUtil.GetValue(_supportedOptionsByOperation,apiop,null); if ( rv == null ) { @@ -2369,7 +2370,7 @@ public ICollection GetSupportedOptionsByOperation(Type apio * Returns the set of object classes that apply to a particular operation. * @return the set of object classes that apply to a particular operation. */ - public IDictionary> SupportedObjectClassesByOperation { + public IDictionary,ICollection> SupportedObjectClassesByOperation { get { return _supportedObjectClassesByOperation; } @@ -2378,7 +2379,7 @@ public IDictionary> SupportedObjectClassesByOp * Returns the set of operation options that apply to a particular operation. * @return the set of operation options that apply to a particular operation. */ - public IDictionary> SupportedOptionsByOperation { + public IDictionary,ICollection> SupportedOptionsByOperation { get { return _supportedOptionsByOperation; } @@ -2424,26 +2425,25 @@ public override string ToString() * Simple builder class to help facilitate creating a {@link Schema} object. */ public sealed class SchemaBuilder { - private readonly Type _connectorClass; + private readonly SafeType _connectorClass; private readonly ICollection _declaredObjectClasses = new HashSet(); private readonly ICollection _declaredOperationOptions = new HashSet(); - private readonly IDictionary> + private readonly IDictionary,ICollection> _supportedObjectClassesByOperation = - new Dictionary>(); - private readonly IDictionary> + new Dictionary,ICollection>(); + private readonly IDictionary,ICollection> _supportedOptionsByOperation = - new Dictionary>(); + new Dictionary,ICollection>(); /** * */ - public SchemaBuilder(Type connectorClass) { + public SchemaBuilder(SafeType connectorClass) { Assertions.NullCheck(connectorClass, "connectorClass"); - FrameworkUtil.AssertConnectorType(connectorClass); _connectorClass = connectorClass; } @@ -2462,7 +2462,7 @@ public void DefineObjectClass(ObjectClassInfo info) { info.ObjectType); } _declaredObjectClasses.Add(info); - foreach (Type op in + foreach (SafeType op in FrameworkUtil.GetDefaultSupportedOperations(_connectorClass)) { ICollection oclasses = CollectionUtil.GetValue(_supportedObjectClassesByOperation,op,null); @@ -2485,7 +2485,7 @@ public void DefineOperationOption(OperationOptionInfo info) { info.Name); } _declaredOperationOptions.Add(info); - foreach (Type op in + foreach (SafeType op in FrameworkUtil.GetDefaultSupportedOperations(_connectorClass)) { ICollection oclasses = CollectionUtil.GetValue(_supportedOptionsByOperation,op,null); @@ -2533,27 +2533,27 @@ public void DefineOperationOption(String optionName, Type type) { * @throws IllegalArgumentException If the given ObjectClassInfo was * not already defined using {@link #defineObjectClass(ObjectClassInfo)}. */ - public void AddSupportedObjectClass(Type op, + public void AddSupportedObjectClass(SafeType op, ObjectClassInfo def) { Assertions.NullCheck(op, "op"); Assertions.NullCheck(def, "def"); - ICollection apis = + ICollection> apis = FrameworkUtil.Spi2Apis(op); if (!_declaredObjectClasses.Contains(def)) { throw new ArgumentException("ObjectClass "+def.ObjectType+ " not defined in schema."); } - foreach (Type api in apis) { + foreach (SafeType api in apis) { ICollection infos = CollectionUtil.GetValue(_supportedObjectClassesByOperation,api,null); if ( infos == null ) { - throw new ArgumentException("Operation "+op.Name+ + throw new ArgumentException("Operation "+op+ " not implement by connector."); } if ( infos.Contains(def)) { throw new ArgumentException("ObjectClass "+def.ObjectType+ - " already supported for operation "+op.Name); + " already supported for operation "+op); } infos.Add(def); } @@ -2567,27 +2567,27 @@ public void AddSupportedObjectClass(Type op, * @throws IllegalArgumentException If the given ObjectClassInfo was * not already defined using {@link #defineObjectClass(ObjectClassInfo)}. */ - public void RemoveSupportedObjectClass(Type op, + public void RemoveSupportedObjectClass(SafeType op, ObjectClassInfo def) { Assertions.NullCheck(op, "op"); Assertions.NullCheck(def, "def"); - ICollection apis = + ICollection> apis = FrameworkUtil.Spi2Apis(op); if (!_declaredObjectClasses.Contains(def)) { throw new ArgumentException("ObjectClass "+def.ObjectType+ " not defined in schema."); } - foreach (Type api in apis) { + foreach (SafeType api in apis) { ICollection infos = CollectionUtil.GetValue(_supportedObjectClassesByOperation,api,null); if ( infos == null ) { - throw new ArgumentException("Operation "+op.Name+ + throw new ArgumentException("Operation "+op+ " not implement by connector."); } if ( !infos.Contains(def)) { throw new ArgumentException("ObjectClass "+def.ObjectType - +" already removed for operation "+op.Name); + +" already removed for operation "+op); } infos.Remove(def); } @@ -2600,26 +2600,26 @@ public void RemoveSupportedObjectClass(Type op, * @throws IllegalArgumentException If the given OperationOptionInfo was * not already defined using {@link #defineOperationOption(OperationOptionInfo)}. */ - public void AddSupportedOperationOption(Type op, + public void AddSupportedOperationOption(SafeType op, OperationOptionInfo def) { Assertions.NullCheck(op, "op"); Assertions.NullCheck(def, "def"); - ICollection apis = + ICollection> apis = FrameworkUtil.Spi2Apis(op); if (!_declaredOperationOptions.Contains(def)) { throw new ArgumentException("OperationOption "+def.Name+ " not defined in schema."); } - foreach (Type api in apis) { + foreach (SafeType api in apis) { ICollection infos = CollectionUtil.GetValue(_supportedOptionsByOperation,api,null); if ( infos == null ) { - throw new ArgumentException("Operation "+op.Name+ + throw new ArgumentException("Operation "+op+ " not implement by connector."); } if ( infos.Contains(def) ) { throw new ArgumentException("OperationOption "+def.Name+ - " already supported for operation "+op.Name); + " already supported for operation "+op); } infos.Add(def); } @@ -2633,26 +2633,26 @@ public void AddSupportedOperationOption(Type op, * @throws IllegalArgumentException If the given OperationOptionInfo was * not already defined using {@link #defineOperationOption(OperationOptionInfo)}. */ - public void RemoveSupportedOperationOption(Type op, + public void RemoveSupportedOperationOption(SafeType op, OperationOptionInfo def) { Assertions.NullCheck(op, "op"); Assertions.NullCheck(def, "def"); - ICollection apis = + ICollection> apis = FrameworkUtil.Spi2Apis(op); if (!_declaredOperationOptions.Contains(def)) { throw new ArgumentException("OperationOption "+def.Name+ " not defined in schema."); } - foreach (Type api in apis) { + foreach (SafeType api in apis) { ICollection infos = CollectionUtil.GetValue(_supportedOptionsByOperation,api,null); if ( infos == null ) { - throw new ArgumentException("Operation "+op.Name+ + throw new ArgumentException("Operation "+op+ " not implement by connector."); } if ( !infos.Contains(def) ) { throw new ArgumentException("OperationOption "+def.Name+ - " already removed for operation "+op.Name); + " already removed for operation "+op); } infos.Remove(def); } diff --git a/Framework/CommonSerializer.cs b/Framework/CommonSerializer.cs index d3d4c2e1..67f48ef3 100644 --- a/Framework/CommonSerializer.cs +++ b/Framework/CommonSerializer.cs @@ -40,6 +40,7 @@ using System; using System.IO; using System.Collections.Generic; +using Org.IdentityConnectors.Common; namespace Org.IdentityConnectors.Framework.Common.Serializer { /** @@ -103,10 +104,10 @@ public abstract class ObjectSerializerFactory { public static ObjectSerializerFactory GetInstance() { lock (LOCK) { if (_instance == null) { - Type t = - FrameworkInternalBridge.LoadType(IMPL_NAME); - Object obj = Activator.CreateInstance(t); - _instance = (ObjectSerializerFactory)obj; + SafeType t = + FrameworkInternalBridge.LoadType(IMPL_NAME); + + _instance = t.CreateInstance(); } return _instance; } diff --git a/Framework/Spi.cs b/Framework/Spi.cs index 242e2446..47d875e8 100644 --- a/Framework/Spi.cs +++ b/Framework/Spi.cs @@ -40,6 +40,7 @@ using System; using System.Globalization; using System.Collections.Generic; +using Org.IdentityConnectors.Common; using Org.IdentityConnectors.Common.Pooling; using Org.IdentityConnectors.Framework.Common.Objects; namespace Org.IdentityConnectors.Framework.Spi @@ -102,12 +103,12 @@ public abstract class AbstractConfiguration : Configuration { public class ConnectorClassAttribute : System.Attribute { private readonly String _connectorDisplayNameKey; - private readonly Type _connectorConfigurationClass; + private readonly SafeType _connectorConfigurationClass; public ConnectorClassAttribute(String connectorDisplayNameKey, Type connectorConfigurationClass) { _connectorDisplayNameKey = connectorDisplayNameKey; - _connectorConfigurationClass = connectorConfigurationClass; + _connectorConfigurationClass = SafeType.ForRawType(connectorConfigurationClass); } public string ConnectorDisplayNameKey { @@ -116,7 +117,7 @@ public string ConnectorDisplayNameKey { } } - public Type ConnectorConfigurationType { + public SafeType ConnectorConfigurationType { get { return _connectorConfigurationClass; } diff --git a/Framework/Test.cs b/Framework/Test.cs index 713e5865..1c996e77 100644 --- a/Framework/Test.cs +++ b/Framework/Test.cs @@ -73,7 +73,7 @@ public abstract class TestHelpers { /** * Method for convenient testing of local connectors. */ - public static APIConfiguration CreateTestConfiguration(Type clazz, + public static APIConfiguration CreateTestConfiguration(SafeType clazz, Configuration config) { return GetInstance().CreateTestConfigurationImpl(clazz, config); } @@ -170,16 +170,15 @@ public static void Search(SearchOp search, private static TestHelpers GetInstance() { lock(LOCK) { if (_instance == null) { - Type type = FrameworkInternalBridge.LoadType(IMPL_NAME); - Object obj = Activator.CreateInstance(type); - _instance = (TestHelpers)obj; + SafeType type = FrameworkInternalBridge.LoadType(IMPL_NAME); + _instance = type.CreateInstance(); } return _instance; } } - abstract protected APIConfiguration CreateTestConfigurationImpl(Type clazz, + abstract protected APIConfiguration CreateTestConfigurationImpl(SafeType clazz, Configuration config); abstract protected void SearchImpl(SearchOp search, ObjectClass oclass, diff --git a/FrameworkInternal/Api.cs b/FrameworkInternal/Api.cs index d20da4d6..ec9a2658 100644 --- a/FrameworkInternal/Api.cs +++ b/FrameworkInternal/Api.cs @@ -213,11 +213,11 @@ public class APIConfigurationImpl : APIConfiguration { private ObjectPoolConfiguration _connectorPooling; private ConfigurationPropertiesImpl _configurationProperties; - private ICollection _supportedOperations = - CollectionUtil.NewReadOnlySet(new Type[0]); + private ICollection> _supportedOperations = + CollectionUtil.NewReadOnlySet>(new SafeType[0]); - private IDictionary _timeoutMap = - new Dictionary(); + private IDictionary, int> _timeoutMap = + new Dictionary, int>(); public ConfigurationProperties ConfigurationProperties { get { @@ -233,7 +233,7 @@ public ConfigurationProperties ConfigurationProperties { } } } - public IDictionary TimeoutMap { + public IDictionary, int> TimeoutMap { get { return _timeoutMap; } @@ -254,22 +254,20 @@ public ObjectPoolConfiguration ConnectorPoolConfiguration _connectorPooling = value; } } - public ICollection SupportedOperations { + public ICollection> SupportedOperations { get { return _supportedOperations; } set { - _supportedOperations = CollectionUtil.NewReadOnlySet(value); + _supportedOperations = CollectionUtil.NewReadOnlySet>(value); } } - public int GetTimeout(Type operation) { - FrameworkUtil.AssertApiOperation(operation); + public int GetTimeout(SafeType operation) { return CollectionUtil.GetValue(_timeoutMap,operation, APIConstants.NO_TIMEOUT); } - public void SetTimeout(Type operation, int timeout) { - FrameworkUtil.AssertApiOperation(operation); + public void SetTimeout(SafeType operation, int timeout) { _timeoutMap[operation] = timeout; } @@ -499,7 +497,7 @@ public AbstractConnectorFacade(APIConfigurationImpl configuration) { * return an instance of the operation. * @see com.sun.openconnectors.framework.api.ConnectorFacade#getOperation(java.lang.Class) */ - public APIOperation GetOperation(Type api) { + public APIOperation GetOperation(SafeType api) { if (!SupportedOperations.Contains(api)) { return null; } @@ -509,7 +507,7 @@ public APIOperation GetOperation(Type api) { /** * {@inheritDoc} */ - public ICollection SupportedOperations { + public ICollection> SupportedOperations { get { return _configuration.SupportedOperations; } @@ -522,7 +520,7 @@ public ICollection SupportedOperations { * {@inheritDoc} */ public Schema Schema() { - return ((SchemaApiOp) this.GetOperationCheckSupported(typeof(SchemaApiOp))) + return ((SchemaApiOp) this.GetOperationCheckSupported(SafeType.Get())) .Schema(); } @@ -530,7 +528,7 @@ public Schema Schema() { * {@inheritDoc} */ public Uid Create(ObjectClass oclass, ICollection attrs, OperationOptions options) { - CreateApiOp op = ((CreateApiOp) GetOperationCheckSupported(typeof(CreateApiOp))); + CreateApiOp op = ((CreateApiOp) GetOperationCheckSupported(SafeType.Get())); return op.Create(oclass,attrs,options); } @@ -538,14 +536,16 @@ public Uid Create(ObjectClass oclass, ICollection attrs, Ope * {@inheritDoc} */ public void Delete(ObjectClass objClass, Uid uid, OperationOptions options) { - ((DeleteApiOp) this.GetOperationCheckSupported(typeof(DeleteApiOp))).Delete(objClass, uid, options); + ((DeleteApiOp) + this.GetOperationCheckSupported(SafeType.Get())) + .Delete(objClass, uid, options); } /** * {@inheritDoc} */ public void Search(ObjectClass oclass,Filter filter, ResultsHandler handler, OperationOptions options) { - ((SearchApiOp) this.GetOperationCheckSupported(typeof(SearchApiOp))).Search( + ((SearchApiOp) this.GetOperationCheckSupported(SafeType.Get())).Search( oclass,filter, handler, options); } @@ -553,7 +553,7 @@ public void Search(ObjectClass oclass,Filter filter, ResultsHandler handler, Ope * {@inheritDoc} */ public Uid Update(UpdateApiType type, ObjectClass objclass, ICollection attrs, OperationOptions options) { - return ((UpdateApiOp) this.GetOperationCheckSupported(typeof(UpdateApiOp))) + return ((UpdateApiOp) this.GetOperationCheckSupported(SafeType.Get())) .Update(type, objclass, attrs, options); } @@ -562,7 +562,7 @@ public Uid Update(UpdateApiType type, ObjectClass objclass, ICollection.Get())).Authenticate( username, password, options); } @@ -570,7 +570,7 @@ public void Authenticate(String username, GuardedString password, OperationOptio * {@inheritDoc} */ public ConnectorObject GetObject(ObjectClass objClass, Uid uid, OperationOptions options) { - return ((GetApiOp) this.GetOperationCheckSupported(typeof(GetApiOp))) + return ((GetApiOp) this.GetOperationCheckSupported(SafeType.Get())) .GetObject(objClass, uid, options); } /** @@ -579,7 +579,7 @@ public ConnectorObject GetObject(ObjectClass objClass, Uid uid, OperationOptions public Object RunScriptOnConnector(ScriptContext request, OperationOptions options) { return ((ScriptOnConnectorApiOp) this - .GetOperationCheckSupported(typeof(ScriptOnConnectorApiOp))) + .GetOperationCheckSupported(SafeType.Get())) .RunScriptOnConnector(request, options); } @@ -589,7 +589,7 @@ public Object RunScriptOnConnector(ScriptContext request, public Object RunScriptOnResource(ScriptContext request, OperationOptions options) { return ((ScriptOnResourceApiOp) this - .GetOperationCheckSupported(typeof(ScriptOnResourceApiOp))) + .GetOperationCheckSupported(SafeType.Get())) .RunScriptOnResource(request, options); } @@ -597,29 +597,29 @@ public Object RunScriptOnResource(ScriptContext request, * {@inheritDoc} */ public void Test() { - ((TestApiOp) this.GetOperationCheckSupported(typeof(TestApiOp))).Test(); + ((TestApiOp) this.GetOperationCheckSupported(SafeType.Get())).Test(); } /** * {@inheritDoc} */ public void Validate() { - ((ValidateApiOp) this.GetOperationCheckSupported(typeof(ValidateApiOp))).Validate(); + ((ValidateApiOp) this.GetOperationCheckSupported(SafeType.Get())).Validate(); } public void Sync(ObjectClass objClass, SyncToken token, SyncResultsHandler handler, OperationOptions options) { - ((SyncApiOp)this.GetOperationCheckSupported(typeof(SyncApiOp))) + ((SyncApiOp)this.GetOperationCheckSupported(SafeType.Get())) .Sync(objClass, token, handler, options); } public SyncToken GetLatestSyncToken() { - return ((SyncApiOp)this.GetOperationCheckSupported(typeof(SyncApiOp))) + return ((SyncApiOp)this.GetOperationCheckSupported(SafeType.Get())) .GetLatestSyncToken(); } - private APIOperation GetOperationCheckSupported(Type api) { + private APIOperation GetOperationCheckSupported(SafeType api) { // check if this operation is supported. if (!SupportedOperations.Contains(api)) { String MSG = "Operation ''{0}'' not supported."; @@ -634,7 +634,7 @@ private APIOperation GetOperationCheckSupported(Type api) { * @param api The operation to implement. * @return The implementation */ - protected abstract APIOperation GetOperationImplementation(Type api); + protected abstract APIOperation GetOperationImplementation(SafeType api); protected APIConfigurationImpl GetAPIConfiguration() { return _configuration; @@ -643,8 +643,8 @@ protected APIConfigurationImpl GetAPIConfiguration() { /** * Creates a new {@link APIOperation} proxy given a handler. */ - protected APIOperation NewAPIOperationProxy(Type api, InvocationHandler handler) { - return (APIOperation) Proxy.NewProxyInstance(api, handler); + protected APIOperation NewAPIOperationProxy(SafeType api, InvocationHandler handler) { + return (APIOperation) Proxy.NewProxyInstance(api.RawType, handler); } private static bool LOGGINGPROXY_ENABLED; @@ -654,7 +654,7 @@ static AbstractConnectorFacade() { LOGGINGPROXY_ENABLED = StringUtil.IsTrue(enabled); } - protected APIOperation CreateLoggingProxy(Type api, APIOperation target) { + protected APIOperation CreateLoggingProxy(SafeType api, APIOperation target) { APIOperation ret = target; if (LOGGINGPROXY_ENABLED) { LoggingProxy logging = new LoggingProxy(api, target); @@ -796,10 +796,10 @@ public static Object AdaptFromObjectStreamHandler(Type interfaceType, #region LoggingProxy public class LoggingProxy : InvocationHandler { - private readonly Type _op; + private readonly SafeType _op; private readonly object _target; - public LoggingProxy(Type api, object target) { + public LoggingProxy(SafeType api, object target) { _op = api; _target = target; } @@ -841,7 +841,7 @@ public Object Invoke(Object proxy, MethodInfo method, Object [] args) { } private void AddMethodName(StringBuilder bld, MethodInfo method) { - bld.Append(_op.Name); + bld.Append(_op.RawType.Name); bld.Append('.'); bld.Append(method.Name); } diff --git a/FrameworkInternal/ApiLocal.cs b/FrameworkInternal/ApiLocal.cs index 3af4e165..3a1a3876 100644 --- a/FrameworkInternal/ApiLocal.cs +++ b/FrameworkInternal/ApiLocal.cs @@ -109,7 +109,7 @@ public PoolableConnector NewObject() { CSharpClassProperties.CreateBean((ConfigurationPropertiesImpl)_apiConfiguration.ConfigurationProperties, _localInfo.ConnectorConfigurationClass); PoolableConnector connector = - (PoolableConnector)(Activator.CreateInstance(_localInfo.ConnectorClass)); + (PoolableConnector)_localInfo.ConnectorClass.CreateInstance(); connector.Init(config); return connector; } @@ -185,7 +185,7 @@ internal static class CSharpClassProperties public static ConfigurationPropertiesImpl CreateConfigurationProperties(Configuration defaultObject) { - Type config = defaultObject.GetType(); + SafeType config = SafeType.Get(defaultObject); ConfigurationPropertiesImpl properties = new ConfigurationPropertiesImpl(); IList temp = @@ -243,8 +243,8 @@ public static ConfigurationPropertiesImpl public static Configuration CreateBean(ConfigurationPropertiesImpl properties, - Type config) { - Configuration rv = (Configuration)Activator.CreateInstance(config); + SafeType config) { + Configuration rv = config.CreateInstance(); rv.ConnectorMessages=properties.Parent.ConnectorInfo.Messages; IDictionary descriptors = GetFilteredProperties(config); @@ -256,7 +256,7 @@ public static Configuration String FMT = "Class ''{0}'' does not have a property ''{1}''."; String MSG = String.Format(FMT, - config.Name, + config.RawType.Name, name); throw new ArgumentException(MSG); } @@ -271,11 +271,11 @@ public static Configuration } private static IDictionary - GetFilteredProperties(Type config) + GetFilteredProperties(SafeType config) { IDictionary rv = new Dictionary(); - PropertyInfo [] descriptors = config.GetProperties(); + PropertyInfo [] descriptors = config.RawType.GetProperties(); foreach (PropertyInfo descriptor in descriptors) { String propName = descriptor.Name; if ( !descriptor.CanWrite ) { @@ -367,28 +367,24 @@ private IList ProcessAssembly(Assembly assembly) { } private LocalConnectorInfoImpl CreateConnectorInfo(Assembly assembly, - Type connectorClass, + Type rawConnectorClass, ConnectorClassAttribute attribute) { String fileName = assembly.Location; - if (!typeof(Connector).IsAssignableFrom(connectorClass)) { + if (!typeof(Connector).IsAssignableFrom(rawConnectorClass)) { String MSG = ( "File "+fileName+ - " declares a connector "+connectorClass+ + " declares a connector "+rawConnectorClass+ " that does not implement Connector."); throw new ConfigurationException(MSG); } - Type connectorConfigurationClass = attribute.ConnectorConfigurationType; + SafeType connectorClass = + SafeType.ForRawType(rawConnectorClass); + SafeType connectorConfigurationClass = attribute.ConnectorConfigurationType; if ( connectorConfigurationClass == null ) { String MSG = ( "File "+fileName+ " contains a ConnectorInfo attribute "+ "with no connector configuration class."); throw new ConfigurationException(MSG); } - if (!typeof(Configuration).IsAssignableFrom(connectorConfigurationClass)) { - String MSG = ("File "+fileName+" declared a connector "+ - "configuration class "+connectorConfigurationClass+ - " that does not implement IConfiguration."); - throw new ConfigurationException(MSG); - } String connectorDisplayNameKey = attribute.ConnectorDisplayNameKey; if ( connectorDisplayNameKey == null ) { @@ -400,7 +396,7 @@ private LocalConnectorInfoImpl CreateConnectorInfo(Assembly assembly, ConnectorKey key = new ConnectorKey(assembly.GetName().Name, assembly.GetName().Version.ToString(), - connectorClass.Namespace+"."+connectorClass.Name); + connectorClass.RawType.Namespace+"."+connectorClass.RawType.Name); LocalConnectorInfoImpl rv = new LocalConnectorInfoImpl(); rv.ConnectorClass = connectorClass; rv.ConnectorConfigurationClass = connectorConfigurationClass; @@ -413,11 +409,11 @@ private LocalConnectorInfoImpl CreateConnectorInfo(Assembly assembly, private APIConfigurationImpl CreateDefaultAPIConfiguration(LocalConnectorInfoImpl localInfo) { - Type connectorClass = + SafeType connectorClass = localInfo.ConnectorClass; APIConfigurationImpl rv = new APIConfigurationImpl(); Configuration config = - (Configuration)Activator.CreateInstance(localInfo.ConnectorConfigurationClass); + localInfo.ConnectorConfigurationClass.CreateInstance(); bool pooling = IsPoolingSupported(connectorClass); rv.IsConnectorPoolingSupported=pooling; rv.ConfigurationProperties=(CSharpClassProperties.CreateConfigurationProperties(config)); @@ -426,8 +422,8 @@ private APIConfigurationImpl return rv; } - private static bool IsPoolingSupported(Type clazz) { - return ReflectionUtil.IsParentTypeOf(typeof(PoolableConnector),clazz); + private static bool IsPoolingSupported(SafeType clazz) { + return ReflectionUtil.IsParentTypeOf(typeof(PoolableConnector),clazz.RawType); } /// /// Given an assembly, returns the list of cultures that @@ -473,7 +469,7 @@ private ConnectorMessagesImpl LoadMessages(Assembly assembly, String nameBase) { if ( StringUtil.IsBlank(nameBase) ) { String pkage = - info.ConnectorClass.Namespace; + info.ConnectorClass.RawType.Namespace; nameBase = pkage+".Messages"; } ConnectorMessagesImpl rv = new ConnectorMessagesImpl(); @@ -526,8 +522,8 @@ public RemoteConnectorInfoImpl ToRemote() { rv.Messages=Messages; return rv; } - public Type ConnectorClass {get;set;} - public Type ConnectorConfigurationClass {get;set;} + public SafeType ConnectorClass {get;set;} + public SafeType ConnectorConfigurationClass {get;set;} } #endregion @@ -540,13 +536,13 @@ internal class LocalConnectorFacadeImpl : AbstractConnectorFacade { /** * Map the API interfaces to their implementation counterparts. */ - private static readonly IDictionary API_TO_IMPL= - new Dictionary(); + private static readonly IDictionary,ConstructorInfo> API_TO_IMPL= + new Dictionary,ConstructorInfo>(); - private static void AddImplementation(Type inter, - Type impl) { + private static void AddImplementation(SafeType inter, + SafeType impl) { ConstructorInfo info = - impl.GetConstructor(new Type[]{typeof(ConnectorOperationalContext), + impl.RawType.GetConstructor(new Type[]{typeof(ConnectorOperationalContext), typeof(Connector)}); if ( info == null ) { throw new ArgumentException(impl+" does not define the proper constructor"); @@ -555,16 +551,26 @@ private static void AddImplementation(Type inter, } static LocalConnectorFacadeImpl() { - AddImplementation(typeof(CreateApiOp), typeof(CreateImpl)); - AddImplementation(typeof(DeleteApiOp), typeof(DeleteImpl)); - AddImplementation(typeof(SchemaApiOp), typeof(SchemaImpl)); - AddImplementation(typeof(SearchApiOp), typeof(SearchImpl)); - AddImplementation(typeof(UpdateApiOp), typeof(UpdateImpl)); - AddImplementation(typeof(AuthenticationApiOp), typeof(AuthenticationImpl)); - AddImplementation(typeof(TestApiOp), typeof(TestImpl)); - AddImplementation(typeof(ScriptOnConnectorApiOp), typeof(ScriptOnConnectorImpl)); - AddImplementation(typeof(ScriptOnResourceApiOp), typeof(ScriptOnResourceImpl)); - AddImplementation(typeof(SyncApiOp), typeof(SyncImpl)); + AddImplementation(SafeType.Get(), + SafeType.Get()); + AddImplementation(SafeType.Get(), + SafeType.Get()); + AddImplementation(SafeType.Get(), + SafeType.Get()); + AddImplementation(SafeType.Get(), + SafeType.Get()); + AddImplementation(SafeType.Get(), + SafeType.Get()); + AddImplementation(SafeType.Get(), + SafeType.Get()); + AddImplementation(SafeType.Get(), + SafeType.Get()); + AddImplementation(SafeType.Get(), + SafeType.Get()); + AddImplementation(SafeType.Get(), + SafeType.Get()); + AddImplementation(SafeType.Get(), + SafeType.Get()); } @@ -594,11 +600,11 @@ public LocalConnectorFacadeImpl(LocalConnectorInfoImpl connectorInfo, // ConnectorFacade Interface // ======================================================================= - protected override APIOperation GetOperationImplementation(Type api) { + protected override APIOperation GetOperationImplementation(SafeType api) { APIOperation ret = null; // need to figure out if api operation is a get op.. - if (api.Equals(typeof(GetApiOp))) { - APIOperation op = GetAPIOperationRunner(typeof(SearchApiOp)); + if (api.RawType.Equals(typeof(GetApiOp))) { + APIOperation op = GetAPIOperationRunner(SafeType.Get()); ret = new GetImpl((SearchApiOp) op); } else { ret = GetAPIOperationRunner(api); @@ -606,13 +612,13 @@ protected override APIOperation GetOperationImplementation(Type api) { return ret; } - APIOperation GetAPIOperationRunner(Type api) { + APIOperation GetAPIOperationRunner(SafeType api) { APIOperation proxy; //first create the inner proxy - this is the proxy that obtaining //a connection from the pool, etc //NOTE: we want to skip this part of the proxy for //validate op, but we will want the timeout proxy - if ( api.Equals(typeof(ValidateApiOp))) { + if ( api.RawType.Equals(typeof(ValidateApiOp))) { OperationalContext context = new OperationalContext(connectorInfo,GetAPIConfiguration()); proxy = new ValidateImpl(context); @@ -628,8 +634,7 @@ APIOperation GetAPIOperationRunner(Type api) { ConnectorAPIOperationRunnerProxy handler = new ConnectorAPIOperationRunnerProxy(context,constructor); proxy = - (APIOperation)Proxy.NewProxyInstance(api, - handler); + NewAPIOperationProxy(api,handler); } //TODO: timeout diff --git a/FrameworkInternal/ApiLocalOperations.cs b/FrameworkInternal/ApiLocalOperations.cs index ee893eab..ee6dbfd6 100644 --- a/FrameworkInternal/ApiLocalOperations.cs +++ b/FrameworkInternal/ApiLocalOperations.cs @@ -173,7 +173,7 @@ public Object Invoke(Object proxy, MethodInfo method, object[] args) Connector connector = null; ObjectPool pool = _context.GetPool(); // get the connector class.. - Type connectorClazz = _context.GetConnectorClass(); + SafeType connectorClazz = _context.GetConnectorClass(); try { // pooling is implemented get one.. if (pool != null) { @@ -181,7 +181,7 @@ public Object Invoke(Object proxy, MethodInfo method, object[] args) } else { // get a new instance of the connector.. - connector = (Connector)Activator.CreateInstance(connectorClazz); + connector = connectorClazz.CreateInstance(); // initialize the connector.. connector.Init(_context.GetConfiguration()); } @@ -255,7 +255,7 @@ public ObjectPool GetPool() { } - public Type GetConnectorClass() { + public SafeType GetConnectorClass() { return GetConnectorInfo().ConnectorClass; } diff --git a/FrameworkInternal/ApiRemote.cs b/FrameworkInternal/ApiRemote.cs index a1121530..93b51b5f 100644 --- a/FrameworkInternal/ApiRemote.cs +++ b/FrameworkInternal/ApiRemote.cs @@ -249,7 +249,7 @@ public RemoteConnectorFacadeImpl(APIConfigurationImpl configuration) :base(configuration) { } - protected override APIOperation GetOperationImplementation(Type api) { + protected override APIOperation GetOperationImplementation(SafeType api) { InvocationHandler handler = new RemoteOperationInvocationHandler( GetAPIConfiguration(), api); @@ -266,10 +266,10 @@ protected override APIOperation GetOperationImplementation(Type api) { */ public class RemoteOperationInvocationHandler : InvocationHandler { private readonly APIConfigurationImpl _configuration; - private readonly Type _operation; + private readonly SafeType _operation; public RemoteOperationInvocationHandler(APIConfigurationImpl configuration, - Type operation) { + SafeType operation) { _configuration = configuration; _operation = operation; } diff --git a/FrameworkInternal/ApiRemoteMessages.cs b/FrameworkInternal/ApiRemoteMessages.cs index 425d632d..0ecef654 100644 --- a/FrameworkInternal/ApiRemoteMessages.cs +++ b/FrameworkInternal/ApiRemoteMessages.cs @@ -41,6 +41,7 @@ using System.Collections.Generic; using Org.IdentityConnectors.Common; using Org.IdentityConnectors.Framework.Api; +using Org.IdentityConnectors.Framework.Api.Operations; namespace Org.IdentityConnectors.Framework.Impl.Api.Remote.Messages { /// @@ -114,7 +115,7 @@ public class OperationRequest : Message { /** * The operation to perform. */ - private readonly Type _operation; + private readonly SafeType _operation; /** * The name of the method since operations can have more @@ -132,7 +133,7 @@ public class OperationRequest : Message { public OperationRequest(ConnectorKey key, APIConfigurationImpl apiConfiguration, - Type operation, + SafeType operation, string operationMethodName, IList arguments) { _connectorKey = key; @@ -154,7 +155,7 @@ public APIConfigurationImpl Configuration { } } - public Type Operation { + public SafeType Operation { get { return _operation; } diff --git a/FrameworkInternal/Serializer.cs b/FrameworkInternal/Serializer.cs index af5f7807..3b149758 100644 --- a/FrameworkInternal/Serializer.cs +++ b/FrameworkInternal/Serializer.cs @@ -1159,15 +1159,25 @@ public override Object Deserialize(ObjectDecoder decoder) { decoder.ReadObjectField("connectorPoolConfiguration",null,null)); rv.ConfigurationProperties=((ConfigurationPropertiesImpl) decoder.ReadObjectField("ConfigurationProperties",typeof(ConfigurationPropertiesImpl),null)); - IDictionary map = + IDictionary timeoutMapObj = (IDictionary)decoder.ReadObjectField("timeoutMap",null,null); - rv.TimeoutMap= - CollectionUtil.NewDictionary(map); - ICollection setObj = + IDictionary,int> timeoutMap = + new Dictionary,int>(); + foreach (KeyValuePair entry in timeoutMapObj) { + Type type = (Type)entry.Key; + int val = (int)entry.Value; + timeoutMap[SafeType.ForRawType(type)] = val; + } + rv.TimeoutMap=timeoutMap; + ICollection supportedOperationsObj = (ICollection)decoder.ReadObjectField("SupportedOperations",typeof(ICollection),null); - ICollection set = - CollectionUtil.NewSet(setObj); - rv.SupportedOperations=(set); + ICollection> supportedOperations = + new HashSet>(); + foreach (object obj in supportedOperationsObj) { + Type type = (Type)obj; + supportedOperations.Add(SafeType.ForRawType(type)); + } + rv.SupportedOperations=supportedOperations; rv.ProducerBufferSize=(decoder.ReadIntField("producerBufferSize",0)); return rv; } @@ -1175,6 +1185,22 @@ public override Object Deserialize(ObjectDecoder decoder) { public override void Serialize(Object obj, ObjectEncoder encoder) { APIConfigurationImpl val = (APIConfigurationImpl)obj; + + ICollection supportedOperations = + new HashSet(); + if ( val.SupportedOperations != null ) { + foreach (SafeType op in val.SupportedOperations ) { + supportedOperations.Add(op.RawType); + } + } + IDictionary timeoutMap = + new Dictionary(); + if ( val.TimeoutMap != null ) { + foreach (KeyValuePair, int> entry in val.TimeoutMap) { + timeoutMap[entry.Key.RawType] = entry.Value; + } + } + encoder.WriteIntField("producerBufferSize", val.ProducerBufferSize); encoder.WriteBooleanField("connectorPoolingSupported", @@ -1184,9 +1210,9 @@ public override void Serialize(Object obj, ObjectEncoder encoder) { encoder.WriteObjectField("ConfigurationProperties", val.ConfigurationProperties,true); encoder.WriteObjectField("timeoutMap", - val.TimeoutMap,false); + timeoutMap,false); encoder.WriteObjectField("SupportedOperations", - val.SupportedOperations,true); + supportedOperations,true); } } private class ConnectorMessagesHandler : AbstractObjectSerializationHandler { @@ -1699,11 +1725,11 @@ public override Object Deserialize(ObjectDecoder decoder) { } IDictionary objectClassNamesByOperationObj = (IDictionary)decoder.ReadObjectField("objectClassesByOperation",null,null); - IDictionary> + IDictionary,ICollection> objectClassesByOperation = - new Dictionary>(); + new Dictionary, ICollection>(); foreach (KeyValuePair entry in objectClassNamesByOperationObj) { - Type op = (Type)entry.Key; + SafeType op = SafeType.ForRawType((Type)entry.Key); ICollection namesObj = (ICollection)entry.Value; ICollection infos = @@ -1718,11 +1744,11 @@ public override Object Deserialize(ObjectDecoder decoder) { } IDictionary optionsByOperationObj = (IDictionary)decoder.ReadObjectField("optionsByOperation",null,null); - IDictionary> + IDictionary,ICollection> optionsByOperation = - new Dictionary>(); + new Dictionary, ICollection>(); foreach (KeyValuePair entry in optionsByOperationObj) { - Type op = (Type)entry.Key; + SafeType op = SafeType.ForRawType((Type)entry.Key); ICollection namesObj = (ICollection)entry.Value; ICollection infos = @@ -1751,23 +1777,23 @@ public override void Serialize(Object obj, ObjectEncoder encoder) { optionNamesByOperation = new Dictionary>(); - foreach (KeyValuePair> + foreach (KeyValuePair, ICollection> entry in val.SupportedObjectClassesByOperation) { ICollection value = entry.Value; ICollection names = new HashSet(); foreach (ObjectClassInfo info in value) { names.Add(info.ObjectType); } - objectClassNamesByOperation[entry.Key] = names; + objectClassNamesByOperation[entry.Key.RawType] = names; } - foreach (KeyValuePair> + foreach (KeyValuePair, ICollection> entry in val.SupportedOptionsByOperation) { ICollection value = entry.Value; ICollection names = new HashSet(); foreach (OperationOptionInfo info in value) { names.Add(info.Name); } - optionNamesByOperation[entry.Key] = names; + optionNamesByOperation[entry.Key.RawType] = names; } encoder.WriteObjectField("objectClassesByOperation",objectClassNamesByOperation,false); encoder.WriteObjectField("optionsByOperation",optionNamesByOperation,false); @@ -2156,7 +2182,7 @@ public override sealed Object Deserialize(ObjectDecoder decoder) { decoder.ReadObjectField("Arguments",typeof(IList),null); return new OperationRequest(connectorKey, configuration, - operation, + SafeType.ForRawType(operation), operationMethodName, arguments); } @@ -2166,7 +2192,7 @@ public override sealed void Serialize(Object obj, ObjectEncoder encoder) OperationRequest val = (OperationRequest)obj; encoder.WriteClassField("operation", - val.Operation); + val.Operation.RawType); encoder.WriteStringField("operationMethodName", val.OperationMethodName); encoder.WriteObjectField("ConnectorKey", diff --git a/FrameworkInternal/Server.cs b/FrameworkInternal/Server.cs index 70c68e64..15a0f38b 100644 --- a/FrameworkInternal/Server.cs +++ b/FrameworkInternal/Server.cs @@ -121,8 +121,9 @@ private const String IMPL_NAME * Get the singleton instance of the {@link ConnectorServer}. */ public static ConnectorServer NewInstance() { - Type type = Type.GetType(IMPL_NAME,true); - return (ConnectorServer)Activator.CreateInstance(type); + SafeType type = + SafeType.ForRawType(Type.GetType(IMPL_NAME,true)); + return type.CreateInstance(); } private void AssertNotStarted() { @@ -462,7 +463,7 @@ private HelloResponse ProcessHelloRequest(HelloRequest request) { private MethodInfo GetOperationMethod(OperationRequest request) { MethodInfo [] methods = - request.Operation.GetMethods(); + request.Operation.RawType.GetMethods(); MethodInfo found = null; foreach (MethodInfo m in methods) { if ( m.Name.ToUpper().Equals(request.OperationMethodName.ToUpper()) ) { diff --git a/FrameworkInternal/Test.cs b/FrameworkInternal/Test.cs index 1c1bb25a..c9824bf1 100644 --- a/FrameworkInternal/Test.cs +++ b/FrameworkInternal/Test.cs @@ -58,16 +58,16 @@ public class TestHelpersImpl : TestHelpers { /** * Method for convenient testing of local connectors. */ - protected override APIConfiguration CreateTestConfigurationImpl(Type clazz, + protected override APIConfiguration CreateTestConfigurationImpl(SafeType clazz, Configuration config) { LocalConnectorInfoImpl info = new LocalConnectorInfoImpl(); - info.ConnectorConfigurationClass=(config.GetType()); + info.ConnectorConfigurationClass=SafeType.Get(config); info.ConnectorClass=(clazz); info.ConnectorDisplayNameKey=("DUMMY_DISPLAY_NAME"); info.ConnectorKey=( - new ConnectorKey(clazz.Name+".bundle", + new ConnectorKey(clazz.RawType.Name+".bundle", "1.0", - clazz.Name)); + clazz.RawType.Name)); info.Messages=(new ConnectorMessagesImpl()); APIConfigurationImpl rv = new APIConfigurationImpl(); rv.IsConnectorPoolingSupported=( @@ -84,8 +84,8 @@ protected override APIConfiguration CreateTestConfigurationImpl(Type clazz, } - private static bool IsConnectorPoolingSupported(Type clazz) { - return ReflectionUtil.IsParentTypeOf(typeof(PoolableConnector),clazz); + private static bool IsConnectorPoolingSupported(SafeType clazz) { + return ReflectionUtil.IsParentTypeOf(typeof(PoolableConnector),clazz.RawType); } /** diff --git a/FrameworkTests/ObjectSerializationTests.cs b/FrameworkTests/ObjectSerializationTests.cs index 0e6f113c..f0c5e1d6 100644 --- a/FrameworkTests/ObjectSerializationTests.cs +++ b/FrameworkTests/ObjectSerializationTests.cs @@ -261,11 +261,16 @@ public void TestClasses() { CollectionUtil.SetsEqual( CollectionUtil.NewSet(FrameworkUtil.GetAllSupportedAttributeTypes()), (ICollection)CloneObject(FrameworkUtil.GetAllSupportedAttributeTypes()))); + ICollection apiOperations = + new HashSet(); + foreach (SafeType op in FrameworkUtil.AllAPIOperations()) { + apiOperations.Add(op.RawType); + } //if this fails, need to add to the OperationMappings class Assert.IsTrue( CollectionUtil.SetsEqual( - CollectionUtil.NewSet(FrameworkUtil.AllAPIOperations()), - (ICollection)CloneObject(FrameworkUtil.AllAPIOperations()))); + CollectionUtil.NewSet(apiOperations), + (ICollection)CloneObject(apiOperations))); } @@ -439,8 +444,8 @@ public void TestAPIConfiguration() { v1.IsConnectorPoolingSupported=(true); v1.ProducerBufferSize=(200); v1.SupportedOperations=(FrameworkUtil.AllAPIOperations()); - IDictionary map = - CollectionUtil.NewDictionary(typeof(CreateApiOp),6); + IDictionary,int> map = + CollectionUtil.NewDictionary,int>(SafeType.Get(),6); v1.TimeoutMap=(map); APIConfigurationImpl v2 = (APIConfigurationImpl) @@ -601,13 +606,13 @@ public void TestSchema() { bld.ObjectType = ObjectClass.ORGANIZATION_NAME; ObjectClassInfo info = bld.Build(); ICollection temp = CollectionUtil.NewSet(info); - IDictionary> map = - new Dictionary>(); - map[typeof(CreateApiOp)] = temp; + IDictionary,ICollection> map = + new Dictionary,ICollection>(); + map[SafeType.Get()] = temp; ICollection temp2 = CollectionUtil.NewSet(opInfo); - IDictionary> map2 = - new Dictionary>(); - map2[typeof(CreateApiOp)] = temp2; + IDictionary,ICollection> map2 = + new Dictionary,ICollection>(); + map2[SafeType.Get()] = temp2; Schema v1 = new Schema(CollectionUtil.NewSet(info), CollectionUtil.NewSet(opInfo), map, @@ -853,7 +858,7 @@ public void TestOperationRequest() { "my version", "my connector"), apiImpl, - typeof(CreateApiOp), + SafeType.Get(), "mymethodName", args); OperationRequest v2 = (OperationRequest)CloneObject(v1); @@ -861,7 +866,7 @@ public void TestOperationRequest() { Assert.AreEqual("my version", v2.ConnectorKey.BundleVersion); Assert.AreEqual("my connector", v2.ConnectorKey.ConnectorName); Assert.IsNotNull(v2.Configuration); - Assert.AreEqual(typeof(CreateApiOp), v2.Operation); + Assert.AreEqual(SafeType.Get(), v2.Operation); Assert.AreEqual("mymethodName",v2.OperationMethodName); Assert.IsTrue( CollectionUtil.Equals( diff --git a/TestBundleV1/TestConnector.cs b/TestBundleV1/TestConnector.cs index 92864cd8..6a3f8153 100644 --- a/TestBundleV1/TestConnector.cs +++ b/TestBundleV1/TestConnector.cs @@ -108,7 +108,7 @@ public int GetConnectionNumber() { } [ConnectorClass("TestConnector", - typeof(TstConnectorConfig), + typeof(TstConnectorConfig), MessageCatalogPath="TestBundleV1.Messages" )] public class TstConnector : CreateOp, PoolableConnector, SchemaOp, SearchOp, SyncOp @@ -216,7 +216,7 @@ public SyncToken GetLatestSyncToken() } public Schema Schema() { - SchemaBuilder builder = new SchemaBuilder(typeof(TstConnector)); + SchemaBuilder builder = new SchemaBuilder(SafeType.Get()); for ( int i = 0 ; i < 2; i++ ) { ObjectClassInfoBuilder classBuilder = new ObjectClassInfoBuilder(); classBuilder.ObjectType=("class"+i); From 46989547896127a03a2ada7a81ebf7830a4d3937 Mon Sep 17 00:00:00 2001 From: dvernon Date: Tue, 16 Sep 2008 20:05:43 +0000 Subject: [PATCH 014/342] Issue #64 - Incremental checkin. Add implementation and test of GetLatestSyncToken(). --- .../ActiveDirectorySyncToken.cs | 67 +++++++++++++++++++ 1 file changed, 67 insertions(+) create mode 100644 ActiveDirectoryConnector/ActiveDirectorySyncToken.cs diff --git a/ActiveDirectoryConnector/ActiveDirectorySyncToken.cs b/ActiveDirectoryConnector/ActiveDirectorySyncToken.cs new file mode 100644 index 00000000..60fbac7a --- /dev/null +++ b/ActiveDirectoryConnector/ActiveDirectorySyncToken.cs @@ -0,0 +1,67 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using Org.IdentityConnectors.Framework.Common.Objects; +using Org.IdentityConnectors.Framework.Common.Exceptions; + +namespace Org.IdentityConnectors.ActiveDirectory +{ + public class ActiveDirectorySyncToken + { + internal long LastModifiedUsn { get; set; } + internal long LastDeleteUsn { get; set; } + internal bool UseGlobalCatalog { get; set; } + internal string SyncServer { get; set; } + + public ActiveDirectorySyncToken(SyncToken token, string serverName, bool useGlobalCatalog) + : this(token == null ? null : (string)token.Value, serverName, useGlobalCatalog) + { + } + + public ActiveDirectorySyncToken(String tokenValue, string configServerName, bool configUseGlobalCatalog) + { + UseGlobalCatalog = configUseGlobalCatalog; + SyncServer = configServerName; + + if ((tokenValue == null) || (tokenValue.Length == 0)) + { + LastDeleteUsn = 0; + LastModifiedUsn = 0; + return; + } + + string[] tokenParts = (tokenValue).Split('|'); + if (tokenParts.Length != 4) + { + throw new ConnectorException("Unable to parse sync token"); + } + + string tokenSyncServer = tokenParts[3]; + bool tokenUseGlobalCatalog = bool.Parse(tokenParts[2]); + + // If the token server is the same as the configured server, + // use the token value (usn) to limit the query. The token is + // server specific though, so we cant use the usn if it didn't come + // from this server. + // If no server is configured, just try to use what we used last time. + if ((SyncServer != null) && (SyncServer.Equals(configServerName)) && + (UseGlobalCatalog.Equals(tokenUseGlobalCatalog))) + { + LastModifiedUsn = long.Parse(tokenParts[0]); + LastDeleteUsn = long.Parse(tokenParts[1]); + } + else + { + LastModifiedUsn = 0; + LastDeleteUsn = 0; + } + } + + public SyncToken GetSyncToken() + { + return new SyncToken(String.Format("{0}|{1}|{2}|{3}", + LastModifiedUsn, LastDeleteUsn, UseGlobalCatalog, SyncServer)); + } + } +} From 5c0c62674d62d202d50fba1b21db1ae8d7fe6efe Mon Sep 17 00:00:00 2001 From: dvernon Date: Tue, 16 Sep 2008 20:06:24 +0000 Subject: [PATCH 015/342] Issue #64 - Incremental checkin. Add implementation and test of GetLatestSyncToken(). --- .../ActiveDirectorySyncToken.cs | 42 ++++++++++++++++++- 1 file changed, 41 insertions(+), 1 deletion(-) diff --git a/ActiveDirectoryConnector/ActiveDirectorySyncToken.cs b/ActiveDirectoryConnector/ActiveDirectorySyncToken.cs index 60fbac7a..974806c9 100644 --- a/ActiveDirectoryConnector/ActiveDirectorySyncToken.cs +++ b/ActiveDirectoryConnector/ActiveDirectorySyncToken.cs @@ -1,4 +1,44 @@ -using System; +/* + * Copyright 2008 Sun Microsystems, Inc. All rights reserved. + * + * U.S. Government Rights - Commercial software. Government users + * are subject to the Sun Microsystems, Inc. standard license agreement + * and applicable provisions of the FAR and its supplements. + * + * Use is subject to license terms. + * + * This distribution may include materials developed by third parties. + * Sun, Sun Microsystems, the Sun logo, Java and Project Identity + * Connectors are trademarks or registered trademarks of Sun + * Microsystems, Inc. or its subsidiaries in the U.S. and other + * countries. + * + * UNIX is a registered trademark in the U.S. and other countries, + * exclusively licensed through X/Open Company, Ltd. + * + * ----------- + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER. + * + * Copyright 2008 Sun Microsystems, Inc. All rights reserved. + * + * The contents of this file are subject to the terms of the Common Development + * and Distribution License(CDDL) (the License). You may not use this file + * except in compliance with the License. + * + * You can obtain a copy of the License at + * http://identityconnectors.dev.java.net/CDDLv1.0.html + * See the License for the specific language governing permissions and + * limitations under the License. + * + * When distributing the Covered Code, include this CDDL Header Notice in each + * file and include the License file at identityconnectors/legal/license.txt. + * If applicable, add the following below this CDDL Header, with the fields + * enclosed by brackets [] replaced by your own identifying information: + * "Portions Copyrighted [year] [name of copyright owner]" + * ----------- + */ + +using System; using System.Collections.Generic; using System.Linq; using System.Text; From 794ade1fc28372073cc63acbf3b19e01f5a9441e Mon Sep 17 00:00:00 2001 From: rcauble Date: Tue, 16 Sep 2008 20:11:53 +0000 Subject: [PATCH 016/342] Issue#273: use SafeType --- .../ActiveDirectoryConnector.cs | 15 ++++++++------- 1 file changed, 8 insertions(+), 7 deletions(-) diff --git a/ActiveDirectoryConnector/ActiveDirectoryConnector.cs b/ActiveDirectoryConnector/ActiveDirectoryConnector.cs index 3dd9c4dd..2c54f331 100644 --- a/ActiveDirectoryConnector/ActiveDirectoryConnector.cs +++ b/ActiveDirectoryConnector/ActiveDirectoryConnector.cs @@ -42,7 +42,7 @@ using ActiveDs; using System.Collections.Generic; using System.Collections.ObjectModel; -using Org.IdentityConnectors.Common.Pooling; +using Org.IdentityConnectors.Common; using Org.IdentityConnectors.Framework.Api.Operations; using Org.IdentityConnectors.Framework.Spi; using Org.IdentityConnectors.Framework.Spi.Operations; @@ -326,16 +326,17 @@ public Schema Schema() ociBuilder.AddAllAttributeInfo(ouAttributeInfos); ObjectClassInfo ouInfo = ociBuilder.Build(); - SchemaBuilder schemaBuilder = new SchemaBuilder(this.GetType()); + SchemaBuilder schemaBuilder = + new SchemaBuilder(SafeType.Get(this)); schemaBuilder.DefineObjectClass(userInfo); schemaBuilder.DefineObjectClass(groupInfo); - schemaBuilder.RemoveSupportedObjectClass(typeof(AuthenticateOp), groupInfo); + schemaBuilder.RemoveSupportedObjectClass(SafeType.Get(), groupInfo); schemaBuilder.DefineObjectClass(ouInfo); - schemaBuilder.RemoveSupportedObjectClass(typeof(AuthenticateOp), ouInfo); - schemaBuilder.RemoveSupportedObjectClass(typeof(CreateOp), ouInfo); - schemaBuilder.RemoveSupportedObjectClass(typeof(DeleteOp), ouInfo); - schemaBuilder.RemoveSupportedObjectClass(typeof(SearchOp), ouInfo); + schemaBuilder.RemoveSupportedObjectClass(SafeType.Get(), ouInfo); + schemaBuilder.RemoveSupportedObjectClass(SafeType.Get(), ouInfo); + schemaBuilder.RemoveSupportedObjectClass(SafeType.Get(), ouInfo); + schemaBuilder.RemoveSupportedObjectClass(SafeType.Get>(), ouInfo); _schema = schemaBuilder.Build(); } From 73a75b3f0c4a8e4b419eda21d1a6a7ca620c9833 Mon Sep 17 00:00:00 2001 From: rcauble Date: Wed, 17 Sep 2008 18:45:10 +0000 Subject: [PATCH 017/342] Issue#198,251,252: ported to C# --- .../ActiveDirectoryConnector.cs | 5 +- Framework/CommonObjects.cs | 126 +++++++++++------- FrameworkInternal/Resources.resx | 3 +- FrameworkInternal/Serializer.cs | 9 +- FrameworkTests/ObjectSerializationTests.cs | 8 +- 5 files changed, 93 insertions(+), 58 deletions(-) diff --git a/ActiveDirectoryConnector/ActiveDirectoryConnector.cs b/ActiveDirectoryConnector/ActiveDirectoryConnector.cs index 2c54f331..f774c7bb 100644 --- a/ActiveDirectoryConnector/ActiveDirectoryConnector.cs +++ b/ActiveDirectoryConnector/ActiveDirectoryConnector.cs @@ -538,7 +538,8 @@ private ConnectorAttributeInfo GetConnectorAttributeInfo(string name, ConnectorAttributeInfoBuilder builder = new ConnectorAttributeInfoBuilder(); builder.Name = name; builder.ValueType = type; - builder.Writeable = writable; + builder.Creatable = writable; + builder.Updateable = writable; builder.Readable = readable; builder.Required = required; builder.MultiValue = multivalue; @@ -895,7 +896,7 @@ public object RunScriptOnResource(ScriptContext request, OperationOptions option { arguments.Add("USERNAME", options.RunAsUser); arguments.Add("PASSWORD", - ActiveDirectoryUtils.GetSecureString(options.RunWithPassword)); + options.RunWithPassword.ToSecureString()); } } diff --git a/Framework/CommonObjects.cs b/Framework/CommonObjects.cs index 059b475a..7469239f 100644 --- a/Framework/CommonObjects.cs +++ b/Framework/CommonObjects.cs @@ -617,9 +617,22 @@ public class ConnectorAttribute { private readonly IList _value; internal ConnectorAttribute(string name, IList val) { - if (name == null) { - throw new ArgumentException("Name may not be null."); - } + if (StringUtil.IsBlank(name)) { + throw new ArgumentException("Name must not be blank!"); + } + if (OperationalAttributes.PASSWORD_NAME.Equals(name) || + OperationalAttributes.CURRENT_PASSWORD_NAME.Equals(name) || + OperationalAttributes.RESET_PASSWORD_NAME.Equals(name)) { + // check the value.. + if (val == null || val.Count != 1) { + String MSG = "Must be a single value."; + throw new ArgumentException(MSG); + } + if (!(val[0] is GuardedString)) { + const string MSG = "Password value must be an instance of GuardedString."; + throw new ArgumentException(MSG); + } + } _name = name; // copy to prevent corruption preserve null _value = (val == null) ? null : CollectionUtil.NewReadOnlyList(val); @@ -744,15 +757,7 @@ public ConnectorAttribute Build() { return new Uid(GetSingleStringValue()); } else if (Org.IdentityConnectors.Framework.Common.Objects.Name.NAME.Equals(_name)) { return new Name(GetSingleStringValue()); - } else if (OperationalAttributes.PASSWORD_NAME.Equals(_name) || - OperationalAttributes.CURRENT_PASSWORD_NAME.Equals(_name) || - OperationalAttributes.RESET_PASSWORD_NAME.Equals(_name)) { - CheckSingleValue(); - if (!(_value[0] is GuardedString)) { - const string MSG = "Password value must be an instance of GuardedString."; - throw new ArgumentException(MSG); - } - } + } return new ConnectorAttribute(Name, _value); } private void CheckSingleValue() { @@ -1181,20 +1186,34 @@ public sealed class ConnectorAttributeInfo { private readonly Type _type; private readonly bool _required; private readonly bool _readable; - private readonly bool _writeable; + private readonly bool _creatable; + private readonly bool _updateable; private readonly bool _multivalue; private readonly bool _returnedByDefault; - public ConnectorAttributeInfo(string name, Type type, - bool readable, bool writeable, + internal ConnectorAttributeInfo(string name, Type type, + bool readable, bool creatable, bool required, bool multivalue, - bool returnedByDefault) { + bool updateable, bool returnedByDefault) { + if (StringUtil.IsBlank(name)) { + throw new ArgumentException("Name must not be blank!"); + } + if ((OperationalAttributes.PASSWORD_NAME.Equals(name) || + OperationalAttributes.RESET_PASSWORD_NAME.Equals(name) || + OperationalAttributes.CURRENT_PASSWORD_NAME.Equals(name)) && + !typeof(GuardedString).Equals(type)) { + String MSG = "Password based attributes must be of type GuardedString."; + throw new ArgumentException(MSG); + } + // check the type.. + FrameworkUtil.CheckAttributeType(type); _name = name; _type = type; _readable = readable; - _writeable = writeable; + _creatable = creatable; _required = required; _multivalue = multivalue; + _updateable = updateable; _returnedByDefault = returnedByDefault; } @@ -1238,15 +1257,26 @@ public bool IsReadable { } /** - * Determines if the attribute is writable. + * Determines if the attribute is writable on create. * - * @return true if the attribute is writable else false. + * @return true if the attribute is writable on create else false. */ - public bool IsWritable { + public bool IsCreatable { get { - return _writeable; + return _creatable; } } + + /** + * Determines if the attribute is writable on update. + * + * @return true if the attribute is writable on update else false. + */ + public bool IsUpdateable { + get { + return _updateable; + } + } /** * Determines whether this attribute is required for creates. @@ -1298,7 +1328,7 @@ public override bool Equals(Object o) { if (IsReadable != other.IsReadable) { return false; } - if (IsWritable != other.IsWritable) { + if (IsCreatable != other.IsCreatable) { return false; } if (IsRequired != other.IsRequired) { @@ -1310,6 +1340,9 @@ public override bool Equals(Object o) { if (IsReturnedByDefault != other.IsReturnedByDefault) { return false; } + if (IsUpdateable != other.IsUpdateable) { + return false; + } return true; } return false; @@ -1325,8 +1358,9 @@ public override string ToString() { map["Type"] = ValueType; map["Required"] = IsRequired; map["Readable"] = IsReadable; - map["Writeable"] = IsWritable; + map["Createable"] = IsCreatable; map["MultiValue"] = IsMultiValue; + map["Updateable"] = IsUpdateable; map["ReturnedByDefault"] = IsReturnedByDefault; return map.ToString(); } @@ -1343,39 +1377,31 @@ public sealed class ConnectorAttributeInfoBuilder { /// public Type ValueType { get; set; } public bool Readable { get; set; } - public bool Writeable { get; set; } + public bool Creatable { get; set; } public bool Required { get; set; } public bool MultiValue { get; set; } + public bool Updateable { get; set; } public bool ReturnedByDefault { get; set; } public ConnectorAttributeInfoBuilder() { Name = null; Readable = true; - Writeable = true; + Creatable = true; Required = false; + Updateable = true; MultiValue = false; ValueType = typeof(string); ReturnedByDefault = true; } public ConnectorAttributeInfo Build() { - if (StringUtil.IsBlank(Name)) { - throw new InvalidOperationException("Name must not be blank!"); - } - if ((OperationalAttributes.PASSWORD_NAME.Equals(Name) || - OperationalAttributes.RESET_PASSWORD_NAME.Equals(Name) || - OperationalAttributes.CURRENT_PASSWORD_NAME.Equals(Name)) && - !typeof(GuardedString).Equals(ValueType)) { - string MSG = "Password based attributes must be of type GuardedString."; - throw new ArgumentException(MSG); - } - FrameworkUtil.CheckAttributeType(ValueType); return new ConnectorAttributeInfo(Name, ValueType, Readable, - Writeable, + Creatable, Required, MultiValue, + Updateable, ReturnedByDefault); } public static ConnectorAttributeInfo Build(String name) { @@ -1397,23 +1423,25 @@ public static ConnectorAttributeInfo Build(String name, Type type, bool required }.Build(); } public static ConnectorAttributeInfo Build( String name, - bool required, bool readable, bool writeable) { + bool required, bool readable, bool creatable, bool updateable) { ConnectorAttributeInfoBuilder bld = new ConnectorAttributeInfoBuilder(); bld.Name = name; bld.Required = required; bld.Readable = readable; - bld.Writeable = writeable; + bld.Creatable = creatable; + bld.Updateable = updateable; return bld.Build(); } public static ConnectorAttributeInfo Build( String name, Type type, - bool required, bool readable, bool writeable) { + bool required, bool readable, bool creatable, bool updateable) { ConnectorAttributeInfoBuilder bld = new ConnectorAttributeInfoBuilder(); bld.Name = name; bld.ValueType = type; bld.Required = required; bld.Readable = readable; - bld.Writeable = writeable; + bld.Creatable = creatable; + bld.Updateable = updateable; return bld.Build(); } } @@ -1678,7 +1706,7 @@ public static class OperationalAttributeInfos { public static readonly ConnectorAttributeInfo PASSWORD = ConnectorAttributeInfoBuilder.Build( OperationalAttributes.PASSWORD_NAME, typeof(GuardedString), - true, false, true); + true, false, true, true); /** * Used in conjunction with password to do an account level password change. @@ -1688,7 +1716,7 @@ public static class OperationalAttributeInfos { public static readonly ConnectorAttributeInfo CURRENT_PASSWORD = ConnectorAttributeInfoBuilder.Build( OperationalAttributes.CURRENT_PASSWORD_NAME, typeof(GuardedString), - false, false, true); + false, false, true, true); /** * Used to do an administrator reset of the password. The value is the reset @@ -1697,7 +1725,7 @@ public static class OperationalAttributeInfos { public static readonly ConnectorAttributeInfo RESET_PASSWORD = ConnectorAttributeInfoBuilder.Build( OperationalAttributes.RESET_PASSWORD_NAME, typeof(GuardedString), - false, false, true); + false, false, true, true); /** * Used to determine if a password is expired or to expire a password. @@ -1838,7 +1866,7 @@ public static class PredefinedAttributeInfos { */ public static readonly ConnectorAttributeInfo LAST_PASSWORD_CHANGE_DATE = ConnectorAttributeInfoBuilder.Build( - PredefinedAttributes.LAST_PASSWORD_CHANGE_DATE_NAME, typeof(long), false, true, false); + PredefinedAttributes.LAST_PASSWORD_CHANGE_DATE_NAME, typeof(long), false, true, false, false); /** * Common password policy attribute where the password must be changed every @@ -1855,7 +1883,7 @@ public static class PredefinedAttributeInfos { */ public static readonly ConnectorAttributeInfo LAST_LOGIN_DATE = ConnectorAttributeInfoBuilder.Build( - PredefinedAttributes.LAST_LOGIN_DATE_NAME, typeof(long), false, true, false); + PredefinedAttributes.LAST_LOGIN_DATE_NAME, typeof(long), false, true, false, false); static PredefinedAttributeInfos() { // define GROUPS attribute info @@ -1980,9 +2008,9 @@ public string RunAsUser { /** * Get the password to run the operation as.. */ - public string RunWithPassword { + public GuardedString RunWithPassword { get { - return (string) CollectionUtil.GetValue( + return (GuardedString) CollectionUtil.GetValue( _operationOptions, OP_RUN_WITH_PASSWORD, null); } } @@ -2066,7 +2094,7 @@ public string[] AttributesToGet { /** * Set the run with password option. */ - public string RunWithPassword { + public GuardedString RunWithPassword { set { Assertions.NullCheck(value, "RunWithPassword"); _options[OperationOptions.OP_RUN_WITH_PASSWORD] = value; diff --git a/FrameworkInternal/Resources.resx b/FrameworkInternal/Resources.resx index 669f6121..e9dbd214 100644 --- a/FrameworkInternal/Resources.resx +++ b/FrameworkInternal/Resources.resx @@ -369,7 +369,8 @@ OperationOptionInfo | SyncDeltaType | SyncToken | SyncDelta type CDATA #REQUIRED required CDATA #IMPLIED readable CDATA #IMPLIED - writeable CDATA #IMPLIED + creatable CDATA #IMPLIED + updateable CDATA #IMPLIED multivalue CDATA #IMPLIED returnedbydefault CDATA #IMPLIED > diff --git a/FrameworkInternal/Serializer.cs b/FrameworkInternal/Serializer.cs index 3b149758..b784b53c 100644 --- a/FrameworkInternal/Serializer.cs +++ b/FrameworkInternal/Serializer.cs @@ -1602,8 +1602,10 @@ public override Object Deserialize(ObjectDecoder decoder) { decoder.ReadBooleanField("required",false)); builder.Readable=( decoder.ReadBooleanField("readable",false)); - builder.Writeable=( - decoder.ReadBooleanField("writeable",false)); + builder.Creatable=( + decoder.ReadBooleanField("creatable",false)); + builder.Updateable=( + decoder.ReadBooleanField("updateable",false)); builder.MultiValue=( decoder.ReadBooleanField("multivalue",false)); builder.ReturnedByDefault=( @@ -1617,7 +1619,8 @@ public override void Serialize(Object obj, ObjectEncoder encoder) { encoder.WriteClassField("type", val.ValueType); encoder.WriteBooleanField("required", val.IsRequired); encoder.WriteBooleanField("readable", val.IsReadable); - encoder.WriteBooleanField("writeable", val.IsWritable); + encoder.WriteBooleanField("creatable", val.IsCreatable); + encoder.WriteBooleanField("updateable", val.IsUpdateable); encoder.WriteBooleanField("multivalue", val.IsMultiValue); encoder.WriteBooleanField("returnedbydefault", val.IsReturnedByDefault); } diff --git a/FrameworkTests/ObjectSerializationTests.cs b/FrameworkTests/ObjectSerializationTests.cs index f0c5e1d6..dd7f8799 100644 --- a/FrameworkTests/ObjectSerializationTests.cs +++ b/FrameworkTests/ObjectSerializationTests.cs @@ -524,7 +524,8 @@ public void TestAttributeInfo() { builder.ValueType=(typeof(String)); builder.Required=(true); builder.Readable=(true); - builder.Writeable=(true); + builder.Creatable=(true); + builder.Updateable=(true); builder.MultiValue=(true); builder.ReturnedByDefault = false; ConnectorAttributeInfo v1 = builder.Build(); @@ -535,7 +536,8 @@ public void TestAttributeInfo() { Assert.IsTrue(v2.IsMultiValue); Assert.IsTrue(v2.IsReadable); Assert.IsTrue(v2.IsRequired); - Assert.IsTrue(v2.IsWritable); + Assert.IsTrue(v2.IsUpdateable); + Assert.IsTrue(v2.IsCreatable); Assert.IsFalse(v2.IsReturnedByDefault); } @@ -572,7 +574,7 @@ public void TestObjectClassInfo() { builder.ValueType=(typeof(String)); builder.Required=(true); builder.Readable=(true); - builder.Writeable=(true); + builder.Updateable=(true); builder.MultiValue=(true); ObjectClassInfoBuilder obld = new ObjectClassInfoBuilder(); obld.ObjectType = ObjectClass.ORGANIZATION_NAME; From 770b1f34126f78dd2c00cfeabf60a2c3bd304aa8 Mon Sep 17 00:00:00 2001 From: rcauble Date: Thu, 18 Sep 2008 16:28:49 +0000 Subject: [PATCH 018/342] Issue#254: fixed in C# --- Framework/Test.cs | 12 ++++++++++++ FrameworkInternal/Api.cs | 2 +- FrameworkInternal/Test.cs | 22 +++++++++++++++++++++- 3 files changed, 34 insertions(+), 2 deletions(-) diff --git a/Framework/Test.cs b/Framework/Test.cs index 1c996e77..51932ca7 100644 --- a/Framework/Test.cs +++ b/Framework/Test.cs @@ -78,6 +78,16 @@ public static APIConfiguration CreateTestConfiguration(SafeType clazz return GetInstance().CreateTestConfigurationImpl(clazz, config); } + /** + * Creates an dummy message catalog ideal for unit testing. + * All messages are formatted as follows: + *

+ * message-key: arg0.toString(), ..., argn.toString + * @return A dummy message catalog. + */ + public static ConnectorMessages CreateDummyMessages() { + return GetInstance().CreateDummyMessagesImpl(); + } public static IList SearchToList(SearchApiOp search, ObjectClass oclass, @@ -299,5 +309,7 @@ public static IDictionary LoadPropertiesFile(string filename) { } return ret; } + + abstract protected ConnectorMessages CreateDummyMessagesImpl(); } } diff --git a/FrameworkInternal/Api.cs b/FrameworkInternal/Api.cs index ec9a2658..dd7827d2 100644 --- a/FrameworkInternal/Api.cs +++ b/FrameworkInternal/Api.cs @@ -299,7 +299,7 @@ public string GetConnectorDisplayName() { public string ConnectorDisplayNameKey { get; set; } - public ConnectorMessagesImpl Messages { get; set; } + public ConnectorMessages Messages { get; set; } public APIConfigurationImpl DefaultAPIConfiguration { get { diff --git a/FrameworkInternal/Test.cs b/FrameworkInternal/Test.cs index c9824bf1..8ac3be7b 100644 --- a/FrameworkInternal/Test.cs +++ b/FrameworkInternal/Test.cs @@ -38,6 +38,7 @@ * ----------- */ using System; +using System.Text; using Org.IdentityConnectors.Common; using Org.IdentityConnectors.Framework.Api; @@ -68,7 +69,7 @@ protected override APIConfiguration CreateTestConfigurationImpl(SafeType(SearchOp search, RawSearcherImpl.RawSearch( search, oclass, filter, handler, options); } + + protected override ConnectorMessages CreateDummyMessagesImpl() { + return new DummyConnectorMessages(); + } + + private class DummyConnectorMessages : ConnectorMessages { + public String Format(String key, String dflt, params Object [] args) { + StringBuilder builder = new StringBuilder(); + builder.Append(key); + builder.Append(": "); + String sep = ""; + foreach (Object arg in args ) { + builder.Append(sep); + builder.Append(arg); + sep=", "; + } + return builder.ToString(); + } + } } From a3fee0eefef29dbf2ee0bbf7d57d1589731e8b42 Mon Sep 17 00:00:00 2001 From: dvernon Date: Thu, 18 Sep 2008 20:32:11 +0000 Subject: [PATCH 019/342] Issue #64 - Incremental checkin. Added default attributes to return for Groups and Ou's, fixed bug in @@groups@@ handling, and changed message catalog. --- .../ActiveDirectoryConnector.cs | 26 ++++++++++++++++++- .../CustomAttributeHandlers.cs | 2 +- ActiveDirectoryConnector/Messages.resx | 2 +- 3 files changed, 27 insertions(+), 3 deletions(-) diff --git a/ActiveDirectoryConnector/ActiveDirectoryConnector.cs b/ActiveDirectoryConnector/ActiveDirectoryConnector.cs index f774c7bb..d0a9aac8 100644 --- a/ActiveDirectoryConnector/ActiveDirectoryConnector.cs +++ b/ActiveDirectoryConnector/ActiveDirectoryConnector.cs @@ -69,7 +69,7 @@ public class ActiveDirectoryConnector : CreateOp, Connector, SchemaOp, DeleteOp, AuthenticateOp, AttributeNormalizer, PoolableConnector { // This is the list of attributes returned by default if no attributes are - // requested in the options field of ExecuteQuery + // requested in the options field of ExecuteQuery for Account public readonly static ICollection AccountAttributesReturnedByDefault = new HashSet(StringComparer.CurrentCultureIgnoreCase) { // "userPassword", @@ -119,6 +119,28 @@ public class ActiveDirectoryConnector : CreateOp, Connector, SchemaOp, DeleteOp, "homeDirectory", }; + // This is the list of attributes returned by default if no attributes are + // requested in the options field of ExecuteQuery for groups + public readonly static ICollection GroupAttributesReturnedByDefault = + new HashSet(StringComparer.CurrentCultureIgnoreCase) + { + "cn", + "samAccountName", + "description", + "managedby", + "mail", + "groupType", + "authOrig", + "unauthOrig", + }; + + // This is the list of attributes returned by default if no attributes are + // requested in the options field of ExecuteQuery for groups + public readonly static ICollection OuAttributesReturnedByDefault = + new HashSet(StringComparer.CurrentCultureIgnoreCase) + { + }; + public static IDictionary> AttributesReturnedByDefault = null; @@ -149,6 +171,8 @@ static ActiveDirectoryConnector() // populate default attributes AttributesReturnedByDefault = new Dictionary>(); AttributesReturnedByDefault.Add(ObjectClass.ACCOUNT, AccountAttributesReturnedByDefault); + AttributesReturnedByDefault.Add(ObjectClass.GROUP, GroupAttributesReturnedByDefault); + AttributesReturnedByDefault.Add(ouObjectClass, OuAttributesReturnedByDefault); } #region CreateOp Members diff --git a/ActiveDirectoryConnector/CustomAttributeHandlers.cs b/ActiveDirectoryConnector/CustomAttributeHandlers.cs index 75b30270..00e8b26b 100644 --- a/ActiveDirectoryConnector/CustomAttributeHandlers.cs +++ b/ActiveDirectoryConnector/CustomAttributeHandlers.cs @@ -330,7 +330,7 @@ internal void UpdateDeFromCa_OpAtt_Groups(ObjectClass oclass, ICollection newValues = attribute.Value; PropertyValueCollection oldValues = null; if(directoryEntry.Properties.Contains(ActiveDirectoryConnector.ATT_MEMBEROF)) { - PropertyValueCollection pvc = directoryEntry.Properties[ActiveDirectoryConnector.ATT_MEMBEROF]; + oldValues = directoryEntry.Properties[ActiveDirectoryConnector.ATT_MEMBEROF]; } ICollection groupsToAdd = new HashSet(); diff --git a/ActiveDirectoryConnector/Messages.resx b/ActiveDirectoryConnector/Messages.resx index 975d8b60..1d02e8be 100644 --- a/ActiveDirectoryConnector/Messages.resx +++ b/ActiveDirectoryConnector/Messages.resx @@ -118,7 +118,7 @@ System.Resources.ResXResourceWriter, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 - Active Directory Connector + Windows Active Directory Active Directory Hostname From c5396e5d03ed59adf672fd2f23a567441542eb8c Mon Sep 17 00:00:00 2001 From: dvernon Date: Thu, 18 Sep 2008 21:07:57 +0000 Subject: [PATCH 020/342] accoIssue #64 - Incremental checkin. Fixed a couple of errors due to update. --- .../ActiveDirectoryConnector.cs | 1 + .../ActiveDirectoryConnectorTest.cs | 40 +++++++++---------- 2 files changed, 19 insertions(+), 22 deletions(-) diff --git a/ActiveDirectoryConnector/ActiveDirectoryConnector.cs b/ActiveDirectoryConnector/ActiveDirectoryConnector.cs index d0a9aac8..706e3c25 100644 --- a/ActiveDirectoryConnector/ActiveDirectoryConnector.cs +++ b/ActiveDirectoryConnector/ActiveDirectoryConnector.cs @@ -139,6 +139,7 @@ public class ActiveDirectoryConnector : CreateOp, Connector, SchemaOp, DeleteOp, public readonly static ICollection OuAttributesReturnedByDefault = new HashSet(StringComparer.CurrentCultureIgnoreCase) { + Name.NAME, }; public static IDictionary> AttributesReturnedByDefault = null; diff --git a/ActiveDirectoryConnectorTests/ActiveDirectoryConnectorTest.cs b/ActiveDirectoryConnectorTests/ActiveDirectoryConnectorTest.cs index 2d5c3796..8bc9ff58 100644 --- a/ActiveDirectoryConnectorTests/ActiveDirectoryConnectorTest.cs +++ b/ActiveDirectoryConnectorTests/ActiveDirectoryConnectorTest.cs @@ -117,7 +117,8 @@ public void TestSchema() { Assert.IsNotNull(caInfo); Console.WriteLine("{0} {1} {2} {3}", caInfo.Name, - caInfo.IsWritable ? "writable" : "", + caInfo.IsCreatable ? "createable" : "", + caInfo.IsUpdateable ? "updateable" : "", caInfo.IsRequired ? "required" : "", caInfo.IsMultiValue ? "multivalue" : ""); if(ConnectorAttributeUtil.IsSpecial(caInfo)) { @@ -1224,11 +1225,7 @@ public void TestUserPasswordChange() ICollection createAttributes = GetNormalAttributes_Account(); // remove password, and set to something memorable createAttributes.Remove(ConnectorAttributeUtil.Find(OperationalAttributes.PASSWORD_NAME, createAttributes)); - GuardedString gsCurrentPassword = new GuardedString(); - foreach (char c in "1Password") - { - gsCurrentPassword.AppendChar(c); - } + GuardedString gsCurrentPassword = GetGuardedString("1Password"); createAttributes.Add(ConnectorAttributeBuilder.BuildPassword(gsCurrentPassword)); createAttributes.Add(ConnectorAttributeBuilder.BuildEnabled(true)); createUid = CreateAndVerifyObject(connector, @@ -1240,11 +1237,7 @@ public void TestUserPasswordChange() ICollection updateReplaceAttributes = new HashSet(); - GuardedString gsNewPassword = new GuardedString(); - foreach (char c in "LongPassword2MeetTheRequirements!") - { - gsNewPassword.AppendChar(c); - } + GuardedString gsNewPassword = GetGuardedString("LongPassword2MeetTheRequirements!"); updateReplaceAttributes.Add(ConnectorAttributeBuilder.BuildCurrentPassword(gsCurrentPassword)); updateReplaceAttributes.Add(ConnectorAttributeBuilder.BuildPassword(gsNewPassword)); UpdateReplaceAndVerifyObject(connector, ObjectClass.ACCOUNT, @@ -1270,11 +1263,7 @@ public void TestUserPasswordChange() // now a negative test case - GuardedString gsBogusPassword = new GuardedString(); - foreach (char c in "BogusPassword") - { - gsBogusPassword.AppendChar(c); - } + GuardedString gsBogusPassword = GetGuardedString("BogusPassword"); ICollection updateErrorReplaceAttributes = new HashSet(); updateErrorReplaceAttributes.Add(ConnectorAttributeBuilder.BuildCurrentPassword(gsBogusPassword)); @@ -1510,7 +1499,7 @@ public void RunScript(ActiveDirectoryConnector connector, String user, } if (password.Length > 0) { - builder.RunWithPassword = password; + builder.RunWithPassword = GetGuardedString(password); } ScriptContext context = new ScriptContext("Shell", scriptText, arguments); @@ -2005,12 +1994,8 @@ public ICollection GetNormalAttributes_Group() Random random = new Random(); Int32 randomNumber = random.Next(10000); - // the container ... is a fabricated attribute - attributes.Add(ConnectorAttributeBuilder.Build( - "ad_container", GetProperty(CONFIG_PROPERTY_CONTAINER))); attributes.Add(ConnectorAttributeBuilder.Build( - "displayName", "nunit test group" + randomNumber + "," + - GetProperty(CONFIG_PROPERTY_CONTAINER))); + "mail", "groupmail@example.com")); attributes.Add(ConnectorAttributeBuilder.Build( "description", "Original Description" + randomNumber)); attributes.Add(ConnectorAttributeBuilder.Build( @@ -2160,5 +2145,16 @@ public ICollection GetDefaultAttributesToGet(ObjectClass oclass) return attributesToGet; } + + public GuardedString GetGuardedString(string regularString) + { + GuardedString guardedString = new GuardedString(); + foreach (char c in regularString) + { + guardedString.AppendChar(c); + } + return guardedString; + } } + } From 47a74032bf1711a81711cbbe91a6e83b74fc698d Mon Sep 17 00:00:00 2001 From: rcauble Date: Wed, 24 Sep 2008 16:15:15 +0000 Subject: [PATCH 021/342] make ScriptExecutor implement IDisposable --- BooScriptExecutorFactory/BooScriptExecutorFactory.cs | 2 ++ Common/Script.cs | 2 +- ShellScriptExecutorFactory/ShellScriptExecutorFactory.cs | 3 +++ 3 files changed, 6 insertions(+), 1 deletion(-) diff --git a/BooScriptExecutorFactory/BooScriptExecutorFactory.cs b/BooScriptExecutorFactory/BooScriptExecutorFactory.cs index ff6a3983..d161499f 100644 --- a/BooScriptExecutorFactory/BooScriptExecutorFactory.cs +++ b/BooScriptExecutorFactory/BooScriptExecutorFactory.cs @@ -81,6 +81,8 @@ public BooScriptExecutor(Assembly [] referencedAssemblies, string script) { _engine.References.Add(assembly); } } + public void Dispose() { + } public object Execute(IDictionary arguments) { // add all the globals IDictionary args = CollectionUtil.NullAsEmpty(arguments); diff --git a/Common/Script.cs b/Common/Script.cs index cfe7ea8a..b4002b5d 100644 --- a/Common/Script.cs +++ b/Common/Script.cs @@ -45,7 +45,7 @@ namespace Org.IdentityConnectors.Common.Script { - public interface ScriptExecutor { + public interface ScriptExecutor : IDisposable { /// /// Executes the script with the given arguments. /// diff --git a/ShellScriptExecutorFactory/ShellScriptExecutorFactory.cs b/ShellScriptExecutorFactory/ShellScriptExecutorFactory.cs index 3c2ea830..7308d6e2 100644 --- a/ShellScriptExecutorFactory/ShellScriptExecutorFactory.cs +++ b/ShellScriptExecutorFactory/ShellScriptExecutorFactory.cs @@ -80,6 +80,9 @@ class ShellScriptExecutor : ScriptExecutor { public ShellScriptExecutor(string script) { _script = script; + } + public void Dispose() { + } public object Execute(IDictionary arguments) { // create the process info.. From 7290918d2b41612aa7796797ac50b4edd6c93a32 Mon Sep 17 00:00:00 2001 From: rcauble Date: Wed, 24 Sep 2008 16:25:02 +0000 Subject: [PATCH 022/342] backout previous changes --- BooScriptExecutorFactory/BooScriptExecutorFactory.cs | 2 -- Common/Script.cs | 2 +- ShellScriptExecutorFactory/ShellScriptExecutorFactory.cs | 3 --- 3 files changed, 1 insertion(+), 6 deletions(-) diff --git a/BooScriptExecutorFactory/BooScriptExecutorFactory.cs b/BooScriptExecutorFactory/BooScriptExecutorFactory.cs index d161499f..ff6a3983 100644 --- a/BooScriptExecutorFactory/BooScriptExecutorFactory.cs +++ b/BooScriptExecutorFactory/BooScriptExecutorFactory.cs @@ -81,8 +81,6 @@ public BooScriptExecutor(Assembly [] referencedAssemblies, string script) { _engine.References.Add(assembly); } } - public void Dispose() { - } public object Execute(IDictionary arguments) { // add all the globals IDictionary args = CollectionUtil.NullAsEmpty(arguments); diff --git a/Common/Script.cs b/Common/Script.cs index b4002b5d..cfe7ea8a 100644 --- a/Common/Script.cs +++ b/Common/Script.cs @@ -45,7 +45,7 @@ namespace Org.IdentityConnectors.Common.Script { - public interface ScriptExecutor : IDisposable { + public interface ScriptExecutor { /// /// Executes the script with the given arguments. /// diff --git a/ShellScriptExecutorFactory/ShellScriptExecutorFactory.cs b/ShellScriptExecutorFactory/ShellScriptExecutorFactory.cs index 7308d6e2..3c2ea830 100644 --- a/ShellScriptExecutorFactory/ShellScriptExecutorFactory.cs +++ b/ShellScriptExecutorFactory/ShellScriptExecutorFactory.cs @@ -80,9 +80,6 @@ class ShellScriptExecutor : ScriptExecutor { public ShellScriptExecutor(string script) { _script = script; - } - public void Dispose() { - } public object Execute(IDictionary arguments) { // create the process info.. From ee929aacafebd96fede5c998e07c52140e23dfcf Mon Sep 17 00:00:00 2001 From: wdroste Date: Thu, 2 Oct 2008 16:06:47 +0000 Subject: [PATCH 023/342] Issue #288 -Fixed update issue. --- Framework/CommonExceptions.cs | 6 +++ FrameworkInternal/ApiLocalOperations.cs | 59 ++++++++++++------------- FrameworkTests/UpdateImplTests.cs | 1 + 3 files changed, 36 insertions(+), 30 deletions(-) diff --git a/Framework/CommonExceptions.cs b/Framework/CommonExceptions.cs index e9c84f61..93feb921 100644 --- a/Framework/CommonExceptions.cs +++ b/Framework/CommonExceptions.cs @@ -38,6 +38,7 @@ * ----------- */ using System; +using Org.IdentityConnectors.Framework.Common.Objects; namespace Org.IdentityConnectors.Framework.Common.Exceptions { @@ -233,10 +234,15 @@ public PermissionDeniedException(String message, Exception ex) : base(message,ex } public class UnknownUidException : InvalidCredentialException { + const string MSG = "Object with Uid '{0}' and ObjectClass '{1}' does not exist!"; public UnknownUidException() : base() { } + public UnknownUidException(Uid uid, ObjectClass objclass) : + base(String.Format(MSG, uid, objclass)){ + } + public UnknownUidException(String message) : base(message) { } diff --git a/FrameworkInternal/ApiLocalOperations.cs b/FrameworkInternal/ApiLocalOperations.cs index ee6dbfd6..a982813c 100644 --- a/FrameworkInternal/ApiLocalOperations.cs +++ b/FrameworkInternal/ApiLocalOperations.cs @@ -1243,9 +1243,7 @@ public Uid Update(UpdateApiType type, ObjectClass objclass, Uid uid = ConnectorAttributeUtil.GetUidAttribute(normalizedAttributes); ConnectorObject o = GetConnectorObject(objclass, uid, options); if (o == null) { - string MSG = "Object with Uid '{0}' and ObjectClass '{1}' does not exist!"; - string ERR = String.Format(MSG, uid, objclass); - throw new UnknownUidException(ERR); + throw new UnknownUidException(uid, objclass); } // merge the update data.. ICollection mergeAttrs = Merge(type, normalizedAttributes, o.GetAttributes()); @@ -1274,37 +1272,38 @@ public ICollection Merge(UpdateApiType type, // get the name of the update attributes string name = updateAttr.Name; ConnectorAttribute baseAttr = CollectionUtil.GetValue(baseAttrMap, name, null); - // if this is a delete and the base attribute doesn't exist - if (UpdateApiType.DELETE.Equals(type) && baseAttr == null) { - continue; - } - // exclude attributes that are the same on replace - if (UpdateApiType.REPLACE.Equals(type) && updateAttr.Equals(baseAttr)) { - continue; - } ICollection values; ConnectorAttribute modifiedAttr; - if (UpdateApiType.ADD.Equals(type) && baseAttr != null) { - // create a new list with the base attribute to add to.. - values = CollectionUtil.NewList(baseAttr.Value); - CollectionUtil.AddAll(values,updateAttr.Value); - modifiedAttr = ConnectorAttributeBuilder.Build(name, values); + if (UpdateApiType.ADD.Equals(type)) { + if (baseAttr == null) { + modifiedAttr = updateAttr; + } else { + // create a new list with the base attribute to add to.. + values = CollectionUtil.NewList(baseAttr.Value); + CollectionUtil.AddAll(values,updateAttr.Value); + modifiedAttr = ConnectorAttributeBuilder.Build(name, values); + } } else if (UpdateApiType.DELETE.Equals(type)) { - // create a list with the base attribute to remove from.. - values = CollectionUtil.NewList(baseAttr.Value); - foreach (Object val in updateAttr.Value) { - values.Remove(val); - } - // if the values are empty send a null to the connector.. - if (values.Count == 0) { - modifiedAttr = ConnectorAttributeBuilder.Build(name); - } else { - modifiedAttr = ConnectorAttributeBuilder.Build(name, values); - } + if (baseAttr == null) { + // nothing to actually do the attribute does not exist + continue; + } else { + // create a list with the base attribute to remove from.. + values = CollectionUtil.NewList(baseAttr.Value); + foreach (Object val in updateAttr.Value) { + values.Remove(val); + } + // if the values are empty send a null to the connector.. + if (values.Count == 0) { + modifiedAttr = ConnectorAttributeBuilder.Build(name); + } else { + modifiedAttr = ConnectorAttributeBuilder.Build(name, values); + } + } + } else if (UpdateApiType.REPLACE.Equals(type)) { + modifiedAttr = updateAttr; } else { - // replace the attribute w/ the change set value. - values = updateAttr.Value; - modifiedAttr = ConnectorAttributeBuilder.Build(name, values); + throw new ArgumentException("Unknown Type: " + type); } ret.Add(modifiedAttr); } diff --git a/FrameworkTests/UpdateImplTests.cs b/FrameworkTests/UpdateImplTests.cs index 1cdcf8ed..7e97d0af 100644 --- a/FrameworkTests/UpdateImplTests.cs +++ b/FrameworkTests/UpdateImplTests.cs @@ -320,6 +320,7 @@ public void MergeReplaceSameAttribute() { ConnectorAttribute cattr = ConnectorAttributeBuilder.Build("abc", 1); baseAttrs.Add(battr); changeset.Add(cattr); + expected.Add(cattr); actual = up.Merge(UpdateApiType.REPLACE, changeset, baseAttrs); Assert.IsTrue(AreEqual(expected, actual)); } From 1953a149fba8b1d3833ab1d03fd5e28e90c7e6ed Mon Sep 17 00:00:00 2001 From: tthompson29 Date: Thu, 2 Oct 2008 21:34:56 +0000 Subject: [PATCH 024/342] adding ADConnector to dotnet project --- Connector Gateway.sln | 2 ++ 1 file changed, 2 insertions(+) diff --git a/Connector Gateway.sln b/Connector Gateway.sln index 4dca1949..96414aaf 100644 --- a/Connector Gateway.sln +++ b/Connector Gateway.sln @@ -24,6 +24,8 @@ Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "BooScriptExecutorFactory", EndProject Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "ShellScriptExecutorFactory", "ShellScriptExecutorFactory\ShellScriptExecutorFactory.csproj", "{4700690A-2D29-40A0-86AC-E5A9F71A479A}" EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "ActiveDirectoryConnector", "ActiveDirectoryConnector\ActiveDirectoryConnector.csproj", "{BDF495CA-0FCD-4E51-A871-D467CDE3B43E}" +EndProject Global GlobalSection(SolutionConfigurationPlatforms) = preSolution Debug|Any CPU = Debug|Any CPU From 4990080a6244271830ec7137e377aac6b65fff65 Mon Sep 17 00:00:00 2001 From: tthompson29 Date: Fri, 3 Oct 2008 04:33:10 +0000 Subject: [PATCH 025/342] removed specific-version assembly references --- .../ActiveDirectoryConnectorTests.csproj | 200 +++++++++--------- 1 file changed, 100 insertions(+), 100 deletions(-) diff --git a/ActiveDirectoryConnectorTests/ActiveDirectoryConnectorTests.csproj b/ActiveDirectoryConnectorTests/ActiveDirectoryConnectorTests.csproj index dddbcf85..910a78f1 100644 --- a/ActiveDirectoryConnectorTests/ActiveDirectoryConnectorTests.csproj +++ b/ActiveDirectoryConnectorTests/ActiveDirectoryConnectorTests.csproj @@ -36,112 +36,112 @@ "Portions Copyrighted [year] [name of copyright owner]" --> - - Debug - AnyCPU - 9.0.21022 - 2.0 - {DB8F7DA9-11A2-4BEB-8C14-25D8229EBE7E} - Library - Properties - Sun.OpenConnectors.ActiveDirectory - ActiveDirectoryConnectorTests - v3.5 - 512 - - - - - true - full - false - bin\Debug\ - DEBUG;TRACE - prompt - 4 - - - pdbonly - true - bin\Release\ - TRACE - prompt - 4 - - - Project - - - - - - - - - - - - - - - - - - - - - - - - - 3.5 - - - - 3.5 - - - 3.5 - - - - - - - - - - - {BDF495CA-0FCD-4E51-A871-D467CDE3B43E} - ActiveDirectoryConnector - - - {F140E8DA-52B4-4159-992A-9DA10EA8EEFB} - Common - - - {8B24461B-456A-4032-89A1-CD418F7B5B62} - Framework - - - - - Always - - - - - - - - copy "$(ProjectDir)\..\FrameworkInternal\bin\Debug\FrameworkInternal.dll" "$(ProjectDir)\$(OutDir)"; + + + + copy "$(ProjectDir)\..\FrameworkInternal\bin\Debug\FrameworkInternal.dll" "$(ProjectDir)\$(OutDir)"; copy "$(ProjectDir)\..\ShellScriptExecutorFactory\bin\Debug\Shell.ScriptExecutorFactory.dll" "$(ProjectDir)\$(OutDir)"; - + \ No newline at end of file From 3af20391e4d83f1449ba3ce4a57a2d196a12aa31 Mon Sep 17 00:00:00 2001 From: tthompson29 Date: Fri, 3 Oct 2008 05:03:14 +0000 Subject: [PATCH 026/342] modified ADConnectorTests to run automatically --- .../ActiveDirectoryConnectorTests.csproj | 22 +++++++++++-------- 1 file changed, 13 insertions(+), 9 deletions(-) diff --git a/ActiveDirectoryConnectorTests/ActiveDirectoryConnectorTests.csproj b/ActiveDirectoryConnectorTests/ActiveDirectoryConnectorTests.csproj index 910a78f1..420844cc 100644 --- a/ActiveDirectoryConnectorTests/ActiveDirectoryConnectorTests.csproj +++ b/ActiveDirectoryConnectorTests/ActiveDirectoryConnectorTests.csproj @@ -130,18 +130,22 @@ - + + + + + + + + + + + copy "$(ProjectDir)\..\FrameworkInternal\bin\Debug\FrameworkInternal.dll" "$(ProjectDir)\$(OutDir)"; -copy "$(ProjectDir)\..\ShellScriptExecutorFactory\bin\Debug\Shell.ScriptExecutorFactory.dll" "$(ProjectDir)\$(OutDir)"; - + copy "$(ProjectDir)\..\ShellScriptExecutorFactory\bin\Debug\Shell.ScriptExecutorFactory.dll" "$(ProjectDir)\$(OutDir)"; + \ No newline at end of file From b78bb69f6671af31cbbdf77c72635f5f4f4d7c92 Mon Sep 17 00:00:00 2001 From: dvernon Date: Fri, 3 Oct 2008 13:27:13 +0000 Subject: [PATCH 027/342] Issue #64 - Incremental checkin. Cleanup and changing GetLatestSyncToken to use highestcommittedusn. --- .../ActiveDirectoryConfiguration.cs | 22 +- .../ActiveDirectoryConnector.cs | 228 ++++++++---------- .../ActiveDirectoryConnectorTest.cs | 43 +++- 3 files changed, 160 insertions(+), 133 deletions(-) diff --git a/ActiveDirectoryConnector/ActiveDirectoryConfiguration.cs b/ActiveDirectoryConnector/ActiveDirectoryConfiguration.cs index fe4b079e..ec9ff2f9 100644 --- a/ActiveDirectoryConnector/ActiveDirectoryConfiguration.cs +++ b/ActiveDirectoryConnector/ActiveDirectoryConfiguration.cs @@ -64,43 +64,43 @@ public String SyncSearchContext public String DomainName { get; set; } - [ConfigurationProperty(Confidential = false, DisplayMessageKey = "display.DirectoryAdminName", HelpMessageKey = "help.DirectoryAdminName")] + [ConfigurationProperty(Confidential = false, DisplayMessageKey = "display_DirectoryAdminName", HelpMessageKey = "help_DirectoryAdminName")] public String DirectoryAdminName { get; set; } - [ConfigurationProperty(Confidential = true, DisplayMessageKey = "display.DirectoryAdminPassword", HelpMessageKey = "help.DirectoryAdminPassword")] + [ConfigurationProperty(Confidential = true, DisplayMessageKey = "display_DirectoryAdminPassword", HelpMessageKey = "help_DirectoryAdminPassword")] public String DirectoryAdminPassword { get; set; } - [ConfigurationProperty(Confidential = false, DisplayMessageKey = "display.ObjectClass", HelpMessageKey = "help.ObjectClass")] + [ConfigurationProperty(Confidential = false, DisplayMessageKey = "display_ObjectClass", HelpMessageKey = "help_ObjectClass")] public String ObjectClass { get; set; } - [ConfigurationProperty(Confidential = false, DisplayMessageKey = "display.UserProvidesPasswordOnChange", HelpMessageKey = "help.UserProvidesPasswordOnChange")] + [ConfigurationProperty(Confidential = false, DisplayMessageKey = "display_UserProvidesPasswordOnChange", HelpMessageKey = "help_UserProvidesPasswordOnChange")] public bool UserProvidesPasswordOnChange{get;set;} - [ConfigurationProperty(Confidential = false, DisplayMessageKey = "display.CreateHomeDirectory", HelpMessageKey = "help.CreateHomeDirectory")] + [ConfigurationProperty(Confidential = false, DisplayMessageKey = "display_CreateHomeDirectory", HelpMessageKey = "help_CreateHomeDirectory")] public bool CreateHomeDirectory { get; set; } - [ConfigurationProperty(Confidential = false, DisplayMessageKey = "display.SearchContext", HelpMessageKey = "help.SearchContext")] - public String SearchContext + [ConfigurationProperty(Confidential = false, DisplayMessageKey = "display_SearchContainer", HelpMessageKey = "help_SearchContainer")] + public String SearchContainer { get; set; } - [ConfigurationProperty(Confidential = false, DisplayMessageKey = "display.SearchChildDomains", HelpMessageKey = "help.SearchChildDomains")] + [ConfigurationProperty(Confidential = false, DisplayMessageKey = "display_SearchChildDomains", HelpMessageKey = "help_SearchChildDomains")] public bool SearchChildDomains {get;set;} - [ConfigurationProperty(Confidential = false, DisplayMessageKey = "display.EncryptionType", HelpMessageKey = "help.EncryptionType")] + [ConfigurationProperty(Confidential = false, DisplayMessageKey = "display_EncryptionType", HelpMessageKey = "help_EncryptionType")] public String EncryptionType { get; set; } - [ConfigurationProperty(Confidential = false, DisplayMessageKey = "display.LDAPHostName", HelpMessageKey = "help.LDAPHostName")] + [ConfigurationProperty(Confidential = false, DisplayMessageKey = "display_LDAPHostName", HelpMessageKey = "help_LDAPHostName")] public String LDAPHostName { get; set; } public ActiveDirectoryConfiguration() { DomainName = ""; - SearchContext = ""; + SearchContainer = ""; DirectoryAdminName = "cn=DirectoryAdmin"; ObjectClass = "User"; UserProvidesPasswordOnChange = false; diff --git a/ActiveDirectoryConnector/ActiveDirectoryConnector.cs b/ActiveDirectoryConnector/ActiveDirectoryConnector.cs index 706e3c25..3a6aec20 100644 --- a/ActiveDirectoryConnector/ActiveDirectoryConnector.cs +++ b/ActiveDirectoryConnector/ActiveDirectoryConnector.cs @@ -132,6 +132,7 @@ public class ActiveDirectoryConnector : CreateOp, Connector, SchemaOp, DeleteOp, "groupType", "authOrig", "unauthOrig", + "objectClass", }; // This is the list of attributes returned by default if no attributes are @@ -296,77 +297,77 @@ public Schema Schema() return _schema; } - // could have blocked on the lock while someone else built the - // schema, so check one more time before building - if (_schema == null) - { - String serverName = _configuration.LDAPHostName; - Forest forest = null; + ActiveDirectorySchema ADSchema = GetADSchema(); + + // get the user attribute infos and operations + ICollection userAttributeInfos = + GetUserAttributeInfos(ADSchema); + ObjectClassInfoBuilder ociBuilder = new ObjectClassInfoBuilder(); + ociBuilder.ObjectType = ObjectClass.ACCOUNT_NAME; + ociBuilder.AddAllAttributeInfo(userAttributeInfos); + ObjectClassInfo userInfo = ociBuilder.Build(); + + // get the group attribute infos and operations + ICollection groupAttributeInfos = + GetGroupAttributeInfos(ADSchema); + ociBuilder = new ObjectClassInfoBuilder(); + ociBuilder.ObjectType = ObjectClass.GROUP_NAME; + ociBuilder.AddAllAttributeInfo(groupAttributeInfos); + ObjectClassInfo groupInfo = ociBuilder.Build(); + + // get the organizationalUnit attribute infos and operations + ICollection ouAttributeInfos = + GetOuAttributeInfos(ADSchema); + ociBuilder = new ObjectClassInfoBuilder(); + ociBuilder.ObjectType = OBJECTCLASS_OU; + ociBuilder.AddAllAttributeInfo(ouAttributeInfos); + ObjectClassInfo ouInfo = ociBuilder.Build(); + + SchemaBuilder schemaBuilder = + new SchemaBuilder(SafeType.Get(this)); + + schemaBuilder.DefineObjectClass(userInfo); + schemaBuilder.DefineObjectClass(groupInfo); + schemaBuilder.RemoveSupportedObjectClass(SafeType.Get(), groupInfo); + schemaBuilder.DefineObjectClass(ouInfo); + schemaBuilder.RemoveSupportedObjectClass(SafeType.Get(), ouInfo); + schemaBuilder.RemoveSupportedObjectClass(SafeType.Get(), ouInfo); + schemaBuilder.RemoveSupportedObjectClass(SafeType.Get(), ouInfo); + schemaBuilder.RemoveSupportedObjectClass(SafeType.Get>(), ouInfo); - if ((serverName == null) || (serverName.Length == 0)) - { - // get the active directory schema - DirectoryContext context = new DirectoryContext( - DirectoryContextType.Domain, - _configuration.DomainName, - _configuration.DirectoryAdminName, - _configuration.DirectoryAdminPassword); - DomainController dc = DomainController.FindOne(context); - forest = dc.Forest; - } - else - { - DirectoryContext context = new DirectoryContext( - DirectoryContextType.DirectoryServer, - _configuration.LDAPHostName, - _configuration.DirectoryAdminName, - _configuration.DirectoryAdminPassword); - forest = Forest.GetForest(context); - } + _schema = schemaBuilder.Build(); - ActiveDirectorySchema ADSchema = forest.Schema; - - - // get the user attribute infos and operations - ICollection userAttributeInfos = - GetUserAttributeInfos(ADSchema); - ObjectClassInfoBuilder ociBuilder = new ObjectClassInfoBuilder(); - ociBuilder.ObjectType = ObjectClass.ACCOUNT_NAME; - ociBuilder.AddAllAttributeInfo(userAttributeInfos); - ObjectClassInfo userInfo = ociBuilder.Build(); - - // get the group attribute infos and operations - ICollection groupAttributeInfos = - GetGroupAttributeInfos(ADSchema); - ociBuilder = new ObjectClassInfoBuilder(); - ociBuilder.ObjectType = ObjectClass.GROUP_NAME; - ociBuilder.AddAllAttributeInfo(groupAttributeInfos); - ObjectClassInfo groupInfo = ociBuilder.Build(); - - // get the organizationalUnit attribute infos and operations - ICollection ouAttributeInfos = - GetOuAttributeInfos(ADSchema); - ociBuilder = new ObjectClassInfoBuilder(); - ociBuilder.ObjectType = OBJECTCLASS_OU; - ociBuilder.AddAllAttributeInfo(ouAttributeInfos); - ObjectClassInfo ouInfo = ociBuilder.Build(); - - SchemaBuilder schemaBuilder = - new SchemaBuilder(SafeType.Get(this)); - - schemaBuilder.DefineObjectClass(userInfo); - schemaBuilder.DefineObjectClass(groupInfo); - schemaBuilder.RemoveSupportedObjectClass(SafeType.Get(), groupInfo); - schemaBuilder.DefineObjectClass(ouInfo); - schemaBuilder.RemoveSupportedObjectClass(SafeType.Get(), ouInfo); - schemaBuilder.RemoveSupportedObjectClass(SafeType.Get(), ouInfo); - schemaBuilder.RemoveSupportedObjectClass(SafeType.Get(), ouInfo); - schemaBuilder.RemoveSupportedObjectClass(SafeType.Get>(), ouInfo); - - _schema = schemaBuilder.Build(); + return _schema; + } + + private ActiveDirectorySchema GetADSchema() + { + String serverName = _configuration.LDAPHostName; + Forest forest = null; + + if ((serverName == null) || (serverName.Length == 0)) + { + // get the active directory schema + DirectoryContext context = new DirectoryContext( + DirectoryContextType.Domain, + _configuration.DomainName, + _configuration.DirectoryAdminName, + _configuration.DirectoryAdminPassword); + DomainController dc = DomainController.FindOne(context); + forest = dc.Forest; + } + else + { + DirectoryContext context = new DirectoryContext( + DirectoryContextType.DirectoryServer, + _configuration.LDAPHostName, + _configuration.DirectoryAdminName, + _configuration.DirectoryAdminPassword); + forest = Forest.GetForest(context); } - return _schema; + ActiveDirectorySchema ADSchema = forest.Schema; + return ADSchema; } public ICollection GetUserAttributeInfos( @@ -609,7 +610,7 @@ public void ExecuteQuery(ObjectClass oclass, string query, } ExecuteQuery(oclass, query, handler, options, - false, null, _configuration.LDAPHostName, useGC, _configuration.SearchContext); + false, null, _configuration.LDAPHostName, useGC, _configuration.SearchContainer); } catch (Exception e) { @@ -686,6 +687,7 @@ private void ExecuteQuery(ObjectClass oclass, string query, { Trace.TraceInformation("Found object {0}", result.Path); ConnectorObjectBuilder builder = new ConnectorObjectBuilder(); + builder.ObjectClass = oclass; bool isDeleted = false; if(result.Properties.Contains(ATT_IS_DELETED)) { @@ -836,6 +838,26 @@ private void AddAttributeIfNotNull(ConnectorObjectBuilder builder, public void Test() { _configuration.Validate(); + + bool objectFound = true; + // now make sure they specified a valid value for the User Object Class + ActiveDirectorySchema ADSchema = GetADSchema(); + ActiveDirectorySchemaClass ADSchemaClass = null; + try + { + ADSchemaClass = ADSchema.FindClass(_configuration.ObjectClass); + + } + catch (ActiveDirectoryObjectNotFoundException exception) + { + objectFound = false; + } + if ((!objectFound) || (ADSchemaClass == null)) + { + throw new ConnectorException( + String.Format("Invalid Object Class was specified in the connector configuration. Object Class \'{0}\' was not found in Active Directory", + _configuration.ObjectClass)); + } } #endregion @@ -1035,65 +1057,29 @@ public void Sync(ObjectClass objClass, SyncToken token, public SyncToken GetLatestSyncToken() { - String serverName = GetSyncServerName(); - - ActiveDirectorySyncToken adSyncToken = - new ActiveDirectorySyncToken((string)null, serverName, UseGlobalCatalog()); - - string updatedQuery = GetSyncUpdateQuery(adSyncToken); - string deletedQuery = GetSyncDeleteQuery(adSyncToken); - - string updateSearchRoot; - string deleteSearchRoot; - - if (UseGlobalCatalog()) + string serverName = GetSyncServerName(); + long highestCommittedUsn = 0; + bool useGlobalCatalog = UseGlobalCatalog(); + if (useGlobalCatalog) { - updateSearchRoot = ActiveDirectoryUtils.GetGCPath(serverName, - _configuration.SyncSearchContext); - deleteSearchRoot = ActiveDirectoryUtils.GetGCPath(serverName, null); + DirectoryContext context = new DirectoryContext(DirectoryContextType.DirectoryServer, + serverName, _configuration.DirectoryAdminName, _configuration.DirectoryAdminPassword); + GlobalCatalog gc = GlobalCatalog.GetGlobalCatalog(context); + highestCommittedUsn = gc.HighestCommittedUsn; } else { - updateSearchRoot = ActiveDirectoryUtils.GetLDAPPath(serverName, - _configuration.SyncSearchContext); - deleteSearchRoot = ActiveDirectoryUtils.GetGCPath(serverName, null); - } - - DirectoryEntry updateSearchRootEntry = new DirectoryEntry(updateSearchRoot, - _configuration.DirectoryAdminName, _configuration.DirectoryAdminPassword); - DirectoryEntry deleteSearchRootEntry = new DirectoryEntry(deleteSearchRoot, - _configuration.DirectoryAdminName, _configuration.DirectoryAdminPassword); - - - DirectorySearcher updateSearcher = - new DirectorySearcher(updateSearchRootEntry, updatedQuery); - DirectorySearcher deleteSearcher = - new DirectorySearcher(deleteSearchRootEntry, deletedQuery); - - // according to the docs, I would think that this would make the sort order - // to be descending. It does not, so setting the direction below. - updateSearcher.Sort = new SortOption(ATT_USN_CHANGED, SortDirection.Descending); - updateSearcher.Sort.Direction = SortDirection.Descending; - updateSearcher.PageSize = 10; - SearchResultCollection updateResults = updateSearcher.FindAll(); - if ((updateResults != null) && (updateResults.Count > 0)) - { - adSyncToken.LastModifiedUsn = (long)updateResults[0].Properties[ATT_USN_CHANGED][0]; - } - - // according to the docs, I would think that this would make the sort order - // to be descending. It does not, so setting the direction below. - deleteSearcher.Sort = new SortOption(ATT_USN_CHANGED, SortDirection.Descending); - deleteSearcher.Sort.Direction = SortDirection.Descending; - deleteSearcher.PageSize = 10; - deleteSearcher.Tombstone = true; - SearchResultCollection deleteResults = deleteSearcher.FindAll(); - if ((deleteResults != null) && (deleteResults.Count > 0)) - { - adSyncToken.LastDeleteUsn = (long)deleteResults[0].Properties[ATT_USN_CHANGED][0]; + DirectoryContext context = new DirectoryContext(DirectoryContextType.DirectoryServer, + serverName, _configuration.DirectoryAdminName, _configuration.DirectoryAdminPassword); + DomainController dc = DomainController.GetDomainController(context); + highestCommittedUsn = dc.HighestCommittedUsn; } - return adSyncToken.GetSyncToken(); + ActiveDirectorySyncToken token = + new ActiveDirectorySyncToken("", serverName, useGlobalCatalog); + token.LastDeleteUsn = highestCommittedUsn; + token.LastModifiedUsn = highestCommittedUsn; + return token.GetSyncToken(); } string GetSyncServerName() diff --git a/ActiveDirectoryConnectorTests/ActiveDirectoryConnectorTest.cs b/ActiveDirectoryConnectorTests/ActiveDirectoryConnectorTest.cs index 8bc9ff58..7223bc6f 100644 --- a/ActiveDirectoryConnectorTests/ActiveDirectoryConnectorTest.cs +++ b/ActiveDirectoryConnectorTests/ActiveDirectoryConnectorTest.cs @@ -54,6 +54,7 @@ using System.IO; using System.Security.AccessControl; using System.Security.Principal; +using Org.IdentityConnectors.Framework.Common.Exceptions; namespace Org.IdentityConnectors.ActiveDirectory { @@ -86,6 +87,30 @@ public void TestConfiguration() { Assert.AreEqual(directoryAdminName, config.DirectoryAdminName); } + [Test] + public void TestTest() + { + ActiveDirectoryConnector connectorGood = new ActiveDirectoryConnector(); + ActiveDirectoryConfiguration config = (ActiveDirectoryConfiguration)GetConfiguration(); + connectorGood.Init(config); + connectorGood.Test(); + + bool threwException = false; + try + { + config.ObjectClass = "BadObjectClass"; + ActiveDirectoryConnector connectorBad = new ActiveDirectoryConnector(); + connectorBad.Init(config); + connectorBad.Test(); + } + catch (ConnectorException e) + { + threwException = true; + } + + Assert.IsTrue(threwException, "Bad configuration should have caused an exception"); + } + [Test] public void TestSchema() { @@ -1395,6 +1420,22 @@ public void TestSync(bool searchChildDomains, String syncSearchContext) } } + [Test] + public void TestGetLastSyncToken() + { + //Initialize Connector + ActiveDirectoryConnector connector = new ActiveDirectoryConnector(); + ActiveDirectoryConfiguration configuration = + (ActiveDirectoryConfiguration)GetConfiguration(); + configuration.SearchChildDomains = false; + connector.Init(configuration); + SyncToken noGCToken = connector.GetLatestSyncToken(); + + configuration.SearchChildDomains = true; + connector.Init(configuration); + SyncToken GCToken = connector.GetLatestSyncToken(); + } + public long GetUpdateUsnFromToken(SyncToken token) { string[] tokenParts = ((string)token.Value).Split('|'); @@ -1764,7 +1805,7 @@ public Configuration GetConfiguration() config.LDAPHostName = GetProperty(CONFIG_PROPERTY_LDAPHOSTNAME); config.DirectoryAdminName = GetProperty(CONFIG_PROPERTY_USER); config.DirectoryAdminPassword = GetProperty(CONFIG_PROPERTY_PASSWORD); - config.SearchContext = GetProperty(CONFIG_PROPERTY_SEARCH_CONTEXT); + config.SearchContainer = GetProperty(CONFIG_PROPERTY_SEARCH_CONTEXT); config.SyncDomainController = GetProperty(CONFIG_PROPERTY_SYNC_DOMAIN_CONTROLLER); config.SyncGlobalCatalogServer = GetProperty(CONFIG_PROPERTY_GC_DOMAIN_CONTROLLER); return config; From dabd77ff623d9334f52db29fade4e97fa9ad59cc Mon Sep 17 00:00:00 2001 From: tthompson29 Date: Fri, 3 Oct 2008 14:54:59 +0000 Subject: [PATCH 028/342] fixed copy paths to be relative to the current Configuration --- .../ActiveDirectoryConnectorTests.csproj | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/ActiveDirectoryConnectorTests/ActiveDirectoryConnectorTests.csproj b/ActiveDirectoryConnectorTests/ActiveDirectoryConnectorTests.csproj index 420844cc..51d92607 100644 --- a/ActiveDirectoryConnectorTests/ActiveDirectoryConnectorTests.csproj +++ b/ActiveDirectoryConnectorTests/ActiveDirectoryConnectorTests.csproj @@ -144,8 +144,8 @@ - copy "$(ProjectDir)\..\FrameworkInternal\bin\Debug\FrameworkInternal.dll" "$(ProjectDir)\$(OutDir)"; - copy "$(ProjectDir)\..\ShellScriptExecutorFactory\bin\Debug\Shell.ScriptExecutorFactory.dll" "$(ProjectDir)\$(OutDir)"; + copy "$(ProjectDir)\..\FrameworkInternal\bin\$(Configuration)\FrameworkInternal.dll" "$(ProjectDir)\$(OutDir)"; + copy "$(ProjectDir)\..\ShellScriptExecutorFactory\bin\$(Configuration)\Shell.ScriptExecutorFactory.dll" "$(ProjectDir)\$(OutDir)"; \ No newline at end of file From abafa6196cf98d21020fc7dd01aeb37ba420f6a6 Mon Sep 17 00:00:00 2001 From: wdroste Date: Fri, 3 Oct 2008 16:11:42 +0000 Subject: [PATCH 029/342] Issue #288 -Fixed update. --- FrameworkInternal/ApiLocalOperations.cs | 13 +++++++++++-- 1 file changed, 11 insertions(+), 2 deletions(-) diff --git a/FrameworkInternal/ApiLocalOperations.cs b/FrameworkInternal/ApiLocalOperations.cs index a982813c..e9e9f1d6 100644 --- a/FrameworkInternal/ApiLocalOperations.cs +++ b/FrameworkInternal/ApiLocalOperations.cs @@ -1261,11 +1261,10 @@ public ICollection Merge(UpdateApiType type, ICollection baseAttrs) { // return the merged attributes ICollection ret = new HashSet(); - ret.Add(ConnectorAttributeUtil.GetUidAttribute(baseAttrs)); IDictionary baseAttrMap = ConnectorAttributeUtil.ToMap(baseAttrs); // run through attributes of the current object.. foreach (ConnectorAttribute updateAttr in updateAttrs) { - // ignore uid because its immutable from this layer.. + // ignore uid because its immutable.. if (updateAttr is Uid) { continue; } @@ -1307,6 +1306,16 @@ public ICollection Merge(UpdateApiType type, } ret.Add(modifiedAttr); } + // add the rest of the base attributes that were not update attrs + IDictionary updateAttrMap = + ConnectorAttributeUtil.ToMap(updateAttrs); + foreach (ConnectorAttribute a in baseAttrs) { + if (!updateAttrMap.ContainsKey(a.Name)) { + ret.Add(a); + } + } + // always add the UID.. + ret.Add(updateAttrMap[Uid.NAME]); return ret; } From c5a1105626bc3dde1119f0a572007e814a2de007 Mon Sep 17 00:00:00 2001 From: tthompson29 Date: Mon, 6 Oct 2008 15:18:45 +0000 Subject: [PATCH 030/342] fixed illegal characters --- .../ActiveDirectoryConnectorTests.csproj | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/ActiveDirectoryConnectorTests/ActiveDirectoryConnectorTests.csproj b/ActiveDirectoryConnectorTests/ActiveDirectoryConnectorTests.csproj index 51d92607..96a561be 100644 --- a/ActiveDirectoryConnectorTests/ActiveDirectoryConnectorTests.csproj +++ b/ActiveDirectoryConnectorTests/ActiveDirectoryConnectorTests.csproj @@ -138,7 +138,7 @@ - + From 3dc875e723f48ac50bbce6ea796ca998598bf241 Mon Sep 17 00:00:00 2001 From: tthompson29 Date: Mon, 6 Oct 2008 16:06:35 +0000 Subject: [PATCH 031/342] added some temporary tracing for test properties --- ActiveDirectoryConnectorTests/ActiveDirectoryConnectorTest.cs | 1 + 1 file changed, 1 insertion(+) diff --git a/ActiveDirectoryConnectorTests/ActiveDirectoryConnectorTest.cs b/ActiveDirectoryConnectorTests/ActiveDirectoryConnectorTest.cs index 7223bc6f..ef20168d 100644 --- a/ActiveDirectoryConnectorTests/ActiveDirectoryConnectorTest.cs +++ b/ActiveDirectoryConnectorTests/ActiveDirectoryConnectorTest.cs @@ -2145,6 +2145,7 @@ private static void VerifyObject(ICollection requestedAttrib public string GetProperty(string propertyName) { string x = TestHelpers.GetProperty(propertyName, null); + Console.WriteLine(String.Format("GetProperty: {0}={1}", propertyName, x)); return x; } From 590d1cabf59ceb3e23f04af5596e95fa54deb3d3 Mon Sep 17 00:00:00 2001 From: dvernon Date: Mon, 6 Oct 2008 18:31:56 +0000 Subject: [PATCH 032/342] Issue #64 - Incremental checkin. Trying to get unit tests to pass in build. --- ActiveDirectoryConnector/ActiveDirectoryConnector.cs | 2 ++ 1 file changed, 2 insertions(+) diff --git a/ActiveDirectoryConnector/ActiveDirectoryConnector.cs b/ActiveDirectoryConnector/ActiveDirectoryConnector.cs index 3a6aec20..04c2f269 100644 --- a/ActiveDirectoryConnector/ActiveDirectoryConnector.cs +++ b/ActiveDirectoryConnector/ActiveDirectoryConnector.cs @@ -212,10 +212,12 @@ public Uid Create(ObjectClass oclass, try { + /* if (!DirectoryEntry.Exists(ldapContainerPath)) { throw new ConnectorException("Container does not exist"); } + */ // Get the correct container, and put the new user in it DirectoryEntry containerDe = new DirectoryEntry(ldapContainerPath, From a595fbd940189edb34db18daa51bd739d25dff68 Mon Sep 17 00:00:00 2001 From: wdroste Date: Wed, 8 Oct 2008 16:49:24 +0000 Subject: [PATCH 033/342] Issue #281 -Added 'ConnectorAttributeAccessor' and returned 'this' for most of the builders. --- Framework/CommonObjects.cs | 259 +++++++++++++++++++++++++++++++++++-- 1 file changed, 248 insertions(+), 11 deletions(-) diff --git a/Framework/CommonObjects.cs b/Framework/CommonObjects.cs index 7469239f..fcf45071 100644 --- a/Framework/CommonObjects.cs +++ b/Framework/CommonObjects.cs @@ -742,11 +742,13 @@ public IList Value { } } - public void AddValue(params Object [] args) { + public ConnectorAttributeBuilder AddValue(params Object [] args) { AddValuesInternal(args); + return this; } - public void AddValue(ICollection values) { + public ConnectorAttributeBuilder AddValue(ICollection values) { AddValuesInternal(values); + return this; } public ConnectorAttribute Build() { @@ -1130,39 +1132,44 @@ public void SetName(Name name) { * Takes all the attribute from a {@link ConnectorObject} and add/overwrite * the current attributes. */ - public void Add(ConnectorObject obj) { + public ConnectorObjectBuilder Add(ConnectorObject obj) { // simply add all the attributes it will include (Uid, ObjectClass..) foreach (ConnectorAttribute attr in obj.GetAttributes()) { AddAttribute(attr); } ObjectClass = obj.ObjectClass; + return this; } - public void AddAttribute(params ConnectorAttribute [] attrs) { + public ConnectorObjectBuilder AddAttribute(params ConnectorAttribute [] attrs) { ValidateParameter(attrs, "attrs"); foreach (ConnectorAttribute a in attrs) { //DONT use Add - it throws exceptions if already there _attributes[a.Name] = a; } + return this; } - public void AddAttributes(ICollection attrs) { + public ConnectorObjectBuilder AddAttributes(ICollection attrs) { ValidateParameter(attrs, "attrs"); foreach (ConnectorAttribute a in attrs) { _attributes[a.Name] = a; } + return this; } /** * Adds values to the attribute. */ - public void AddAttribute(String name, params object [] objs) { + public ConnectorObjectBuilder AddAttribute(String name, params object [] objs) { AddAttribute(ConnectorAttributeBuilder.Build(name, objs)); + return this; } /** * Adds each object in the collection. */ - public void AddAttribute(String name, ICollection obj) { + public ConnectorObjectBuilder AddAttribute(String name, ICollection obj) { AddAttribute(ConnectorAttributeBuilder.Build(name, obj)); + return this; } public ConnectorObject Build() { // check that there are attributes to return.. @@ -1633,18 +1640,20 @@ public ObjectClassInfoBuilder() { /** * Add each {@link AttributeInfo} object to the {@link ObjectClassInfo}. */ - public void AddAttributeInfo(ConnectorAttributeInfo info) { + public ObjectClassInfoBuilder AddAttributeInfo(ConnectorAttributeInfo info) { if (_info.ContainsKey(info.Name)) { const string MSG = "ConnectorAttributeInfo of name ''{0}'' already exists!"; throw new ArgumentException(String.Format(MSG, info.Name)); } _info[info.Name] = info; + return this; } - public void AddAllAttributeInfo(ICollection info) { + public ObjectClassInfoBuilder AddAllAttributeInfo(ICollection info) { foreach (ConnectorAttributeInfo cainfo in info) { AddAttributeInfo(cainfo); } + return this; } public ObjectClassInfo Build() { @@ -2889,24 +2898,26 @@ public String ScriptText { * type types that the framework can serialize. * @see ObjectSerializerFactory for a list of supported types. */ - public void AddScriptArgument(String name, Object value) { + public ScriptContextBuilder AddScriptArgument(String name, Object value) { if ( name == null ) { throw new ArgumentException("Argument 'name' cannot be null."); } //don't validate value here - we do that implicitly when //we clone in the constructor of ScriptRequest _scriptArguments[name] = value; + return this; } /** * Removes the given script argument. * @param name The name of the argument. Must not be null. */ - public void RemoveScriptArgument(String name) { + public ScriptContextBuilder RemoveScriptArgument(String name) { if ( name == null ) { throw new ArgumentException("Argument 'name' cannot be null."); } _scriptArguments.Remove(name); + return this; } /** @@ -3295,4 +3306,230 @@ public String GetUidValue() { } } #endregion + + #region ConnectorAttributesAccessor + /** + * Attributes Accessor convenience methods for accessing attributes. + * + * This class wraps a set of attributes to make lookup faster than the + * {@link AttributeUtil#find(String, Set)} method, since that method must + * re-create the map each time. + * + * @author Warren Strange + */ + public class ConnectorAttributesAccessor { + + ICollection _attrs; + IDictionary _attrMap; + + public ConnectorAttributesAccessor(ICollection attrs) { + _attrs = attrs; + _attrMap = ConnectorAttributeUtil.ToMap(attrs); + } + + /** + * Find the named attribute + * + * @param name - + * the attribute name to search for + * @return the Attribute, or null if not found. + */ + public ConnectorAttribute Find(String name) { + return CollectionUtil.GetValue(_attrMap, name, null); + } + + /** + * Get the {@link Name} attribute from the set of attributes. + * + * @return the {@link Name} attribute in the set. + */ + public Name GetName() { + return (Name) Find(Name.NAME); + } + + /** + * Return the enabled status of the account. + * + * If the ENABLE operational attribute is present, it's value takes + * precedence over the current value. If it is missing, the currentlyEnabled + * status is returned instead. + * + * @param dflt + * the default state if enable is not found. + * @return true if the account is enabled, false otherwise + */ + public bool GetEnabled(bool dflt) { + bool e = dflt; + ConnectorAttribute enable = Find(OperationalAttributes.ENABLE_NAME); + if (enable != null) { + e = ConnectorAttributeUtil.GetBooleanValue(enable).Value; + } + return e; + } + + /** + * Get the password as a GuardeString + * + * @return the password as a guarded String + */ + public GuardedString GetPassword() { + ConnectorAttribute a = Find(OperationalAttributes.PASSWORD_NAME); + return a == null ? null : ConnectorAttributeUtil.GetGuardedStringValue(a); + } + + /** + * Return a list of attributes + * + * @param name - + * name of attribute to search for. + * + * @return The List (generic object) iff it exists otherwise null. + */ + public IList FindList(String name) { + ConnectorAttribute a = Find(name); + return (a == null) ? null : a.Value; + } + + /** + * Return the multivalued attribute as a list of strings. This will throw a + * ClassCastException if the underlying attribute list is not of type + * String. + * + * @param name + * the name of the attribute to search for + * @return a List of String values for the attribute + */ + public IList FindStringList(String name) { + IList l = FindList(name); + if (l != null) { + IList ret = new List(l.Count); + foreach (object o in l) { + ret.Add((String) o); + } + return ret; + } + return null; + } + + /** + * Determines if the set as the attribute specified. + * + * @param name + * attribute name + * @return true if the named attribute exists, false otherwise + */ + public bool HasAttribute(String name) { + return Find(name) != null; + } + + /** + * Get the string value from the specified (single-valued) attribute. + * + * @param name + * Attribute from which to retrieve the long value. + * @return null if the value is null otherwise the long value for the + * attribute. + * @throws ClassCastException + * iff the object in the attribute is not an long. + * @throws IllegalArgumentException + * iff the attribute is a multi-valued (rather than + * single-valued). + */ + public String FindString(String name) { + ConnectorAttribute a = Find(name); + return a == null ? null : ConnectorAttributeUtil.GetStringValue(a); + } + + /** + * Get the integer value from the specified (single-valued) attribute. + * + * @param name + * Attribute from which to retrieve the long value. + * @return null if the value is null otherwise the long value for the + * attribute. + * @throws ClassCastException + * iff the object in the attribute is not an long. + * @throws IllegalArgumentException + * iff the attribute is a multi-valued (rather than + * single-valued). + */ + public int? FindInteger(String name) { + ConnectorAttribute a = Find(name); + return (a == null) ? null : ConnectorAttributeUtil.GetIntegerValue(a); + } + + /** + * Get the long value from the specified (single-valued) attribute. + * + * @param name + * Attribute from which to retrieve the long value. + * @return null if the value is null otherwise the long value for the + * attribute. + * @throws ClassCastException + * iff the object in the attribute is not an long. + * @throws IllegalArgumentException + * iff the attribute is a multi-valued (rather than + * single-valued). + */ + public long? FindLong(String name) { + ConnectorAttribute a = Find(name); + return a == null ? null : ConnectorAttributeUtil.GetLongValue(a); + } + + /** + * Get the date value from the specified (single-valued) attribute that + * contains a long. + * + * @param name + * Attribute from which to retrieve the date value. + * @return null if the value is null otherwise the date value for the + * attribute. + * @throws ClassCastException + * iff the object in the attribute is not an long. + * @throws IllegalArgumentException + * iff the attribute is a multi-valued (rather than + * single-valued). + */ + public DateTime? FindDateTime(String name) { + ConnectorAttribute a = Find(name); + return a == null ? null : ConnectorAttributeUtil.GetDateTimeValue(a); + } + + /** + * Get the integer value from the specified (single-valued) attribute. + * + * @param name + * Attribute from which to retrieve the integer value. + * @return null if the value is null otherwise the integer value for the + * attribute. + * @throws ClassCastException + * iff the object in the attribute is not an integer. + * @throws IllegalArgumentException + * iff the attribute is a multi-valued (rather than + * single-valued).. + */ + public double? FindDouble(String name) { + ConnectorAttribute a = Find(name); + return a == null ? null : ConnectorAttributeUtil.GetDoubleValue(a); + } + + /** + * Get the boolean value from the specified (single-valued) attribute. + * + * @param name + * Attribute from which to retrieve the boolean value. + * @return null if the value is null otherwise the boolean value for the + * attribute. + * @throws ClassCastException + * iff the object in the attribute is not an {@link Boolean}. + * @throws IllegalArgumentException + * iff the attribute is a multi-valued (rather than + * single-valued). + */ + public bool? FindBoolean(String name) { + ConnectorAttribute a = Find(name); + return a == null ? null : ConnectorAttributeUtil.GetBooleanValue(a); + } + } + #endregion } From 4d582d1cbb7536ec722dffbc07c72f73c637ee85 Mon Sep 17 00:00:00 2001 From: rcauble Date: Thu, 9 Oct 2008 16:12:54 +0000 Subject: [PATCH 034/342] Issue#294: handle timeout properly for get --- FrameworkInternal/ApiLocal.cs | 25 +++++++++++++------------ 1 file changed, 13 insertions(+), 12 deletions(-) diff --git a/FrameworkInternal/ApiLocal.cs b/FrameworkInternal/ApiLocal.cs index 3a1a3876..dc3dfc2d 100644 --- a/FrameworkInternal/ApiLocal.cs +++ b/FrameworkInternal/ApiLocal.cs @@ -601,18 +601,6 @@ public LocalConnectorFacadeImpl(LocalConnectorInfoImpl connectorInfo, // ======================================================================= protected override APIOperation GetOperationImplementation(SafeType api) { - APIOperation ret = null; - // need to figure out if api operation is a get op.. - if (api.RawType.Equals(typeof(GetApiOp))) { - APIOperation op = GetAPIOperationRunner(SafeType.Get()); - ret = new GetImpl((SearchApiOp) op); - } else { - ret = GetAPIOperationRunner(api); - } - return ret; - } - - APIOperation GetAPIOperationRunner(SafeType api) { APIOperation proxy; //first create the inner proxy - this is the proxy that obtaining //a connection from the pool, etc @@ -623,6 +611,19 @@ APIOperation GetAPIOperationRunner(SafeType api) { new OperationalContext(connectorInfo,GetAPIConfiguration()); proxy = new ValidateImpl(context); } + else if ( api.RawType.Equals( typeof(GetApiOp) ) ) { + ConstructorInfo constructor = + API_TO_IMPL[SafeType.Get()]; + ConnectorOperationalContext context = + new ConnectorOperationalContext(connectorInfo, + GetAPIConfiguration(), + GetPool()); + + ConnectorAPIOperationRunnerProxy handler = + new ConnectorAPIOperationRunnerProxy(context,constructor); + proxy = + new GetImpl((SearchApiOp)NewAPIOperationProxy(SafeType.Get(),handler)); + } else { ConstructorInfo constructor = API_TO_IMPL[api]; From 81d45ca7f20c75553bd2e91f6be76850b7f8b3fc Mon Sep 17 00:00:00 2001 From: dvernon Date: Thu, 9 Oct 2008 21:47:21 +0000 Subject: [PATCH 035/342] Issue #64 - Incremental checkin. Changed TestSyncDC and TestSyncGC to use GetLatestSyncToken instead of running through an entire sync from the beginning in the hopes of making the tests run faster. --- .../ActiveDirectoryConnectorTest.cs | 72 +++++++++++++------ 1 file changed, 52 insertions(+), 20 deletions(-) diff --git a/ActiveDirectoryConnectorTests/ActiveDirectoryConnectorTest.cs b/ActiveDirectoryConnectorTests/ActiveDirectoryConnectorTest.cs index ef20168d..512b6038 100644 --- a/ActiveDirectoryConnectorTests/ActiveDirectoryConnectorTest.cs +++ b/ActiveDirectoryConnectorTests/ActiveDirectoryConnectorTest.cs @@ -130,7 +130,7 @@ public void TestSchema() Assert.That((ocInfo.ObjectType == ObjectClass.ACCOUNT.GetObjectClassValue()) || (ocInfo.ObjectType == ObjectClass.GROUP.GetObjectClassValue()) || (ocInfo.ObjectType == ActiveDirectoryConnector.OBJECTCLASS_OU)); - Console.WriteLine("****** " + ocInfo.ObjectType); + Trace.WriteLine("****** " + ocInfo.ObjectType); // skip this for organizational unit ... it doesnt really have this if (ocInfo.ObjectType.Equals(ActiveDirectoryConnector.ouObjectClass)) @@ -141,11 +141,11 @@ public void TestSchema() foreach (ConnectorAttributeInfo caInfo in ocInfo.ConnectorAttributeInfos) { Assert.IsNotNull(caInfo); - Console.WriteLine("{0} {1} {2} {3}", caInfo.Name, + Trace.WriteLine(String.Format("{0} {1} {2} {3}", caInfo.Name, caInfo.IsCreatable ? "createable" : "", caInfo.IsUpdateable ? "updateable" : "", caInfo.IsRequired ? "required" : "", - caInfo.IsMultiValue ? "multivalue" : ""); + caInfo.IsMultiValue ? "multivalue" : "")); if(ConnectorAttributeUtil.IsSpecial(caInfo)) { foundOperationalAttributes = true; } else { @@ -1339,9 +1339,9 @@ public void TestSync(bool searchChildDomains, String syncSearchContext) SyncTestHelper syncHelper = new SyncTestHelper(); // do the first sync - connector.Sync(ObjectClass.ACCOUNT, syncHelper.Token, syncHelper.SyncHandler_Initial, null); + //connector.Sync(ObjectClass.ACCOUNT, syncHelper.Token, syncHelper.SyncHandler_Initial, null); - syncHelper.Init(); + syncHelper.Init(connector.GetLatestSyncToken()); ICollection attributes = null; // create some users @@ -1355,11 +1355,11 @@ public void TestSync(bool searchChildDomains, String syncSearchContext) } // sync, and verify - connector.Sync(ObjectClass.ACCOUNT, syncHelper.Token, syncHelper.SyncHandler_ModifiedAccounts, null); + connector.Sync(ObjectClass.ACCOUNT, syncHelper._token, syncHelper.SyncHandler_ModifiedAccounts, null); syncHelper.CheckAllSyncsProcessed(); // reset everything - syncHelper.Init(); + syncHelper.Init(connector.GetLatestSyncToken()); // modify a user, then add some users, then modify one of the added users attributes = new List(); @@ -1384,10 +1384,10 @@ public void TestSync(bool searchChildDomains, String syncSearchContext) syncHelper.AddModUid(createdUids.Last(), attributes); // sync, and verify - connector.Sync(ObjectClass.ACCOUNT, syncHelper.Token, syncHelper.SyncHandler_ModifiedAccounts, null); + connector.Sync(ObjectClass.ACCOUNT, syncHelper._token, syncHelper.SyncHandler_ModifiedAccounts, null); syncHelper.CheckAllSyncsProcessed(); - syncHelper.Init(); + syncHelper.Init(connector.GetLatestSyncToken()); // delete the user foreach (Uid uid in createdUids) { @@ -1396,7 +1396,7 @@ public void TestSync(bool searchChildDomains, String syncSearchContext) syncHelper.AddDelUid(uid); } // sync and verify - connector.Sync(ObjectClass.ACCOUNT, syncHelper.Token, syncHelper.SyncHandler_DeletedAccounts, null); + connector.Sync(ObjectClass.ACCOUNT, syncHelper._token, syncHelper.SyncHandler_DeletedAccounts, null); syncHelper.CheckAllSyncsProcessed(); createUid = CreateAndVerifyObject(connector, ObjectClass.ACCOUNT, @@ -1407,8 +1407,8 @@ public void TestSync(bool searchChildDomains, String syncSearchContext) // now get the latest sync token, and it // should be greater or equal to the last one we saw SyncToken latestToken = connector.GetLatestSyncToken(); - Assert.Greater(GetUpdateUsnFromToken(latestToken), GetUpdateUsnFromToken(syncHelper.Token)); - Assert.GreaterOrEqual(GetDeleteUsnFromToken(latestToken), GetDeleteUsnFromToken(syncHelper.Token)); + Assert.Greater(GetUpdateUsnFromToken(latestToken), GetUpdateUsnFromToken(syncHelper._token)); + Assert.GreaterOrEqual(GetDeleteUsnFromToken(latestToken), GetDeleteUsnFromToken(syncHelper._token)); } finally { @@ -1453,12 +1453,13 @@ class SyncTestHelper IDictionary> _mods = null; IList _dels = null; - public SyncToken Token { get; set; } + public SyncToken _token { get; set; } - public void Init() + public void Init(SyncToken token) { _mods = new Dictionary>(); _dels = new List(); + _token = token; } public void AddModUid(Uid uid, ICollection attributes) @@ -1474,13 +1475,13 @@ public void AddDelUid(Uid uid) public bool SyncHandler_Initial(SyncDelta delta) { // do nothing .. just establishing the baseline - Token = delta.Token; + _token = delta.Token; return true; } public bool SyncHandler_ModifiedAccounts(SyncDelta delta) { - Token = delta.Token; + _token = delta.Token; if(delta.DeltaType.Equals(SyncDeltaType.UPDATE)) { // just ignore extra ones. they might have come in by other means if (_mods.ContainsKey(delta.Uid)) @@ -1497,7 +1498,7 @@ public bool SyncHandler_ModifiedAccounts(SyncDelta delta) public bool SyncHandler_DeletedAccounts(SyncDelta delta) { - Token = delta.Token; + _token = delta.Token; _dels.Remove(delta.Uid); return true; @@ -1557,6 +1558,37 @@ public void RunScript(ActiveDirectoryConnector connector, String user, Assert.AreEqual(arg0, returnedArray[0]); Assert.AreEqual(arg1, returnedArray[1]); } +/* + [Test] + public void testBooScript() + { + //Initialize Connector + ActiveDirectoryConnector connector = new ActiveDirectoryConnector(); + connector.Init(GetConfiguration()); + + try + { + string tempFileName = Path.GetTempFileName(); + StringBuilder scriptText = new StringBuilder(); + scriptText.Append("print(\"this is, \");"); + scriptText.Append("print(\"a test.\");"); + + IDictionary arguments = new Dictionary(); + string arg0 = "argument_zero"; + string arg1 = "argument one"; + arguments.Add("ARG0", arg0); + arguments.Add("ARG1", arg1); + + OperationOptionsBuilder builder = new OperationOptionsBuilder(); + + ScriptContext context = new ScriptContext("Boo", scriptText.ToString(), arguments); + object resultObject = connector.RunScriptOnResource(context, builder.Build()); + } + finally + { + } + } +*/ // does a create and verify, then looks up and returns // the new user's dn (used for adding to a group) @@ -2144,9 +2176,9 @@ private static void VerifyObject(ICollection requestedAttrib // this needs to be replaced by the real one. public string GetProperty(string propertyName) { - string x = TestHelpers.GetProperty(propertyName, null); - Console.WriteLine(String.Format("GetProperty: {0}={1}", propertyName, x)); - return x; + string propertyValue = TestHelpers.GetProperty(propertyName, null); + Trace.WriteLine(String.Format("GetProperty: {0} = {1}", propertyName, propertyValue)); + return propertyValue; } public ConnectorObject GetConnectorObjectFromUid( From b82dec5a2661092c704d6e9ec4922886bc2bf8b2 Mon Sep 17 00:00:00 2001 From: tknappek Date: Fri, 10 Oct 2008 14:04:42 +0000 Subject: [PATCH 036/342] Issue#269: SPI methods changed to virtual to override them in ExchangeConnector --- .../ActiveDirectoryConnector.cs | 20 +++++++++---------- 1 file changed, 10 insertions(+), 10 deletions(-) diff --git a/ActiveDirectoryConnector/ActiveDirectoryConnector.cs b/ActiveDirectoryConnector/ActiveDirectoryConnector.cs index 04c2f269..ea742ff4 100644 --- a/ActiveDirectoryConnector/ActiveDirectoryConnector.cs +++ b/ActiveDirectoryConnector/ActiveDirectoryConnector.cs @@ -179,7 +179,7 @@ static ActiveDirectoryConnector() #region CreateOp Members // implementation of CreateSpiOp - public Uid Create(ObjectClass oclass, + public virtual Uid Create(ObjectClass oclass, ICollection attributes, OperationOptions options) { Uid uid = null; @@ -271,7 +271,7 @@ public Uid Create(ObjectClass oclass, #region Connector Members // implementation of Connector - public void Init(Configuration configuration) + public virtual void Init(Configuration configuration) { Trace.TraceInformation("Init method"); _configuration = (ActiveDirectoryConfiguration)configuration; @@ -290,7 +290,7 @@ public void Dispose() #region SchemaOp Members // implementation of SchemaSpiOp - public Schema Schema() + public virtual Schema Schema() { Trace.TraceInformation("Schema method"); @@ -594,13 +594,13 @@ private ConnectorAttributeInfo GetConnectorAttributeInfo(string name, #region SearchOp Members // implementation of SearchSpiOp - public Org.IdentityConnectors.Framework.Common.Objects.Filters.FilterTranslator CreateFilterTranslator(ObjectClass oclass, OperationOptions options) + public virtual Org.IdentityConnectors.Framework.Common.Objects.Filters.FilterTranslator CreateFilterTranslator(ObjectClass oclass, OperationOptions options) { return new ActiveDirectoryFilterTranslator(); } // implementation of SearchSpiOp - public void ExecuteQuery(ObjectClass oclass, string query, + public virtual void ExecuteQuery(ObjectClass oclass, string query, ResultsHandler handler, OperationOptions options) { try @@ -837,7 +837,7 @@ private void AddAttributeIfNotNull(ConnectorObjectBuilder builder, #region TestOp Members - public void Test() + public virtual void Test() { _configuration.Validate(); @@ -867,7 +867,7 @@ public void Test() #region AdvancedUpdateOp Members // implementation of AdvancedUpdateSpiOp - public Uid Update(UpdateType type, ObjectClass oclass, + public virtual Uid Update(UpdateType type, ObjectClass oclass, ICollection attributes, OperationOptions options) { Uid updatedUid = null; @@ -899,7 +899,7 @@ public Uid Update(UpdateType type, ObjectClass oclass, #region DeleteOp Members // implementation of DeleteSpiOp - public void Delete(ObjectClass objClass, Uid uid, OperationOptions options) + public virtual void Delete(ObjectClass objClass, Uid uid, OperationOptions options) { DirectoryEntry de = null; @@ -1015,7 +1015,7 @@ public bool SyncHandler(ConnectorObject obj) } } - public void Sync(ObjectClass objClass, SyncToken token, + public virtual void Sync(ObjectClass objClass, SyncToken token, SyncResultsHandler handler, OperationOptions options) { if (!ObjectClass.ACCOUNT.Equals(objClass)) @@ -1057,7 +1057,7 @@ public void Sync(ObjectClass objClass, SyncToken token, } - public SyncToken GetLatestSyncToken() + public virtual SyncToken GetLatestSyncToken() { string serverName = GetSyncServerName(); long highestCommittedUsn = 0; From 8246907d8a857e6f7de8c2a6e77b83a5547c0b80 Mon Sep 17 00:00:00 2001 From: dvernon Date: Mon, 13 Oct 2008 18:54:15 +0000 Subject: [PATCH 037/342] Issue #64 - Incremental checkin. Adding more messages to resource file, and cleaning up unused configuration setting. --- .../ActiveDirectoryConfiguration.cs | 4 - ActiveDirectoryConnector/Messages.resx | 76 +++++++++++++++++-- 2 files changed, 70 insertions(+), 10 deletions(-) diff --git a/ActiveDirectoryConnector/ActiveDirectoryConfiguration.cs b/ActiveDirectoryConnector/ActiveDirectoryConfiguration.cs index ec9ff2f9..9575c608 100644 --- a/ActiveDirectoryConnector/ActiveDirectoryConfiguration.cs +++ b/ActiveDirectoryConnector/ActiveDirectoryConfiguration.cs @@ -76,9 +76,6 @@ public String DirectoryAdminPassword public String ObjectClass { get; set; } - [ConfigurationProperty(Confidential = false, DisplayMessageKey = "display_UserProvidesPasswordOnChange", HelpMessageKey = "help_UserProvidesPasswordOnChange")] - public bool UserProvidesPasswordOnChange{get;set;} - [ConfigurationProperty(Confidential = false, DisplayMessageKey = "display_CreateHomeDirectory", HelpMessageKey = "help_CreateHomeDirectory")] public bool CreateHomeDirectory { get; set; } @@ -103,7 +100,6 @@ public ActiveDirectoryConfiguration() SearchContainer = ""; DirectoryAdminName = "cn=DirectoryAdmin"; ObjectClass = "User"; - UserProvidesPasswordOnChange = false; CreateHomeDirectory = false; SearchChildDomains = false; EncryptionType = "NONE"; diff --git a/ActiveDirectoryConnector/Messages.resx b/ActiveDirectoryConnector/Messages.resx index 1d02e8be..3933636d 100644 --- a/ActiveDirectoryConnector/Messages.resx +++ b/ActiveDirectoryConnector/Messages.resx @@ -120,16 +120,80 @@ Windows Active Directory - - Active Directory Hostname + + Create Home Directory + + + Directory Adminstrator's Account + + + Directory Administrator's Password + + + Domain Name + + + EncryptionType + + + Active Directory Domain Controller Hostname + + + Object class for User Objects + + + Search Child Domains + + + Search Container + + + ActiveSync Domain Controller - The hostname of the global catalog (used for sync) + ActiveSync Global Catalog Server + + + ActiveSync Search Context + + + Specify whether or not the home directory for the user will be created. - - Hostname of the Active Directory Domain Controller + + Enter the administrator user name with which the system should authenticate. The setting should be in the form 'domainname'\'username'. + + + Enter the password that should be used when authenticating. + + + Name of the windows domain (e.g. windowsdomain.mycompany.com) + + + + + + For cross-domain administration, enter the hostname, IP address, or domain name of the LDAP server. + + + Specify the class of object that will be managed on this resource. + + + Select if you want searches of Active Directory to include child domains. In addition, the Search Container and Sync Search Context attributes must be set to the top of the parent domain, e.g. DC=mydomain,DC=com. + + + Specify the container object from which users should be retrieved when loading from the resource. + +For example, if you want to retrieve users from the Users container, enter CN=Users, +DC=MYDOMAIN, +DC=COM. + + + Domain controller to use during active sync. Only used if not searching child domains. - Help texxt for display_SyncGlobalCatalogServer + Name of the global catalog server. This is needed only if searching child domains + + + Distinguished name of the object under which to search for changes during ActiveSync \ No newline at end of file From e2b7767fe184fa3e03a4ab35ab5faae7b71b1343 Mon Sep 17 00:00:00 2001 From: dvernon Date: Tue, 14 Oct 2008 19:51:26 +0000 Subject: [PATCH 038/342] Issue #64 - Incremental checkin. Changed the way Authenticate method does authentication, and separated random range of debug runs and release runs in the hopes of eliminating unit test failures in the build due to concurrent runs of both configurations. --- .../ActiveDirectoryConfiguration.cs | 5 --- .../ActiveDirectoryConnector.cs | 2 +- .../ActiveDirectoryConnector.csproj | 3 ++ .../ActiveDirectoryUtils.cs | 30 ++++++++++++++++- .../PasswordChangeHandler.cs | 32 +++++++++++++++--- .../ActiveDirectoryConnectorTest.cs | 33 ++++++++++++------- 6 files changed, 82 insertions(+), 23 deletions(-) diff --git a/ActiveDirectoryConnector/ActiveDirectoryConfiguration.cs b/ActiveDirectoryConnector/ActiveDirectoryConfiguration.cs index 9575c608..898c3729 100644 --- a/ActiveDirectoryConnector/ActiveDirectoryConfiguration.cs +++ b/ActiveDirectoryConnector/ActiveDirectoryConfiguration.cs @@ -86,10 +86,6 @@ public String SearchContainer [ConfigurationProperty(Confidential = false, DisplayMessageKey = "display_SearchChildDomains", HelpMessageKey = "help_SearchChildDomains")] public bool SearchChildDomains {get;set;} - [ConfigurationProperty(Confidential = false, DisplayMessageKey = "display_EncryptionType", HelpMessageKey = "help_EncryptionType")] - public String EncryptionType - { get; set; } - [ConfigurationProperty(Confidential = false, DisplayMessageKey = "display_LDAPHostName", HelpMessageKey = "help_LDAPHostName")] public String LDAPHostName { get; set; } @@ -102,7 +98,6 @@ public ActiveDirectoryConfiguration() ObjectClass = "User"; CreateHomeDirectory = false; SearchChildDomains = false; - EncryptionType = "NONE"; LDAPHostName = ""; } diff --git a/ActiveDirectoryConnector/ActiveDirectoryConnector.cs b/ActiveDirectoryConnector/ActiveDirectoryConnector.cs index ea742ff4..97529831 100644 --- a/ActiveDirectoryConnector/ActiveDirectoryConnector.cs +++ b/ActiveDirectoryConnector/ActiveDirectoryConnector.cs @@ -1171,7 +1171,7 @@ public void Authenticate(string username, Org.IdentityConnectors.Common.Security.GuardedString password, OperationOptions options) { - PasswordChangeHandler handler = new PasswordChangeHandler(); + PasswordChangeHandler handler = new PasswordChangeHandler(_configuration); String ldapEntryPath = ActiveDirectoryUtils.GetLDAPPath(_configuration.LDAPHostName, username); diff --git a/ActiveDirectoryConnector/ActiveDirectoryConnector.csproj b/ActiveDirectoryConnector/ActiveDirectoryConnector.csproj index 1ccf09e7..948dc06e 100644 --- a/ActiveDirectoryConnector/ActiveDirectoryConnector.csproj +++ b/ActiveDirectoryConnector/ActiveDirectoryConnector.csproj @@ -69,6 +69,9 @@ + + 3.5 + 3.5 diff --git a/ActiveDirectoryConnector/ActiveDirectoryUtils.cs b/ActiveDirectoryConnector/ActiveDirectoryUtils.cs index e07d7202..ff2216d1 100644 --- a/ActiveDirectoryConnector/ActiveDirectoryUtils.cs +++ b/ActiveDirectoryConnector/ActiveDirectoryUtils.cs @@ -50,6 +50,7 @@ using System.Security; using ActiveDs; using Org.IdentityConnectors.Common.Security; +using System.DirectoryServices.ActiveDirectory; namespace Org.IdentityConnectors.ActiveDirectory { @@ -226,7 +227,7 @@ internal void UpdateADObject(ObjectClass oclass, if (gsNewPassword != null) { GuardedString gsCurrentPassword = ConnectorAttributeUtil.GetCurrentPasswordValue(attributes); - PasswordChangeHandler changeHandler = new PasswordChangeHandler(); + PasswordChangeHandler changeHandler = new PasswordChangeHandler(_configuration); if (gsCurrentPassword == null) { // just a normal password change @@ -590,6 +591,33 @@ internal static string GetDnFromPath(string fullPath) return pathName.Retrieve((int)ADS_FORMAT_ENUM.ADS_FORMAT_X500_DN); } + internal static DomainController GetDomainController(ActiveDirectoryConfiguration configuration) + { + String serverName = configuration.LDAPHostName; + DomainController controller = null; + + if ((serverName == null) || (serverName.Length == 0)) + { + // get the active directory schema + DirectoryContext context = new DirectoryContext( + DirectoryContextType.Domain, + configuration.DomainName, + configuration.DirectoryAdminName, + configuration.DirectoryAdminPassword); + controller = DomainController.FindOne(context); + } + else + { + DirectoryContext context = new DirectoryContext( + DirectoryContextType.DirectoryServer, + configuration.LDAPHostName, + configuration.DirectoryAdminName, + configuration.DirectoryAdminPassword); + controller = DomainController.GetDomainController(context); + } + + return controller; + } } } diff --git a/ActiveDirectoryConnector/PasswordChangeHandler.cs b/ActiveDirectoryConnector/PasswordChangeHandler.cs index 061f16ad..fdb27ac1 100644 --- a/ActiveDirectoryConnector/PasswordChangeHandler.cs +++ b/ActiveDirectoryConnector/PasswordChangeHandler.cs @@ -46,9 +46,12 @@ using Org.IdentityConnectors.Common.Security; using ActiveDs; using Org.IdentityConnectors.Framework.Common.Exceptions; +using System.DirectoryServices.AccountManagement; +using System.DirectoryServices.ActiveDirectory; namespace Org.IdentityConnectors.ActiveDirectory { + /** * This class will decrypt passwords, and handle * authentication and password changes (both @@ -58,9 +61,11 @@ internal class PasswordChangeHandler { String _currentPassword; String _newPassword; + ActiveDirectoryConfiguration _configuration = null; - internal PasswordChangeHandler() + internal PasswordChangeHandler(ActiveDirectoryConfiguration configuration) { + _configuration = configuration; } /// @@ -148,10 +153,27 @@ internal void Authenticate(DirectoryEntry directoryEntry, string username, sAMAccountName, _currentPassword); try { - // this will cause a bind. Maybe there is a better - // way to do this. If you know of one, please change - // this, as this seems a little weird - userDe.RefreshCache(); + string serverName = _configuration.LDAPHostName; + PrincipalContext context = null; + if ((serverName == null) || (serverName.Length == 0)) + { + DomainController domainController = ActiveDirectoryUtils.GetDomainController(_configuration); + context = new PrincipalContext(ContextType.Domain, + domainController.Domain.Name); + } + else + { + context = new PrincipalContext(ContextType.Machine, _configuration.LDAPHostName); + } + + if (context == null) + { + throw new ConnectorException("Unable to get PrincipalContext"); + } + + if(!context.ValidateCredentials(sAMAccountName, _currentPassword)) { + throw new InvalidCredentialException(); + } } catch (Exception e) { diff --git a/ActiveDirectoryConnectorTests/ActiveDirectoryConnectorTest.cs b/ActiveDirectoryConnectorTests/ActiveDirectoryConnectorTest.cs index 512b6038..36881b96 100644 --- a/ActiveDirectoryConnectorTests/ActiveDirectoryConnectorTest.cs +++ b/ActiveDirectoryConnectorTests/ActiveDirectoryConnectorTest.cs @@ -61,6 +61,8 @@ namespace Org.IdentityConnectors.ActiveDirectory [TestFixture] public class ActiveDirectoryConnectorTest { + Random _rand = new Random(); + public static readonly string CONFIG_PROPERTY_USER = "config_user"; public static readonly string CONFIG_PROPERTY_PASSWORD = "config_password"; public static readonly string CONFIG_PROPERTY_HOST = "config_host"; @@ -808,8 +810,7 @@ public void TestSearchByRegularAttributeWithWildcard_account() try { - Random random = new Random(); - Int32 randomNumber = random.Next(1000000); + int randomNumber = GetRandomNumber(); String snPrefix = "nunitWCTest"; @@ -819,7 +820,7 @@ public void TestSearchByRegularAttributeWithWildcard_account() GetNormalAttributes_Account(); attributes.Remove(ConnectorAttributeUtil.Find("sn", attributes)); attributes.Add(ConnectorAttributeBuilder.Build("sn", - snPrefix + random.Next())); + snPrefix + GetRandomNumber())); Uid tempUid = CreateAndVerifyObject(connector, ObjectClass.ACCOUNT, attributes); Assert.IsNotNull(tempUid); @@ -924,6 +925,7 @@ public void Test_OpAtt_Enabled_Account() } // Test scripting + [Ignore] // ignoring until issue # 302 is addressed [Test] public void TestScriptOnResource() { @@ -1007,8 +1009,7 @@ public void TestRemoveAttributeValue() try { - Random random = new Random(); - Int32 randomNumber = random.Next(1000000); + int randomNumber = GetRandomNumber(); ICollection attributes = new HashSet(); attributes.Add(ConnectorAttributeBuilder.Build( @@ -1856,8 +1857,7 @@ public Configuration GetConfiguration() public ICollection GetNormalAttributes_Account() { ICollection attributes = new HashSet(); - Random random = new Random(); - Int32 randomNumber = random.Next(10000); + int randomNumber = GetRandomNumber(); // the container ... is a fabricated attribute attributes.Add(ConnectorAttributeBuilder.Build( @@ -1885,8 +1885,7 @@ public ICollection GetNormalAttributes_Account() public ICollection GetAllAttributes_Account() { ICollection attributes = new HashSet(); - Random random = new Random(); - Int32 randomNumber = random.Next(10000); + int randomNumber = GetRandomNumber(); // the container ... is a fabricated attribute attributes.Add(ConnectorAttributeBuilder.Build( @@ -2064,8 +2063,7 @@ public ICollection GetAllAttributes_Account() public ICollection GetNormalAttributes_Group() { ICollection attributes = new List(); - Random random = new Random(); - Int32 randomNumber = random.Next(10000); + int randomNumber = GetRandomNumber(); attributes.Add(ConnectorAttributeBuilder.Build( "mail", "groupmail@example.com")); @@ -2229,6 +2227,19 @@ public GuardedString GetGuardedString(string regularString) } return guardedString; } + + int GetRandomNumber() + { + const int randomRange = 100000; + int number = _rand.Next(randomRange); +#if DEBUG + // make sure the debug numbers are in a different + // range than release ones to eliminate conflicts during + // the build where both configurations are run concurrently + number += randomRange; +#endif + return number; + } } } From 456bbfa46185285dcb8e270b64803a539c531ad3 Mon Sep 17 00:00:00 2001 From: tthompson29 Date: Tue, 14 Oct 2008 21:37:33 +0000 Subject: [PATCH 039/342] added ADConnectorTests to main solution file --- .../ActiveDirectoryConnectorTests.csproj | 228 +++++++++--------- Connector Gateway.sln | 72 +----- 2 files changed, 119 insertions(+), 181 deletions(-) diff --git a/ActiveDirectoryConnectorTests/ActiveDirectoryConnectorTests.csproj b/ActiveDirectoryConnectorTests/ActiveDirectoryConnectorTests.csproj index 96a561be..be5c653c 100644 --- a/ActiveDirectoryConnectorTests/ActiveDirectoryConnectorTests.csproj +++ b/ActiveDirectoryConnectorTests/ActiveDirectoryConnectorTests.csproj @@ -36,116 +36,120 @@ "Portions Copyrighted [year] [name of copyright owner]" --> - - Debug - AnyCPU - 9.0.21022 - 2.0 - {DB8F7DA9-11A2-4BEB-8C14-25D8229EBE7E} - Library - Properties - Sun.OpenConnectors.ActiveDirectory - ActiveDirectoryConnectorTests - v3.5 - 512 - - - - - true - full - false - bin\Debug\ - DEBUG;TRACE - prompt - 4 - - - pdbonly - true - bin\Release\ - TRACE - prompt - 4 - - - Project - - - - - - - - - - - - - - - - - - - - - - - - - 3.5 - - - - 3.5 - - - 3.5 - - - - - - - - - - - {BDF495CA-0FCD-4E51-A871-D467CDE3B43E} - ActiveDirectoryConnector - - - {F140E8DA-52B4-4159-992A-9DA10EA8EEFB} - Common - - - {8B24461B-456A-4032-89A1-CD418F7B5B62} - Framework - - - - - Always - - - - - - - - - - - - - - - - - - copy "$(ProjectDir)\..\FrameworkInternal\bin\$(Configuration)\FrameworkInternal.dll" "$(ProjectDir)\$(OutDir)"; - copy "$(ProjectDir)\..\ShellScriptExecutorFactory\bin\$(Configuration)\Shell.ScriptExecutorFactory.dll" "$(ProjectDir)\$(OutDir)"; - - + + Debug + AnyCPU + 9.0.21022 + 2.0 + {DB8F7DA9-11A2-4BEB-8C14-25D8229EBE7E} + Library + Properties + Sun.OpenConnectors.ActiveDirectory + ActiveDirectoryConnectorTests + v3.5 + 512 + + + + + true + full + false + bin\Debug\ + DEBUG;TRACE + prompt + 4 + + + pdbonly + true + bin\Release\ + TRACE + prompt + 4 + + + Project + + + + + + + + + + + + + + + + + + + + + + + + + 3.5 + + + + 3.5 + + + 3.5 + + + + + + + + + + + {BDF495CA-0FCD-4E51-A871-D467CDE3B43E} + ActiveDirectoryConnector + + + {F140E8DA-52B4-4159-992A-9DA10EA8EEFB} + Common + + + {8B24461B-456A-4032-89A1-CD418F7B5B62} + Framework + + + {5B011775-B121-4EEE-A410-BA2D2F5BFB8B} + FrameworkInternal + + + {4700690A-2D29-40A0-86AC-E5A9F71A479A} + ShellScriptExecutorFactory + + + + + Always + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/Connector Gateway.sln b/Connector Gateway.sln index 96414aaf..14b67eb8 100644 --- a/Connector Gateway.sln +++ b/Connector Gateway.sln @@ -1,7 +1,7 @@  Microsoft Visual Studio Solution File, Format Version 10.00 # Visual Studio 2008 -# SharpDevelop 3.0.0.2970 +# SharpDevelop 3.0.0.3437 Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Service", "Service\Service.csproj", "{A9D6374A-D51F-4FA3-8C02-5B1D23FAA82E}" EndProject Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Framework", "Framework\Framework.csproj", "{8B24461B-456A-4032-89A1-CD418F7B5B62}" @@ -26,6 +26,8 @@ Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "ShellScriptExecutorFactory" EndProject Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "ActiveDirectoryConnector", "ActiveDirectoryConnector\ActiveDirectoryConnector.csproj", "{BDF495CA-0FCD-4E51-A871-D467CDE3B43E}" EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "ActiveDirectoryConnectorTests", "ActiveDirectoryConnectorTests\ActiveDirectoryConnectorTests.csproj", "{DB8F7DA9-11A2-4BEB-8C14-25D8229EBE7E}" +EndProject Global GlobalSection(SolutionConfigurationPlatforms) = preSolution Debug|Any CPU = Debug|Any CPU @@ -38,40 +40,16 @@ Global {A9D6374A-D51F-4FA3-8C02-5B1D23FAA82E}.Release.ActiveCfg = Release {A9D6374A-D51F-4FA3-8C02-5B1D23FAA82E}.Release.Build.0 = Release {A9D6374A-D51F-4FA3-8C02-5B1D23FAA82E}.Release|Any CPU.ActiveCfg = Release|Any CPU - {BCD7E3AD-1CF9-48A6-8FE4-C81E84B87CC1}.Debug.ActiveCfg = Debug - {BCD7E3AD-1CF9-48A6-8FE4-C81E84B87CC1}.Debug.Build.0 = Debug - {BCD7E3AD-1CF9-48A6-8FE4-C81E84B87CC1}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {BCD7E3AD-1CF9-48A6-8FE4-C81E84B87CC1}.Release.ActiveCfg = Release - {BCD7E3AD-1CF9-48A6-8FE4-C81E84B87CC1}.Release.Build.0 = Release - {BCD7E3AD-1CF9-48A6-8FE4-C81E84B87CC1}.Release|Any CPU.ActiveCfg = Release|Any CPU {D09E6A1F-55F6-4661-8E5A-C485E4B08BB3}.Debug.ActiveCfg = Debug {D09E6A1F-55F6-4661-8E5A-C485E4B08BB3}.Debug.Build.0 = Debug {D09E6A1F-55F6-4661-8E5A-C485E4B08BB3}.Debug|Any CPU.ActiveCfg = Debug|Any CPU {D09E6A1F-55F6-4661-8E5A-C485E4B08BB3}.Release.ActiveCfg = Release {D09E6A1F-55F6-4661-8E5A-C485E4B08BB3}.Release.Build.0 = Release {D09E6A1F-55F6-4661-8E5A-C485E4B08BB3}.Release|Any CPU.ActiveCfg = Release|Any CPU - {82DC8049-B0E4-4FA5-98D3-80C58A9B97F2}.Debug|Any CPU.Build.0 = Debug|Any CPU - {82DC8049-B0E4-4FA5-98D3-80C58A9B97F2}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {82DC8049-B0E4-4FA5-98D3-80C58A9B97F2}.Release|Any CPU.Build.0 = Release|Any CPU - {82DC8049-B0E4-4FA5-98D3-80C58A9B97F2}.Release|Any CPU.ActiveCfg = Release|Any CPU - {B5C52AB5-CE4F-4165-AFC6-4680F162B8DA}.Debug|Any CPU.Build.0 = Debug|Any CPU - {B5C52AB5-CE4F-4165-AFC6-4680F162B8DA}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {B5C52AB5-CE4F-4165-AFC6-4680F162B8DA}.Release|Any CPU.Build.0 = Release|Any CPU - {B5C52AB5-CE4F-4165-AFC6-4680F162B8DA}.Release|Any CPU.ActiveCfg = Release|Any CPU - {BCD7E3AD-1CF9-48A6-8FE4-C81E84B87CC1}.Debug|Any CPU.Build.0 = Debug|Any CPU - {BCD7E3AD-1CF9-48A6-8FE4-C81E84B87CC1}.Release|Any CPU.Build.0 = Release|Any CPU {D09E6A1F-55F6-4661-8E5A-C485E4B08BB3}.Debug|Any CPU.Build.0 = Debug|Any CPU {D09E6A1F-55F6-4661-8E5A-C485E4B08BB3}.Release|Any CPU.Build.0 = Release|Any CPU {A9D6374A-D51F-4FA3-8C02-5B1D23FAA82E}.Debug|Any CPU.Build.0 = Debug|Any CPU {A9D6374A-D51F-4FA3-8C02-5B1D23FAA82E}.Release|Any CPU.Build.0 = Release|Any CPU - {2420D04D-8EA0-4D5E-BB6D-4FEAC74F0D01}.Debug.Build.0 = Debug - {2420D04D-8EA0-4D5E-BB6D-4FEAC74F0D01}.Debug.ActiveCfg = Debug - {2420D04D-8EA0-4D5E-BB6D-4FEAC74F0D01}.Release.Build.0 = Release - {2420D04D-8EA0-4D5E-BB6D-4FEAC74F0D01}.Release.ActiveCfg = Release - {2420D04D-8EA0-4D5E-BB6D-4FEAC74F0D01}.Debug|Any CPU.Build.0 = Debug|Any CPU - {2420D04D-8EA0-4D5E-BB6D-4FEAC74F0D01}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {2420D04D-8EA0-4D5E-BB6D-4FEAC74F0D01}.Release|Any CPU.Build.0 = Release|Any CPU - {2420D04D-8EA0-4D5E-BB6D-4FEAC74F0D01}.Release|Any CPU.ActiveCfg = Release|Any CPU {8B24461B-456A-4032-89A1-CD418F7B5B62}.Debug.Build.0 = Debug {8B24461B-456A-4032-89A1-CD418F7B5B62}.Debug.ActiveCfg = Debug {8B24461B-456A-4032-89A1-CD418F7B5B62}.Release.Build.0 = Release @@ -96,58 +74,14 @@ Global {3E737796-3A83-4924-9FF1-DC542F21F59E}.Debug|Any CPU.ActiveCfg = Debug|Any CPU {3E737796-3A83-4924-9FF1-DC542F21F59E}.Release|Any CPU.Build.0 = Release|Any CPU {3E737796-3A83-4924-9FF1-DC542F21F59E}.Release|Any CPU.ActiveCfg = Release|Any CPU - 3F54A749-E66F-4034-88A6-5B01DEA03909.Debug|Any CPU.Build.0 = Debug|Any CPU - 3F54A749-E66F-4034-88A6-5B01DEA03909.Debug|Any CPU.ActiveCfg = Debug|Any CPU - 3F54A749-E66F-4034-88A6-5B01DEA03909.Release|Any CPU.Build.0 = Release|Any CPU - 3F54A749-E66F-4034-88A6-5B01DEA03909.Release|Any CPU.ActiveCfg = Release|Any CPU - 4F63241B-68F8-45A5-B8DA-9A331D933ECC.Debug|Any CPU.Build.0 = Debug|Any CPU - 4F63241B-68F8-45A5-B8DA-9A331D933ECC.Debug|Any CPU.ActiveCfg = Debug|Any CPU - 4F63241B-68F8-45A5-B8DA-9A331D933ECC.Release|Any CPU.Build.0 = Release|Any CPU - 4F63241B-68F8-45A5-B8DA-9A331D933ECC.Release|Any CPU.ActiveCfg = Release|Any CPU - {B50CB653-E406-42E5-97F9-7CFCAB240D26}.Debug|Any CPU.Build.0 = Debug|Any CPU - {B50CB653-E406-42E5-97F9-7CFCAB240D26}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {B50CB653-E406-42E5-97F9-7CFCAB240D26}.Release|Any CPU.Build.0 = Release|Any CPU - {B50CB653-E406-42E5-97F9-7CFCAB240D26}.Release|Any CPU.ActiveCfg = Release|Any CPU {1CBA8F74-050C-432B-8437-08BD13BDC684}.Debug|Any CPU.Build.0 = Debug|Any CPU {1CBA8F74-050C-432B-8437-08BD13BDC684}.Debug|Any CPU.ActiveCfg = Debug|Any CPU {1CBA8F74-050C-432B-8437-08BD13BDC684}.Release|Any CPU.Build.0 = Release|Any CPU {1CBA8F74-050C-432B-8437-08BD13BDC684}.Release|Any CPU.ActiveCfg = Release|Any CPU - {B0D1B8FA-EFEC-437C-A54D-8EC570B3781F}.Debug|Any CPU.Build.0 = Debug|Any CPU - {B0D1B8FA-EFEC-437C-A54D-8EC570B3781F}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {B0D1B8FA-EFEC-437C-A54D-8EC570B3781F}.Release|Any CPU.Build.0 = Release|Any CPU - {B0D1B8FA-EFEC-437C-A54D-8EC570B3781F}.Release|Any CPU.ActiveCfg = Release|Any CPU - {028822D2-4309-41AE-A91C-97E895FA024B}.Debug|Any CPU.Build.0 = Debug|Any CPU - {028822D2-4309-41AE-A91C-97E895FA024B}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {028822D2-4309-41AE-A91C-97E895FA024B}.Release|Any CPU.Build.0 = Release|Any CPU - {028822D2-4309-41AE-A91C-97E895FA024B}.Release|Any CPU.ActiveCfg = Release|Any CPU - {C8D6EEBE-FA37-4154-81E2-A7E469D0461A}.Debug|Any CPU.Build.0 = Debug|Any CPU - {C8D6EEBE-FA37-4154-81E2-A7E469D0461A}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {C8D6EEBE-FA37-4154-81E2-A7E469D0461A}.Release|Any CPU.Build.0 = Release|Any CPU - {C8D6EEBE-FA37-4154-81E2-A7E469D0461A}.Release|Any CPU.ActiveCfg = Release|Any CPU - {0B3DF008-F9B5-46FF-BF12-05037A4DE359}.Debug|Any CPU.Build.0 = Debug|Any CPU - {0B3DF008-F9B5-46FF-BF12-05037A4DE359}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {0B3DF008-F9B5-46FF-BF12-05037A4DE359}.Release|Any CPU.Build.0 = Release|Any CPU - {0B3DF008-F9B5-46FF-BF12-05037A4DE359}.Release|Any CPU.ActiveCfg = Release|Any CPU - {CE006C46-4B5C-4E6C-BAA3-EAD79C38B9D9}.Debug|Any CPU.Build.0 = Debug|Any CPU - {CE006C46-4B5C-4E6C-BAA3-EAD79C38B9D9}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {CE006C46-4B5C-4E6C-BAA3-EAD79C38B9D9}.Release|Any CPU.Build.0 = Release|Any CPU - {CE006C46-4B5C-4E6C-BAA3-EAD79C38B9D9}.Release|Any CPU.ActiveCfg = Release|Any CPU {5B011775-B121-4EEE-A410-BA2D2F5BFB8B}.Debug|Any CPU.Build.0 = Debug|Any CPU {5B011775-B121-4EEE-A410-BA2D2F5BFB8B}.Debug|Any CPU.ActiveCfg = Debug|Any CPU {5B011775-B121-4EEE-A410-BA2D2F5BFB8B}.Release|Any CPU.Build.0 = Release|Any CPU {5B011775-B121-4EEE-A410-BA2D2F5BFB8B}.Release|Any CPU.ActiveCfg = Release|Any CPU - {2583369C-4DBF-4578-8350-AF454D8136CF}.Debug|Any CPU.Build.0 = Debug|Any CPU - {2583369C-4DBF-4578-8350-AF454D8136CF}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {2583369C-4DBF-4578-8350-AF454D8136CF}.Release|Any CPU.Build.0 = Release|Any CPU - {2583369C-4DBF-4578-8350-AF454D8136CF}.Release|Any CPU.ActiveCfg = Release|Any CPU - {F2AE3129-23A0-4C02-8DEA-E839D4DF4ACA}.Debug|Any CPU.Build.0 = Debug|Any CPU - {F2AE3129-23A0-4C02-8DEA-E839D4DF4ACA}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {F2AE3129-23A0-4C02-8DEA-E839D4DF4ACA}.Release|Any CPU.Build.0 = Release|Any CPU - {F2AE3129-23A0-4C02-8DEA-E839D4DF4ACA}.Release|Any CPU.ActiveCfg = Release|Any CPU - {5BCC455D-0E5F-46C6-8E54-812FD4AF11BE}.Debug|Any CPU.Build.0 = Debug|Any CPU - {5BCC455D-0E5F-46C6-8E54-812FD4AF11BE}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {5BCC455D-0E5F-46C6-8E54-812FD4AF11BE}.Release|Any CPU.Build.0 = Release|Any CPU - {5BCC455D-0E5F-46C6-8E54-812FD4AF11BE}.Release|Any CPU.ActiveCfg = Release|Any CPU {0747C440-70E4-4E63-9F9D-03B3A010C991}.Debug|Any CPU.Build.0 = Debug|Any CPU {0747C440-70E4-4E63-9F9D-03B3A010C991}.Debug|Any CPU.ActiveCfg = Debug|Any CPU {0747C440-70E4-4E63-9F9D-03B3A010C991}.Release|Any CPU.Build.0 = Release|Any CPU From 0f53f45aa3b9af6aa67b6f59f03a4ac3b203faa6 Mon Sep 17 00:00:00 2001 From: dvernon Date: Wed, 15 Oct 2008 14:13:27 +0000 Subject: [PATCH 040/342] Issue #64 - Incremental checkin. Reverting way Authenticate method does authentication because it doesnt work on the build machine (and who knows where else), but the old way works everywhere. Added internationalization of certain exceptions. --- .../ActiveDirectoryConnector.cs | 43 +++++++++++++------ .../ActiveDirectoryUtils.cs | 23 +++++++--- ActiveDirectoryConnector/Messages.resx | 42 +++++++++++++++--- .../PasswordChangeHandler.cs | 18 ++++++-- .../ActiveDirectoryConnectorTest.cs | 7 ++- 5 files changed, 102 insertions(+), 31 deletions(-) diff --git a/ActiveDirectoryConnector/ActiveDirectoryConnector.cs b/ActiveDirectoryConnector/ActiveDirectoryConnector.cs index 97529831..a6fc9af5 100644 --- a/ActiveDirectoryConnector/ActiveDirectoryConnector.cs +++ b/ActiveDirectoryConnector/ActiveDirectoryConnector.cs @@ -197,12 +197,15 @@ public virtual Uid Create(ObjectClass oclass, Trace.TraceInformation("Create method"); if (_configuration == null) { - throw new ConfigurationException("Connector has not been configured"); + throw new ConfigurationException(_configuration.ConnectorMessages.Format( + "ex_ConnectorNotConfigured", "Connector has not been configured")); } Name nameAttribute = ConnectorAttributeUtil.GetNameFromAttributes(attributes); if (nameAttribute == null) { - throw new ConnectorException("The name operational attribute cannot be null"); + throw new ConnectorException( + _configuration.ConnectorMessages.Format("ex_OperationalAttributeNull", + "The name operational attribute cannot be null")); } String ldapContainerPath = ActiveDirectoryUtils.GetLDAPPath(_configuration.LDAPHostName, @@ -224,7 +227,7 @@ public virtual Uid Create(ObjectClass oclass, _configuration.DirectoryAdminName, _configuration.DirectoryAdminPassword); DirectoryEntry newDe = containerDe.Children.Add( ActiveDirectoryUtils.GetRelativeName(nameAttribute), - ActiveDirectoryUtils.GetADObjectClass(oclass)); + _utils.GetADObjectClass(oclass)); newDe.CommitChanges(); @@ -631,13 +634,13 @@ private void ExecuteQuery(ObjectClass oclass, string query, if (query == null) { fullQueryBuilder.Append("(objectclass="); - fullQueryBuilder.Append(ActiveDirectoryUtils.GetADObjectClass(oclass)); + fullQueryBuilder.Append(_utils.GetADObjectClass(oclass)); fullQueryBuilder.Append(")"); } else { fullQueryBuilder.Append("(&(objectclass="); - fullQueryBuilder.Append(ActiveDirectoryUtils.GetADObjectClass(oclass)); + fullQueryBuilder.Append(_utils.GetADObjectClass(oclass)); fullQueryBuilder.Append(")"); fullQueryBuilder.Append(query); fullQueryBuilder.Append(")"); @@ -857,7 +860,9 @@ public virtual void Test() if ((!objectFound) || (ADSchemaClass == null)) { throw new ConnectorException( - String.Format("Invalid Object Class was specified in the connector configuration. Object Class \'{0}\' was not found in Active Directory", + _configuration.ConnectorMessages.Format( + "ex_InvalidObjectClassInConfiguration", + "Invalid Object Class was specified in the connector configuration. Object Class \'{0}\' was not found in Active Directory", _configuration.ObjectClass)); } } @@ -875,13 +880,15 @@ public virtual Uid Update(UpdateType type, ObjectClass oclass, Trace.TraceInformation("Update method"); if (_configuration == null) { - throw new ConfigurationException("Connector has not been configured"); + throw new ConfigurationException(_configuration.ConnectorMessages.Format( + "ex_ConnectorNotConfigured", "Connector has not been configured")); } updatedUid = ConnectorAttributeUtil.GetUidAttribute(attributes); if (updatedUid == null) { - throw new ConnectorException("Uid was not present in attributes to be modified"); + throw new ConnectorException(_configuration.ConnectorMessages.Format( + "ex_UIDNotPresent", "Uid was not present")); } DirectoryEntry updateEntry = @@ -921,7 +928,9 @@ public virtual void Delete(ObjectClass objClass, Uid uid, OperationOptions optio } else { - throw new ConnectorException(String.Format("Delete is not supported for ObjectClass {0}", objClass.GetObjectClassValue())); + throw new ConnectorException(_configuration.ConnectorMessages.Format( + "ex_DeleteNotSupported", "Delete is not supported for ObjectClass {0}", + objClass.GetObjectClassValue())); } } @@ -964,11 +973,13 @@ public class SyncResults { SyncResultsHandler _syncResultsHandler; ActiveDirectorySyncToken _adSyncToken; + ActiveDirectoryConfiguration _configuration; internal SyncResults(SyncResultsHandler syncResultsHandler, - ActiveDirectorySyncToken adSyncToken) { + ActiveDirectorySyncToken adSyncToken, ActiveDirectoryConfiguration configuration) { _syncResultsHandler = syncResultsHandler; _adSyncToken = adSyncToken; + _configuration = configuration; } public bool SyncHandler(ConnectorObject obj) @@ -978,8 +989,9 @@ public bool SyncHandler(ConnectorObject obj) ConnectorAttribute tokenAttr = ConnectorAttributeUtil.Find(ATT_USN_CHANGED, obj.GetAttributes()); if(tokenAttr == null) { - string msg = String.Format("Attribute {0} is not present " + - "in connector object. Cannot proceed with synchronization", ATT_USN_CHANGED); + string msg = _configuration.ConnectorMessages.Format("ex_missingSyncAttribute", + "Attribute {0} is not present in connector object. Cannot proceed with Synchronization", + ATT_USN_CHANGED); Trace.TraceError(msg); throw new ConnectorException(msg); } @@ -1020,7 +1032,10 @@ public virtual void Sync(ObjectClass objClass, SyncToken token, { if (!ObjectClass.ACCOUNT.Equals(objClass)) { - throw new ConnectorException(String.Format("Sync operation is not available for ObjectClass {0}", objClass.GetObjectClassValue())); + throw new ConnectorException(_configuration.ConnectorMessages.Format( + "ex_SyncNotAvailable", + "Sync operation is not available for ObjectClass {0}", + objClass.GetObjectClassValue())); } String serverName = GetSyncServerName(); @@ -1032,7 +1047,7 @@ public virtual void Sync(ObjectClass objClass, SyncToken token, string deletedQuery = GetSyncDeleteQuery(adSyncToken); OperationOptionsBuilder builder = new OperationOptionsBuilder(); - SyncResults syncResults = new SyncResults(handler, adSyncToken); + SyncResults syncResults = new SyncResults(handler, adSyncToken, _configuration); // find modified usn's ExecuteQuery(objClass, modifiedQuery, syncResults.SyncHandler, builder.Build(), diff --git a/ActiveDirectoryConnector/ActiveDirectoryUtils.cs b/ActiveDirectoryConnector/ActiveDirectoryUtils.cs index ff2216d1..34398c28 100644 --- a/ActiveDirectoryConnector/ActiveDirectoryUtils.cs +++ b/ActiveDirectoryConnector/ActiveDirectoryUtils.cs @@ -267,7 +267,8 @@ internal void UpdateADObject(ObjectClass oclass, else { throw new ConnectorException( - String.Format("Invalid object class: {0}", oclass.GetObjectClassValue())); + _configuration.ConnectorMessages.Format("ex_InvalidObjectClass", + "Invalid object class: {0}", oclass.GetObjectClassValue())); } } @@ -277,7 +278,9 @@ internal ConnectorAttribute GetConnectorAttributeFromADEntry(ObjectClass oclass, // Boolean translated = false; if (searchResult == null) { - throw new ConnectorException("Could not add connector attribute to search result"); + throw new ConnectorException(_configuration.ConnectorMessages.Format( + "ex_AttributeNull", + "Could not add connector attribute to search result")); } return _customHandlers.GetCaFromDe(oclass, @@ -292,7 +295,9 @@ internal void AddConnectorAttributeToADProperties(ObjectClass oclass, // Boolean translated = false; if (directoryEntry == null) { - throw new ConnectorException("Could not add connector attribute to directory entry"); + throw new ConnectorException(_configuration.ConnectorMessages.Format( + "ex_CouldNotAddNullAttributeToDe", + "Could not add connector attribute to directory entry")); } _customHandlers.UpdateDeFromCa(oclass, type, @@ -380,7 +385,7 @@ private static void AddConnectorAttributeToADProperties_general( /// /// /// - internal static Object GetSingleValue(PropertyValueCollection pvc) + internal Object GetSingleValue(PropertyValueCollection pvc) { if((pvc == null) || (pvc.Count == 0)) { @@ -389,7 +394,9 @@ internal static Object GetSingleValue(PropertyValueCollection pvc) if (pvc.Count > 1) { - String msg = String.Format("Expecting single value, but found multiple values for attribute {0}", + String msg = _configuration.ConnectorMessages.Format( + "ex_ExpectingSingleValue", + "Expecting single value, but found multiple values for attribute {0}", pvc.PropertyName); throw new ConnectorException(msg); } @@ -424,7 +431,7 @@ internal static DirectoryEntry GetDirectoryEntryFromUid(String serverName, /// /// /// - internal static String GetADObjectClass(ObjectClass oclass) + internal String GetADObjectClass(ObjectClass oclass) { if (oclass.Equals(ObjectClass.ACCOUNT)) @@ -441,7 +448,9 @@ internal static String GetADObjectClass(ObjectClass oclass) } else { - String msg = String.Format("ObjectClass \'{0}\' is not valid for this connector", + String msg = _configuration.ConnectorMessages.Format( + "ex_ObjectClassInvalidForConnector", + "ObjectClass \'{0}\' is not valid for this connector", oclass.GetObjectClassValue()); throw new ConnectorException(msg); } diff --git a/ActiveDirectoryConnector/Messages.resx b/ActiveDirectoryConnector/Messages.resx index 3933636d..950b269d 100644 --- a/ActiveDirectoryConnector/Messages.resx +++ b/ActiveDirectoryConnector/Messages.resx @@ -132,9 +132,6 @@ Domain Name - - EncryptionType - Active Directory Domain Controller Hostname @@ -168,9 +165,6 @@ Name of the windows domain (e.g. windowsdomain.mycompany.com) - - - For cross-domain administration, enter the hostname, IP address, or domain name of the LDAP server. @@ -196,4 +190,40 @@ DC=COM. Distinguished name of the object under which to search for changes during ActiveSync + + Connector has not been configured + + + Delete is not supported for ObjectClass {0} + + + Invalid object class: {0} + + + Attribute {0} is not present in connector object. Cannot proceed with synchronization + + + The name operational attribute cannot be null + + + Sync operation is not available for ObjectClass {0} + + + Uid was not present + + + Invalid Object Class was specified in the connector configuration. Object Class \'{0}\' was not found in Active Directory + + + Could not add connector attribute to <null> search result + + + Could not add connector attribute to <null> directory entry + + + Expecting single value, but found multiple values for attribute {0} + + + ObjectClass \'{0}\' is not valid for this connector + \ No newline at end of file diff --git a/ActiveDirectoryConnector/PasswordChangeHandler.cs b/ActiveDirectoryConnector/PasswordChangeHandler.cs index fdb27ac1..490c768b 100644 --- a/ActiveDirectoryConnector/PasswordChangeHandler.cs +++ b/ActiveDirectoryConnector/PasswordChangeHandler.cs @@ -150,9 +150,17 @@ internal void Authenticate(DirectoryEntry directoryEntry, string username, String sAMAccountName = (String)directoryEntry.Properties[ActiveDirectoryConnector.ATT_SAMACCOUNT_NAME][0]; DirectoryEntry userDe = new DirectoryEntry(directoryEntry.Path, - sAMAccountName, _currentPassword); + sAMAccountName, _currentPassword); + try { + userDe.RefreshCache(); + + /* + * + * This seems like the right way to do this ... but it doesn't work + * everywhere, so I'm reverting to creating a directory entry and doing + * a refreshcashe to force a bind. string serverName = _configuration.LDAPHostName; PrincipalContext context = null; if ((serverName == null) || (serverName.Length == 0)) @@ -163,7 +171,9 @@ internal void Authenticate(DirectoryEntry directoryEntry, string username, } else { - context = new PrincipalContext(ContextType.Machine, _configuration.LDAPHostName); + context = new PrincipalContext(ContextType.Machine, + _configuration.LDAPHostName, _configuration.DirectoryAdminName, + _configuration.DirectoryAdminPassword); } if (context == null) @@ -171,9 +181,11 @@ internal void Authenticate(DirectoryEntry directoryEntry, string username, throw new ConnectorException("Unable to get PrincipalContext"); } - if(!context.ValidateCredentials(sAMAccountName, _currentPassword)) { + if (!context.ValidateCredentials(sAMAccountName, _currentPassword)) + { throw new InvalidCredentialException(); } + */ } catch (Exception e) { diff --git a/ActiveDirectoryConnectorTests/ActiveDirectoryConnectorTest.cs b/ActiveDirectoryConnectorTests/ActiveDirectoryConnectorTest.cs index 36881b96..797ed813 100644 --- a/ActiveDirectoryConnectorTests/ActiveDirectoryConnectorTest.cs +++ b/ActiveDirectoryConnectorTests/ActiveDirectoryConnectorTest.cs @@ -84,6 +84,7 @@ public class ActiveDirectoryConnectorTest [Test] public void TestConfiguration() { ActiveDirectoryConfiguration config = new ActiveDirectoryConfiguration(); + config.ConnectorMessages = TestHelpers.CreateDummyMessages(); String directoryAdminName = GetProperty(CONFIG_PROPERTY_USER); config.DirectoryAdminName = directoryAdminName; Assert.AreEqual(directoryAdminName, config.DirectoryAdminName); @@ -448,7 +449,10 @@ public void TestCreateWithHomeDirectoryNoCreateConfig_Account() public void TestSearchNoFilter_Account() { ActiveDirectoryConnector connector = new ActiveDirectoryConnector(); - connector.Init(GetConfiguration()); + Configuration config = GetConfiguration(); + config.ConnectorMessages = TestHelpers.CreateDummyMessages(); + connector.Init(config); + ICollection createdUids = new HashSet(); try @@ -1841,6 +1845,7 @@ public Configuration GetConfiguration() config.SearchContainer = GetProperty(CONFIG_PROPERTY_SEARCH_CONTEXT); config.SyncDomainController = GetProperty(CONFIG_PROPERTY_SYNC_DOMAIN_CONTROLLER); config.SyncGlobalCatalogServer = GetProperty(CONFIG_PROPERTY_GC_DOMAIN_CONTROLLER); + config.ConnectorMessages = TestHelpers.CreateDummyMessages(); return config; } From f106aacc262be9b04f2ada09c60a20b12d4989a2 Mon Sep 17 00:00:00 2001 From: dvernon Date: Wed, 15 Oct 2008 15:24:19 +0000 Subject: [PATCH 041/342] Issue #64 - Incremental checkin. Added try/catch to handle the case where I found a result during search, but it was deleted before I read it's attributes. --- .../ActiveDirectoryConnector.cs | 161 ++++++++++-------- 1 file changed, 87 insertions(+), 74 deletions(-) diff --git a/ActiveDirectoryConnector/ActiveDirectoryConnector.cs b/ActiveDirectoryConnector/ActiveDirectoryConnector.cs index a6fc9af5..cd7fe469 100644 --- a/ActiveDirectoryConnector/ActiveDirectoryConnector.cs +++ b/ActiveDirectoryConnector/ActiveDirectoryConnector.cs @@ -690,96 +690,109 @@ private void ExecuteQuery(ObjectClass oclass, string query, foreach (SearchResult result in resultSet) { - Trace.TraceInformation("Found object {0}", result.Path); - ConnectorObjectBuilder builder = new ConnectorObjectBuilder(); - builder.ObjectClass = oclass; - - bool isDeleted = false; - if(result.Properties.Contains(ATT_IS_DELETED)) { - ResultPropertyValueCollection pvc = result.Properties[ATT_IS_DELETED]; - if(pvc.Count > 0) { - isDeleted = (bool)pvc[0]; - } - } - - if (isDeleted.Equals(false)) + try { - // if we were using the global catalog (gc), we have to - // now retrieve the object from a domain controller (dc) - // because the gc may not have have all of the attributes, - // depending on which attributes are replicated to the gc. - SearchResult savedGcResult = null; - SearchResult savedDcResult = result; - if (useGlobalCatalog) - { - savedGcResult = result; - - String dcSearchRootPath = ActiveDirectoryUtils.GetLDAPPath( - _configuration.LDAPHostName, searchRoot); - - DirectoryEntry dcSearchRoot = new DirectoryEntry(dcSearchRootPath, - _configuration.DirectoryAdminName, _configuration.DirectoryAdminPassword); + Trace.TraceInformation("Found object {0}", result.Path); + ConnectorObjectBuilder builder = new ConnectorObjectBuilder(); + builder.ObjectClass = oclass; - string dcSearchQuery = String.Format("(" + ATT_DISTINGUISHED_NAME + "={0})", - ActiveDirectoryUtils.GetDnFromPath(savedGcResult.Path)); - DirectorySearcher dcSearrcher = - new DirectorySearcher(dcSearchRoot, dcSearchQuery); - savedDcResult = dcSearrcher.FindOne(); - if (savedDcResult == null) + bool isDeleted = false; + if (result.Properties.Contains(ATT_IS_DELETED)) + { + ResultPropertyValueCollection pvc = result.Properties[ATT_IS_DELETED]; + if (pvc.Count > 0) { - // in this case, we found it in the gc, but not in the - // domain controller. We cant return a result. The could - // be a case where the account was deleted, but the global - // catalog doesn't know it yet - Trace.TraceWarning(String.Format("Found result in global catalog " + - "for ''{0}'', but could not retrieve the entry from the domain " + - "controller", savedGcResult.Path)); - continue; + isDeleted = (bool)pvc[0]; } } - foreach (string attributeName in attributesToReturn) + if (isDeleted.Equals(false)) { - SearchResult savedResults = savedDcResult; - // if we are using the global catalog, we had to get the - // dc's version of the directory entry, but for usnchanged, - // we need the gc version of it - if (useGlobalCatalog && attributeName.Equals(ATT_USN_CHANGED, - StringComparison.CurrentCultureIgnoreCase)) + // if we were using the global catalog (gc), we have to + // now retrieve the object from a domain controller (dc) + // because the gc may not have have all of the attributes, + // depending on which attributes are replicated to the gc. + SearchResult savedGcResult = null; + SearchResult savedDcResult = result; + if (useGlobalCatalog) { - savedResults = savedGcResult; + savedGcResult = result; + + String dcSearchRootPath = ActiveDirectoryUtils.GetLDAPPath( + _configuration.LDAPHostName, searchRoot); + + DirectoryEntry dcSearchRoot = new DirectoryEntry(dcSearchRootPath, + _configuration.DirectoryAdminName, _configuration.DirectoryAdminPassword); + + string dcSearchQuery = String.Format("(" + ATT_DISTINGUISHED_NAME + "={0})", + ActiveDirectoryUtils.GetDnFromPath(savedGcResult.Path)); + DirectorySearcher dcSearrcher = + new DirectorySearcher(dcSearchRoot, dcSearchQuery); + savedDcResult = dcSearrcher.FindOne(); + if (savedDcResult == null) + { + // in this case, we found it in the gc, but not in the + // domain controller. We cant return a result. The could + // be a case where the account was deleted, but the global + // catalog doesn't know it yet + Trace.TraceWarning(String.Format("Found result in global catalog " + + "for ''{0}'', but could not retrieve the entry from the domain " + + "controller", savedGcResult.Path)); + continue; + } } + foreach (string attributeName in attributesToReturn) + { + SearchResult savedResults = savedDcResult; + // if we are using the global catalog, we had to get the + // dc's version of the directory entry, but for usnchanged, + // we need the gc version of it + if (useGlobalCatalog && attributeName.Equals(ATT_USN_CHANGED, + StringComparison.CurrentCultureIgnoreCase)) + { + savedResults = savedGcResult; + } + + AddAttributeIfNotNull(builder, + _utils.GetConnectorAttributeFromADEntry( + oclass, attributeName, savedResults)); + } + } + else + { + // get uid AddAttributeIfNotNull(builder, _utils.GetConnectorAttributeFromADEntry( - oclass, attributeName, savedResults)); + oclass, Uid.NAME, result)); + + // get uid + AddAttributeIfNotNull(builder, + _utils.GetConnectorAttributeFromADEntry( + oclass, Name.NAME, result)); + + // get usnchanged + AddAttributeIfNotNull(builder, + _utils.GetConnectorAttributeFromADEntry( + oclass, ATT_USN_CHANGED, result)); + + // add isDeleted + builder.AddAttribute(ATT_IS_DELETED, true); } + + String msg = String.Format("Returning ''{0}''", + (result.Path != null) ? result.Path : ""); + Trace.TraceInformation(msg); + handler(builder.Build()); } - else + catch (DirectoryServicesCOMException e) { - // get uid - AddAttributeIfNotNull(builder, - _utils.GetConnectorAttributeFromADEntry( - oclass, Uid.NAME, result)); - - // get uid - AddAttributeIfNotNull(builder, - _utils.GetConnectorAttributeFromADEntry( - oclass, Name.NAME, result)); - - // get usnchanged - AddAttributeIfNotNull(builder, - _utils.GetConnectorAttributeFromADEntry( - oclass, ATT_USN_CHANGED, result)); - - // add isDeleted - builder.AddAttribute(ATT_IS_DELETED, true); + // there is a chance that we found the result, but + // in the mean time, it was deleted. In that case, + // log an error and continue + Trace.TraceWarning("Error in creating ConnectorObject from DirectoryEntry. It may have been deleted during search."); + Trace.TraceWarning(e.Message); } - - String msg = String.Format("Returning ''{0}''", - (result.Path != null) ? result.Path : ""); - Trace.TraceInformation(msg); - handler(builder.Build()); } } From 5d495ed1ac392eb152dc9dba6db515e035e439b4 Mon Sep 17 00:00:00 2001 From: dvernon Date: Wed, 15 Oct 2008 18:11:43 +0000 Subject: [PATCH 042/342] Issue #64 - Incremental checkin. Added internationalized Authentication Failure message. --- ActiveDirectoryConnector/Messages.resx | 3 +++ ActiveDirectoryConnector/PasswordChangeHandler.cs | 4 +++- 2 files changed, 6 insertions(+), 1 deletion(-) diff --git a/ActiveDirectoryConnector/Messages.resx b/ActiveDirectoryConnector/Messages.resx index 950b269d..168fd7b8 100644 --- a/ActiveDirectoryConnector/Messages.resx +++ b/ActiveDirectoryConnector/Messages.resx @@ -226,4 +226,7 @@ DC=COM. ObjectClass \'{0}\' is not valid for this connector + + Invalid credentials supplied for user {0} + \ No newline at end of file diff --git a/ActiveDirectoryConnector/PasswordChangeHandler.cs b/ActiveDirectoryConnector/PasswordChangeHandler.cs index 490c768b..8e870009 100644 --- a/ActiveDirectoryConnector/PasswordChangeHandler.cs +++ b/ActiveDirectoryConnector/PasswordChangeHandler.cs @@ -189,7 +189,9 @@ internal void Authenticate(DirectoryEntry directoryEntry, string username, } catch (Exception e) { - throw new InvalidCredentialException(); + throw new InvalidCredentialException(_configuration.ConnectorMessages.Format( + "ex_InvalidCredentials", "Invalid credentials supplied for user {0}", + directoryEntry.Path)); } } From 727d73c98d6c25b97104d908e5e2a09f19911889 Mon Sep 17 00:00:00 2001 From: dvernon Date: Fri, 17 Oct 2008 17:24:35 +0000 Subject: [PATCH 043/342] Issue #64 - Incremental checkin. Changing the way Authenticate works (back to the right way). Added EXPIRE_PASSWORD attribute. --- .../ActiveDirectoryConnector.cs | 13 +- .../CustomAttributeHandlers.cs | 61 ++++++- ActiveDirectoryConnector/Messages.resx | 3 + .../PasswordChangeHandler.cs | 63 +++---- .../ActiveDirectoryConnectorTest.cs | 160 +++++++++++++++++- 5 files changed, 243 insertions(+), 57 deletions(-) diff --git a/ActiveDirectoryConnector/ActiveDirectoryConnector.cs b/ActiveDirectoryConnector/ActiveDirectoryConnector.cs index cd7fe469..04cf99a9 100644 --- a/ActiveDirectoryConnector/ActiveDirectoryConnector.cs +++ b/ActiveDirectoryConnector/ActiveDirectoryConnector.cs @@ -160,7 +160,7 @@ public class ActiveDirectoryConnector : CreateOp, Connector, SchemaOp, DeleteOp, public static readonly string ATT_MEMBEROF = "memberOf"; public static readonly string ATT_HOME_DIRECTORY = "homeDirectory"; public static readonly string ATT_OBJECT_SID = "objectSid"; - + public static readonly string ATT_PWD_LAST_SET = "pwdLastSet"; public static readonly string OBJECTCLASS_OU = "organizationalUnit"; public static readonly ObjectClass ouObjectClass = new ObjectClass(OBJECTCLASS_OU); @@ -215,12 +215,10 @@ public virtual Uid Create(ObjectClass oclass, try { - /* if (!DirectoryEntry.Exists(ldapContainerPath)) { throw new ConnectorException("Container does not exist"); } - */ // Get the correct container, and put the new user in it DirectoryEntry containerDe = new DirectoryEntry(ldapContainerPath, @@ -386,6 +384,7 @@ public ICollection GetUserAttributeInfos( attributeInfos.Add(OperationalAttributeInfos.DISABLE_DATE); attributeInfos.Add(OperationalAttributeInfos.LOCK_OUT); */ + attributeInfos.Add(OperationalAttributeInfos.PASSWORD_EXPIRED); attributeInfos.Add(OperationalAttributeInfos.CURRENT_PASSWORD); // dont think I need this // attributeInfos.Add(OperationalAttributeInfos.RESET_PASSWORD); @@ -1200,13 +1199,7 @@ public void Authenticate(string username, OperationOptions options) { PasswordChangeHandler handler = new PasswordChangeHandler(_configuration); - String ldapEntryPath = ActiveDirectoryUtils.GetLDAPPath(_configuration.LDAPHostName, - username); - - DirectoryEntry directoryEntry = new DirectoryEntry(ldapEntryPath, - _configuration.DirectoryAdminName, _configuration.DirectoryAdminPassword); - - handler.Authenticate(directoryEntry, username, password); + handler.Authenticate(username, password); } #endregion diff --git a/ActiveDirectoryConnector/CustomAttributeHandlers.cs b/ActiveDirectoryConnector/CustomAttributeHandlers.cs index 00e8b26b..6240b5c8 100644 --- a/ActiveDirectoryConnector/CustomAttributeHandlers.cs +++ b/ActiveDirectoryConnector/CustomAttributeHandlers.cs @@ -117,6 +117,8 @@ internal CustomAttributeHandlers(ActiveDirectoryConfiguration configuration) { UpdateDeFromCa_Att_HomeDirectory); UpdateDeFromCaDelegates.Add(OperationalAttributes.ENABLE_NAME, UpdateDeFromCa_OpAtt_Enable); + UpdateDeFromCaDelegates.Add(OperationalAttributes.PASSWORD_EXPIRED_NAME, + UpdateDeFromCa_OpAtt_PasswordExpired); // supporting class not implemented in the framework /* UpdateDeFromCaDelegates.Add(OperationalAttributes.ENABLE_DATE_NAME, @@ -166,6 +168,8 @@ internal CustomAttributeHandlers(ActiveDirectoryConfiguration configuration) { GetCaFromDe_OpAtt_Groups); GetCaFromDeDelegates.Add(OperationalAttributes.ENABLE_NAME, GetCaFromDe_OpAtt_Enabled); + GetCaFromDeDelegates.Add(OperationalAttributes.PASSWORD_EXPIRED_NAME, + GetCaFromDe_OpAtt_PasswordExpired); // supporting class not implemented in the framework /* GetCaFromDeDelegates.Add(OperationalAttributes.ENABLE_DATE_NAME, @@ -465,7 +469,28 @@ internal void UpdateDeFromCa_OpAtt_Enable(ObjectClass oclass, !ConnectorAttributeUtil.GetBooleanValue(attribute)); } -// supporting class not implemented in the framework + internal void UpdateDeFromCa_OpAtt_PasswordExpired(ObjectClass oclass, + UpdateType type, DirectoryEntry directoryEntry, + ConnectorAttribute attribute) + { + bool? passwordExpired = ConnectorAttributeUtil.GetBooleanValue(attribute); + if ((passwordExpired.HasValue) && (passwordExpired.Value == true)) + { + directoryEntry.Properties[ActiveDirectoryConnector.ATT_PWD_LAST_SET].Clear(); + directoryEntry.Properties[ActiveDirectoryConnector.ATT_PWD_LAST_SET].Value = GetLargeIntegerFromLong(0); + } + else + { + // this value can't be set (other than to zero) I'm throwing my own exception + // here, because if not, AD thows this (at least on my machine): + // System.DirectoryServices.DirectoryServicesCOMException : A device attached to the system is not functioning. (Exception from HRESULT: 0x8007001F) + throw new ConnectorException(_configuration.ConnectorMessages.Format( + "ex_PasswordMustBeReset", + "Password expiration can only be reset by reseting the password")); + } + } + + // supporting class not implemented in the framework /* internal void UpdateDeFromCa_OpAtt_EnableDate(ObjectClass oclass, UpdateType type, DirectoryEntry directoryEntry, @@ -817,8 +842,22 @@ private ConnectorAttribute GetCaFromDe_OpAtt_Enabled( return ConnectorAttributeBuilder.BuildEnabled(!disabled); } - -// supporting class not implemented in the framework + + private ConnectorAttribute GetCaFromDe_OpAtt_PasswordExpired( + ObjectClass oclass, string attributeName, SearchResult searchResult) + { + ConnectorAttribute realAttribute = GetCaFromDe_Att_Generic( + oclass, ActiveDirectoryConnector.ATT_PWD_LAST_SET, searchResult); + long? lastSetDate = ConnectorAttributeUtil.GetLongValue(realAttribute); + if ((lastSetDate.HasValue) && (lastSetDate.Value != 0)) + { + return ConnectorAttributeBuilder.BuildPasswordExpired(false); + } + + return ConnectorAttributeBuilder.BuildPasswordExpired(true); + } + + // supporting class not implemented in the framework /* private ConnectorAttribute GetCaFromDe_OpAtt_EnableDate( ObjectClass oclass, string attributeName, SearchResult searchResult) @@ -976,6 +1015,22 @@ internal ConnectorAttribute ReturnConnectorAttribute return newAttribute; } + // gets a long from a LargeInteger (COM object) + long GetLongFromLargeInteger(LargeInteger largeInteger) + { + long longValue = largeInteger.HighPart << 32; + longValue += largeInteger.LowPart; + return longValue; + } + + // sets a LargeInteger (COM object) from a long + LargeInteger GetLargeIntegerFromLong(long longValue) + { + LargeInteger largeInteger = new LargeIntegerClass(); + largeInteger.HighPart = (int)((longValue & 0xFFFFFFFF0000L) >> 32); + largeInteger.LowPart = (int)(longValue & 0x0000FFFFFFFFL); + return largeInteger; + } } } diff --git a/ActiveDirectoryConnector/Messages.resx b/ActiveDirectoryConnector/Messages.resx index 168fd7b8..4ae0c69a 100644 --- a/ActiveDirectoryConnector/Messages.resx +++ b/ActiveDirectoryConnector/Messages.resx @@ -229,4 +229,7 @@ DC=COM. Invalid credentials supplied for user {0} + + Password expiration can only be reset by reseting the password + \ No newline at end of file diff --git a/ActiveDirectoryConnector/PasswordChangeHandler.cs b/ActiveDirectoryConnector/PasswordChangeHandler.cs index 8e870009..bd0b6907 100644 --- a/ActiveDirectoryConnector/PasswordChangeHandler.cs +++ b/ActiveDirectoryConnector/PasswordChangeHandler.cs @@ -143,57 +143,38 @@ internal void changePassword(DirectoryEntry directoryEntry, /// /// /// - internal void Authenticate(DirectoryEntry directoryEntry, string username, + internal void Authenticate(/*DirectoryEntry directoryEntry,*/ string username, Org.IdentityConnectors.Common.Security.GuardedString password) { password.Access(setCurrentPassword); - String sAMAccountName = (String)directoryEntry.Properties[ActiveDirectoryConnector.ATT_SAMACCOUNT_NAME][0]; - DirectoryEntry userDe = new DirectoryEntry(directoryEntry.Path, - sAMAccountName, _currentPassword); + string serverName = _configuration.LDAPHostName; + PrincipalContext context = null; + if ((serverName == null) || (serverName.Length == 0)) + { + DomainController domainController = ActiveDirectoryUtils.GetDomainController(_configuration); + context = new PrincipalContext(ContextType.Domain, + domainController.Domain.Name, + username, _currentPassword); + } + else + { + context = new PrincipalContext(ContextType.Machine, + _configuration.LDAPHostName, + username, _currentPassword); + } - try + if (context == null) { - userDe.RefreshCache(); - - /* - * - * This seems like the right way to do this ... but it doesn't work - * everywhere, so I'm reverting to creating a directory entry and doing - * a refreshcashe to force a bind. - string serverName = _configuration.LDAPHostName; - PrincipalContext context = null; - if ((serverName == null) || (serverName.Length == 0)) - { - DomainController domainController = ActiveDirectoryUtils.GetDomainController(_configuration); - context = new PrincipalContext(ContextType.Domain, - domainController.Domain.Name); - } - else - { - context = new PrincipalContext(ContextType.Machine, - _configuration.LDAPHostName, _configuration.DirectoryAdminName, - _configuration.DirectoryAdminPassword); - } - - if (context == null) - { - throw new ConnectorException("Unable to get PrincipalContext"); - } - - if (!context.ValidateCredentials(sAMAccountName, _currentPassword)) - { - throw new InvalidCredentialException(); - } - */ + throw new ConnectorException("Unable to get PrincipalContext"); } - catch (Exception e) + + if (!context.ValidateCredentials(username, _currentPassword)) { throw new InvalidCredentialException(_configuration.ConnectorMessages.Format( - "ex_InvalidCredentials", "Invalid credentials supplied for user {0}", - directoryEntry.Path)); + "ex_InvalidCredentials", "Invalid credentials supplied for user {0}", + username)); } - } } } diff --git a/ActiveDirectoryConnectorTests/ActiveDirectoryConnectorTest.cs b/ActiveDirectoryConnectorTests/ActiveDirectoryConnectorTest.cs index 797ed813..4bbb60ed 100644 --- a/ActiveDirectoryConnectorTests/ActiveDirectoryConnectorTest.cs +++ b/ActiveDirectoryConnectorTests/ActiveDirectoryConnectorTest.cs @@ -1262,7 +1262,8 @@ public void TestUserPasswordChange() ObjectClass.ACCOUNT, createAttributes); // make sure authenticate works here - connector.Authenticate(ConnectorAttributeUtil.GetNameFromAttributes(createAttributes).GetNameValue(), + connector.Authenticate( + ConnectorAttributeUtil.GetAsStringValue(ConnectorAttributeUtil.Find("sAMAccountName", createAttributes)), gsCurrentPassword, null); ICollection updateReplaceAttributes = @@ -1274,14 +1275,16 @@ public void TestUserPasswordChange() createUid, updateReplaceAttributes); // make sure authenticate works here - connector.Authenticate(ConnectorAttributeUtil.GetNameFromAttributes(createAttributes).GetNameValue(), + connector.Authenticate( + ConnectorAttributeUtil.GetAsStringValue(ConnectorAttributeUtil.Find("sAMAccountName", createAttributes)), gsNewPassword, null); bool caughtAuthenticateFailedException = false; try { // make sure authenticate doesnt work with original password - connector.Authenticate(ConnectorAttributeUtil.GetNameFromAttributes(createAttributes).GetNameValue(), + connector.Authenticate( + ConnectorAttributeUtil.GetAsStringValue(ConnectorAttributeUtil.Find("sAMAccountName", createAttributes)), gsCurrentPassword, null); } catch (Exception e) @@ -1326,6 +1329,157 @@ public void TestUserPasswordChange() } } + [Test] + public void TestAuthenticateUser() + { + //Initialize Connector + ActiveDirectoryConnector connector = new ActiveDirectoryConnector(); + connector.Init(GetConfiguration()); + Uid createUid = null; + + try + { + // create user + ICollection createAttributes = GetNormalAttributes_Account(); + // remove password, and set to something memorable + createAttributes.Remove(ConnectorAttributeUtil.Find(OperationalAttributes.PASSWORD_NAME, createAttributes)); + GuardedString gsCurrentPassword = GetGuardedString("1Password"); + createAttributes.Add(ConnectorAttributeBuilder.BuildPassword(gsCurrentPassword)); + createAttributes.Add(ConnectorAttributeBuilder.BuildEnabled(true)); + createUid = CreateAndVerifyObject(connector, + ObjectClass.ACCOUNT, createAttributes); + + // make sure authenticate works here + connector.Authenticate( + ConnectorAttributeUtil.GetAsStringValue(ConnectorAttributeUtil.Find("sAMAccountName", createAttributes)), + gsCurrentPassword, null); + + // make sure authenticate fails - wrong password + bool caughtException = false; + try + { + connector.Authenticate( + ConnectorAttributeUtil.GetAsStringValue(ConnectorAttributeUtil.Find("sAMAccountName", createAttributes)), + GetGuardedString("boguspassword"), null); + + } + catch (InvalidCredentialException e) + { + caughtException = true; + } + Assert.IsTrue(caughtException, "Negative test case should throw InvalidCredentialsException"); + + // change password + ICollection updateReplaceAttributes = + new HashSet(); + GuardedString gsNewPassword = GetGuardedString("LongPassword2MeetTheRequirements!"); + updateReplaceAttributes.Add(ConnectorAttributeBuilder.BuildCurrentPassword(gsCurrentPassword)); + updateReplaceAttributes.Add(ConnectorAttributeBuilder.BuildPassword(gsNewPassword)); + UpdateReplaceAndVerifyObject(connector, ObjectClass.ACCOUNT, + createUid, updateReplaceAttributes); + + // make sure authenticate works here - new password + connector.Authenticate( + ConnectorAttributeUtil.GetAsStringValue(ConnectorAttributeUtil.Find("sAMAccountName", createAttributes)), + gsNewPassword, null); + + // make sure it fails with the wrong password + caughtException = false; + try { + connector.Authenticate( + ConnectorAttributeUtil.GetAsStringValue(ConnectorAttributeUtil.Find("sAMAccountName", createAttributes)), + GetGuardedString("bogusPassword"), null); + } + catch (Exception e) + { + caughtException = true; + } + Assert.IsTrue(caughtException, "Negative test case should throw an exception"); + + // now set user must change password attribute + ICollection expirePasswordAttrs = + new HashSet(); + expirePasswordAttrs.Add(ConnectorAttributeBuilder.BuildPasswordExpired(true)); + UpdateReplaceAndVerifyObject(connector, ObjectClass.ACCOUNT, + createUid, expirePasswordAttrs); + + // make sure authenticate fails - correct password, but expired + caughtException = false; + try + { + // make sure authenticate fails with correct password + connector.Authenticate( + ConnectorAttributeUtil.GetAsStringValue(ConnectorAttributeUtil.Find("sAMAccountName", createAttributes)), + gsNewPassword, null); + } + catch (Exception e) + { + caughtException = true; + } + Assert.IsTrue(caughtException, "Negative test case should throw an exception"); + + // make sure authenticate fails - incorrect password, and expired + caughtException = false; + try + { + // make sure authenticate fails with wrong password (invalid credentials exception) + connector.Authenticate( + ConnectorAttributeUtil.GetAsStringValue(ConnectorAttributeUtil.Find("sAMAccountName", createAttributes)), + GetGuardedString("bogusPassword"), null); + } + catch (InvalidCredentialException e) + { + caughtException = true; + } + Assert.IsTrue(caughtException, "Negative test case should throw an exception"); + } + finally + { + if (createUid != null) + { + //remove the one we created + DeleteAndVerifyObject(connector, ObjectClass.ACCOUNT, + createUid, false, true); + } + } + } + + [Test] + public void TestPasswordExpiration() + { + //Initialize Connector + ActiveDirectoryConnector connector = new ActiveDirectoryConnector(); + connector.Init(GetConfiguration()); + Uid createUid = null; + + try + { + // create user + ICollection createAttributes = GetNormalAttributes_Account(); + // remove password, and set to something memorable + createAttributes.Remove(ConnectorAttributeUtil.Find(OperationalAttributes.PASSWORD_NAME, createAttributes)); + GuardedString gsCurrentPassword = GetGuardedString("1Password"); + createAttributes.Add(ConnectorAttributeBuilder.BuildPassword(gsCurrentPassword)); + createAttributes.Add(ConnectorAttributeBuilder.BuildEnabled(true)); + createUid = CreateAndVerifyObject(connector, + ObjectClass.ACCOUNT, createAttributes); + + // make sure authenticate works here + connector.Authenticate( + ConnectorAttributeUtil.GetAsStringValue(ConnectorAttributeUtil.Find("sAMAccountName", createAttributes)), + gsCurrentPassword, null); + } + finally + { + if (createUid != null) + { + //remove the one we created + DeleteAndVerifyObject(connector, ObjectClass.ACCOUNT, + createUid, false, true); + } + } + } + public void TestSync(bool searchChildDomains, String syncSearchContext) { //Initialize Connector From b37be947e4d1b821d457eeb7efa9b1c3b17ac9c5 Mon Sep 17 00:00:00 2001 From: rcauble Date: Fri, 17 Oct 2008 18:26:12 +0000 Subject: [PATCH 044/342] Issue#292: started renaming gateway --- Connector Gateway.sln => DotNetConnectors.sln | 0 1 file changed, 0 insertions(+), 0 deletions(-) rename Connector Gateway.sln => DotNetConnectors.sln (100%) diff --git a/Connector Gateway.sln b/DotNetConnectors.sln similarity index 100% rename from Connector Gateway.sln rename to DotNetConnectors.sln From 8c92fda6577e650bed08c834af28e620f08d15dc Mon Sep 17 00:00:00 2001 From: rcauble Date: Fri, 17 Oct 2008 18:55:22 +0000 Subject: [PATCH 045/342] Issue#292: renamed gateway to connector server --- FrameworkInternal/Server.cs | 2 +- FrameworkTests/app.config | 12 ++++++------ Service/Program.cs | 4 ++-- Service/ProjectInstaller.cs | 2 +- Service/Service.cs | 22 +++++++++++----------- Service/Service.csproj | 6 +++--- Service/app.config | 12 ++++++------ ServiceInstall/File.top | 16 ++++++++-------- ServiceInstall/Setup.wxs | 6 +++--- 9 files changed, 41 insertions(+), 41 deletions(-) diff --git a/FrameworkInternal/Server.cs b/FrameworkInternal/Server.cs index 15a0f38b..2328c58c 100644 --- a/FrameworkInternal/Server.cs +++ b/FrameworkInternal/Server.cs @@ -83,7 +83,7 @@ private const String IMPL_NAME private int _port = 0; /** - * Base 64 sha1 hash of the gateway key + * Base 64 sha1 hash of the connector server key */ private String _keyHash; diff --git a/FrameworkTests/app.config b/FrameworkTests/app.config index 2a6bf2c2..e228e5fa 100644 --- a/FrameworkTests/app.config +++ b/FrameworkTests/app.config @@ -4,11 +4,11 @@ - - - - - + + + + + @@ -16,7 +16,7 @@ + initializeData="c:\connectorserver.log" /> diff --git a/Service/Program.cs b/Service/Program.cs index 784ab29a..24c56a72 100644 --- a/Service/Program.cs +++ b/Service/Program.cs @@ -57,11 +57,11 @@ static class Program private static void Usage() { - Console.WriteLine("Usage: Gateway.exe [option], where command is one of the following: "); + Console.WriteLine("Usage: ConnectorServer.exe [option], where command is one of the following: "); Console.WriteLine(" /install [/serviceName ] - Installs the service."); Console.WriteLine(" /uninstall [/serviceName ] - Uninstalls the service."); Console.WriteLine(" /run - Runs the service from the console."); - Console.WriteLine(" /setKey [] - Sets the gateway key."); + Console.WriteLine(" /setKey [] - Sets the connector server key."); } private static IDictionary ParseOptions(string [] args) diff --git a/Service/ProjectInstaller.cs b/Service/ProjectInstaller.cs index df8d1d19..f8282687 100644 --- a/Service/ProjectInstaller.cs +++ b/Service/ProjectInstaller.cs @@ -52,7 +52,7 @@ public class ProjectInstaller : Installer static ProjectInstaller() { - ServiceName = "Connector Gateway"; + ServiceName = "Connector Server"; } private ServiceProcessInstaller serviceProcessInstaller; diff --git a/Service/Service.cs b/Service/Service.cs index b4f13c1f..e39b278e 100644 --- a/Service/Service.cs +++ b/Service/Service.cs @@ -56,11 +56,11 @@ namespace Org.IdentityConnectors.Framework.Service { public class Service : ServiceBase { - private const string PROP_PORT = "gateway.port"; - private const string PROP_SSL = "gateway.usessl"; - private const string PROP_CERTSTORE = "gateway.certificatestorename"; - private const string PROP_IFADDRESS = "gateway.ifaddress"; - public const string PROP_KEY = "gateway.key"; + private const string PROP_PORT = "connectorserver.port"; + private const string PROP_SSL = "connectorserver.usessl"; + private const string PROP_CERTSTORE = "connectorserver.certificatestorename"; + private const string PROP_IFADDRESS = "connectorserver.ifaddress"; + public const string PROP_KEY = "connectorserver.key"; private ConnectorServer _server; @@ -129,7 +129,7 @@ protected override void OnStart(string[] args) { try { initializeCurrentDirectory(); - Trace.TraceInformation("Starting connector gateway: "+Environment.CurrentDirectory); + Trace.TraceInformation("Starting connector server: "+Environment.CurrentDirectory); NameValueCollection settings = GetApplicationSettings(); String portStr = @@ -158,10 +158,10 @@ protected override void OnStart(string[] args) IOUtil.GetIPAddress(ifaddress); } _server.Start(); - Trace.TraceInformation("Started connector gateway"); + Trace.TraceInformation("Started connector server"); } catch (Exception e) { - TraceUtil.TraceException("Exception occured starting gateway", + TraceUtil.TraceException("Exception occured starting connector server", e); throw; } @@ -179,14 +179,14 @@ public void StopService() protected override void OnStop() { try { - Trace.TraceInformation("Stopping connector gateway"); + Trace.TraceInformation("Stopping connector server"); if (_server != null) { _server.Stop(); } - Trace.TraceInformation("Stopped connector gateway"); + Trace.TraceInformation("Stopped connector server"); } catch (Exception e) { - TraceUtil.TraceException("Exception occured stopping gateway", + TraceUtil.TraceException("Exception occured stopping connector server", e); } } diff --git a/Service/Service.csproj b/Service/Service.csproj index 29ff113c..157bfbad 100644 --- a/Service/Service.csproj +++ b/Service/Service.csproj @@ -42,7 +42,7 @@ AnyCPU Exe Sun.OpenConnectors.Framework.Service - Gateway + ConnectorServer v3.5 False false @@ -55,7 +55,7 @@ True DEBUG;TRACE Exe - Gateway + ConnectorServer False 4 @@ -67,7 +67,7 @@ False TRACE Exe - Gateway + ConnectorServer False 4 diff --git a/Service/app.config b/Service/app.config index 2778427c..0b44a417 100644 --- a/Service/app.config +++ b/Service/app.config @@ -10,11 +10,11 @@ - - - - - + + + + + @@ -24,7 +24,7 @@ - + diff --git a/ServiceInstall/File.top b/ServiceInstall/File.top index 8a8ce3e6..84086191 100644 --- a/ServiceInstall/File.top +++ b/ServiceInstall/File.top @@ -3,19 +3,19 @@ - - + + - - + + diff --git a/ServiceInstall/Setup.wxs b/ServiceInstall/Setup.wxs index 9343638a..54724a64 100644 --- a/ServiceInstall/Setup.wxs +++ b/ServiceInstall/Setup.wxs @@ -1,7 +1,7 @@  - + + + + + New-MailUser + Name + + ExternalEmailAddress + OrganizationalUnit + Password + UserPrincipalName + Alias + DisplayName + DomainController + FirstName + Initials + LastName + MacAttachmentFormat + MessageBodyFormat + MessageFormat + ResetPasswordOnNextLogon + SamAccountName + TemplateInstance + UsePreferMessageFormat + + + + Set-MailUser + Identity + + AcceptMessagesOnlyFrom + AcceptMessagesOnlyFromDLMembers + Alias + CreateDTMFMap + CustomAttribute1 + CustomAttribute2 + CustomAttribute3 + CustomAttribute4 + CustomAttribute5 + CustomAttribute6 + CustomAttribute7 + CustomAttribute8 + CustomAttribute9 + CustomAttribute10 + CustomAttribute11 + CustomAttribute12 + CustomAttribute13 + CustomAttribute14 + CustomAttribute15 + DisplayName + DomainController + EmailAddresses + EmailAddressPolicyEnabled + Extensions + ExternalEmailAddress + GrantSendOnBehalfTo + HiddenFromAddressListsEnabled + Instance + MacAttachmentFormat + MessageBodyFormat + MessageFormat + Name + PrimarySmtpAddress + RecipientLimits + RejectMessagesFrom + RejectMessagesFromDLMembers + RequireSenderAuthenticationEnabled + SamAccountName + SecondaryAddress + SecondaryDialPlan + SimpleDisplayName + UMDtmfMap + UseMapiRichTextFormat + UsePreferMessageFormat + UserPrincipalName + WindowsEmailAddress + MaxReceiveSize + MaxSendSize + + + + Get-MailUser + + Identity + OrganizationalUnit + ReadFromDomainController + + + Filter + + AcceptMessagesOnlyFrom + AcceptMessagesOnlyFromDLMembers + Alias + CustomAttribute1 + CustomAttribute2 + CustomAttribute3 + CustomAttribute4 + CustomAttribute5 + CustomAttribute6 + CustomAttribute7 + CustomAttribute8 + CustomAttribute9 + CustomAttribute10 + CustomAttribute11 + CustomAttribute12 + CustomAttribute13 + CustomAttribute14 + CustomAttribute15 + DisplayName + DistinguishedName + EmailAddresses + EmailAddressPolicyEnabled + ExchangeVersion + ExternalEmailAddress + GrantSendOnBehalfTo + Guid + HiddenFromAddressListsEnabled + MaxReceiveSize + MaxSendSize + Name + PrimarySmtpAddress + RecipientLimits + RecipientType + RecipientTypeDetails + RejectMessagesFrom + RejectMessagesFromDLMembers + SamAccountName + SimpleDisplayName + UMDtmfMap + UserPrincipalName + WhenChanged + WhenCreated + WindowsEmailAddress + + + + Enable-MailUser + Identity + + ExternalEmailAddress + Alias + DomainController + MacAttachmentFormat + MessageBodyFormat + MessageFormat + UsePreferMessageFormat + + + + + + Get-User + Identity + + + Set-User + Identity + + AssistantName + City + Company + CountryOrRegion + CreateDTMFMap + Department + DisplayName + DomainController + Fax + FirstName + HomePhone + Initials + Instance + LastName + Manager + MobilePhone + Name + Notes + Office + OtherFax + OtherHomePhone + OtherTelephone + Pager + Phone + PhoneticDisplayName + PostalCode + PostOfficeBox + ResetPasswordOnNextLogon + SamAccountName + SimpleDisplayName + StateOrProvince + StreetAddress + Title + UMDialPlan + UMDtmfMap + UserPrincipalName + WebPage + WindowsEmailAddress + + + + + + Enable-Mailbox + Identity + + Database + Equipment + Alias + LinkedDomainController + LinkedMasterAccount + Room + Shared + ActiveSyncMailboxPolicy + DomainController + LinkedCredential + ManagedFolderMailboxPolicy + ManagedFolderMailboxPolicyAllowed + + + + \ No newline at end of file diff --git a/ExchangeConnector/ExchangeConfiguration.cs b/ExchangeConnector/ExchangeConfiguration.cs new file mode 100644 index 00000000..afaf33f7 --- /dev/null +++ b/ExchangeConnector/ExchangeConfiguration.cs @@ -0,0 +1,56 @@ +/* + * Copyright 2008 Sun Microsystems, Inc. All rights reserved. + * + * U.S. Government Rights - Commercial software. Government users + * are subject to the Sun Microsystems, Inc. standard license agreement + * and applicable provisions of the FAR and its supplements. + * + * Use is subject to license terms. + * + * This distribution may include materials developed by third parties. + * Sun, Sun Microsystems, the Sun logo, Java and Project Identity + * Connectors are trademarks or registered trademarks of Sun + * Microsystems, Inc. or its subsidiaries in the U.S. and other + * countries. + * + * UNIX is a registered trademark in the U.S. and other countries, + * exclusively licensed through X/Open Company, Ltd. + * + * ----------- + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER. + * + * Copyright 2008 Sun Microsystems, Inc. All rights reserved. + * + * The contents of this file are subject to the terms of the Common Development + * and Distribution License(CDDL) (the License). You may not use this file + * except in compliance with the License. + * + * You can obtain a copy of the License at + * http://identityconnectors.dev.java.net/CDDLv1.0.html + * See the License for the specific language governing permissions and + * limitations under the License. + * + * When distributing the Covered Code, include this CDDL Header Notice in each + * file and include the License file at identityconnectors/legal/license.txt. + * If applicable, add the following below this CDDL Header, with the fields + * enclosed by brackets [] replaced by your own identifying information: + * "Portions Copyrighted [year] [name of copyright owner]" + * ----------- + */ + +using Org.IdentityConnectors.Framework.Common.Exceptions; +using System; +using Org.IdentityConnectors.ActiveDirectory; +using Org.IdentityConnectors.Framework.Spi; + +namespace Org.IdentityConnectors.Exchange +{ + /// + /// MS Exchange specific configuration + /// + public class ExchangeConfiguration : ActiveDirectoryConfiguration + { + + } + +} diff --git a/ExchangeConnector/ExchangeConnector.cs b/ExchangeConnector/ExchangeConnector.cs new file mode 100644 index 00000000..1d1d80a4 --- /dev/null +++ b/ExchangeConnector/ExchangeConnector.cs @@ -0,0 +1,323 @@ +/* + * Copyright 2008 Sun Microsystems, Inc. All rights reserved. + * + * U.S. Government Rights - Commercial software. Government users + * are subject to the Sun Microsystems, Inc. standard license agreement + * and applicable provisions of the FAR and its supplements. + * + * Use is subject to license terms. + * + * This distribution may include materials developed by third parties. + * Sun, Sun Microsystems, the Sun logo, Java and Project Identity + * Connectors are trademarks or registered trademarks of Sun + * Microsystems, Inc. or its subsidiaries in the U.S. and other + * countries. + * + * UNIX is a registered trademark in the U.S. and other countries, + * exclusively licensed through X/Open Company, Ltd. + * + * ----------- + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER. + * + * Copyright 2008 Sun Microsystems, Inc. All rights reserved. + * + * The contents of this file are subject to the terms of the Common Development + * and Distribution License(CDDL) (the License). You may not use this file + * except in compliance with the License. + * + * You can obtain a copy of the License at + * http://identityconnectors.dev.java.net/CDDLv1.0.html + * See the License for the specific language governing permissions and + * limitations under the License. + * + * When distributing the Covered Code, include this CDDL Header Notice in each + * file and include the License file at identityconnectors/legal/license.txt. + * If applicable, add the following below this CDDL Header, with the fields + * enclosed by brackets [] replaced by your own identifying information: + * "Portions Copyrighted [year] [name of copyright owner]" + * ----------- + */ +using System; +using System.Collections.Generic; +using System.Diagnostics; +using System.Management.Automation.Runspaces; +using Org.IdentityConnectors.ActiveDirectory; +using Org.IdentityConnectors.Common; +using Org.IdentityConnectors.Framework.Common.Objects; +using Org.IdentityConnectors.Framework.Spi; +using Org.IdentityConnectors.Framework.Spi.Operations; + +namespace Org.IdentityConnectors.Exchange +{ + /// + /// MS Exchange extension of Active Directory connector + /// + //TODO: what about MessageCatalogPath inheritance? + [ConnectorClass("connector_displayName", + typeof(ExchangeConfiguration), + MessageCatalogPath = "Org.IdentityConnectors.Exchange.Messages" + )] + public class ExchangeConnector : ActiveDirectoryConnector + { + private static readonly string CLASS = typeof(ExchangeConnector).ToString(); + + //object class names + public const string MAILBOX_NAME = "mailbox"; + public const string MAILUSER_NAME = "mailuser"; + + //object classes + public static readonly ObjectClass MAILBOX = new ObjectClass(MAILBOX_NAME); + public static readonly ObjectClass MAILUSER = new ObjectClass(MAILUSER_NAME); + + //local vars + private ExchangeConfiguration _configuration = null; + private RunSpaceInstance _runspace = null; + private bool _disposed = false; + private IDictionary _mapOcInfo = null; + + + /// + /// Implementation of CreateOp.Create + /// + /// (oc + /// + /// + /// + public override Uid Create(ObjectClass oclass, + ICollection attributes, OperationOptions options) + { + const string METHOD = "Create"; + + Debug.WriteLine(METHOD + ":entry", CLASS); + + + //first create the object in AD + Uid uid = base.Create(oclass, attributes, options); + + try + { + if (oclass.Equals(MAILBOX)) + { + //enable mailbox for person + Command cmd = ExchangeUtils.GetCommand(CommandInfo.ENABLE_MAILBOX, attributes); + _runspace.InvokePipeline(cmd); + } else if (oclass.Equals(MAILUSER)) + { + //enable mailuser + Command cmd = ExchangeUtils.GetCommand(CommandInfo.ENABLE_MAILUSER, attributes); + _runspace.InvokePipeline(cmd); + + } + + Debug.WriteLine(METHOD + ":exit", CLASS); + + } + catch (Exception) + { + //do the rollback - delete the uid + base.Delete(oclass, uid, options); + throw; + } + + return uid; + } + + /// + /// Implementation of UpdateOp.Update + /// + /// + /// + /// + /// + /// + public override Uid Update(UpdateType type, ObjectClass oclass, + ICollection attributes, OperationOptions options) + { + //TODO: Implement Update + return base.Update(type, oclass, attributes, options); + } + + /// + /// Tests if the connector is properly configured and ready + /// + public override void Test() + { + //validate the configuration first, this will check AD configuration too + _configuration.Validate(); + //AD validation (includes configuration validation too) + base.Test(); + //runspace check + _runspace.Test(); + } + + /// + /// Implementation of SynOp.Sync + /// + /// + /// + /// + /// + public override void Sync(ObjectClass objClass, SyncToken token, + SyncResultsHandler handler, OperationOptions options) + { + //TODO: implement Sync + base.Sync(objClass, token, handler, options); + } + + /// + /// Implementation of SynOp.GetLatestSyncToken + /// + /// + public override SyncToken GetLatestSyncToken() + { + //TODO: Implement GetLatestSyncToken + return base.GetLatestSyncToken(); + } + + /// + /// Implementation of SearchOp.ExecuteQuery + /// + /// + /// + /// + /// + public override void ExecuteQuery(ObjectClass oclass, string query, + ResultsHandler handler, OperationOptions options) + { + //TODO: Implement ExecuteQuery + base.ExecuteQuery(oclass, query, handler, options); + } + + + /// + /// Implementation of SearchOp.CreateFilterTranslator + /// + /// + /// + /// + public override Org.IdentityConnectors.Framework.Common.Objects.Filters.FilterTranslator CreateFilterTranslator(ObjectClass oclass, OperationOptions options) + { + //TODO: Implement CreateFilterTranslator + return base.CreateFilterTranslator(oclass, options); + } + + /// + /// Inits the connector with configuration injected + /// + /// + public override void Init(Configuration configuration) + { + base.Init(configuration); + _configuration = (ExchangeConfiguration)configuration; + _runspace = new RunSpaceInstance(RunSpaceInstance.SnapIn.Exchange); + _mapOcInfo = ExchangeUtils.GetOCInfo(); + } + + + /// + /// Dispose resources + /// + public override void Dispose() + { + //lock is probably not necessary + lock (this) + { + if (_disposed) + { + return; + } + if (_runspace != null) + { + _runspace.Dispose(); + } + base.Dispose(); + _disposed = true; + } + + } + + /// + /// Defines the supported object classes by the connector, used for schema building + /// + /// List of supported object classes + protected override IList GetSupportedObjectClasses() + { + IList ocList = base.GetSupportedObjectClasses(); + Assertions.NullCheck(ocList, "ocList"); + ocList.Add(MAILBOX); + ocList.Add(MAILUSER); + return ocList; + } + + /// + /// Gets the object class info for specified object class, used for schema building + /// + /// ObjectClass to get info for + /// ObjectClass' ObjectClassInfo + protected override ObjectClassInfo GetObjectClassInfo(ObjectClass oc) + { + ObjectClassInfo ret = CollectionUtil.GetValue(_mapOcInfo, oc, null) ?? base.GetObjectClassInfo(oc); + Assertions.NullCheck(ret, "ret"); + return ret; + } + + } + + + /// + /// Command definition object + /// + internal sealed class CommandInfo + { + private static IDictionary cinfos = null; + + internal static readonly CommandInfo ENABLE_MAILBOX = new CommandInfo("Enable-Mailbox"); + internal static readonly CommandInfo ENABLE_MAILUSER = new CommandInfo("Enable-MailUser"); + internal static readonly CommandInfo SET_MAILUSER = new CommandInfo("Set-MailUser"); + + + private CommandInfo(string name) + { + Name = name; + if (cinfos == null) + { + cinfos = ExchangeUtils.GetCommandInfo(); + } + + } + + /// + /// Comamnd Name + /// + internal string Name { get; private set; } + + /// + /// Comand Parameters + /// + internal string[] Parameters + { + get + { + var ret = CollectionUtil.GetValue(cinfos, Name, null); + Assertions.NullCheck(ret, "ret"); + + return ret.Parameter; + } + } + + /// + /// Name parameter + /// + internal string NameParameter + { + get + { + var ret = CollectionUtil.GetValue(cinfos, Name, null); + Assertions.NullCheck(ret, "ret"); + + return ret.NameParameter; + } + } + } +} + \ No newline at end of file diff --git a/ExchangeConnector/ExchangeConnector.csproj b/ExchangeConnector/ExchangeConnector.csproj new file mode 100644 index 00000000..1f8ca22b --- /dev/null +++ b/ExchangeConnector/ExchangeConnector.csproj @@ -0,0 +1,84 @@ + + + + Debug + AnyCPU + 9.0.21022 + 2.0 + {F1CB12B6-0DD7-4DAB-9B21-630449B8610D} + Library + Properties + Org.IdentityConnectors.Exchange + Exchange.Connector + v3.5 + 512 + C:\Documents and Settings\Administrator\Application Data\ICSharpCode/SharpDevelop3.0\Settings.SourceAnalysis + + + true + full + false + bin\Debug\ + DEBUG;TRACE + prompt + 4 + bin\Debug\Exchange.Connector.xml + + + pdbonly + true + bin\Release\ + TRACE + prompt + 4 + + + + + 3.5 + + + False + ..\..\..\..\Program Files\Reference Assemblies\Microsoft\WindowsPowerShell\v1.0\System.Management.Automation.dll + + + + + + + + + + + + + + {BDF495CA-0FCD-4E51-A871-D467CDE3B43E} + ActiveDirectoryConnector + False + + + {F140E8DA-52B4-4159-992A-9DA10EA8EEFB} + Common + False + + + {8B24461B-456A-4032-89A1-CD418F7B5B62} + Framework + + + + + + + + + + + \ No newline at end of file diff --git a/ExchangeConnector/ExchangeUtils.cs b/ExchangeConnector/ExchangeUtils.cs new file mode 100644 index 00000000..905dbe0c --- /dev/null +++ b/ExchangeConnector/ExchangeUtils.cs @@ -0,0 +1,338 @@ +/* + * Copyright 2008 Sun Microsystems, Inc. All rights reserved. + * + * U.S. Government Rights - Commercial software. Government users + * are subject to the Sun Microsystems, Inc. standard license agreement + * and applicable provisions of the FAR and its supplements. + * + * Use is subject to license terms. + * + * This distribution may include materials developed by third parties. + * Sun, Sun Microsystems, the Sun logo, Java and Project Identity + * Connectors are trademarks or registered trademarks of Sun + * Microsystems, Inc. or its subsidiaries in the U.S. and other + * countries. + * + * UNIX is a registered trademark in the U.S. and other countries, + * exclusively licensed through X/Open Company, Ltd. + * + * ----------- + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER. + * + * Copyright 2008 Sun Microsystems, Inc. All rights reserved. + * + * The contents of this file are subject to the terms of the Common Development + * and Distribution License(CDDL) (the License). You may not use this file + * except in compliance with the License. + * + * You can obtain a copy of the License at + * http://identityconnectors.dev.java.net/CDDLv1.0.html + * See the License for the specific language governing permissions and + * limitations under the License. + * + * When distributing the Covered Code, include this CDDL Header Notice in each + * file and include the License file at identityconnectors/legal/license.txt. + * If applicable, add the following below this CDDL Header, with the fields + * enclosed by brackets [] replaced by your own identifying information: + * "Portions Copyrighted [year] [name of copyright owner]" + * ----------- + */ +using System; +using System.Collections; +using System.Collections.Generic; +using System.Diagnostics; +using System.IO; +using System.Management.Automation.Runspaces; +using System.Reflection; + +using Microsoft.Win32; +using System.Xml.Serialization; +using Org.IdentityConnectors.Common; +using Org.IdentityConnectors.Framework.Common.Objects; +using Org.IdentityConnectors.Framework.Common.Objects.Filters; +using Org.IdentityConnectors.Framework.Common.Serializer; +using Org.IdentityConnectors.Framework.Spi.Operations; + +namespace Org.IdentityConnectors.Exchange +{ + /// + /// Description of ExchangeUtils. + /// + public class ExchangeUtils + { + private static readonly string CLASS = typeof(ExchangeUtils).ToString(); + + private const string EXCHANGE_REG_KEY = "Software\\Microsoft\\Exchange\\v8.0\\Setup\\"; + private const string EXCHANGE_REG_VALUE = "MsiInstallPath"; + + /// use reflection to load the Exchange assembly + internal static Assembly AssemblyResolver(object p, ResolveEventArgs args) + { + //Add path for the Exchange 2007 DLLs + if (args.Name.Contains("Microsoft.Exchange")) + { + String installPath = GetRegistryStringValue(EXCHANGE_REG_KEY, EXCHANGE_REG_VALUE); + installPath += "\\bin\\" + args.Name.Split(',')[0] + ".dll"; + return Assembly.LoadFrom(installPath); + + } + + return null; + } + + /// + /// Get registry value, which is expected to be a string + /// + /// Registry Key Name + /// Registry Value Name + /// + internal static String GetRegistryStringValue(string keyName, string valName) + { + const string METHOD = "GetRegistryStringValue"; + Debug.WriteLine(METHOD + ":entry", CLASS); + //argument check + if (keyName == null) + { + keyName = ""; + } + if (valName == null) + { + throw new ArgumentNullException("valName"); + } + + RegistryKey regKey = Registry.LocalMachine.OpenSubKey(keyName, false); + try + { + Object val = regKey.GetValue(valName); + if (val != null) + { + RegistryValueKind regType = regKey.GetValueKind(valName); + if (!regType.Equals(RegistryValueKind.String)) + { + throw new InvalidDataException(String.Format("Invalid Registry data type, key name: {0} value name: {1} should be String", keyName, valName)); + } + return Convert.ToString(val); + } + else + { + throw new InvalidDataException(String.Format("Missing value for key name: {0} value name: {1}", keyName, valName)); + } + } + finally + { + if (regKey != null) + { + regKey.Close(); + } + Debug.WriteLine(METHOD + ":exit", CLASS); + } + } + + + + /// + /// reads the object class info definitions from xml + /// + ///Dictionary of object classes + internal static IDictionary GetOCInfo() + { + Assembly assembly = Assembly.GetExecutingAssembly(); + Stream stream = assembly.GetManifestResourceStream("Org.IdentityConnectors.Exchange.ObjectClasses.xml"); + + Assertions.NullCheck(stream, "stream"); + + //we just read + TextReader streamReader = new StreamReader(stream); + String xml = streamReader.ReadToEnd(); + streamReader.Close(); + + //read from xml + var ret = (ICollection)SerializerUtil.DeserializeXmlObject(xml, true); + + Assertions.NullCheck(ret, "ret"); + + //create map of object infos + var map = new Dictionary(ret.Count); + foreach (ObjectClassInfo o in ret) + { + map.Add(new ObjectClass(o.ObjectType.ToString()), o); + } + + return map; + } + + /// + /// + /// + internal static IDictionary GetCommandInfo () + { + Assembly assembly = Assembly.GetExecutingAssembly(); + Stream stream = assembly.GetManifestResourceStream("Org.IdentityConnectors.Exchange.CommandInfos.xml"); + + Assertions.NullCheck(stream, "stream"); + + //we just read + TextReader streamReader = new StreamReader(stream); + + XmlSerializer ser = new XmlSerializer(typeof(XCommandInfos)); + XCommandInfos cInfos = (XCommandInfos)ser.Deserialize(streamReader); + streamReader.Close(); + + Assertions.NullCheck(cInfos, "cInfos"); + + //create map of command infos + var map = new Dictionary(cInfos.XCommandInfo.Length); + foreach (XCommandInfo o in cInfos.XCommandInfo) + { + map.Add(o.Name, o); + } + + return map; + + } + + /// + /// creates command based on the commanf info, reading the calues from attributes + /// + /// Command defition + /// Attribute values + /// Ready to execute Command + internal static Command GetCommand(CommandInfo cmdInfo, ICollection attributes) + { + Assertions.NullCheck(cmdInfo, "cmdInfo"); + Assertions.NullCheck(attributes, "attributes"); + + //create command + Command cmd = new Command(cmdInfo.Name); + + //map name attribute, if mapping specified + if (!string.IsNullOrEmpty(cmdInfo.NameParameter)) + { + object val = GetAttValue(Name.NAME, attributes); + if (val != null) + { + cmd.Parameters.Add(cmdInfo.NameParameter, val); + } + } + + foreach (string attName in cmdInfo.Parameters) + { + object val = GetAttValue(attName, attributes); + if (val != null) + { + cmd.Parameters.Add(attName, val); + } + } + return cmd; + } + + /// + /// Helper method: Gets attribute value from the attribute collection + /// + /// attribute name + /// collection of attribute + /// attribute value as object, null if not found + internal static object GetAttValue(String attName, ICollection attributes) + { + Assertions.NullCheck(attName, "attName"); + Assertions.NullCheck(attributes, "attributes"); + + object value = null; + ConnectorAttribute attribute = ConnectorAttributeUtil.Find(attName, attributes); + + if (attribute != null) + { + value = ConnectorAttributeUtil.GetSingleValue(attribute); + } + + return value; + } + + /// + /// Helper method for filtering the specified attributes from collection of attributes + /// + /// Collection of attributes + /// Attribute names to be filtered out + /// Filtered collection of attributes + internal static ICollection FilterOut(ICollection attributes, params string[] attName) + { + Assertions.NullCheck(attributes, "attributes"); + if (attName == null || attName.Length == 0) + { + return attributes; + } + + IList names = new ArrayList(attName); + ICollection filtered = new List(); + foreach (ConnectorAttribute attribute in attributes) + { + if (!names.Contains(attribute.Name)) + { + filtered.Add(attribute); + } + } + return filtered; + } + + + + + } + + /// + /// DAO class for getting serialized data from xml + /// + [XmlRoot("CommandInfos")] + public class XCommandInfos + { + private readonly ArrayList lstCommandInfos = new ArrayList(); + + /// + /// Command info array + /// + [XmlElement("CommandInfo")] + public XCommandInfo[] XCommandInfo + { + get + { + var items = new XCommandInfo[lstCommandInfos.Count]; + lstCommandInfos.CopyTo(items); + return items; + + } + set + { + if (value == null) return; + var items = (XCommandInfo[])value; + lstCommandInfos.Clear(); + foreach (XCommandInfo item in items) + lstCommandInfos.Add(item); + + } + } + } + + /// + /// DO class for getting serialized data from XML + /// + public class XCommandInfo + { + + /// + /// Command name + /// + public string Name { get; set; } + + /// + /// Special parameter name used as id for this command + /// + public string NameParameter { get; set; } + + /// + /// Command parameters + /// + public string[] Parameter { get; set; } + } + + +} diff --git a/ExchangeConnector/LegacyExchangeConnector.cs b/ExchangeConnector/LegacyExchangeConnector.cs new file mode 100644 index 00000000..76404b84 --- /dev/null +++ b/ExchangeConnector/LegacyExchangeConnector.cs @@ -0,0 +1,402 @@ +/* + * Copyright 2008 Sun Microsystems, Inc. All rights reserved. + * + * U.S. Government Rights - Commercial software. Government users + * are subject to the Sun Microsystems, Inc. standard license agreement + * and applicable provisions of the FAR and its supplements. + * + * Use is subject to license terms. + * + * This distribution may include materials developed by third parties. + * Sun, Sun Microsystems, the Sun logo, Java and Project Identity + * Connectors are trademarks or registered trademarks of Sun + * Microsystems, Inc. or its subsidiaries in the U.S. and other + * countries. + * + * UNIX is a registered trademark in the U.S. and other countries, + * exclusively licensed through X/Open Company, Ltd. + * + * ----------- + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER. + * + * Copyright 2008 Sun Microsystems, Inc. All rights reserved. + * + * The contents of this file are subject to the terms of the Common Development + * and Distribution License(CDDL) (the License). You may not use this file + * except in compliance with the License. + * + * You can obtain a copy of the License at + * http://identityconnectors.dev.java.net/CDDLv1.0.html + * See the License for the specific language governing permissions and + * limitations under the License. + * + * When distributing the Covered Code, include this CDDL Header Notice in each + * file and include the License file at identityconnectors/legal/license.txt. + * If applicable, add the following below this CDDL Header, with the fields + * enclosed by brackets [] replaced by your own identifying information: + * "Portions Copyrighted [year] [name of copyright owner]" + * ----------- + */ +using System.Collections.Generic; +using System.Diagnostics; +using System.Management.Automation.Runspaces; +using Org.IdentityConnectors.ActiveDirectory; +using Org.IdentityConnectors.Common; +using Org.IdentityConnectors.Framework.Common.Exceptions; +using Org.IdentityConnectors.Framework.Common.Objects; +using Org.IdentityConnectors.Framework.Common.Objects.Filters; +using Org.IdentityConnectors.Framework.Spi; +using Org.IdentityConnectors.Framework.Spi.Operations; + +namespace Org.IdentityConnectors.Exchange +{ + /// + /// MS Exchange connector - build to have the same functionality as Exchange resource adapter + /// + /// + //TODO: what about MessageCatalogPath inheritance? + [ConnectorClass("connector_displayName", + typeof(ExchangeConfiguration), + MessageCatalogPath = "Org.IdentityConnectors.ActiveDirectory.Messages" + )] + public class LegacyExchangeConnector : ActiveDirectoryConnector + { + private static readonly string CLASS = typeof(LegacyExchangeConnector).ToString(); + + //hardcoded stuff + internal const string ATT_RECIPIENT_TYPE = "RecipientType"; + internal const string ATT_EXTERNAL_MAIL = "ExternalEmailAddress"; + internal const string ATT_DATABASE = "Database"; + + private static readonly ConnectorAttributeInfo ATTINFO_RECIPIENT_TYPE = + ConnectorAttributeInfoBuilder.Build(ATT_RECIPIENT_TYPE, typeof(string), true, true, true, false); + + private static readonly ConnectorAttributeInfo ATTINFO_EXTERNAL_MAIL = + ConnectorAttributeInfoBuilder.Build(ATT_EXTERNAL_MAIL, typeof(string), false); + + private static readonly ConnectorAttributeInfo ATTINFO_DATABASE = + ConnectorAttributeInfoBuilder.Build(ATT_DATABASE, typeof(string), false, true, true, false); + + private const string RCPT_TYPE_MAIL_BOX = "mailbox"; + private const string RCPT_TYPE_MAIL_USER = "mailuser"; + + + //local vars + private ExchangeConfiguration _configuration = null; + private RunSpaceInstance _runspace = null; + private bool _disposed = false; + + + #region CreateOp.Create implementation + /// + /// Implementation of CreateOp.Create + /// + /// (oc + /// + /// + /// + public override Uid Create(ObjectClass oclass, + ICollection attributes, OperationOptions options) + { + const string METHOD = "Create"; + Debug.WriteLine(METHOD + ":entry", CLASS); + + //get recipient type + string rcptType = ExchangeUtils.GetAttValue(ATT_RECIPIENT_TYPE, attributes) as string; + + if (rcptType != RCPT_TYPE_MAIL_BOX && rcptType != RCPT_TYPE_MAIL_USER) + { + //AD account only, we do nothing + return base.Create(oclass, attributes, options); + } + + //first create the object in AD + Uid uid = base.Create(oclass, filterOut(attributes), options); + + //prepare the command + CommandInfo cmdInfo = rcptType == RCPT_TYPE_MAIL_BOX ? CommandInfo.ENABLE_MAILBOX : CommandInfo.ENABLE_MAILUSER; + Command cmd = ExchangeUtils.GetCommand(cmdInfo, attributes); + + try + { + //execute the command + _runspace.InvokePipeline(cmd); + } catch + { + Trace.TraceWarning("Rolling back AD create for UID: " + uid.GetUidValue()); + //rollback AD create + try + { + base.Delete(oclass, uid, options); + } catch + { + //ignore delete error + Trace.TraceWarning("Not able to rollback AD create for UID: " + uid.GetUidValue()); + } + //rethrow original exception + throw; + } + + Debug.WriteLine(METHOD + ":exit", CLASS); + return uid; + } + #endregion + + /// + /// Implementation of UpdateOp.Update + /// + /// + /// + /// + /// + /// + public override Uid Update(UpdateType type, ObjectClass oclass, + ICollection attributes, OperationOptions options) + { + const string METHOD = "Update"; + Debug.WriteLine(METHOD + ":entry", CLASS); + + Assertions.NullCheck(type, "updatetype"); + Assertions.NullCheck(oclass, "oclass"); + Assertions.NullCheck(attributes, "attributes"); + + //update in AD first + Uid uid = base.Update(type, oclass, filterOut(attributes), options); + //get recipient type + string rcptType = ExchangeUtils.GetAttValue(ATT_RECIPIENT_TYPE, attributes) as string; + + //update is possible for mailuser's external email only + if (rcptType == RCPT_TYPE_MAIL_USER) + { + if (type == UpdateType.REPLACE) + { + //get name attribute + string name = ExchangeUtils.GetAttValue(Name.NAME, attributes) as string; + if (name == null) + { + //we don't know name, but we need it - NOTE: searching for all the default attributes, we need only Name here, it can be improved + ConnectorObject co = ADSearchByUid(uid, oclass, options); + Assertions.NullCheck(co, "co"); + //add to attributes + attributes.Add(co.Name); + } + + Command cmd = ExchangeUtils.GetCommand(CommandInfo.SET_MAILUSER, attributes); + _runspace.InvokePipeline(cmd); + } + else + { + throw new ConnectorException(string.Format("Update type [{0}] not supported", type)); + } + } + + Debug.WriteLine(METHOD + ":exit", CLASS); + return uid; + } + + /// + /// Tests if the connector is properly configured and ready + /// + public override void Test() + { + //validate the configuration first, this will check AD configuration too + _configuration.Validate(); + //AD validation (includes configuration validation too) + base.Test(); + //runspace check + _runspace.Test(); + } + + /// + /// Implementation of SynOp.Sync + /// + /// + /// + /// + /// + public override void Sync(ObjectClass objClass, SyncToken token, + SyncResultsHandler handler, OperationOptions options) + { + //TODO: implement Sync + base.Sync(objClass, token, handler, options); + } + + /// + /// Implementation of SynOp.GetLatestSyncToken + /// + /// + public override SyncToken GetLatestSyncToken() + { + //TODO: Implement GetLatestSyncToken + return base.GetLatestSyncToken(); + } + + /// + /// Implementation of SearchOp.ExecuteQuery + /// + /// + /// + /// + /// + public override void ExecuteQuery(ObjectClass oclass, string query, + ResultsHandler handler, OperationOptions options) + { + //TODO: Implement ExecuteQuery + base.ExecuteQuery(oclass, query, handler, options); + } + + + /// + /// Implementation of SearchOp.CreateFilterTranslator + /// + /// + /// + /// + public override Org.IdentityConnectors.Framework.Common.Objects.Filters.FilterTranslator CreateFilterTranslator(ObjectClass oclass, OperationOptions options) + { + return new LegacyExchangeConnectorFilterTranslator(); + } + + /// + /// Inits the connector with configuration injected + /// + /// + public override void Init(Configuration configuration) + { + base.Init(configuration); + _configuration = (ExchangeConfiguration)configuration; + _runspace = new RunSpaceInstance(RunSpaceInstance.SnapIn.Exchange); + } + + + /// + /// Dispose resources + /// + public override void Dispose() + { + //lock is probably not necessary + lock (this) + { + if (_disposed) + { + return; + } + if (_runspace != null) + { + _runspace.Dispose(); + } + base.Dispose(); + _disposed = true; + } + + } + + /// + /// Gets the object class info for specified object class, used for schema building + /// + /// ObjectClass to get info for + /// ObjectClass' ObjectClassInfo + protected override ObjectClassInfo GetObjectClassInfo(ObjectClass oc) + { + //get the object class from base + ObjectClassInfo oinfo = base.GetObjectClassInfo(oc); + + //add additional attributes for ACCOUNT + if (oc == ObjectClass.ACCOUNT) + { + + var oiBuilder = new ObjectClassInfoBuilder + { + IsContainer = oinfo.IsContainer, + ObjectType = oinfo.ObjectType + }; + oiBuilder.AddAllAttributeInfo(oinfo.ConnectorAttributeInfos); + oiBuilder.AddAttributeInfo(ATTINFO_DATABASE); + oiBuilder.AddAttributeInfo(ATTINFO_RECIPIENT_TYPE); + oiBuilder.AddAttributeInfo(ATTINFO_EXTERNAL_MAIL); + } + + //return + return oinfo; + } + + /// + /// helper method to filter out all attributes used in LegacyExchangeConnector only + /// + /// + /// + private static ICollection filterOut(ICollection attributes) + { + return ExchangeUtils.FilterOut(attributes, ATT_RECIPIENT_TYPE, ATT_DATABASE, ATT_EXTERNAL_MAIL); + } + + /// + /// helper method for searching object in AD by UID + /// + /// + /// + /// + /// + private ConnectorObject ADSearchByUid(Uid uid, ObjectClass oclass, OperationOptions options) + { + Assertions.NullCheck(uid, "uid"); + Assertions.NullCheck(oclass, "oclass"); + if (options == null) + { + options = new OperationOptionsBuilder().Build(); + } + + ConnectorObject ret = null; + Filter filter = FilterBuilder.EqualTo(uid); + var translator = + base.CreateFilterTranslator(oclass, options); + IList queries = translator.Translate(filter); + + if (queries.Count == 1) + { + var result = new SingleResultHandler(); + base.ExecuteQuery(oclass, queries[0], result.Handle, options); + ret = result.CObject; + } + + + return ret; + } + + /// + /// simple single result handler + /// + private sealed class SingleResultHandler + { + public ConnectorObject CObject { get; set; } + public bool Handle(ConnectorObject co) + { + CObject = co; + return false; + } + } + + + } + + /// + /// Filter translator which does MS Exchange specific things + /// + public class LegacyExchangeConnectorFilterTranslator : ActiveDirectoryFilterTranslator + { + protected override string[] GetLdapNamesForAttribute(ConnectorAttribute attr) + { + + if (attr.Is(LegacyExchangeConnector.ATT_DATABASE)) + { + return new string[] { "objectGUID" }; + } + if (attr.Is(LegacyExchangeConnector.ATT_EXTERNAL_MAIL)) + { + return new string[] { "homeMDB" }; + } + return base.GetLdapNamesForAttribute(attr); + } + } + +} diff --git a/ExchangeConnector/ObjectClasses.xml b/ExchangeConnector/ObjectClasses.xml new file mode 100644 index 00000000..f16c47ad --- /dev/null +++ b/ExchangeConnector/ObjectClasses.xml @@ -0,0 +1,114 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/ExchangeConnector/Properties/AssemblyInfo.cs b/ExchangeConnector/Properties/AssemblyInfo.cs new file mode 100644 index 00000000..9e58746d --- /dev/null +++ b/ExchangeConnector/Properties/AssemblyInfo.cs @@ -0,0 +1,36 @@ +using System.Reflection; +using System.Runtime.CompilerServices; +using System.Runtime.InteropServices; + +// General Information about an assembly is controlled through the following +// set of attributes. Change these attribute values to modify the information +// associated with an assembly. +[assembly: AssemblyTitle("ExchangeConnector")] +[assembly: AssemblyDescription("")] +[assembly: AssemblyConfiguration("")] +[assembly: AssemblyCompany("IDM")] +[assembly: AssemblyProduct("ExchangeConnector")] +[assembly: AssemblyCopyright("Copyright © IDM 2008")] +[assembly: AssemblyTrademark("")] +[assembly: AssemblyCulture("")] + +// Setting ComVisible to false makes the types in this assembly not visible +// to COM components. If you need to access a type in this assembly from +// COM, set the ComVisible attribute to true on that type. +[assembly: ComVisible(false)] + +// The following GUID is for the ID of the typelib if this project is exposed to COM +[assembly: Guid("d23ec8a7-e491-4f91-bda5-8900423a410e")] + +// Version information for an assembly consists of the following four values: +// +// Major Version +// Minor Version +// Build Number +// Revision +// +// You can specify all the values or you can default the Build and Revision Numbers +// by using the '*' as shown below: +// [assembly: AssemblyVersion("1.0.*")] +[assembly: AssemblyVersion("1.0.0.0")] +[assembly: AssemblyFileVersion("1.0.0.0")] diff --git a/ExchangeConnector/RunSpaceInstance.cs b/ExchangeConnector/RunSpaceInstance.cs new file mode 100644 index 00000000..06b23be1 --- /dev/null +++ b/ExchangeConnector/RunSpaceInstance.cs @@ -0,0 +1,353 @@ +/* + * Copyright 2008 Sun Microsystems, Inc. All rights reserved. + * + * U.S. Government Rights - Commercial software. Government users + * are subject to the Sun Microsystems, Inc. standard license agreement + * and applicable provisions of the FAR and its supplements. + * + * Use is subject to license terms. + * + * This distribution may include materials developed by third parties. + * Sun, Sun Microsystems, the Sun logo, Java and Project Identity + * Connectors are trademarks or registered trademarks of Sun + * Microsystems, Inc. or its subsidiaries in the U.S. and other + * countries. + * + * UNIX is a registered trademark in the U.S. and other countries, + * exclusively licensed through X/Open Company, Ltd. + * + * ----------- + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER. + * + * Copyright 2008 Sun Microsystems, Inc. All rights reserved. + * + * The contents of this file are subject to the terms of the Common Development + * and Distribution License(CDDL) (the License). You may not use this file + * except in compliance with the License. + * + * You can obtain a copy of the License at + * http://identityconnectors.dev.java.net/CDDLv1.0.html + * See the License for the specific language governing permissions and + * limitations under the License. + * + * When distributing the Covered Code, include this CDDL Header Notice in each + * file and include the License file at identityconnectors/legal/license.txt. + * If applicable, add the following below this CDDL Header, with the fields + * enclosed by brackets [] replaced by your own identifying information: + * "Portions Copyrighted [year] [name of copyright owner]" + * ----------- + */ + +using System; +using System.Collections; +using System.Collections.Generic; +using System.Collections.ObjectModel; +using System.Diagnostics; +using System.Management.Automation; +using System.Management.Automation.Runspaces; +using System.Text; + +using Org.IdentityConnectors.Framework.Common.Exceptions; + +namespace Org.IdentityConnectors.Exchange +{ + + /// + /// The implementation of the run space. This wraps the real run space object + /// from powershell for use in the pool + /// First written for the exchange adapter, the snapin is not needed if you do + /// not use it for exchange + /// + /// Two possible ways of executing a command using different access point to + /// the Runspace: + /// - RunspaceInvoke: simple commands in string form, the command string can + /// contain multiple commands and is basically the same form + /// as what you use when typing a command in the exchange + /// shell + /// - PipelineInvoke: complex (multi) command structured pipelines which also + /// allow complex parameters, like objects, to be passed in. + /// + public sealed class RunSpaceInstance : IDisposable + { + private static readonly string CLASS = typeof(RunSpaceInstance).ToString(); + + // the exchange snap in which needs to be loaded + private const string EXCHANGE_SNAPIN = "Microsoft.Exchange.Management.PowerShell.Admin"; + + private RunspaceConfiguration _runSpaceConfig = null; + private Runspace _runSpace = null; + private RunspaceInvoke _runSpaceInvoke = null; + + //SnapIn type enum + /// + /// Snapin type to load + /// + public enum SnapIn + { + /// + /// None + /// + None, + /// + /// MS Exchange snapin + /// + Exchange + }; + + /// + /// RunSpaceInstance Constructor + /// + /// Type of snapin to be loaded + public RunSpaceInstance(SnapIn snapin) + { + // initialize this + InitRunSpace(snapin); + } + + /// + /// Implementation of IDisposable + /// + public void Dispose() + { + Dispose(true); + GC.SuppressFinalize(this); + } + + /// + /// Dispose/Finalize pattern + /// + /// + private void Dispose(bool disposing) + { + if (disposing) + { + // Free other state (managed objects). + // anything we should do? + } + // clean up the runspace with attached things: + // the API docs show that the RunspaceInvoke will call Dispose on + // the Runspace which in turn calls Close on the Runspace. + if (_runSpaceInvoke != null) + { + _runSpaceInvoke.Dispose(); + } + else + { + if (_runSpace != null) + { + _runSpace.Dispose(); + } + } + } + + /// + /// Finalize + /// + ~RunSpaceInstance() + { + // Simply call Dispose(false). + Dispose(false); + } + + + + /// + /// main initialisation routine for the Runspace + /// + private void InitRunSpace(SnapIn snapin) + { + string METHOD = "InitRunSpace with snapin " + snapin; + Debug.WriteLine(METHOD + ":entry", CLASS); + + + // create a new config from scratch + PSSnapInException snapOutput = null; + _runSpaceConfig = RunspaceConfiguration.Create(); + + switch (snapin) + { + case SnapIn.Exchange: + // used for force load of the exchange dll's + AppDomain.CurrentDomain.AssemblyResolve += + new ResolveEventHandler(ExchangeUtils.AssemblyResolver); + + PSSnapInInfo info = _runSpaceConfig.AddPSSnapIn(EXCHANGE_SNAPIN, + out snapOutput); + break; + } + + //check snapOutput + if (snapOutput != null) + { + throw snapOutput; + } + + // create the real Runspace and open it for processing + _runSpace = RunspaceFactory.CreateRunspace(_runSpaceConfig); + _runSpace.Open(); + _runSpaceInvoke = new RunspaceInvoke(_runSpace); + + Debug.WriteLine(METHOD + ":exit", CLASS); + } + + + /// + /// Test the state of this RunspaceInstance, throws InvalidRunspaceStateException if in incorrect state + /// + public void Test() + { + const string METHOD = "Test"; + Debug.WriteLine(METHOD + ":entry", CLASS); + + // compare the state against the passed in state + if (_runSpace != null + && _runSpace.RunspaceStateInfo.State == RunspaceState.Opened) + { + Debug.WriteLine(METHOD + ":exit", CLASS); + return ; + } + + throw new InvalidRunspaceStateException("Runspace is not in Opened state"); + } + + /// invoke the command + /// command string to execute + /// collection of objects with the result + /// if no command is passed in return null + /// if no output/errors from the invoke return an empty collection + public ICollection InvokeCommand(String commandString) + { + return InvokeCommand(commandString, null); + } + + /// + /// invoke the command + /// The input is passed in to the environment as the $input variable and + /// can be used in the script as follows: + /// invokeCommand("$input | Set-Mailbox", inputEnum) + /// inputEnum in the example could be the output of an earlier + /// invokeCommand call (and thus a complex set of objects) + /// + /// command string to execute + /// input passed in as $input in the execution + /// environment + /// collection of objects with the result + /// if no command is passed in return null + /// if no output from the invoke return an empty collection + public ICollection InvokeCommand(String commandString, + IEnumerable input) + { + const string METHOD = "InvokeCommand"; + Debug.WriteLine(METHOD + ":entry", CLASS); + + IList errors = null; + // trim the spaces and check the length + if (commandString == null || commandString.Trim().Length == 0) + { + Trace.TraceError("CommandString argument can't be null or empty"); + throw new ArgumentException("CommandString argument can't be null or empty"); + } + + // run the command + Collection returns = + _runSpaceInvoke.Invoke(commandString, input, out errors); + //check for errors + checkErrors(errors); + + // an empty collection instead of null when we have executed + if (returns == null) + { + Debug.WriteLine(METHOD + ":exit", CLASS); + returns = new Collection(); + } //if returns + Trace.WriteLine(String.Format("{0} results returned", returns.Count), CLASS); + Debug.WriteLine(METHOD + ":exit", CLASS); + return returns; + + } + + /// + /// invoke the pipeline + /// + /// a collection of commands to execute + /// collection of objects with the result + /// if no command is passed in return null + /// if no output from the invoke return an empty collection + public ICollection InvokePipeline(Collection commands) + { + const string METHOD = "InvokePipeline"; + Debug.WriteLine(METHOD + ":entry", CLASS); + + IList errors = null; + + if (commands == null || commands.Count == 0) + { + Trace.TraceInformation("Commands argument is null or empty", CLASS); + throw new ArgumentException("Commands argument is null or empty"); + } + + // make sure the output is set + errors = null; + Collection results; + + // create the pipeline + Pipeline pipe = _runSpace.CreatePipeline(); + // add the commands to the pipeline + foreach (Command item in commands) + { + pipe.Commands.Add(item); + } // foreach item + // run the pipeline if we have something to execute + results = pipe.Invoke(); + PipelineReader reader = pipe.Error; + errors = (IList)reader.ReadToEnd(); + //check for errors + checkErrors(errors); + // an empty collection instead of null when we have executed + if (results == null) + { + Trace.TraceInformation("NO result returned"); + results = new Collection(); + } //if results + Debug.WriteLine(METHOD + ":exit", CLASS); + return results; + + } + + /// + /// invoke the pipeline + /// + /// a command to execute + /// collection of objects with the result + /// if no command is passed in return null + /// if no output from the invoke return an empty collection + public ICollection InvokePipeline(Command item) + { + // create a new collection and add the command + // specifically not a CommandCollection: that will not work here + Collection commands = new Collection(); + commands.Add(item); + return InvokePipeline(commands); + } + + /// + /// Checks whether errors List contains some error, if so the errors are concatenated and exception is thrown + /// + /// List of error messages + private void checkErrors(IList errors) + { + StringBuilder builder = new StringBuilder(); + foreach (Object error in errors) + { + builder.Append(error.ToString()); + builder.Append("\n"); + } + + if (builder.Length > 0) + { + throw new ConnectorException("Exception when executing PowerShell: " + builder.ToString()); + } + } + + } // class RunSpaceInstance +} From 015095cbe55fa4ffe031542178ec5eafc3207c49 Mon Sep 17 00:00:00 2001 From: dvernon Date: Mon, 24 Nov 2008 16:04:32 +0000 Subject: [PATCH 088/342] Changed order of Configuration Properties --- .../ActiveDirectoryConfiguration.cs | 22 +++++++++---------- 1 file changed, 11 insertions(+), 11 deletions(-) diff --git a/ActiveDirectoryConnector/ActiveDirectoryConfiguration.cs b/ActiveDirectoryConnector/ActiveDirectoryConfiguration.cs index ead9de4d..36bb283f 100644 --- a/ActiveDirectoryConnector/ActiveDirectoryConfiguration.cs +++ b/ActiveDirectoryConnector/ActiveDirectoryConfiguration.cs @@ -49,45 +49,45 @@ namespace Org.IdentityConnectors.ActiveDirectory { public class ActiveDirectoryConfiguration : Org.IdentityConnectors.Framework.Spi.AbstractConfiguration { - [ConfigurationProperty(OperationTypes=new Type[]{typeof(SyncOp)}, Confidential = false, DisplayMessageKey = "display_SyncGlobalCatalogServer", HelpMessageKey = "help_SyncGlobalCatalogServer")] + [ConfigurationProperty(OperationTypes=new Type[]{typeof(SyncOp)}, Confidential = false, DisplayMessageKey = "display_SyncGlobalCatalogServer", HelpMessageKey = "help_SyncGlobalCatalogServer", Order=8)] public String SyncGlobalCatalogServer { get; set; } - [ConfigurationProperty(OperationTypes = new Type[] { typeof(SyncOp) }, Confidential = false, DisplayMessageKey = "display_SyncDomainController", HelpMessageKey = "help_SyncDomainController")] + [ConfigurationProperty(OperationTypes = new Type[] { typeof(SyncOp) }, Confidential = false, DisplayMessageKey = "display_SyncDomainController", HelpMessageKey = "help_SyncDomainController", Order=7)] public String SyncDomainController { get; set; } - [ConfigurationProperty(OperationTypes = new Type[] { typeof(SyncOp) }, Confidential = false, DisplayMessageKey = "display_SyncSearchContext", HelpMessageKey = "help_SyncSearchContext")] + [ConfigurationProperty(OperationTypes = new Type[] { typeof(SyncOp) }, Confidential = false, DisplayMessageKey = "display_SyncSearchContext", HelpMessageKey = "help_SyncSearchContext", Order=9)] public String SyncSearchContext { get; set; } - [ConfigurationProperty(Confidential = false, DisplayMessageKey = "display_domainName", HelpMessageKey = "help_domainName")] + [ConfigurationProperty(Confidential = false, DisplayMessageKey = "display_domainName", HelpMessageKey = "help_domainName", Order=1)] public String DomainName { get; set; } - [ConfigurationProperty(Confidential = false, DisplayMessageKey = "display_DirectoryAdminName", HelpMessageKey = "help_DirectoryAdminName")] + [ConfigurationProperty(Confidential = false, DisplayMessageKey = "display_DirectoryAdminName", HelpMessageKey = "help_DirectoryAdminName", Order=3)] public String DirectoryAdminName { get; set; } - [ConfigurationProperty(Confidential = true, DisplayMessageKey = "display_DirectoryAdminPassword", HelpMessageKey = "help_DirectoryAdminPassword")] + [ConfigurationProperty(Confidential = true, DisplayMessageKey = "display_DirectoryAdminPassword", HelpMessageKey = "help_DirectoryAdminPassword", Order=4)] public String DirectoryAdminPassword { get; set; } - [ConfigurationProperty(Confidential = false, DisplayMessageKey = "display_ObjectClass", HelpMessageKey = "help_ObjectClass")] + [ConfigurationProperty(Confidential = false, DisplayMessageKey = "display_ObjectClass", HelpMessageKey = "help_ObjectClass", Order=10)] public String ObjectClass { get; set; } - [ConfigurationProperty(Confidential = false, DisplayMessageKey = "display_CreateHomeDirectory", HelpMessageKey = "help_CreateHomeDirectory")] + [ConfigurationProperty(Confidential = false, DisplayMessageKey = "display_CreateHomeDirectory", HelpMessageKey = "help_CreateHomeDirectory", Order=11)] public bool CreateHomeDirectory { get; set; } - [ConfigurationProperty(Confidential = false, DisplayMessageKey = "display_SearchContainer", HelpMessageKey = "help_SearchContainer")] + [ConfigurationProperty(Confidential = false, DisplayMessageKey = "display_SearchContainer", HelpMessageKey = "help_SearchContainer", Order=2)] public String SearchContainer { get; set; } - [ConfigurationProperty(Confidential = false, DisplayMessageKey = "display_SearchChildDomains", HelpMessageKey = "help_SearchChildDomains")] + [ConfigurationProperty(Confidential = false, DisplayMessageKey = "display_SearchChildDomains", HelpMessageKey = "help_SearchChildDomains", Order=6)] public bool SearchChildDomains {get;set;} - [ConfigurationProperty(Confidential = false, DisplayMessageKey = "display_LDAPHostName", HelpMessageKey = "help_LDAPHostName")] + [ConfigurationProperty(Confidential = false, DisplayMessageKey = "display_LDAPHostName", HelpMessageKey = "help_LDAPHostName", Order=5)] public String LDAPHostName { get; set; } From dd0259a9249431839d81b9d1f060b329d93dbadb Mon Sep 17 00:00:00 2001 From: dvernon Date: Mon, 24 Nov 2008 19:09:03 +0000 Subject: [PATCH 089/342] Issue 348 - reformat help text for searchContainer for IDM --- ActiveDirectoryConnector/Messages.resx | 6 +----- 1 file changed, 1 insertion(+), 5 deletions(-) diff --git a/ActiveDirectoryConnector/Messages.resx b/ActiveDirectoryConnector/Messages.resx index 90b9a341..6686ea1a 100644 --- a/ActiveDirectoryConnector/Messages.resx +++ b/ActiveDirectoryConnector/Messages.resx @@ -175,11 +175,7 @@ Select if you want searches of Active Directory to include child domains. In addition, the Search Container and Sync Search Context attributes must be set to the top of the parent domain, e.g. DC=mydomain,DC=com. - Specify the container object from which users should be retrieved when loading from the resource. - -For example, if you want to retrieve users from the Users container, enter CN=Users, -DC=MYDOMAIN, -DC=COM. + Specify the container object from which users should be retrieved when loading from the resource. For example, if you want to retrieve users from the Users container, enter CN=Users,DC=MYDOMAIN,DC=COM. Domain controller to use during active sync. Only used if not searching child domains. From 1edb22c854090425105f359a8d67ac97aa35e8eb Mon Sep 17 00:00:00 2001 From: dvernon Date: Mon, 24 Nov 2008 21:59:26 +0000 Subject: [PATCH 090/342] Issue #352 - Added password as a required config attribute. Also messages for config errors --- .../ActiveDirectoryConfiguration.cs | 19 +++++++++++++++---- ActiveDirectoryConnector/Messages.resx | 17 ++++++++++++++++- 2 files changed, 31 insertions(+), 5 deletions(-) diff --git a/ActiveDirectoryConnector/ActiveDirectoryConfiguration.cs b/ActiveDirectoryConnector/ActiveDirectoryConfiguration.cs index 36bb283f..eea6f515 100644 --- a/ActiveDirectoryConnector/ActiveDirectoryConfiguration.cs +++ b/ActiveDirectoryConnector/ActiveDirectoryConfiguration.cs @@ -110,25 +110,36 @@ public override void Validate() // can't lookup the schema without the domain name if ((DomainName == null) || (DomainName.Length == 0)) { - message += "->Domain name not supplied "; + message += ConnectorMessages.Format( + "confReqParam_domainName", "Domain name not supplied "); foundError = true; } if ((DirectoryAdminName == null) || (DirectoryAdminName.Length == 0)) { - message += "->Directory administrator name not supplied "; + message += ConnectorMessages.Format( + "confReqParam_adminName", "Directory administrator name not supplied "); + foundError = true; + } + + if ((DirectoryAdminPassword == null) || (DirectoryAdminPassword.Length == 0)) + { + message += ConnectorMessages.Format( + "confReqParam_adminPass", "Directory administrator password not supplied "); foundError = true; } if ((ObjectClass == null) || (ObjectClass.Length == 0)) { - message += "->ObjectClass was not supplied "; + message += ConnectorMessages.Format( + "confReqParam_objClass", "ObjectClass was not supplied "); foundError = true; } if ((SearchContainer == null) || (SearchContainer.Length == 0)) { - message += "->Search Container was not supplied "; + message += ConnectorMessages.Format( + "confReqParam_searchContainer", "Search Container was not supplied "); foundError = true; } diff --git a/ActiveDirectoryConnector/Messages.resx b/ActiveDirectoryConnector/Messages.resx index 6686ea1a..ab791a4c 100644 --- a/ActiveDirectoryConnector/Messages.resx +++ b/ActiveDirectoryConnector/Messages.resx @@ -118,7 +118,7 @@ System.Resources.ResXResourceWriter, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 - Windows Active Directory Connector + Windows Active Directory Connector. Create Home Directory @@ -240,4 +240,19 @@ An execption occurred during validation of user {0}. The user was successfully authenticated, but the user's sid could not be determined. + + Directory administrator name not supplied. + + + Directory administrator password not supplied. + + + Domain name not supplied. + + + ObjectClass was not supplied. + + + Search Container was not supplied. + \ No newline at end of file From 998e077ba8e476cc25df3791e5467fb03894b63e Mon Sep 17 00:00:00 2001 From: kyarbro Date: Tue, 2 Dec 2008 22:39:46 +0000 Subject: [PATCH 091/342] issue # 351 - change defaults for object pooling --- Common/Pooling.cs | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/Common/Pooling.cs b/Common/Pooling.cs index d605da0b..ce8cf214 100644 --- a/Common/Pooling.cs +++ b/Common/Pooling.cs @@ -51,29 +51,29 @@ public sealed class ObjectPoolConfiguration { /** * Max objects (idle+active). */ - private int _maxObjects = 5; + private int _maxObjects = 10; /** * Max idle objects. */ - private int _maxIdle = 3; + private int _maxIdle = 10; /** * Max time to wait if the pool is waiting for a free object to become * available before failing. Zero means don't wait */ - private long _maxWait = 60*1000; + private long _maxWait = 150 * 1000; /** * Minimum time to wait before evicting an idle object. * Zero means don't wait */ - private long _minEvictableIdleTimeMillis = 60 * 1000; + private long _minEvictableIdleTimeMillis = 120 * 1000; /** * Minimum number of idle objects. */ - private int _minIdle = 0; + private int _minIdle = 1; /** From e63c2c73ee16952e3a9e43c8e4df706f2540e719 Mon Sep 17 00:00:00 2001 From: rcauble Date: Wed, 3 Dec 2008 16:07:25 +0000 Subject: [PATCH 092/342] Issue#341: overhaul to update api/spi --- .../ActiveDirectoryConnector.cs | 28 +- Framework/ApiOperations.cs | 158 ++++++++-- Framework/Common.cs | 2 +- Framework/CommonObjects.cs | 48 ++- Framework/SpiOperations.cs | 191 ++++++++---- FrameworkInternal/Api.cs | 28 +- FrameworkInternal/ApiLocalOperations.cs | 279 ++++++++++-------- FrameworkInternal/Serializer.cs | 2 - FrameworkTests/ObjectSerializationTests.cs | 11 +- FrameworkTests/UpdateImplTests.cs | 174 +++-------- 10 files changed, 559 insertions(+), 362 deletions(-) diff --git a/ActiveDirectoryConnector/ActiveDirectoryConnector.cs b/ActiveDirectoryConnector/ActiveDirectoryConnector.cs index 0344303c..24529a5f 100644 --- a/ActiveDirectoryConnector/ActiveDirectoryConnector.cs +++ b/ActiveDirectoryConnector/ActiveDirectoryConnector.cs @@ -57,6 +57,13 @@ namespace Org.IdentityConnectors.ActiveDirectory { + public enum UpdateType { + ADD, + DELETE, + REPLACE + } + + /// /// The Active Directory Connector /// @@ -65,7 +72,7 @@ namespace Org.IdentityConnectors.ActiveDirectory MessageCatalogPath = "Org.IdentityConnectors.ActiveDirectory.Messages" )] public class ActiveDirectoryConnector : CreateOp, Connector, SchemaOp, DeleteOp, - SearchOp, TestOp, AdvancedUpdateOp, ScriptOnResourceOp, SyncOp, + SearchOp, TestOp, UpdateAttributeValuesOp, ScriptOnResourceOp, SyncOp, AuthenticateOp, AttributeNormalizer, PoolableConnector { // This is the list of attributes returned by default if no attributes are @@ -1138,9 +1145,26 @@ public virtual void Test() #endregion #region AdvancedUpdateOp Members + public Uid Update(ObjectClass objclass, Uid uid, ICollection attrs, OperationOptions options) { + return Update(UpdateType.REPLACE,objclass,ConnectorAttributeUtil.AddUid(attrs,uid),options); + } + + public Uid AddAttributeValues(ObjectClass objclass, + Uid uid, + ICollection valuesToAdd, + OperationOptions options) { + return Update(UpdateType.ADD,objclass,ConnectorAttributeUtil.AddUid(valuesToAdd, uid),options); + } + + public Uid RemoveAttributeValues(ObjectClass objclass, + Uid uid, + ICollection valuesToRemove, + OperationOptions options) { + return Update(UpdateType.DELETE,objclass,ConnectorAttributeUtil.AddUid(valuesToRemove, uid),options); + } // implementation of AdvancedUpdateSpiOp - public virtual Uid Update(UpdateType type, ObjectClass oclass, + protected virtual Uid Update(UpdateType type, ObjectClass oclass, ICollection attributes, OperationOptions options) { Uid updatedUid = null; diff --git a/Framework/ApiOperations.cs b/Framework/ApiOperations.cs index 94f3fc52..c9df6f33 100644 --- a/Framework/ApiOperations.cs +++ b/Framework/ApiOperations.cs @@ -262,42 +262,140 @@ void Sync(ObjectClass objClass, SyncToken token, SyncToken GetLatestSyncToken(); } - /// - /// Determines the type of update to perform. - /// - public enum UpdateApiType { - /** - * Replace each attribute value with the one provided. - */ - REPLACE, - /** - * Added the values provided to the existing attribute values on the - * native target. - */ - ADD, - /** - * Remove the attribute values from the existing target values. - */ - DELETE - } - + /** + * Updates a {@link ConnectorObject}. This operation + * is supported for those connectors that implement + * either {@link UpdateOp} or the more advanced + * {@link UpdateAttributeValuesOp}. + */ public interface UpdateApiOp : APIOperation { + /** - * Update the object specified. The type is used to determine if the updates - * are additive, subtractive, or replacement of values provided. - * - * @param type - * determines the type of update to expect. - * @param obj - * information to find the object and the attributes to perform - * the type of update against. + * Update the object specified by the {@link ObjectClass} and {@link Uid}, + * replacing the current values of each attribute with the values + * provided. + *

+ * For each input attribute, replace + * all of the current values of that attribute in the target object with + * the values of that attribute. + *

+ * If the target object does not currently contain an attribute that the + * input set contains, then add this + * attribute (along with the provided values) to the target object. + *

+ * If the value of an attribute in the input set is + * {@code null}, then do one of the following, depending on + * which is most appropriate for the target: + *

    + *
  • If possible, remove that attribute from the target + * object entirely.
  • + *
  • Otherwise, replace all of the current values of that + * attribute in the target object with a single value of + * {@code null}.
  • + *
+ * @param objclass + * the type of object to modify. Must not be null. + * @param uid + * the uid of the object to modify. Must not be null. + * @param replaceAttributes + * set of new {@link Attribute}. the values in this set + * represent the new, merged values to be applied to the object. + * This set may also include {@link OperationalAttributes operational attributes}. + * Must not be null. + * @param options + * additional options that impact the way this operation is run. + * May be null. * @return the {@link Uid} of the updated object in case the update changes * the formation of the unique identifier. */ - Uid Update(UpdateApiType type, ObjectClass objectclass, - ICollection attrs, OperationOptions options); - + Uid Update(ObjectClass objclass, + Uid uid, + ICollection replaceAttributes, + OperationOptions options); + + /** + * Update the object specified by the {@link ObjectClass} and {@link Uid}, + * adding to the current values of each attribute the values provided. + *

+ * For each attribute that the input set contains, add to + * the current values of that attribute in the target object all of the + * values of that attribute in the input set. + *

+ * NOTE that this does not specify how to handle duplicate values. + * The general assumption for an attribute of a {@code ConnectorObject} + * is that the values for an attribute may contain duplicates. + * Therefore, in general simply append the provided values + * to the current value for each attribute. + *

+ * IMPLEMENTATION NOTE: for connectors that merely implement {@link UpdateOp} + * and not {@link UpdateAttributeValuesOp} this method will be simulated by + * fetching, merging, and calling + * {@link UpdateOp#update(ObjectClass, Uid, Set, OperationOptions)}. Therefore, + * connector implementations are encourage to implement {@link UpdateAttributeValuesOp} + * from a performance and atomicity standpoint. + * @param objclass + * the type of object to modify. Must not be null. + * @param uid + * the uid of the object to modify. Must not be null. + * @param valuesToAdd + * set of {@link Attribute} deltas. The values for the attributes + * in this set represent the values to add to attributes in the object. + * merged. This set must not include {@link OperationalAttributes operational attributes}. + * Must not be null. + * @param options + * additional options that impact the way this operation is run. + * May be null. + * @return the {@link Uid} of the updated object in case the update changes + * the formation of the unique identifier. + */ + Uid AddAttributeValues(ObjectClass objclass, + Uid uid, + ICollection valuesToAdd, + OperationOptions options); + + /** + * Update the object specified by the {@link ObjectClass} and {@link Uid}, + * removing from the current values of each attribute the values provided. + *

+ * For each attribute that the input set contains, + * remove from the current values of that attribute in the target object + * any value that matches one of the values of the attribute from the input set. + *

+ * NOTE that this does not specify how to handle unmatched values. + * The general assumption for an attribute of a {@code ConnectorObject} + * is that the values for an attribute are merely representational state. + * Therefore, the implementer should simply ignore any provided value + * that does not match a current value of that attribute in the target + * object. Deleting an unmatched value should always succeed. + *

+ * IMPLEMENTATION NOTE: for connectors that merely implement {@link UpdateOp} + * and not {@link UpdateAttributeValuesOp} this method will be simulated by + * fetching, merging, and calling + * {@link UpdateOp#update(ObjectClass, Uid, Set, OperationOptions)}. Therefore, + * connector implementations are encourage to implement {@link UpdateAttributeValuesOp} + * from a performance and atomicity standpoint. + * @param objclass + * the type of object to modify. Must not be null. + * @param uid + * the uid of the object to modify. Must not be null. + * @param valuesToRemove + * set of {@link Attribute} deltas. The values for the attributes + * in this set represent the values to remove from attributes in the object. + * merged. This set must not include {@link OperationalAttributes operational attributes}. + * Must not be null. + * @param options + * additional options that impact the way this operation is run. + * May be null. + * @return the {@link Uid} of the updated object in case the update changes + * the formation of the unique identifier. + */ + Uid RemoveAttributeValues(ObjectClass objclass, + Uid uid, + ICollection valuesToRemove, + OperationOptions options); + } + public interface ValidateApiOp : APIOperation { /** diff --git a/Framework/Common.cs b/Framework/Common.cs index 86214673..9547d7fa 100644 --- a/Framework/Common.cs +++ b/Framework/Common.cs @@ -91,7 +91,7 @@ static FrameworkUtil() { SafeType.Get(); temp[SafeType.Get()]= SafeType.Get(); - temp[SafeType.Get()]= + temp[SafeType.Get()]= SafeType.Get(); temp[SafeType.Get()]= SafeType.Get(); diff --git a/Framework/CommonObjects.cs b/Framework/CommonObjects.cs index 5c6f5b88..bd442442 100644 --- a/Framework/CommonObjects.cs +++ b/Framework/CommonObjects.cs @@ -254,6 +254,36 @@ public static ICollection GetSpecialAttributes(ICollection FilterUid(ICollection attrs) { + Assertions.NullCheck(attrs, "attrs"); + HashSet ret = new HashSet(); + foreach (ConnectorAttribute attr in attrs) { + if (!(attr is Uid)) { + ret.Add(attr); + } + } + return ret; + } + + /** + * Returns a mutable copy of the original set with the uid attribute added. + * @param attrs The original set. Must not be null. + * @param uid The uid. Must not be null. + * @return A mutable copy of the original set with the uid attribute added. + */ + public static ICollection AddUid(ICollection attrs, Uid uid) { + Assertions.NullCheck(attrs, "attrs"); + Assertions.NullCheck(uid, "uid"); + HashSet ret = new HashSet(attrs); + ret.Add(uid); + return ret; + } /** * Determines if this attribute is a special attribute. @@ -2131,14 +2161,26 @@ public override string ToString() * Builder for {@link OperationOptions}. */ public sealed class OperationOptionsBuilder { - private readonly IDictionary _options = new - Dictionary(); + private readonly IDictionary _options; /** * Create a builder with an empty set of options. */ public OperationOptionsBuilder() { - + _options = new Dictionary(); + } + + /** + * Create a builder from an existing set of options. + * @param options The existing set of options. Must not be null. + */ + public OperationOptionsBuilder(OperationOptions options) { + Assertions.NullCheck(options, "options"); + // clone options to do a deep copy in case anything + // is an array + IDictionary operationOptionsClone = (IDictionary) SerializerUtil + .CloneObject(options.Options); + _options = CollectionUtil.NewDictionary(operationOptionsClone); } /** diff --git a/Framework/SpiOperations.cs b/Framework/SpiOperations.cs index 31f25c70..c40186de 100644 --- a/Framework/SpiOperations.cs +++ b/Framework/SpiOperations.cs @@ -46,54 +46,7 @@ namespace Org.IdentityConnectors.Framework.Spi.Operations { - /** - * Used as a parameter to specify the type of update to perform. - */ - public enum UpdateType { - /** - * Replace each attribute value with the one provided. If the value is - * null then remove the attribute on set it to - * null as applicable. - */ - REPLACE, - /** - * Adds the values provided to the existing attribute values on the - * native target. - */ - ADD, - /** - * Remove the attribute values from the existing target values. - */ - DELETE - } - /** - * The implementation of this method is expected to handle the following types - * of update. - * - * @author Will Droste - * @version $Revision $ - * @since 1.0 - */ - public interface AdvancedUpdateOp : SPIOperation { - - - /** - * The {@link Connector} developer is responsible for updating the object - * provided based on the type provided. If the operation can not be - * accomplished with the information provided throw a type of - * {@link RuntimeException} that best describes the problem. - * - * @param type - * determines the type of update to expect. - * @param obj - * information to find the object and the attributes to perform - * the type of update against. - * @return the {@link Uid} of the updated object in case the update changes - * the formation of the unique identifier. - */ - Uid Update(UpdateType type, ObjectClass objclass, ICollection attrs, OperationOptions options); - } /** * Authenticate an object based on their unique identifier and password. @@ -319,20 +272,140 @@ void Sync(ObjectClass objClass, SyncToken token, } /** - * The developer of a Connector should implement this interface - * if the Connector will allow an authorized caller to update - * (i.e., modify or replace) objects on the target resource. + * The developer of a Connector should implement either this interface or the + * {@link UpdateAttributeValuesOp} interface if the Connector will allow an authorized + * caller to update (i.e., modify or replace) objects on the target resource. + *

+ * This update method is simpler to implement than {link UpdateAttributeValuesOp}, + * which must handle any of several different types of update that the caller + * may specify. However a true implementation of {link UpdateAttributeValuesOp} + * offers better performance and atomicity semantics. + * + * @author Will Droste + * @version $Revision $ + * @since 1.0 */ public interface UpdateOp : SPIOperation { - ///

- /// Update a particular object based on the ObjectClass and change set provided. - /// - /// Type of object to change. - /// Deltas for the object which include the Uid so the - /// object can be found. - /// a new Uid if the deltra prompt a change the Uid otherwise the orginal - /// one passed in. - Uid Update(ObjectClass objectclass, ICollection attrs, OperationOptions options); + /** + * Update the object specified by the {@link ObjectClass} and {@link Uid}, + * replacing the current values of each attribute with the values + * provided. + *

+ * For each input attribute, replace + * all of the current values of that attribute in the target object with + * the values of that attribute. + *

+ * If the target object does not currently contain an attribute that the + * input set contains, then add this + * attribute (along with the provided values) to the target object. + *

+ * If the value of an attribute in the input set is + * {@code null}, then do one of the following, depending on + * which is most appropriate for the target: + *

    + *
  • If possible, remove that attribute from the target + * object entirely.
  • + *
  • Otherwise, replace all of the current values of that + * attribute in the target object with a single value of + * {@code null}.
  • + *
+ * @param objclass + * the type of object to modify. Will never be null. + * @param uid + * the uid of the object to modify. Will never be null. + * @param replaceAttributes + * set of new {@link Attribute}. the values in this set + * represent the new, merged values to be applied to the object. + * This set may also include {@link OperationalAttributes operational attributes}. + * Will never be null. + * @param options + * additional options that impact the way this operation is run. + * Will never be null. + * @return the {@link Uid} of the updated object in case the update changes + * the formation of the unique identifier. + */ + Uid Update(ObjectClass objclass, + Uid uid, + ICollection replaceAttributes, + OperationOptions options); + } + + /** + * More advanced implementation of {@link UpdateOp} to be implemented by + * connectors that wish to offer better performance and atomicity semantics + * for the methods {@link UpdateApiOp#addAttributeValues(ObjectClass, Uid, Set, OperationOptions)} + * and {@link UpdateApiOp#removeAttributeValues(ObjectClass, Uid, Set, OperationOptions)}. + */ + public interface UpdateAttributeValuesOp : UpdateOp { + + /** + * Update the object specified by the {@link ObjectClass} and {@link Uid}, + * adding to the current values of each attribute the values provided. + *

+ * For each attribute that the input set contains, add to + * the current values of that attribute in the target object all of the + * values of that attribute in the input set. + *

+ * NOTE that this does not specify how to handle duplicate values. + * The general assumption for an attribute of a {@code ConnectorObject} + * is that the values for an attribute may contain duplicates. + * Therefore, in general simply append the provided values + * to the current value for each attribute. + *

+ * @param objclass + * the type of object to modify. Will never be null. + * @param uid + * the uid of the object to modify. Will never be null. + * @param valuesToAdd + * set of {@link Attribute} deltas. The values for the attributes + * in this set represent the values to add to attributes in the object. + * merged. This set will never include {@link OperationalAttributes operational attributes}. + * Will never be null. + * @param options + * additional options that impact the way this operation is run. + * Will never be null. + * @return the {@link Uid} of the updated object in case the update changes + * the formation of the unique identifier. + */ + Uid AddAttributeValues(ObjectClass objclass, + Uid uid, + ICollection valuesToAdd, + OperationOptions options); + + /** + * Update the object specified by the {@link ObjectClass} and {@link Uid}, + * removing from the current values of each attribute the values provided. + *

+ * For each attribute that the input set contains, + * remove from the current values of that attribute in the target object + * any value that matches one of the values of the attribute from the input set. + *

+ * NOTE that this does not specify how to handle unmatched values. + * The general assumption for an attribute of a {@code ConnectorObject} + * is that the values for an attribute are merely representational state. + * Therefore, the implementer should simply ignore any provided value + * that does not match a current value of that attribute in the target + * object. Deleting an unmatched value should always succeed. + * @param objclass + * the type of object to modify. Will never be null. + * @param uid + * the uid of the object to modify. Will never be null. + * @param valuesToRemove + * set of {@link Attribute} deltas. The values for the attributes + * in this set represent the values to remove from attributes in the object. + * merged. This set will never include {@link OperationalAttributes operational attributes}. + * Will never be null. + * @param options + * additional options that impact the way this operation is run. + * Will never be null.. + * @return the {@link Uid} of the updated object in case the update changes + * the formation of the unique identifier. + */ + Uid RemoveAttributeValues(ObjectClass objclass, + Uid uid, + ICollection valuesToRemove, + OperationOptions options); + } public interface TestOp : SPIOperation { diff --git a/FrameworkInternal/Api.cs b/FrameworkInternal/Api.cs index b2c0736b..f66fb76b 100644 --- a/FrameworkInternal/Api.cs +++ b/FrameworkInternal/Api.cs @@ -565,9 +565,33 @@ public void Search(ObjectClass oclass,Filter filter, ResultsHandler handler, Ope /** * {@inheritDoc} */ - public Uid Update(UpdateApiType type, ObjectClass objclass, ICollection attrs, OperationOptions options) { + public Uid Update(ObjectClass objclass, Uid uid, ICollection attrs, OperationOptions options) { return ((UpdateApiOp) this.GetOperationCheckSupported(SafeType.Get())) - .Update(type, objclass, attrs, options); + .Update(objclass, uid, attrs, options); + } + + /** + * {@inheritDoc} + */ + public Uid AddAttributeValues( + ObjectClass objclass, + Uid uid, + ICollection attrs, + OperationOptions options) { + return ((UpdateApiOp) this.GetOperationCheckSupported(SafeType.Get())) + .AddAttributeValues(objclass, uid, attrs, options); + } + + /** + * {@inheritDoc} + */ + public Uid RemoveAttributeValues( + ObjectClass objclass, + Uid uid, + ICollection attrs, + OperationOptions options) { + return ((UpdateApiOp) this.GetOperationCheckSupported(SafeType.Get())) + .RemoveAttributeValues(objclass, uid, attrs, options); } /** diff --git a/FrameworkInternal/ApiLocalOperations.cs b/FrameworkInternal/ApiLocalOperations.cs index 5bb6b683..f34866b0 100644 --- a/FrameworkInternal/ApiLocalOperations.cs +++ b/FrameworkInternal/ApiLocalOperations.cs @@ -53,6 +53,7 @@ using Org.IdentityConnectors.Framework.Spi.Operations; using System.Reflection; using System.Collections.Generic; +using System.Linq; namespace Org.IdentityConnectors.Framework.Impl.Api.Local.Operations { @@ -1178,11 +1179,6 @@ public void Test() { * update. */ public class UpdateImpl : ConnectorAPIOperationRunner , UpdateApiOp { - /** - * Static map between API/SPI update types. - */ - private static readonly IDictionary CONV_TYPE = - new Dictionary(); /** * All the operational attributes that can not be added or deleted. */ @@ -1192,9 +1188,6 @@ public class UpdateImpl : ConnectorAPIOperationRunner , UpdateApiOp { "Operational attribute '{0}' can not be added or deleted only replaced."; static UpdateImpl() { - CONV_TYPE[UpdateApiType.ADD]= UpdateType.ADD; - CONV_TYPE[UpdateApiType.DELETE]= UpdateType.DELETE; - CONV_TYPE[UpdateApiType.REPLACE]= UpdateType.REPLACE; OPERATIONAL_ATTRIBUTE_NAMES.Add(Name.NAME); CollectionUtil.AddAll(OPERATIONAL_ATTRIBUTE_NAMES, OperationalAttributes.OPERATIONAL_ATTRIBUTE_NAMES); @@ -1208,159 +1201,215 @@ public UpdateImpl(ConnectorOperationalContext context, Connector connector) :base(context,connector){ } - /** - * Create a new instance of the handler for the type of update the connector - * can support and run it. - * - * @see UpdateApiOp#update(UpdateApiOp.Type, ConnectorObject) - */ - public Uid Update(UpdateApiType type, ObjectClass objclass, - ICollection attributes, - OperationOptions options) { + + public Uid Update(ObjectClass objclass, + Uid uid, + ICollection replaceAttributes, + OperationOptions options) { // validate all the parameters.. - ValidateInput(type, objclass, attributes); - //convert null into empty + ValidateInput(objclass,uid,replaceAttributes,false); + //cast null as empty if ( options == null ) { options = new OperationOptionsBuilder().Build(); } - Uid ret = null; - Connector c = GetConnector(); ObjectNormalizerFacade normalizer = GetNormalizer(objclass); - ICollection normalizedAttributes = - normalizer.NormalizeAttributes(attributes); - if (c is AdvancedUpdateOp) { - // easy way its an advance update - ret = ((AdvancedUpdateOp) c).Update(CONV_TYPE[type], objclass, normalizedAttributes, options); - } else if (c is UpdateOp) { - // check that this connector supports Search.. - if (ReflectionUtil.FindInHierarchyOf(typeof(SearchOp<>),c.GetType()) == null) { - string MSG = "Connector must support: " + typeof(SearchOp<>); - throw new ConfigurationException(MSG); - } - // get the connector object from the resource... - Uid uid = ConnectorAttributeUtil.GetUidAttribute(normalizedAttributes); - ConnectorObject o = GetConnectorObject(objclass, uid, options); - if (o == null) { - throw new UnknownUidException(uid, objclass); - } - // merge the update data.. - ICollection mergeAttrs = Merge(type, normalizedAttributes, o.GetAttributes()); - // update the object.. - ret = ((UpdateOp) c).Update(objclass, mergeAttrs, options); + uid = (Uid)normalizer.NormalizeAttribute(uid); + replaceAttributes = + normalizer.NormalizeAttributes(replaceAttributes); + UpdateOp op = (UpdateOp)GetConnector(); + Uid ret = op.Update(objclass, uid, replaceAttributes, options); + return (Uid)normalizer.NormalizeAttribute(ret); + } + + public Uid AddAttributeValues(ObjectClass objclass, + Uid uid, + ICollection valuesToAdd, + OperationOptions options) { + // validate all the parameters.. + ValidateInput(objclass,uid,valuesToAdd,true); + //cast null as empty + if ( options == null ) { + options = new OperationOptionsBuilder().Build(); + } + + ObjectNormalizerFacade normalizer = + GetNormalizer(objclass); + uid = (Uid)normalizer.NormalizeAttribute(uid); + valuesToAdd = + normalizer.NormalizeAttributes(valuesToAdd); + UpdateOp op = (UpdateOp)GetConnector(); + Uid ret; + if ( op is UpdateAttributeValuesOp ) { + UpdateAttributeValuesOp valueOp = + (UpdateAttributeValuesOp)op; + ret = valueOp.AddAttributeValues(objclass, uid, valuesToAdd, options); + } + else { + ICollection replaceAttributes = + FetchAndMerge(objclass,uid,valuesToAdd,true,options); + ret = op.Update(objclass, uid, replaceAttributes, options); } return (Uid)normalizer.NormalizeAttribute(ret); } + + public Uid RemoveAttributeValues(ObjectClass objclass, + Uid uid, + ICollection valuesToRemove, + OperationOptions options) { + // validate all the parameters.. + ValidateInput(objclass,uid,valuesToRemove,true); + //cast null as empty + if ( options == null ) { + options = new OperationOptionsBuilder().Build(); + } + + ObjectNormalizerFacade normalizer = + GetNormalizer(objclass); + uid = (Uid)normalizer.NormalizeAttribute(uid); + valuesToRemove = + normalizer.NormalizeAttributes(valuesToRemove); + UpdateOp op = (UpdateOp)GetConnector(); + Uid ret; + if ( op is UpdateAttributeValuesOp ) { + UpdateAttributeValuesOp valueOp = + (UpdateAttributeValuesOp)op; + ret = valueOp.RemoveAttributeValues(objclass, uid, valuesToRemove, options); + } + else { + ICollection replaceAttributes = + FetchAndMerge(objclass,uid,valuesToRemove,false,options); + ret = op.Update(objclass, uid, replaceAttributes, options); + } + return (Uid)normalizer.NormalizeAttribute(ret); + } + + private ICollection FetchAndMerge(ObjectClass objclass, Uid uid, + ICollection valuesToChange, + bool add, + OperationOptions options) + { + // check that this connector supports Search.. + if (ReflectionUtil.FindInHierarchyOf(typeof(SearchOp<>),GetConnector().GetType()) != null) { + String MSG = "Connector must support search"; + throw new InvalidOperationException(MSG); + } + + //add attrs to get to operation options, so that the + //object we fetch has exactly the set of attributes we require + //(there may be ones that are not in the default set) + OperationOptionsBuilder builder = new OperationOptionsBuilder(options); + ICollection attrNames = new HashSet(); + foreach (ConnectorAttribute attribute in valuesToChange) { + attrNames.Add(attribute.Name); + } + builder.AttributesToGet=(attrNames.ToArray()); + options = builder.Build(); + + // get the connector object from the resource... + ConnectorObject o = GetConnectorObject(objclass, uid, options); + if (o == null) { + throw new UnknownUidException(uid, objclass); + } + // merge the update data.. + ICollection mergeAttrs = Merge(valuesToChange, o.GetAttributes(),add); + return mergeAttrs; + } /** * Merges two connector objects into a single updated object. */ - public ICollection Merge(UpdateApiType type, - ICollection updateAttrs, - ICollection baseAttrs) { + public ICollection Merge(ICollection updateAttrs, + ICollection baseAttrs, bool add) { // return the merged attributes ICollection ret = new HashSet(); + // create map that can be modified to get the subset of changes IDictionary baseAttrMap = ConnectorAttributeUtil.ToMap(baseAttrs); // run through attributes of the current object.. foreach (ConnectorAttribute updateAttr in updateAttrs) { - // ignore uid because its immutable.. - if (updateAttr is Uid) { - continue; - } // get the name of the update attributes - string name = updateAttr.Name; - ConnectorAttribute baseAttr = CollectionUtil.GetValue(baseAttrMap, name, null); - ICollection values; + String name = updateAttr.Name; + // remove each attribute that is an update attribute.. + ConnectorAttribute baseAttr = CollectionUtil.GetValue(baseAttrMap,name,null); + IList values; ConnectorAttribute modifiedAttr; - if (UpdateApiType.ADD.Equals(type)) { - if (baseAttr == null) { - modifiedAttr = updateAttr; - } else { - // create a new list with the base attribute to add to.. - values = CollectionUtil.NewList(baseAttr.Value); - CollectionUtil.AddAll(values,updateAttr.Value); - modifiedAttr = ConnectorAttributeBuilder.Build(name, values); - } - } else if (UpdateApiType.DELETE.Equals(type)) { - if (baseAttr == null) { - // nothing to actually do the attribute does not exist - continue; - } else { - // create a list with the base attribute to remove from.. - values = CollectionUtil.NewList(baseAttr.Value); - foreach (Object val in updateAttr.Value) { - values.Remove(val); - } - // if the values are empty send a null to the connector.. - if (values.Count == 0) { - modifiedAttr = ConnectorAttributeBuilder.Build(name); - } else { - modifiedAttr = ConnectorAttributeBuilder.Build(name, values); - } - } - } else if (UpdateApiType.REPLACE.Equals(type)) { - modifiedAttr = updateAttr; - } else { - throw new ArgumentException("Unknown Type: " + type); - } + if (add) { + if (baseAttr == null) { + modifiedAttr = updateAttr; + } else { + // create a new list with the base attribute to add to.. + values = CollectionUtil.NewList(baseAttr.Value); + CollectionUtil.AddAll(values,updateAttr.Value); + modifiedAttr = ConnectorAttributeBuilder.Build(name, values); + } + } + else { + if (baseAttr == null) { + // nothing to actually do the attribute do not exist + continue; + } else { + // create a list with the base attribute to remove from.. + values = CollectionUtil.NewList(baseAttr.Value); + foreach (Object val in updateAttr.Value) { + values.Remove(val); + } + // if the values are empty send a null to the connector.. + if (values.Count == 0) { + modifiedAttr = ConnectorAttributeBuilder.Build(name); + } else { + modifiedAttr = ConnectorAttributeBuilder.Build(name, values); + } + } + } ret.Add(modifiedAttr); } - // add the rest of the base attributes that were not update attrs - IDictionary updateAttrMap = - ConnectorAttributeUtil.ToMap(updateAttrs); - foreach (ConnectorAttribute a in baseAttrs) { - if (!updateAttrMap.ContainsKey(a.Name)) { - ret.Add(a); - } - } - // always add the UID.. - ret.Add(updateAttrMap[Uid.NAME]); return ret; } - - - /// - /// Get the ConnectorObject that is merged to create the change set - /// for the SPI simple update. - /// - ConnectorObject GetConnectorObject(ObjectClass oclass, Uid uid, OperationOptions options) { + + /** + * Get the {@link ConnectorObject} to modify. + */ + private ConnectorObject GetConnectorObject(ObjectClass oclass, Uid uid, OperationOptions options) { // attempt to get the connector object.. - GetApiOp get = new GetImpl(new SearchImpl((ConnectorOperationalContext)GetOperationalContext(),GetConnector())); + GetApiOp get = new GetImpl(new SearchImpl((ConnectorOperationalContext)GetOperationalContext(), + GetConnector())); return get.GetObject(oclass, uid, options); } - /// - /// Validate all the input to determine if request can be handled. - /// - public static void ValidateInput(UpdateApiType type, ObjectClass objclass, - ICollection attrs) { - Assertions.NullCheck(type, "type"); + + /** + * Makes things easier if you can trust the input. + */ + public static void ValidateInput(ObjectClass objclass, + Uid uid, + ICollection attrs, bool isDelta) { + Assertions.NullCheck(uid, "uid"); Assertions.NullCheck(objclass, "objclass"); Assertions.NullCheck(attrs, "attrs"); - // check to make sure there's a uid.. - if (ConnectorAttributeUtil.GetUidAttribute(attrs) == null) { + // check to make sure there's not a uid.. + if (ConnectorAttributeUtil.GetUidAttribute(attrs) != null) { throw new ArgumentException( - "Parameter 'attrs' must contain a 'Uid'!"); + "Parameter 'attrs' contains a uid."); } // check for things only valid during ADD/DELETE - if (UpdateApiType.ADD.Equals(type) || UpdateApiType.DELETE.Equals(type)) { + if (isDelta) { foreach (ConnectorAttribute attr in attrs) { Assertions.NullCheck(attr, "attr"); // make sure that none of the values are null.. if (attr.Value == null) { throw new ArgumentException( - "Can not ADD or DELETE 'null' value."); + "Can not add or remove a 'null' value."); } // make sure that if this an delete/add that it doesn't include - // certain attributes because it doesn't make any since.. - string name = attr.Name; + // certain attributes because it doesn't make any sense.. + String name = attr.Name; if (OPERATIONAL_ATTRIBUTE_NAMES.Contains(name)) { String msg = String.Format(OPERATIONAL_ATTRIBUTE_ERR, name); throw new ArgumentException(msg); } } } - } + } } #endregion diff --git a/FrameworkInternal/Serializer.cs b/FrameworkInternal/Serializer.cs index 3a768101..64c7fbd7 100644 --- a/FrameworkInternal/Serializer.cs +++ b/FrameworkInternal/Serializer.cs @@ -1424,8 +1424,6 @@ internal static class CommonObjectHandlers { new List(); static CommonObjectHandlers() { - HANDLERS.Add( new EnumSerializationHandler(typeof(UpdateApiType), - "UpdateApiOpType") ); HANDLERS.Add( new AlreadyExistsExceptionHandler() ); HANDLERS.Add( new ConfigurationExceptionHandler() ); HANDLERS.Add( new ConnectionBrokenExceptionHandler() ); diff --git a/FrameworkTests/ObjectSerializationTests.cs b/FrameworkTests/ObjectSerializationTests.cs index ef822a20..8941a4ac 100644 --- a/FrameworkTests/ObjectSerializationTests.cs +++ b/FrameworkTests/ObjectSerializationTests.cs @@ -845,16 +845,7 @@ public void TestExceptions() { } } - - [Test] - public void TestUpdateApiOpType() { - UpdateApiType v1 = UpdateApiType.ADD; - UpdateApiType v2 = (UpdateApiType)CloneObject(v1); - - Assert.AreEqual(v1, v2); - } - - + [Test] public void TestHelloRequest() { HelloRequest v1 = new HelloRequest(); diff --git a/FrameworkTests/UpdateImplTests.cs b/FrameworkTests/UpdateImplTests.cs index 7e97d0af..41527258 100644 --- a/FrameworkTests/UpdateImplTests.cs +++ b/FrameworkTests/UpdateImplTests.cs @@ -55,22 +55,29 @@ namespace FrameworkTests [TestFixture] public class UpdateImplTests { + [Test] + [ExpectedException(typeof(ArgumentNullException ))] + public void ValidateUidArg() { + UpdateImpl.ValidateInput(ObjectClass.ACCOUNT, null, new HashSet(),true); + } [Test] [ExpectedException(typeof(ArgumentNullException ))] public void ValidateObjectClassArg() { - UpdateImpl.ValidateInput(UpdateApiType.ADD, null, new HashSet()); + UpdateImpl.ValidateInput(null, new Uid("foo"), new HashSet(),true); } [Test] [ExpectedException(typeof(ArgumentNullException ))] public void ValidateAttrsArg() { - UpdateImpl.ValidateInput(UpdateApiType.ADD, ObjectClass.ACCOUNT, null); + UpdateImpl.ValidateInput(ObjectClass.ACCOUNT, new Uid("foo"),null,true); } [Test] [ExpectedException(typeof(ArgumentException ))] - public void ValidateNoUidAttribute() { - UpdateImpl.ValidateInput(UpdateApiType.ADD, ObjectClass.ACCOUNT, new HashSet()); + public void ValidateUidAttribute() { + HashSet attrs=new HashSet(); + attrs.Add(new Uid("foo")); + UpdateImpl.ValidateInput(ObjectClass.ACCOUNT, new Uid("foo"),attrs,true); } [Test] @@ -78,35 +85,17 @@ public void ValidateNoUidAttribute() { public void ValidateAddWithNullAttribute() { ICollection attrs = new HashSet(); attrs.Add(ConnectorAttributeBuilder.Build("something")); - UpdateImpl.ValidateInput(UpdateApiType.ADD, ObjectClass.ACCOUNT, attrs); - } - - [Test] - [ExpectedException(typeof(ArgumentException ))] - public void ValidateDeleteWithNullAttribute() { - ICollection attrs = new HashSet(); - attrs.Add(ConnectorAttributeBuilder.Build("something")); - UpdateImpl.ValidateInput(UpdateApiType.DELETE, ObjectClass.ACCOUNT, attrs); + UpdateImpl.ValidateInput(ObjectClass.ACCOUNT, new Uid("foo"), attrs, true); } - + [Test] [ExpectedException(typeof(ArgumentException))] public void ValidateAttemptToAddName() { ICollection attrs = new HashSet(); attrs.Add(new Name("fadf")); - attrs.Add(new Uid(1 + "")); - UpdateImpl.ValidateInput(UpdateApiType.ADD, ObjectClass.ACCOUNT, attrs); + UpdateImpl.ValidateInput(ObjectClass.ACCOUNT, new Uid("foo"), attrs, true); } - [Test] - [ExpectedException(typeof(ArgumentException))] - public void ValidateAttemptToDeleteName() { - ICollection attrs = new HashSet(); - attrs.Add(new Name("fadf")); - attrs.Add(new Uid(1 + "")); - UpdateImpl.ValidateInput(UpdateApiType.DELETE, ObjectClass.ACCOUNT, attrs); - } - [Test] public void ValidateAttemptToAddDeleteOperationalAttribute() { // list of all the operational attributes.. @@ -119,9 +108,8 @@ public void ValidateAttemptToAddDeleteOperationalAttribute() { foreach (ConnectorAttribute attr in list) { ICollection attrs = new HashSet(); attrs.Add(attr); - attrs.Add(new Uid(1 + "")); try { - UpdateImpl.ValidateInput(UpdateApiType.DELETE, ObjectClass.ACCOUNT, attrs); + UpdateImpl.ValidateInput(ObjectClass.ACCOUNT, new Uid("1"), attrs, true); Assert.Fail("Failed: " + attr.Name); } catch (ArgumentException) { // this is a good thing.. @@ -136,24 +124,6 @@ private static SecureString newSecureString(string password) { } return rv; } - - [Test] - [ExpectedException(typeof(ArgumentException))] - public void ValidateAttemptToAddNull() { - ICollection attrs = new HashSet(); - attrs.Add(ConnectorAttributeBuilder.Build("something w/ null")); - attrs.Add(new Uid(1 + "")); - UpdateImpl.ValidateInput(UpdateApiType.ADD, ObjectClass.ACCOUNT, attrs); - } - - [Test] - [ExpectedException(typeof(ArgumentException))] - public void ValidateAttemptToDeleteNull() { - ICollection attrs = new HashSet(); - attrs.Add(ConnectorAttributeBuilder.Build("something w/ null")); - attrs.Add(new Uid(1 + "")); - UpdateImpl.ValidateInput(UpdateApiType.DELETE, ObjectClass.ACCOUNT, attrs); - } /// /// Validate two collections are equal. (Not fast but effective) @@ -174,15 +144,14 @@ public static bool AreEqual(ICollection arg1, public void MergeAddAttribute() { UpdateImpl up = new UpdateImpl(null, null); ICollection actual; - ConnectorAttribute uid = new Uid(1 + ""); - ICollection baseAttrs = CollectionUtil.NewSet(uid); - ICollection expected = CollectionUtil.NewSet(uid); - ICollection changeset = CollectionUtil.NewSet(uid); + ICollection baseAttrs = CollectionUtil.NewSet(); + ICollection expected = CollectionUtil.NewSet(); + ICollection changeset = CollectionUtil.NewSet(); // attempt to add a value to an attribute.. ConnectorAttribute cattr = ConnectorAttributeBuilder.Build("abc", 2); changeset.Add(cattr); expected.Add(ConnectorAttributeBuilder.Build("abc", 2)); - actual = up.Merge(UpdateApiType.ADD, changeset, baseAttrs); + actual = up.Merge(changeset, baseAttrs,true); Assert.IsTrue(AreEqual(expected, actual)); } @@ -190,17 +159,16 @@ public void MergeAddAttribute() { public void MergeAddToExistingAttribute() { UpdateImpl up = new UpdateImpl(null, null); ICollection actual; - ConnectorAttribute uid = new Uid(1 + ""); - ICollection baseAttrs = CollectionUtil.NewSet(uid); - ICollection expected = CollectionUtil.NewSet(uid); - ICollection changeset = CollectionUtil.NewSet(uid); + ICollection baseAttrs = CollectionUtil.NewSet(); + ICollection expected = CollectionUtil.NewSet(); + ICollection changeset = CollectionUtil.NewSet(); // attempt to add a value to an attribute.. ConnectorAttribute battr = ConnectorAttributeBuilder.Build("abc", 1); ConnectorAttribute cattr = ConnectorAttributeBuilder.Build("abc", 2); baseAttrs.Add(battr); changeset.Add(cattr); expected.Add(ConnectorAttributeBuilder.Build("abc", 1, 2)); - actual = up.Merge(UpdateApiType.ADD, changeset, baseAttrs); + actual = up.Merge(changeset, baseAttrs, true); Assert.IsTrue(AreEqual(expected, actual)); } @@ -208,14 +176,13 @@ public void MergeAddToExistingAttribute() { public void MergeDeleteNonExistentAttribute() { UpdateImpl up = new UpdateImpl(null, null); ICollection actual; - ConnectorAttribute uid = new Uid(1 + ""); - ICollection baseAttrs = CollectionUtil.NewSet(uid); - ICollection expected = CollectionUtil.NewSet(uid); - ICollection changeset = CollectionUtil.NewSet(uid); + ICollection baseAttrs = CollectionUtil.NewSet(); + ICollection expected = CollectionUtil.NewSet(); + ICollection changeset = CollectionUtil.NewSet(); // attempt to add a value to an attribute.. ConnectorAttribute cattr = ConnectorAttributeBuilder.Build("abc", 2); changeset.Add(cattr); - actual = up.Merge(UpdateApiType.DELETE, changeset, baseAttrs); + actual = up.Merge(changeset, baseAttrs, false); Assert.IsTrue(AreEqual(expected, actual)); } @@ -223,17 +190,16 @@ public void MergeDeleteNonExistentAttribute() { public void MergeDeleteToExistingAttribute() { UpdateImpl up = new UpdateImpl(null, null); ICollection actual; - ConnectorAttribute uid = new Uid(1 + ""); - ICollection baseAttrs = CollectionUtil.NewSet(uid); - ICollection expected = CollectionUtil.NewSet(uid); - ICollection changeset = CollectionUtil.NewSet(uid); + ICollection baseAttrs = CollectionUtil.NewSet(); + ICollection expected = CollectionUtil.NewSet(); + ICollection changeset = CollectionUtil.NewSet(); // attempt to add a value to an attribute.. ConnectorAttribute battr = ConnectorAttributeBuilder.Build("abc", 1, 2); ConnectorAttribute cattr = ConnectorAttributeBuilder.Build("abc", 2); baseAttrs.Add(battr); changeset.Add(cattr); expected.Add(ConnectorAttributeBuilder.Build("abc", 1)); - actual = up.Merge(UpdateApiType.DELETE, changeset, baseAttrs); + actual = up.Merge(changeset, baseAttrs, false); Assert.IsTrue(AreEqual(expected, actual)); } @@ -241,88 +207,20 @@ public void MergeDeleteToExistingAttribute() { public void MergeDeleteToExistingAttributeCompletely() { UpdateImpl up = new UpdateImpl(null, null); ICollection actual; - ConnectorAttribute uid = new Uid(1 + ""); - ICollection baseAttrs = CollectionUtil.NewSet(uid); - ICollection expected = CollectionUtil.NewSet(uid); - ICollection changeset = CollectionUtil.NewSet(uid); + ICollection baseAttrs = CollectionUtil.NewSet(); + ICollection expected = CollectionUtil.NewSet(); + ICollection changeset = CollectionUtil.NewSet(); // attempt to add a value to an attribute.. ConnectorAttribute battr = ConnectorAttributeBuilder.Build("abc", 1, 2); ConnectorAttribute cattr = ConnectorAttributeBuilder.Build("abc", 1, 2); baseAttrs.Add(battr); changeset.Add(cattr); expected.Add(ConnectorAttributeBuilder.Build("abc")); - actual = up.Merge(UpdateApiType.DELETE, changeset, baseAttrs); - Assert.IsTrue(AreEqual(expected, actual)); - } - - [Test] - public void MergeReplaceExistingAttribute() { - UpdateImpl up = new UpdateImpl(null, null); - ICollection actual; - ConnectorAttribute uid = new Uid(1 + ""); - ICollection baseAttrs = CollectionUtil.NewSet(uid); - ICollection expected = CollectionUtil.NewSet(uid); - ICollection changeset = CollectionUtil.NewSet(uid); - // attempt to add a value to an attribute.. - ConnectorAttribute battr = ConnectorAttributeBuilder.Build("abc", 1, 2); - ConnectorAttribute cattr = ConnectorAttributeBuilder.Build("abc", 2); - baseAttrs.Add(battr); - changeset.Add(cattr); - expected.Add(ConnectorAttributeBuilder.Build("abc", 2)); - actual = up.Merge(UpdateApiType.REPLACE, changeset, baseAttrs); - Assert.IsTrue(AreEqual(expected, actual)); - } - - [Test] - public void MergeReplaceNonExistentAttribute() { - UpdateImpl up = new UpdateImpl(null, null); - ICollection actual; - ConnectorAttribute uid = new Uid(1 + ""); - ICollection baseAttrs = CollectionUtil.NewSet(uid); - ICollection expected = CollectionUtil.NewSet(uid); - ICollection changeset = CollectionUtil.NewSet(uid); - // attempt to add a value to an attribute.. - ConnectorAttribute cattr = ConnectorAttributeBuilder.Build("abc", 2); - changeset.Add(cattr); - expected.Add(ConnectorAttributeBuilder.Build("abc", 2)); - actual = up.Merge(UpdateApiType.REPLACE, changeset, baseAttrs); + actual = up.Merge(changeset, baseAttrs, false); Assert.IsTrue(AreEqual(expected, actual)); } + - [Test] - public void MergeReplaceAttributeRemoval() { - UpdateImpl up = new UpdateImpl(null, null); - ICollection actual; - ConnectorAttribute uid = new Uid(1 + ""); - ICollection baseAttrs = CollectionUtil.NewSet(uid); - ICollection expected = CollectionUtil.NewSet(uid); - ICollection changeset = CollectionUtil.NewSet(uid); - // attempt to add a value to an attribute.. - ConnectorAttribute battr = ConnectorAttributeBuilder.Build("abc", 1, 2); - ConnectorAttribute cattr = ConnectorAttributeBuilder.Build("abc"); - baseAttrs.Add(battr); - changeset.Add(cattr); - expected.Add(ConnectorAttributeBuilder.Build("abc")); - actual = up.Merge(UpdateApiType.REPLACE, changeset, baseAttrs); - Assert.IsTrue(AreEqual(expected, actual)); - } - [Test] - public void MergeReplaceSameAttribute() { - UpdateImpl up = new UpdateImpl(null, null); - ICollection actual; - ConnectorAttribute uid = new Uid(1 + ""); - ICollection baseAttrs = CollectionUtil.NewSet(uid); - ICollection expected = CollectionUtil.NewSet(uid); - ICollection changeset = CollectionUtil.NewSet(uid); - // attempt to add a value to an attribute.. - ConnectorAttribute battr = ConnectorAttributeBuilder.Build("abc", 1); - ConnectorAttribute cattr = ConnectorAttributeBuilder.Build("abc", 1); - baseAttrs.Add(battr); - changeset.Add(cattr); - expected.Add(cattr); - actual = up.Merge(UpdateApiType.REPLACE, changeset, baseAttrs); - Assert.IsTrue(AreEqual(expected, actual)); - } } } From a8ef46a9b923d030ea90670619c0dec030a0d99b Mon Sep 17 00:00:00 2001 From: dvernon Date: Wed, 3 Dec 2008 20:57:04 +0000 Subject: [PATCH 093/342] Issue #349 - Make member one of the default attributes to return for groups. --- ActiveDirectoryConnector/ActiveDirectoryConnector.cs | 1 + 1 file changed, 1 insertion(+) diff --git a/ActiveDirectoryConnector/ActiveDirectoryConnector.cs b/ActiveDirectoryConnector/ActiveDirectoryConnector.cs index 24529a5f..2715e67d 100644 --- a/ActiveDirectoryConnector/ActiveDirectoryConnector.cs +++ b/ActiveDirectoryConnector/ActiveDirectoryConnector.cs @@ -139,6 +139,7 @@ public class ActiveDirectoryConnector : CreateOp, Connector, SchemaOp, DeleteOp, "mail", "groupType", "objectClass", + "member", }; // This is the list of attributes returned by default if no attributes are From d48c3244088e52710b3672fe3530ce35c569010b Mon Sep 17 00:00:00 2001 From: tknappek Date: Thu, 4 Dec 2008 16:15:17 +0000 Subject: [PATCH 094/342] Updated scope to make it able to extend in ExchangeConnector --- ActiveDirectoryConnector/ActiveDirectoryFilterTranslator.cs | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/ActiveDirectoryConnector/ActiveDirectoryFilterTranslator.cs b/ActiveDirectoryConnector/ActiveDirectoryFilterTranslator.cs index d44cf74f..d4cb058f 100644 --- a/ActiveDirectoryConnector/ActiveDirectoryFilterTranslator.cs +++ b/ActiveDirectoryConnector/ActiveDirectoryFilterTranslator.cs @@ -54,7 +54,7 @@ namespace Org.IdentityConnectors.ActiveDirectory /// C#. There are a few changes, but not many ... that will change over /// time of course. /// - class ActiveDirectoryFilterTranslator : AbstractFilterTranslator + public class ActiveDirectoryFilterTranslator : AbstractFilterTranslator { protected override String CreateAndExpression(String leftExpression, String rightExpression) { @@ -347,7 +347,8 @@ static void GetLdapFilterValue(StringBuilder builder, * Returns null if the attribute cannot be specified in an LDAP * filter. */ - String[] GetLdapNamesForAttribute(ConnectorAttribute attr) { + + protected virtual String[] GetLdapNamesForAttribute(ConnectorAttribute attr) { // Special processing for certain connector attributes. String[] attrNames = null; if (attr is Uid) { From 142ac5cfc990c47e0576dce737c33d1fbcfdea08 Mon Sep 17 00:00:00 2001 From: dvernon Date: Thu, 4 Dec 2008 20:28:48 +0000 Subject: [PATCH 095/342] Fixed test compilation errors broken by framework changes to Update --- .../ActiveDirectoryConnectorTest.cs | 50 ++++++++++++++++--- 1 file changed, 43 insertions(+), 7 deletions(-) diff --git a/ActiveDirectoryConnectorTests/ActiveDirectoryConnectorTest.cs b/ActiveDirectoryConnectorTests/ActiveDirectoryConnectorTest.cs index 4d68a6de..856bff26 100644 --- a/ActiveDirectoryConnectorTests/ActiveDirectoryConnectorTest.cs +++ b/ActiveDirectoryConnectorTests/ActiveDirectoryConnectorTest.cs @@ -269,6 +269,37 @@ public void TestBasics_Group() } } + /* + * This is not a test at all, it's just a convienient way to create a + * bunch of users + * + [Test] + public void TestCreate_aBunchOfAccounts() + { + //Initialize Connector + ActiveDirectoryConnector connector = new ActiveDirectoryConnector(); + connector.Init(GetConfiguration()); + Uid createUid = null; + for (int i = 0; i < 6500; i++) + { + if (i % 500 == 0) + { + Console.Write("Created {0} users", i); + } + + try + { + ICollection createAttributes = GetNormalAttributes_Account(); + createUid = CreateAndVerifyObject(connector, ObjectClass.ACCOUNT, createAttributes); + } + catch (Exception e) + { + // ignore + } + } + } + */ + [Test] public void TestCreate_Account() { @@ -1079,7 +1110,9 @@ public void TestRemoveAttributeValue() modifyAttributes.Add(createUid); modifyAttributes.Add(ConnectorAttributeBuilder.Build("otherHomePhone", "512.555.1212")); - connector.Update(UpdateType.DELETE, ObjectClass.ACCOUNT, modifyAttributes, null); + connector.RemoveAttributeValues(ObjectClass.ACCOUNT, + ConnectorAttributeUtil.GetUidAttribute(modifyAttributes), + modifyAttributes, null); Filter uidFilter = FilterBuilder.EqualTo(createUid); IList objects = TestHelpers.SearchToList(connector, ObjectClass.ACCOUNT, uidFilter); @@ -1136,7 +1169,7 @@ public void TestContainerChange_account() updateAttrs.Add(ConnectorAttributeBuilder.Build("ad_container", groupPath)); updateAttrs.Add(createUserUid); - connector.Update(UpdateType.REPLACE, ObjectClass.ACCOUNT, updateAttrs, null); + connector.Update(ObjectClass.ACCOUNT, ConnectorAttributeUtil.GetUidAttribute(updateAttrs), updateAttrs, null); ICollection results = TestHelpers.SearchToList( connector, ObjectClass.ACCOUNT, FilterBuilder.EqualTo(createUserUid)); @@ -1850,7 +1883,8 @@ public void TestSync(bool searchChildDomains, String syncSearchContext) attributes = new List(); attributes.Add(createdUids.First()); attributes.Add(ConnectorAttributeBuilder.Build("sn", "replaced")); - connector.Update(UpdateType.REPLACE, ObjectClass.ACCOUNT, attributes, null); + connector.Update(ObjectClass.ACCOUNT, + ConnectorAttributeUtil.GetUidAttribute(attributes), attributes, null); syncHelper.AddModUid(createdUids.First(), attributes); for(int i = 0; i < 10; i++) @@ -1865,7 +1899,7 @@ public void TestSync(bool searchChildDomains, String syncSearchContext) attributes = new List(); attributes.Add(createdUids.Last()); attributes.Add(ConnectorAttributeBuilder.Build("sn", "replaced")); - connector.Update(UpdateType.REPLACE, ObjectClass.ACCOUNT, attributes, null); + connector.Update(ObjectClass.ACCOUNT, ConnectorAttributeUtil.GetUidAttribute(attributes), attributes, null); syncHelper.AddModUid(createdUids.Last(), attributes); // sync, and verify @@ -2102,7 +2136,8 @@ public Uid UpdateReplaceAndVerifyObject(ActiveDirectoryConnector connector, connector, oclass, uidFilter); Assert.AreEqual(1, currentConnectorObjects.Count); - Uid updatedUid = connector.Update(UpdateType.REPLACE, oclass, + Uid updatedUid = connector.Update(oclass, + ConnectorAttributeUtil.GetUidAttribute(attributes), attributes, null); Assert.IsNotNull(updatedUid); @@ -2139,7 +2174,8 @@ public Uid UpdateAddAndVerifyUser(ActiveDirectoryConnector connector, // make sure the uid is present in the attributes attributes.Add(uid); // now update with ADD to add additional home phones - Uid updatedUid = connector.Update(UpdateType.ADD, oclass, + Uid updatedUid = connector.AddAttributeValues(oclass, + ConnectorAttributeUtil.GetUidAttribute(attributes), attributes, null); // find it back @@ -2728,7 +2764,7 @@ public GuardedString GetGuardedString(string regularString) int GetRandomNumber() { - const int randomRange = 100000; + const int randomRange = 1000000; int number = _rand.Next(randomRange); #if DEBUG // make sure the debug numbers are in a different From 80997d4ab28d24253f306c28e92d6169107ffb25 Mon Sep 17 00:00:00 2001 From: dvernon Date: Mon, 8 Dec 2008 14:33:48 +0000 Subject: [PATCH 096/342] Issue #358 - Moving Exchange connector's schema generation code to ad to share between the two connectors. --- .../ActiveDirectoryConnector.cs | 532 ++---------------- .../ActiveDirectoryConnector.csproj | 4 + ActiveDirectoryConnector/CommonUtils.cs | 91 +++ ActiveDirectoryConnector/ObjectClasses.xml | 126 +++++ 4 files changed, 283 insertions(+), 470 deletions(-) create mode 100644 ActiveDirectoryConnector/CommonUtils.cs create mode 100644 ActiveDirectoryConnector/ObjectClasses.xml diff --git a/ActiveDirectoryConnector/ActiveDirectoryConnector.cs b/ActiveDirectoryConnector/ActiveDirectoryConnector.cs index 2715e67d..c205309c 100644 --- a/ActiveDirectoryConnector/ActiveDirectoryConnector.cs +++ b/ActiveDirectoryConnector/ActiveDirectoryConnector.cs @@ -75,87 +75,7 @@ public class ActiveDirectoryConnector : CreateOp, Connector, SchemaOp, DeleteOp, SearchOp, TestOp, UpdateAttributeValuesOp, ScriptOnResourceOp, SyncOp, AuthenticateOp, AttributeNormalizer, PoolableConnector { - // This is the list of attributes returned by default if no attributes are - // requested in the options field of ExecuteQuery for Account - public readonly static ICollection AccountAttributesReturnedByDefault = - new HashSet(StringComparer.CurrentCultureIgnoreCase) { -// "userPassword", - "sAMAccountName", - "givenName", - "sn", - "displayName", - "mail", - "telephoneNumber", - "employeeId", - "division", - "mobile", - "middleName", - "description", - "department", - "manager", - "title", - "initials", - "co", - "company", - "facsimileTelephoneNumber", - "homePhone", - "streetAddress", - "l", - "st", - "postalCode", - "TerminalServicesInitialProgram", - "TerminalServicesWorkDirectory", - "AllowLogon", - "MaxConnectionTime", - "MaxDisconnectionTime", - "MaxIdleTime", - "ConnectClientDrivesAtLogon", - "ConnectClientPrintersAtLogon", - "DefaultToMainPrinter", - "BrokenConnectionAction", - "ReconnectionAction", - "EnableRemoteControl", - "TerminalServicesProfilePath", - "TerminalServicesHomeDirectory", - "TerminalServicesHomeDrive", - "uSNChanged", - "ad_container", - "otherHomePhone", - "distinguishedName", - "objectClass", - "homeDirectory", - }; - - // This is the list of attributes returned by default if no attributes are - // requested in the options field of ExecuteQuery for groups - public readonly static ICollection GroupAttributesReturnedByDefault = - new HashSet(StringComparer.CurrentCultureIgnoreCase) - { - "cn", - "samAccountName", - "description", - "displayName", - "managedby", - "mail", - "groupType", - "objectClass", - "member", - }; - - // This is the list of attributes returned by default if no attributes are - // requested in the options field of ExecuteQuery for groups - public readonly static ICollection OuAttributesReturnedByDefault = - new HashSet(StringComparer.CurrentCultureIgnoreCase) - { - Name.NAME, - "ou", - "displayName", - }; - - public static IDictionary> RegularAttributesReturnedByDefault = null; - // optimization for attrs to get in the hopse of improving recon performance for idm - public static IDictionary> SpecialAttributesReturnedByDefault = null; - + public static IDictionary> AttributesReturnedByDefault = null; // special attribute names public static readonly string ATT_CONTAINER = "ad_container"; @@ -183,18 +103,11 @@ public class ActiveDirectoryConnector : CreateOp, Connector, SchemaOp, DeleteOp, ActiveDirectoryConfiguration _configuration = null; ActiveDirectoryUtils _utils = null; - private Schema _schema = null; - private ActiveDirectorySchema _ADSchema = null; - - static ActiveDirectoryConnector() + private static Schema _schema = null; + public ActiveDirectoryConnector() { - // populate default attributes - RegularAttributesReturnedByDefault = new Dictionary>(); - RegularAttributesReturnedByDefault.Add(ObjectClass.ACCOUNT, AccountAttributesReturnedByDefault); - RegularAttributesReturnedByDefault.Add(ObjectClass.GROUP, GroupAttributesReturnedByDefault); - RegularAttributesReturnedByDefault.Add(ouObjectClass, OuAttributesReturnedByDefault); - - SpecialAttributesReturnedByDefault = new Dictionary>(); + // populate default attributes and Schema + Schema(); } #region CreateOp Members @@ -328,22 +241,36 @@ public virtual void Dispose() #endregion + protected ICollection GetDefaultAttributeListForObjectClass( + ObjectClass oclass, ObjectClassInfo oclassInfo) + { + ICollection defaultAttributeList = new List(); + + foreach (ConnectorAttributeInfo attInfo in oclassInfo.ConnectorAttributeInfos) + { + if (attInfo.IsReturnedByDefault) + { + defaultAttributeList.Add(attInfo.Name); + } + } + + return defaultAttributeList; + } + #region SchemaOp Members // implementation of SchemaSpiOp public Schema Schema() { Trace.TraceInformation("Schema method"); - if (_schema != null) { Trace.TraceInformation("Returning cached schema"); return _schema; } - Trace.TraceInformation("Retrieving schema"); - SchemaBuilder schemaBuilder = new SchemaBuilder(SafeType.Get(this)); + AttributesReturnedByDefault = new Dictionary>(); //iterate through supported object classes foreach(ObjectClass oc in GetSupportedObjectClasses()) @@ -351,6 +278,15 @@ public Schema Schema() ObjectClassInfo ocInfo = GetObjectClassInfo(oc); Assertions.NullCheck(ocInfo, "ocInfo"); + //populate the list of default attributes to get + AttributesReturnedByDefault.Add(oc, new HashSet()); + foreach (ConnectorAttributeInfo caInfo in ocInfo.ConnectorAttributeInfos) + { + if( caInfo.IsReturnedByDefault ) { + AttributesReturnedByDefault[oc].Add(caInfo.Name); + } + } + //add object class to schema schemaBuilder.DefineObjectClass(ocInfo); @@ -385,11 +321,13 @@ public Schema Schema() /// Defines the supported object classes by the connector, used for schema building /// /// List of supported object classes - protected virtual IList GetSupportedObjectClasses() + protected virtual ICollection GetSupportedObjectClasses() { - IList ocList = new List {ObjectClass.ACCOUNT, ObjectClass.GROUP, ouObjectClass}; - return ocList; - } + IDictionary objectClassInfos = + CommonUtils.GetOCInfo("Org.IdentityConnectors.ActiveDirectory.ObjectClasses.xml"); + + return objectClassInfos.Keys; + } /// /// Gets the object class info for specified object class, used for schema building @@ -398,45 +336,10 @@ protected virtual IList GetSupportedObjectClasses() /// ObjectClass' ObjectClassInfo protected virtual ObjectClassInfo GetObjectClassInfo(ObjectClass oc) { - if (_ADSchema == null) - { - _ADSchema = GetADSchema(); - } - - if (oc == ObjectClass.ACCOUNT) - { - // get the user attribute infos and operations - ICollection userAttributeInfos = - GetUserAttributeInfos(_ADSchema); - var ociBuilder = new ObjectClassInfoBuilder {ObjectType = ObjectClass.ACCOUNT_NAME, IsContainer = false}; - ociBuilder.AddAllAttributeInfo(userAttributeInfos); - ObjectClassInfo userInfo = ociBuilder.Build(); - return userInfo; - } - - if (oc == ObjectClass.GROUP) - { - // get the group attribute infos and operations - ICollection groupAttributeInfos = - GetGroupAttributeInfos(_ADSchema); - var ociBuilder = new ObjectClassInfoBuilder {ObjectType = ObjectClass.GROUP_NAME, IsContainer = false}; - ociBuilder.AddAllAttributeInfo(groupAttributeInfos); - ObjectClassInfo groupInfo = ociBuilder.Build(); - return groupInfo; - } - - if (oc == ouObjectClass) - { - // get the organizationalUnit attribute infos and operations - ICollection ouAttributeInfos = - GetOuAttributeInfos(_ADSchema); - var ociBuilder = new ObjectClassInfoBuilder {ObjectType = OBJECTCLASS_OU, IsContainer = true}; - ociBuilder.AddAllAttributeInfo(ouAttributeInfos); - ObjectClassInfo ouInfo = ociBuilder.Build(); - return ouInfo; - } + IDictionary objectClassInfos = + CommonUtils.GetOCInfo("Org.IdentityConnectors.ActiveDirectory.ObjectClasses.xml"); - return null; + return objectClassInfos[oc]; } /// @@ -456,7 +359,7 @@ protected virtual IList> GetSupportedOperations(ObjectCla /// protected virtual IList> GetUnSupportedOperations(ObjectClass oc) { - if (oc == ObjectClass.GROUP || oc == ouObjectClass) + if (oc.Equals(ObjectClass.GROUP) || oc.Equals(ouObjectClass)) { return new List> { SafeType.Get(), @@ -497,290 +400,6 @@ private ActiveDirectorySchema GetADSchema() return ADSchema; } - public ICollection GetUserAttributeInfos( - ActiveDirectorySchema ADSchema) - { - ICollection attributeInfos = new Collection(); - // put in operational attributes - attributeInfos.Add(OperationalAttributeInfos.ENABLE); - /* - attributeInfos.Add(OperationalAttributeInfos.ENABLE_DATE); - attributeInfos.Add(OperationalAttributeInfos.DISABLE_DATE); - */ - attributeInfos.Add(OperationalAttributeInfos.LOCK_OUT); - - attributeInfos.Add(OperationalAttributeInfos.PASSWORD_EXPIRATION_DATE); - attributeInfos.Add(OperationalAttributeInfos.PASSWORD_EXPIRED); - attributeInfos.Add(OperationalAttributeInfos.CURRENT_PASSWORD); - // dont think I need this - // attributeInfos.Add(OperationalAttributeInfos.RESET_PASSWORD); - attributeInfos.Add(PredefinedAttributeInfos.GROUPS); - attributeInfos.Add(OperationalAttributeInfos.PASSWORD); - - ConnectorAttributeInfoBuilder descriptionBuilder = new ConnectorAttributeInfoBuilder(); - descriptionBuilder.Name = PredefinedAttributeInfos.DESCRIPTION.Name; - descriptionBuilder.ValueType = PredefinedAttributeInfos.DESCRIPTION.ValueType; - descriptionBuilder.Readable = true; - descriptionBuilder.Creatable = false; - descriptionBuilder.Updateable = false; - descriptionBuilder.Required = false; - descriptionBuilder.ReturnedByDefault = true; - attributeInfos.Add(descriptionBuilder.Build()); - - ConnectorAttributeInfoBuilder shortNameBuilder = new ConnectorAttributeInfoBuilder(); - shortNameBuilder.Name = PredefinedAttributeInfos.SHORT_NAME.Name; - shortNameBuilder.ValueType = PredefinedAttributeInfos.SHORT_NAME.ValueType; - shortNameBuilder.Readable = true; - shortNameBuilder.Creatable = false; - shortNameBuilder.Updateable = false; - shortNameBuilder.Required = false; - shortNameBuilder.ReturnedByDefault = true; - attributeInfos.Add(shortNameBuilder.Build()); - - ICollection attributesToIgnore = new List(); - - attributesToIgnore.Add("CN"); - attributesToIgnore.Add(ATT_USER_PASSWORD); - - // get everything else from the schema - PopulateSchemaFromAD(_configuration.ObjectClass, ADSchema, attributeInfos, - attributesToIgnore, ObjectClass.ACCOUNT); - - // now add in container ... - attributeInfos.Add(GetConnectorAttributeInfo(ATT_CONTAINER, - typeof(string), true, true, false, false, ObjectClass.ACCOUNT)); - - // add in the userPassword - // attributeInfos.Add(GetConnectorAttributeInfo(ATT_USER_PASSWORD, - // typeof(string), false, true, true, false, ObjectClass.ACCOUNT)); - - // add in terminal services attributes - attributeInfos.Add(GetConnectorAttributeInfo( - TerminalServicesUtils.TS_INITIAL_PROGRAM, typeof(string), - false, true, false, false, ObjectClass.ACCOUNT)); - attributeInfos.Add(GetConnectorAttributeInfo( - TerminalServicesUtils.TS_INITIAL_PROGRAM_DIR, typeof(string), - false, true, false, false, ObjectClass.ACCOUNT)); - attributeInfos.Add(GetConnectorAttributeInfo( - TerminalServicesUtils.TS_ALLOW_LOGON, typeof(int), - false, true, false, false, ObjectClass.ACCOUNT)); - attributeInfos.Add(GetConnectorAttributeInfo( - TerminalServicesUtils.TS_MAX_CONNECTION_TIME, typeof(int), - false, true, false, false, ObjectClass.ACCOUNT)); - attributeInfos.Add(GetConnectorAttributeInfo( - TerminalServicesUtils.TS_MAX_DISCONNECTION_TIME, typeof(int), - false, true, false, false, ObjectClass.ACCOUNT)); - attributeInfos.Add(GetConnectorAttributeInfo( - TerminalServicesUtils.TS_MAX_IDLE_TIME, typeof(int), - false, true, false, false, ObjectClass.ACCOUNT)); - attributeInfos.Add(GetConnectorAttributeInfo( - TerminalServicesUtils.TS_CONNECT_CLIENT_DRIVES_AT_LOGON, typeof(int), - false, true, false, false, ObjectClass.ACCOUNT)); - attributeInfos.Add(GetConnectorAttributeInfo( - TerminalServicesUtils.TS_CONNECT_CLIENT_PRINTERS_AT_LOGON, typeof(int), - false, true, false, false, ObjectClass.ACCOUNT)); - attributeInfos.Add(GetConnectorAttributeInfo( - TerminalServicesUtils.TS_DEFAULT_TO_MAIN_PRINTER, typeof(int), - false, true, false, false, ObjectClass.ACCOUNT)); - attributeInfos.Add(GetConnectorAttributeInfo( - TerminalServicesUtils.TS_BROKEN_CONNECTION_ACTION, typeof(int), - false, true, false, false, ObjectClass.ACCOUNT)); - attributeInfos.Add(GetConnectorAttributeInfo( - TerminalServicesUtils.TS_RECONNECTION_ACTION, typeof(int), - false, true, false, false, ObjectClass.ACCOUNT)); - attributeInfos.Add(GetConnectorAttributeInfo( - TerminalServicesUtils.TS_ENABLE_REMOTE_CONTROL, typeof(int), - false, true, false, false, ObjectClass.ACCOUNT)); - attributeInfos.Add(GetConnectorAttributeInfo( - TerminalServicesUtils.TS_PROFILE_PATH, typeof(string), - false, true, false, false, ObjectClass.ACCOUNT)); - attributeInfos.Add(GetConnectorAttributeInfo( - TerminalServicesUtils.TS_HOME_DIRECTORY, typeof(string), - false, true, false, false, ObjectClass.ACCOUNT)); - attributeInfos.Add(GetConnectorAttributeInfo( - TerminalServicesUtils.TS_HOME_DRIVE, typeof(string), - false, true, false, false, ObjectClass.ACCOUNT)); - - return attributeInfos; - } - - public ICollection GetGroupAttributeInfos( - ActiveDirectorySchema ADSchema) - { - ICollection attributeInfos = new Collection(); - - // now add in container ... - attributeInfos.Add(GetConnectorAttributeInfo(ATT_CONTAINER, - typeof(string), true, true, false, false, ObjectClass.GROUP)); - - attributeInfos.Add(PredefinedAttributeInfos.ACCOUNTS); - - ConnectorAttributeInfoBuilder descriptionBuilder = new ConnectorAttributeInfoBuilder(); - descriptionBuilder.Name = PredefinedAttributeInfos.DESCRIPTION.Name; - descriptionBuilder.ValueType = PredefinedAttributeInfos.DESCRIPTION.ValueType; - descriptionBuilder.Readable = true; - descriptionBuilder.Creatable = false; - descriptionBuilder.Updateable = false; - descriptionBuilder.Required = false; - descriptionBuilder.ReturnedByDefault = true; - attributeInfos.Add(descriptionBuilder.Build()); - - ConnectorAttributeInfoBuilder shortNameBuilder = new ConnectorAttributeInfoBuilder(); - shortNameBuilder.Name = PredefinedAttributeInfos.SHORT_NAME.Name; - shortNameBuilder.ValueType = PredefinedAttributeInfos.SHORT_NAME.ValueType; - shortNameBuilder.Readable = true; - shortNameBuilder.Creatable = false; - shortNameBuilder.Updateable = false; - shortNameBuilder.Required = false; - shortNameBuilder.ReturnedByDefault = true; - attributeInfos.Add(shortNameBuilder.Build()); - - attributeInfos.Add(ConnectorAttributeInfoBuilder.Build(Name.NAME, typeof(string), - true, true, true, false)); - - // get everything else from the schema - PopulateSchemaFromAD("Group", ADSchema, attributeInfos, null, ObjectClass.GROUP); - return attributeInfos; - } - - public ICollection GetOuAttributeInfos( - ActiveDirectorySchema ADSchema) - { - ICollection attributeInfos = new Collection(); - - // add in container ... - attributeInfos.Add(GetConnectorAttributeInfo(ATT_OU, - typeof(string), false, true, false, false, ouObjectClass)); - attributeInfos.Add(GetConnectorAttributeInfo(ATT_DISPLAY_NAME, - typeof(string), false, true, false, false, ouObjectClass)); - ConnectorAttributeInfoBuilder descriptionBuilder = new ConnectorAttributeInfoBuilder(); - descriptionBuilder.Name = PredefinedAttributeInfos.DESCRIPTION.Name; - descriptionBuilder.ValueType = PredefinedAttributeInfos.DESCRIPTION.ValueType; - descriptionBuilder.Readable = true; - descriptionBuilder.Creatable = false; - descriptionBuilder.Updateable = false; - descriptionBuilder.Required = false; - descriptionBuilder.ReturnedByDefault = true; - attributeInfos.Add(descriptionBuilder.Build()); - - ConnectorAttributeInfoBuilder shortNameBuilder = new ConnectorAttributeInfoBuilder(); - shortNameBuilder.Name = PredefinedAttributeInfos.SHORT_NAME.Name; - shortNameBuilder.ValueType = PredefinedAttributeInfos.SHORT_NAME.ValueType; - shortNameBuilder.Readable = true; - shortNameBuilder.Creatable = false; - shortNameBuilder.Updateable = false; - shortNameBuilder.Required = false; - shortNameBuilder.ReturnedByDefault = true; - attributeInfos.Add(shortNameBuilder.Build()); - - // add in name ... - attributeInfos.Add( - ConnectorAttributeInfoBuilder.Build(Name.NAME, typeof(string), - true, true, true, false)); - - return attributeInfos; - } - - protected void PopulateSchemaFromAD(String className, - ActiveDirectorySchema ADSchema, - ICollection attributeInfos, - ICollection attributesToIgnore, ObjectClass oclass) - { - if(attributesToIgnore == null) { - attributesToIgnore = new List(); - } - ActiveDirectorySchemaClass schemaClass = ADSchema.FindClass(className); - AddPropertyCollectionToSchema(schemaClass.MandatoryProperties, - attributeInfos, false, oclass); - - AddPropertyCollectionToSchema(schemaClass.OptionalProperties, - attributeInfos, false, oclass); - } - - protected void AddPropertyCollectionToSchema( - ActiveDirectorySchemaPropertyCollection schemaProperties, - ICollection attributeInfos, - Boolean required, ObjectClass oclass) - { - foreach (ActiveDirectorySchemaProperty schemaProperty in - schemaProperties) - { - DirectoryEntry sde = schemaProperty.GetDirectoryEntry(); - PropertyValueCollection systemOnlyPvc = sde.Properties["systemOnly"]; - Boolean writable = true; - if (systemOnlyPvc != null) - { - Object value = systemOnlyPvc.Value; - if ((value != null) && (value.Equals(true))) - { - writable = false; - } - } - - String syntax = schemaProperty.Syntax.ToString(); - syntax = syntax.ToUpper(); - Type connectorType = typeof(string); - - // if this gets larger, break it out - // into a special method. - if ("BOOLEAN".Equals(syntax, StringComparison.CurrentCultureIgnoreCase)) - { - connectorType = typeof(bool); - } - else if ("INTEGER".Equals(syntax, StringComparison.CurrentCultureIgnoreCase) || "INT".Equals(syntax, StringComparison.CurrentCultureIgnoreCase)) - { - connectorType = typeof(int); - } - else if ("INT64".Equals( - syntax, StringComparison.CurrentCultureIgnoreCase)) - { - connectorType = typeof(long); - } - - attributeInfos.Add(GetConnectorAttributeInfo(schemaProperty.Name, - connectorType, writable, true, required, - schemaProperty.IsSingleValued ? false : true, oclass)); - -/* - Console.WriteLine("***->" + schemaProperty.Name + "<-***"); - foreach (String pName in sde.Properties.PropertyNames) - { - Console.WriteLine("***->" + pName + " = " + sde.Properties[pName].Value); - } -*/ - } - } - - private ConnectorAttributeInfo GetConnectorAttributeInfo(string name, - Type type, bool writable, bool readable, bool required, - bool multivalue, ObjectClass oclass) - { - ConnectorAttributeInfoBuilder builder = new ConnectorAttributeInfoBuilder(); - builder.Name = name; - builder.ValueType = type; - builder.Creatable = writable; - builder.Updateable = writable; - builder.Readable = readable; - builder.Required = required; - builder.MultiValue = multivalue; - - // if there is a set of attributes to return by default - // for this object class use it. If not, just use the - // the builder's default value - if(RegularAttributesReturnedByDefault.Keys.Contains(oclass)) { - if (RegularAttributesReturnedByDefault[oclass].Contains(name)) - { - builder.ReturnedByDefault = true; - } - else - { - builder.ReturnedByDefault = false; - } - } - - return builder.Build(); - } - #endregion #region SearchOp Members @@ -1023,7 +642,7 @@ private void ExecuteQuery(ObjectClass oclass, string query, _utils.GetConnectorAttributeFromADEntry( oclass, ATT_USN_CHANGED, result)); - // add isDeleted + // add isDeleted builder.AddAttribute(ATT_IS_DELETED, true); } @@ -1045,57 +664,15 @@ private void ExecuteQuery(ObjectClass oclass, string query, private ICollection GetAttributesToReturn(ObjectClass oclass, OperationOptions options) { - ICollection attributeNames = new HashSet(); + ICollection attributeNames = null; if ((options.AttributesToGet != null) && (options.AttributesToGet.Length > 0)) { - foreach (string name in options.AttributesToGet) - { - attributeNames.Add(name); - } - // now add in operational attributes ... they are always returned - ICollection specialAttributes = null; - if (SpecialAttributesReturnedByDefault.Keys.Contains(oclass)) - { - specialAttributes = SpecialAttributesReturnedByDefault[oclass]; - } - - if (specialAttributes == null) - { - specialAttributes = new List(); - ObjectClassInfo ocInfo = Schema().FindObjectClassInfo(oclass.GetObjectClassValue()); - foreach (ConnectorAttributeInfo info in ocInfo.ConnectorAttributeInfos) - { - Trace.TraceInformation(String.Format( - "Adding {0} to list of returned attributes", info.Name)); - if ((info.IsReturnedByDefault) && (ConnectorAttributeUtil.IsSpecial(info))) - { - specialAttributes.Add(info.Name); - } - } - SpecialAttributesReturnedByDefault.Add(oclass, specialAttributes); - } - - foreach (string specialAttributeName in specialAttributes) - { - if (!attributeNames.Contains(specialAttributeName)) - { - attributeNames.Add(specialAttributeName); - } - } + attributeNames = new HashSet(options.AttributesToGet); } else { - ObjectClassInfo ocInfo = Schema().FindObjectClassInfo(oclass.GetObjectClassValue()); - foreach (ConnectorAttributeInfo info in ocInfo.ConnectorAttributeInfos) - { - Trace.TraceInformation(String.Format( - "Adding {0} to list of returned attributes", info.Name)); - if (info.IsReturnedByDefault) - { - attributeNames.Add(info.Name); - } - } + attributeNames = AttributesReturnedByDefault[oclass]; } // Uid is always returned @@ -1293,7 +870,22 @@ internal SyncResults(SyncResultsHandler syncResultsHandler, public bool SyncHandler(ConnectorObject obj) { SyncDeltaBuilder builder = new SyncDeltaBuilder(); - builder.Object = obj; + ICollection attrs = new HashSet(); + foreach(ConnectorAttribute attribute in obj.GetAttributes()) { + // add all attributes to the object except the + // one used to flag deletes. + if (!attribute.Name.Equals(ATT_IS_DELETED)) + { + attrs.Add(attribute); + } + } + + ConnectorObjectBuilder coBuilder = new ConnectorObjectBuilder(); + coBuilder.SetName(obj.Name); + coBuilder.SetUid(obj.Uid); + coBuilder.AddAttributes(attrs); + builder.Object = coBuilder.Build(); + ConnectorAttribute tokenAttr = ConnectorAttributeUtil.Find(ATT_USN_CHANGED, obj.GetAttributes()); if(tokenAttr == null) { @@ -1311,7 +903,7 @@ public bool SyncHandler(ConnectorObject obj) if (isDeletedAttr != null) { isDeleted = (bool?)ConnectorAttributeUtil.GetSingleValue(isDeletedAttr); - _adSyncToken.LastDeleteUsn = tokenUsnValue; + _adSyncToken.LastDeleteUsn = tokenUsnValue; } else { diff --git a/ActiveDirectoryConnector/ActiveDirectoryConnector.csproj b/ActiveDirectoryConnector/ActiveDirectoryConnector.csproj index f415ee89..0ff408bc 100644 --- a/ActiveDirectoryConnector/ActiveDirectoryConnector.csproj +++ b/ActiveDirectoryConnector/ActiveDirectoryConnector.csproj @@ -92,6 +92,7 @@ + @@ -128,4 +129,7 @@ Designer + + + \ No newline at end of file diff --git a/ActiveDirectoryConnector/CommonUtils.cs b/ActiveDirectoryConnector/CommonUtils.cs new file mode 100644 index 00000000..85edc223 --- /dev/null +++ b/ActiveDirectoryConnector/CommonUtils.cs @@ -0,0 +1,91 @@ +/* + * Copyright 2008 Sun Microsystems, Inc. All rights reserved. + * + * U.S. Government Rights - Commercial software. Government users + * are subject to the Sun Microsystems, Inc. standard license agreement + * and applicable provisions of the FAR and its supplements. + * + * Use is subject to license terms. + * + * This distribution may include materials developed by third parties. + * Sun, Sun Microsystems, the Sun logo, Java and Project Identity + * Connectors are trademarks or registered trademarks of Sun + * Microsystems, Inc. or its subsidiaries in the U.S. and other + * countries. + * + * UNIX is a registered trademark in the U.S. and other countries, + * exclusively licensed through X/Open Company, Ltd. + * + * ----------- + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER. + * + * Copyright 2008 Sun Microsystems, Inc. All rights reserved. + * + * The contents of this file are subject to the terms of the Common Development + * and Distribution License(CDDL) (the License). You may not use this file + * except in compliance with the License. + * + * You can obtain a copy of the License at + * http://identityconnectors.dev.java.net/CDDLv1.0.html + * See the License for the specific language governing permissions and + * limitations under the License. + * + * When distributing the Covered Code, include this CDDL Header Notice in each + * file and include the License file at identityconnectors/legal/license.txt. + * If applicable, add the following below this CDDL Header, with the fields + * enclosed by brackets [] replaced by your own identifying information: + * "Portions Copyrighted [year] [name of copyright owner]" + * ----------- + */ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Reflection; + +using System.Diagnostics; +using System.IO; +using Microsoft.Win32; +using System.Xml.Serialization; +using Org.IdentityConnectors.Common; +using Org.IdentityConnectors.Framework.Common.Objects; +using Org.IdentityConnectors.Framework.Common.Objects.Filters; +using Org.IdentityConnectors.Framework.Common.Serializer; +using Org.IdentityConnectors.Framework.Spi.Operations; + +namespace Org.IdentityConnectors.ActiveDirectory +{ + class CommonUtils + { + /// + /// reads the object class info definitions from xml + /// + ///Dictionary of object classes + internal static IDictionary GetOCInfo(string name) + { + Assembly assembly = Assembly.GetExecutingAssembly(); + Stream stream = assembly.GetManifestResourceStream(name); + + Assertions.NullCheck(stream, "stream"); + + //we just read + TextReader streamReader = new StreamReader(stream); + String xml = streamReader.ReadToEnd(); + streamReader.Close(); + + //read from xml + var ret = (ICollection)SerializerUtil.DeserializeXmlObject(xml, true); + + Assertions.NullCheck(ret, "ret"); + + //create map of object infos + var map = new Dictionary(ret.Count); + foreach (ObjectClassInfo o in ret) + { + map.Add(new ObjectClass(o.ObjectType.ToString()), o); + } + + return map; + } + } +} diff --git a/ActiveDirectoryConnector/ObjectClasses.xml b/ActiveDirectoryConnector/ObjectClasses.xml new file mode 100644 index 00000000..4ddb14fe --- /dev/null +++ b/ActiveDirectoryConnector/ObjectClasses.xml @@ -0,0 +1,126 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + From 8760466ec7b1dd1f02e81ae35891a3141152f1ff Mon Sep 17 00:00:00 2001 From: rcauble Date: Mon, 8 Dec 2008 22:53:05 +0000 Subject: [PATCH 097/342] Issues#355, 365: fixed on C# side --- .../ActiveDirectoryConnector.cs | 2 +- .../ActiveDirectoryConnectorTest.cs | 52 +- Common/CollectionUtil.cs | 3 + Framework/CommonObjects.cs | 475 ++++++++++++------ FrameworkInternal/Serializer.cs | 4 +- FrameworkTests/ObjectSerializationTests.cs | 6 +- 6 files changed, 335 insertions(+), 207 deletions(-) diff --git a/ActiveDirectoryConnector/ActiveDirectoryConnector.cs b/ActiveDirectoryConnector/ActiveDirectoryConnector.cs index c205309c..d0678d52 100644 --- a/ActiveDirectoryConnector/ActiveDirectoryConnector.cs +++ b/ActiveDirectoryConnector/ActiveDirectoryConnector.cs @@ -742,7 +742,7 @@ public Uid RemoveAttributeValues(ObjectClass objclass, } // implementation of AdvancedUpdateSpiOp - protected virtual Uid Update(UpdateType type, ObjectClass oclass, + public virtual Uid Update(UpdateType type, ObjectClass oclass, ICollection attributes, OperationOptions options) { Uid updatedUid = null; diff --git a/ActiveDirectoryConnectorTests/ActiveDirectoryConnectorTest.cs b/ActiveDirectoryConnectorTests/ActiveDirectoryConnectorTest.cs index 856bff26..e2f29d38 100644 --- a/ActiveDirectoryConnectorTests/ActiveDirectoryConnectorTest.cs +++ b/ActiveDirectoryConnectorTests/ActiveDirectoryConnectorTest.cs @@ -149,7 +149,7 @@ public void TestSchema() caInfo.IsCreatable ? "createable" : "", caInfo.IsUpdateable ? "updateable" : "", caInfo.IsRequired ? "required" : "", - caInfo.IsMultiValue ? "multivalue" : "")); + caInfo.IsMultiValued ? "multivalue" : "")); if(ConnectorAttributeUtil.IsSpecial(caInfo)) { foundOperationalAttributes = true; } else { @@ -269,37 +269,6 @@ public void TestBasics_Group() } } - /* - * This is not a test at all, it's just a convienient way to create a - * bunch of users - * - [Test] - public void TestCreate_aBunchOfAccounts() - { - //Initialize Connector - ActiveDirectoryConnector connector = new ActiveDirectoryConnector(); - connector.Init(GetConfiguration()); - Uid createUid = null; - for (int i = 0; i < 6500; i++) - { - if (i % 500 == 0) - { - Console.Write("Created {0} users", i); - } - - try - { - ICollection createAttributes = GetNormalAttributes_Account(); - createUid = CreateAndVerifyObject(connector, ObjectClass.ACCOUNT, createAttributes); - } - catch (Exception e) - { - // ignore - } - } - } - */ - [Test] public void TestCreate_Account() { @@ -1110,9 +1079,7 @@ public void TestRemoveAttributeValue() modifyAttributes.Add(createUid); modifyAttributes.Add(ConnectorAttributeBuilder.Build("otherHomePhone", "512.555.1212")); - connector.RemoveAttributeValues(ObjectClass.ACCOUNT, - ConnectorAttributeUtil.GetUidAttribute(modifyAttributes), - modifyAttributes, null); + connector.Update(UpdateType.DELETE, ObjectClass.ACCOUNT, modifyAttributes, null); Filter uidFilter = FilterBuilder.EqualTo(createUid); IList objects = TestHelpers.SearchToList(connector, ObjectClass.ACCOUNT, uidFilter); @@ -1169,7 +1136,7 @@ public void TestContainerChange_account() updateAttrs.Add(ConnectorAttributeBuilder.Build("ad_container", groupPath)); updateAttrs.Add(createUserUid); - connector.Update(ObjectClass.ACCOUNT, ConnectorAttributeUtil.GetUidAttribute(updateAttrs), updateAttrs, null); + connector.Update(UpdateType.REPLACE, ObjectClass.ACCOUNT, updateAttrs, null); ICollection results = TestHelpers.SearchToList( connector, ObjectClass.ACCOUNT, FilterBuilder.EqualTo(createUserUid)); @@ -1883,8 +1850,7 @@ public void TestSync(bool searchChildDomains, String syncSearchContext) attributes = new List(); attributes.Add(createdUids.First()); attributes.Add(ConnectorAttributeBuilder.Build("sn", "replaced")); - connector.Update(ObjectClass.ACCOUNT, - ConnectorAttributeUtil.GetUidAttribute(attributes), attributes, null); + connector.Update(UpdateType.REPLACE, ObjectClass.ACCOUNT, attributes, null); syncHelper.AddModUid(createdUids.First(), attributes); for(int i = 0; i < 10; i++) @@ -1899,7 +1865,7 @@ public void TestSync(bool searchChildDomains, String syncSearchContext) attributes = new List(); attributes.Add(createdUids.Last()); attributes.Add(ConnectorAttributeBuilder.Build("sn", "replaced")); - connector.Update(ObjectClass.ACCOUNT, ConnectorAttributeUtil.GetUidAttribute(attributes), attributes, null); + connector.Update(UpdateType.REPLACE, ObjectClass.ACCOUNT, attributes, null); syncHelper.AddModUid(createdUids.Last(), attributes); // sync, and verify @@ -2136,8 +2102,7 @@ public Uid UpdateReplaceAndVerifyObject(ActiveDirectoryConnector connector, connector, oclass, uidFilter); Assert.AreEqual(1, currentConnectorObjects.Count); - Uid updatedUid = connector.Update(oclass, - ConnectorAttributeUtil.GetUidAttribute(attributes), + Uid updatedUid = connector.Update(UpdateType.REPLACE, oclass, attributes, null); Assert.IsNotNull(updatedUid); @@ -2174,8 +2139,7 @@ public Uid UpdateAddAndVerifyUser(ActiveDirectoryConnector connector, // make sure the uid is present in the attributes attributes.Add(uid); // now update with ADD to add additional home phones - Uid updatedUid = connector.AddAttributeValues(oclass, - ConnectorAttributeUtil.GetUidAttribute(attributes), + Uid updatedUid = connector.Update(UpdateType.ADD, oclass, attributes, null); // find it back @@ -2764,7 +2728,7 @@ public GuardedString GetGuardedString(string regularString) int GetRandomNumber() { - const int randomRange = 1000000; + const int randomRange = 100000; int number = _rand.Next(randomRange); #if DEBUG // make sure the debug numbers are in a different diff --git a/Common/CollectionUtil.cs b/Common/CollectionUtil.cs index 0bd24b64..346d3cb6 100644 --- a/Common/CollectionUtil.cs +++ b/Common/CollectionUtil.cs @@ -167,6 +167,9 @@ public TValue this[TKey key] { } } } + + + /// /// Delegate that returns the Key, given a value diff --git a/Framework/CommonObjects.cs b/Framework/CommonObjects.cs index bd442442..bba30d19 100644 --- a/Framework/CommonObjects.cs +++ b/Framework/CommonObjects.cs @@ -1221,17 +1221,34 @@ private static void ValidateParameter(Object param, String paramName) { public sealed class ConnectorAttributeInfo { private readonly string _name; private readonly Type _type; - private readonly bool _required; - private readonly bool _readable; - private readonly bool _creatable; - private readonly bool _updateable; - private readonly bool _multivalue; - private readonly bool _returnedByDefault; + private readonly Flags _flags; + + /** + * Enum of modifier flags to use for attributes. Note that + * this enum is designed for configuration by exception such that + * an empty set of flags are the defaults: + *
    + *
  • updateable
  • + *
  • creatable
  • + *
  • returned by default
  • + *
  • readable
  • + *
  • single-valued
  • + *
  • optional
  • + *
+ */ + [FlagsAttribute] + public enum Flags { + NONE = 0, + REQUIRED = 1, + MULTIVALUED = 2, + NOT_CREATABLE = 4, + NOT_UPDATEABLE = 8, + NOT_READABLE = 16, + NOT_RETURNED_BY_DEFAULT = 32 + } internal ConnectorAttributeInfo(string name, Type type, - bool readable, bool creatable, - bool required, bool multivalue, - bool updateable, bool returnedByDefault) { + Flags flags) { if (StringUtil.IsBlank(name)) { throw new ArgumentException("Name must not be blank!"); } @@ -1246,12 +1263,10 @@ internal ConnectorAttributeInfo(string name, Type type, FrameworkUtil.CheckAttributeType(type); _name = name; _type = type; - _readable = readable; - _creatable = creatable; - _required = required; - _multivalue = multivalue; - _updateable = updateable; - _returnedByDefault = returnedByDefault; + _flags = flags; + if (!IsReadable && IsReturnedByDefault) { + throw new ArgumentException("Attribute "+name+" is flagged as not-readable, so it should also be as not-returned-by-default."); + } } @@ -1277,6 +1292,17 @@ public Type ValueType { return _type; } } + + /** + * Returns the set of flags associated with the attribute. + * @return the set of flags associated with the attribute + */ + public Flags InfoFlags { + get { + return _flags; + } + } + public bool Is(string name) { return Name.ToUpper().Equals(name.ToUpper()); @@ -1289,7 +1315,7 @@ public bool Is(string name) { */ public bool IsReadable { get { - return _readable; + return (_flags & Flags.NOT_READABLE) == 0; } } @@ -1300,7 +1326,7 @@ public bool IsReadable { */ public bool IsCreatable { get { - return _creatable; + return (_flags & Flags.NOT_CREATABLE) == 0; } } @@ -1311,7 +1337,7 @@ public bool IsCreatable { */ public bool IsUpdateable { get { - return _updateable; + return (_flags & Flags.NOT_UPDATEABLE) == 0; } } @@ -1322,7 +1348,7 @@ public bool IsUpdateable { */ public bool IsRequired { get { - return _required; + return (_flags & Flags.REQUIRED) != 0; } } @@ -1333,9 +1359,9 @@ public bool IsRequired { * * @return true if the attribute is multi-value otherwise false. */ - public bool IsMultiValue { + public bool IsMultiValued { get { - return _multivalue; + return (_flags & Flags.MULTIVALUED) != 0; } } @@ -1349,7 +1375,7 @@ public bool IsMultiValue { */ public bool IsReturnedByDefault { get { - return _returnedByDefault; + return (_flags & Flags.NOT_RETURNED_BY_DEFAULT) == 0; } } @@ -1362,22 +1388,7 @@ public override bool Equals(Object o) { if (!ValueType.Equals(other.ValueType)) { return false; } - if (IsReadable != other.IsReadable) { - return false; - } - if (IsCreatable != other.IsCreatable) { - return false; - } - if (IsRequired != other.IsRequired) { - return false; - } - if (IsMultiValue != other.IsMultiValue) { - return false; - } - if (IsReturnedByDefault != other.IsReturnedByDefault) { - return false; - } - if (IsUpdateable != other.IsUpdateable) { + if (_flags != other._flags) { return false; } return true; @@ -1390,97 +1401,249 @@ public override int GetHashCode() { } public override string ToString() { - IDictionary map = new Dictionary(); - map["Name"] = Name; - map["Type"] = ValueType; - map["Required"] = IsRequired; - map["Readable"] = IsReadable; - map["Createable"] = IsCreatable; - map["MultiValue"] = IsMultiValue; - map["Updateable"] = IsUpdateable; - map["ReturnedByDefault"] = IsReturnedByDefault; - return map.ToString(); + return SerializerUtil.SerializeXmlObject(this,false); } } #endregion #region ConnectorAttributeInfoBuilder + /** + * Simplifies the process of building 'AttributeInfo' objects. This class is + * responsible for providing a default implementation of {@link AttributeInfo}. + * + * + * AttributeInfoBuilder bld = new AttributeInfoBuilder("email"); + * bld.setRequired(true); + * AttributeInfo info = bld.build(); + * + * + * @author Will Droste + * @version $Revision: 1.9 $ + * @since 1.0 + */ public sealed class ConnectorAttributeInfoBuilder { - public string Name { get; set; } - /// - /// Determines the type for the attribute. Please see - /// {@link FrameworkUtil#checkAttributeType(Class)} for - /// more information. - /// - public Type ValueType { get; set; } - public bool Readable { get; set; } - public bool Creatable { get; set; } - public bool Required { get; set; } - public bool MultiValue { get; set; } - public bool Updateable { get; set; } - public bool ReturnedByDefault { get; set; } - + + private String _name; + private Type _type; + private ConnectorAttributeInfo.Flags _flags; + + /** + * Creates an builder with all the defaults set. The name must be set before + * the 'build' method is called otherwise an {@link IllegalStateException} + * is thrown. + * + *
+         * Name: <not set>
+         * Readable: true
+         * Writeable: true
+         * Required: false
+         * Type: string
+         * MultiValue: false
+         * 
+ */ public ConnectorAttributeInfoBuilder() { - Name = null; - Readable = true; - Creatable = true; - Required = false; - Updateable = true; - MultiValue = false; - ValueType = typeof(string); - ReturnedByDefault = true; - } - - public ConnectorAttributeInfo Build() { - return new ConnectorAttributeInfo(Name, - ValueType, - Readable, - Creatable, - Required, - MultiValue, - Updateable, - ReturnedByDefault); + ValueType=(typeof(String)); + _flags = ConnectorAttributeInfo.Flags.NONE; } - public static ConnectorAttributeInfo Build(String name) { - return new ConnectorAttributeInfoBuilder() { - Name = name - }.Build(); + + /** + * Creates an builder with all the defaults set. The name must be set before + * the 'build' method is called otherwise an {@link IllegalStateException} + * is thrown. + * + *
+         * Name: <not set>
+         * Readable: true
+         * Writeable: true
+         * Required: false
+         * Type: string
+         * MultiValue: false
+         * 
+ */ + public ConnectorAttributeInfoBuilder(String name) : this(name,typeof(String)){ + } + + /** + * Creates an builder with all the defaults set. The name must be set before + * the 'build' method is called otherwise an {@link IllegalStateException} + * is thrown. + * + *
+    	 * Name: <not set>
+    	 * Readable: true
+    	 * Writeable: true
+    	 * Required: false
+    	 * Type: string
+    	 * MultiValue: false
+    	 * 
+ */ + public ConnectorAttributeInfoBuilder(String name, Type type) { + Name=(name); + ValueType=(type); + //noneOf means the defaults + _flags = ConnectorAttributeInfo.Flags.NONE; + } + + /** + * Builds an {@link AttributeInfo} object based on the properties set. + * + * @return {@link AttributeInfo} based on the properties set. + */ + public ConnectorAttributeInfo Build() { + return new ConnectorAttributeInfo(_name, _type, _flags); + } + + /** + * Sets the unique name of the {@link AttributeInfo} object. + * + * @param name + * unique name of the {@link AttributeInfo} object. + */ + public String Name { + set { + if (StringUtil.IsBlank(value)) { + throw new ArgumentException("Argument must not be blank."); + } + _name = value; + } + } + + /** + * Please see {@link FrameworkUtil#checkAttributeType(Class)} for the + * definitive list of supported types. + * + * @param value + * type for an {@link Attribute}'s value. + * @throws IllegalArgumentException + * if the Class is not a supported type. + */ + public Type ValueType { + set { + FrameworkUtil.CheckAttributeType(value); + _type = value; + } + } + + /** + * Determines if the attribute is readable. + */ + public bool Readable { + set { + SetFlag(ConnectorAttributeInfo.Flags.NOT_READABLE,!value); + } + } + + /** + * Determines if the attribute is writable. + */ + public bool Creatable { + set { + SetFlag(ConnectorAttributeInfo.Flags.NOT_CREATABLE,!value); + } + } + + /** + * Determines if this attribute is required. + */ + public bool Required { + set { + SetFlag(ConnectorAttributeInfo.Flags.REQUIRED,value); + } + } + + /** + * Determines if this attribute supports multivalue. + */ + public bool MultiValued { + set { + SetFlag(ConnectorAttributeInfo.Flags.MULTIVALUED,value); + } + } + + /** + * Determines if this attribute writable during update. + */ + public bool Updateable { + set { + SetFlag(ConnectorAttributeInfo.Flags.NOT_UPDATEABLE,!value); + } + } + + public bool ReturnedByDefault { + set { + SetFlag(ConnectorAttributeInfo.Flags.NOT_RETURNED_BY_DEFAULT,!value); + } + } + + /** + * Sets all of the flags for this builder. + * @param flags The set of attribute info flags. Null means clear all flags. + *

+ * NOTE: EnumSet.noneOf(AttributeInfo.Flags.class) results in + * an attribute with the default behavior: + *

    + *
  • updateable
  • + *
  • creatable
  • + *
  • returned by default
  • + *
  • readable
  • + *
  • single-valued
  • + *
  • optional
  • + *
+ */ + public ConnectorAttributeInfo.Flags InfoFlags { + set { + _flags = value; + } + } + + private void SetFlag(ConnectorAttributeInfo.Flags flag, bool value) { + if (value) { + _flags = _flags | flag; + } + else { + _flags = _flags & ~flag; + } } + + /** + * Convenience method to create an AttributeInfo. Equivalent to + * + * new AttributeInfoBuilder(name,type).setFlags(flags).build() + * + * @param name The name of the attribute + * @param type The type of the attribute + * @param flags The flags for the attribute. Null means clear all flags + * @return The attribute info + */ + public static ConnectorAttributeInfo Build(String name, Type type, + ConnectorAttributeInfo.Flags flags) { + return new ConnectorAttributeInfoBuilder(name,type){InfoFlags=flags}.Build(); + } + /** + * Convenience method to create an AttributeInfo. Equivalent to + * + * AttributeInfoBuilder.build(name,type,null) + * + * @param name The name of the attribute + * @param type The type of the attribute + * @param flags The flags for the attribute + * @return The attribute info + */ public static ConnectorAttributeInfo Build(String name, Type type) { - return new ConnectorAttributeInfoBuilder() { - Name = name, - ValueType = type - }.Build(); - } - public static ConnectorAttributeInfo Build(String name, Type type, bool required) { - return new ConnectorAttributeInfoBuilder() { - Name = name, - ValueType = type, - Required = required - }.Build(); - } - public static ConnectorAttributeInfo Build( String name, - bool required, bool readable, bool creatable, bool updateable) { - ConnectorAttributeInfoBuilder bld = new ConnectorAttributeInfoBuilder(); - bld.Name = name; - bld.Required = required; - bld.Readable = readable; - bld.Creatable = creatable; - bld.Updateable = updateable; - return bld.Build(); + return Build(name,type,ConnectorAttributeInfo.Flags.NONE); } - public static ConnectorAttributeInfo Build( String name, Type type, - bool required, bool readable, bool creatable, bool updateable) { - ConnectorAttributeInfoBuilder bld = new ConnectorAttributeInfoBuilder(); - bld.Name = name; - bld.ValueType = type; - bld.Required = required; - bld.Readable = readable; - bld.Creatable = creatable; - bld.Updateable = updateable; - return bld.Build(); - } + /** + * Convenience method to create an AttributeInfo. Equivalent to + * + * AttributeInfoBuilder.build(name,type) + * + * @param name The name of the attribute + * @return The attribute info + */ + public static ConnectorAttributeInfo Build(String name) { + return Build(name,typeof(String)); + } } #endregion @@ -1523,6 +1686,8 @@ public override string ToString() { public sealed class Name : ConnectorAttribute { public readonly static string NAME = ConnectorAttributeUtil.CreateSpecialName("NAME"); + public readonly static ConnectorAttributeInfo INFO = + new ConnectorAttributeInfoBuilder(NAME) {Required=true}.Build(); public Name(String value) : base(NAME, CollectionUtil.NewReadOnlyList(value)) { } @@ -1707,10 +1872,7 @@ public bool IsContainer { public ObjectClassInfo Build() { // determine if name is missing and add it by default if (!_info.ContainsKey(Name.NAME)) { - ConnectorAttributeInfo info = - ConnectorAttributeInfoBuilder.Build( - Name.NAME, typeof(string), true); - _info[info.Name] = info; + _info[Name.NAME] = Name.INFO; } return new ObjectClassInfo(ObjectType, _info.Values, _isContainer); } @@ -1763,7 +1925,9 @@ public static class OperationalAttributeInfos { public static readonly ConnectorAttributeInfo PASSWORD = ConnectorAttributeInfoBuilder.Build( OperationalAttributes.PASSWORD_NAME, typeof(GuardedString), - true, false, true, true); + ConnectorAttributeInfo.Flags.REQUIRED | + ConnectorAttributeInfo.Flags.NOT_READABLE | + ConnectorAttributeInfo.Flags.NOT_RETURNED_BY_DEFAULT); /** * Used in conjunction with password to do an account level password change. @@ -1772,8 +1936,9 @@ public static class OperationalAttributeInfos { */ public static readonly ConnectorAttributeInfo CURRENT_PASSWORD = ConnectorAttributeInfoBuilder.Build( - OperationalAttributes.CURRENT_PASSWORD_NAME, typeof(GuardedString), - false, false, true, true); + OperationalAttributes.CURRENT_PASSWORD_NAME, typeof(GuardedString), + ConnectorAttributeInfo.Flags.NOT_READABLE | + ConnectorAttributeInfo.Flags.NOT_RETURNED_BY_DEFAULT); /** * Used to do an administrator reset of the password. The value is the reset @@ -1782,7 +1947,8 @@ public static class OperationalAttributeInfos { public static readonly ConnectorAttributeInfo RESET_PASSWORD = ConnectorAttributeInfoBuilder.Build( OperationalAttributes.RESET_PASSWORD_NAME, typeof(GuardedString), - false, false, true, true); + ConnectorAttributeInfo.Flags.NOT_READABLE | + ConnectorAttributeInfo.Flags.NOT_RETURNED_BY_DEFAULT); /** * Used to determine if a password is expired or to expire a password. @@ -1936,25 +2102,24 @@ public static class PredefinedAttributeInfos { * application will have to use the NAME to show the value. */ public static readonly ConnectorAttributeInfo SHORT_NAME = - ConnectorAttributeInfoBuilder.Build(PredefinedAttributes.SHORT_NAME, - typeof(String), - false, true, true, true); + ConnectorAttributeInfoBuilder.Build(PredefinedAttributes.SHORT_NAME); /** * Attribute that should hold the value of the object's description, * if one is available. */ public static readonly ConnectorAttributeInfo DESCRIPTION = - ConnectorAttributeInfoBuilder.Build(PredefinedAttributes.DESCRIPTION, - typeof(String), - false, true, true, true); + ConnectorAttributeInfoBuilder.Build(PredefinedAttributes.DESCRIPTION); /** * Read-only attribute that shows the last date/time the password was * changed. */ public static readonly ConnectorAttributeInfo LAST_PASSWORD_CHANGE_DATE = ConnectorAttributeInfoBuilder.Build( - PredefinedAttributes.LAST_PASSWORD_CHANGE_DATE_NAME, typeof(long), false, true, false, false); + PredefinedAttributes.LAST_PASSWORD_CHANGE_DATE_NAME, + typeof(long), + ConnectorAttributeInfo.Flags.NOT_CREATABLE | + ConnectorAttributeInfo.Flags.NOT_UPDATEABLE); /** * Common password policy attribute where the password must be changed every @@ -1971,47 +2136,43 @@ public static class PredefinedAttributeInfos { */ public static readonly ConnectorAttributeInfo LAST_LOGIN_DATE = ConnectorAttributeInfoBuilder.Build( - PredefinedAttributes.LAST_LOGIN_DATE_NAME, typeof(long), false, true, false, false); - - static PredefinedAttributeInfos() { - // define GROUPS attribute info - ConnectorAttributeInfoBuilder bld = new ConnectorAttributeInfoBuilder(); - bld.Name = PredefinedAttributes.GROUPS_NAME; - bld.MultiValue = true; - bld.ReturnedByDefault = false; - GROUPS = bld.Build(); - // define ACCOUNTS attribute info - bld = new ConnectorAttributeInfoBuilder(); - bld.Name = PredefinedAttributes.ACCOUNTS_NAME; - bld.MultiValue = true; - bld.ReturnedByDefault = false; - ACCOUNTS = bld.Build(); - // define ORGANIZATION - bld = new ConnectorAttributeInfoBuilder(); - bld.Name = PredefinedAttributes.ORGANIZATION_NAME; - bld.ReturnedByDefault = false; - ORGANIZATIONS = bld.Build(); - } - + PredefinedAttributes.LAST_LOGIN_DATE_NAME, + typeof(long), + ConnectorAttributeInfo.Flags.NOT_CREATABLE | + ConnectorAttributeInfo.Flags.NOT_UPDATEABLE); + /** * Groups that an account or person belong to. The Attribute values are the * UID value of each group that an account has membership in. */ - public static readonly ConnectorAttributeInfo GROUPS; + public static readonly ConnectorAttributeInfo GROUPS = + ConnectorAttributeInfoBuilder.Build(PredefinedAttributes.GROUPS_NAME, + typeof(String), + ConnectorAttributeInfo.Flags.MULTIVALUED | + ConnectorAttributeInfo.Flags.NOT_RETURNED_BY_DEFAULT); + /** * Accounts that are members of a group or organization. The Attribute * values are the UID value of each account the has a group or organization * membership. */ - public static readonly ConnectorAttributeInfo ACCOUNTS; + public static readonly ConnectorAttributeInfo ACCOUNTS = + ConnectorAttributeInfoBuilder.Build(PredefinedAttributes.ACCOUNTS_NAME, + typeof(String), + ConnectorAttributeInfo.Flags.MULTIVALUED| + ConnectorAttributeInfo.Flags.NOT_RETURNED_BY_DEFAULT); + /** * Organizations that an account or person is a member of. The Attribute * values are the UID value of each organization that an account or person is * a member of. */ - public static readonly ConnectorAttributeInfo ORGANIZATIONS; + public static readonly ConnectorAttributeInfo ORGANIZATIONS = + ConnectorAttributeInfoBuilder.Build(PredefinedAttributes.ORGANIZATION_NAME, + typeof(String), + ConnectorAttributeInfo.Flags.NOT_RETURNED_BY_DEFAULT); } #endregion diff --git a/FrameworkInternal/Serializer.cs b/FrameworkInternal/Serializer.cs index 64c7fbd7..f9cce267 100644 --- a/FrameworkInternal/Serializer.cs +++ b/FrameworkInternal/Serializer.cs @@ -1679,7 +1679,7 @@ public override Object Deserialize(ObjectDecoder decoder) { decoder.ReadBooleanField("creatable",false)); builder.Updateable=( decoder.ReadBooleanField("updateable",false)); - builder.MultiValue=( + builder.MultiValued=( decoder.ReadBooleanField("multivalue",false)); builder.ReturnedByDefault=( decoder.ReadBooleanField("returnedbydefault",true)); @@ -1694,7 +1694,7 @@ public override void Serialize(Object obj, ObjectEncoder encoder) { encoder.WriteBooleanField("readable", val.IsReadable); encoder.WriteBooleanField("creatable", val.IsCreatable); encoder.WriteBooleanField("updateable", val.IsUpdateable); - encoder.WriteBooleanField("multivalue", val.IsMultiValue); + encoder.WriteBooleanField("multivalue", val.IsMultiValued); encoder.WriteBooleanField("returnedbydefault", val.IsReturnedByDefault); } } diff --git a/FrameworkTests/ObjectSerializationTests.cs b/FrameworkTests/ObjectSerializationTests.cs index 8941a4ac..dc4ebb02 100644 --- a/FrameworkTests/ObjectSerializationTests.cs +++ b/FrameworkTests/ObjectSerializationTests.cs @@ -553,14 +553,14 @@ public void TestAttributeInfo() { builder.Readable=(true); builder.Creatable=(true); builder.Updateable=(true); - builder.MultiValue=(true); + builder.MultiValued=(true); builder.ReturnedByDefault = false; ConnectorAttributeInfo v1 = builder.Build(); ConnectorAttributeInfo v2 = (ConnectorAttributeInfo)CloneObject(v1); Assert.AreEqual(v1,v2); Assert.AreEqual("foo", v2.Name); Assert.AreEqual(typeof(String), v2.ValueType); - Assert.IsTrue(v2.IsMultiValue); + Assert.IsTrue(v2.IsMultiValued); Assert.IsTrue(v2.IsReadable); Assert.IsTrue(v2.IsRequired); Assert.IsTrue(v2.IsUpdateable); @@ -602,7 +602,7 @@ public void TestObjectClassInfo() { builder.Required=(true); builder.Readable=(true); builder.Updateable=(true); - builder.MultiValue=(true); + builder.MultiValued=(true); ObjectClassInfoBuilder obld = new ObjectClassInfoBuilder(); obld.ObjectType = ObjectClass.ORGANIZATION_NAME; obld.IsContainer = true; From 5d39a7e3c7e8e49c5e852d2c2d817099182f7f49 Mon Sep 17 00:00:00 2001 From: tknappek Date: Tue, 9 Dec 2008 14:53:34 +0000 Subject: [PATCH 098/342] NormalizeAttribute method made virtual --- ActiveDirectoryConnector/ActiveDirectoryConnector.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/ActiveDirectoryConnector/ActiveDirectoryConnector.cs b/ActiveDirectoryConnector/ActiveDirectoryConnector.cs index d0678d52..b1863818 100644 --- a/ActiveDirectoryConnector/ActiveDirectoryConnector.cs +++ b/ActiveDirectoryConnector/ActiveDirectoryConnector.cs @@ -1094,7 +1094,7 @@ public Uid Authenticate(string username, #region AttributeNormalizer Members - public ConnectorAttribute NormalizeAttribute(ObjectClass oclass, ConnectorAttribute attribute) + public virtual ConnectorAttribute NormalizeAttribute(ObjectClass oclass, ConnectorAttribute attribute) { // if this gets big, use deleagates, but for now, just // handle individual attirbutes; From 31db28a2a049b587f08d0cee2341ad516687a21a Mon Sep 17 00:00:00 2001 From: dvernon Date: Tue, 9 Dec 2008 15:25:35 +0000 Subject: [PATCH 099/342] Issue #366 - correcting config issues --- .../ActiveDirectoryConfiguration.cs | 52 +++++++++---------- ActiveDirectoryConnector/Messages.resx | 6 +-- ActiveDirectoryConnector/ObjectClasses.xml | 4 +- 3 files changed, 31 insertions(+), 31 deletions(-) diff --git a/ActiveDirectoryConnector/ActiveDirectoryConfiguration.cs b/ActiveDirectoryConnector/ActiveDirectoryConfiguration.cs index eea6f515..701eb9ba 100644 --- a/ActiveDirectoryConnector/ActiveDirectoryConfiguration.cs +++ b/ActiveDirectoryConnector/ActiveDirectoryConfiguration.cs @@ -49,55 +49,55 @@ namespace Org.IdentityConnectors.ActiveDirectory { public class ActiveDirectoryConfiguration : Org.IdentityConnectors.Framework.Spi.AbstractConfiguration { - [ConfigurationProperty(OperationTypes=new Type[]{typeof(SyncOp)}, Confidential = false, DisplayMessageKey = "display_SyncGlobalCatalogServer", HelpMessageKey = "help_SyncGlobalCatalogServer", Order=8)] - public String SyncGlobalCatalogServer + [ConfigurationProperty(Confidential = false, DisplayMessageKey = "display_DirectoryAdminName", HelpMessageKey = "help_DirectoryAdminName", Order = 1)] + public String DirectoryAdminName { get; set; } - [ConfigurationProperty(OperationTypes = new Type[] { typeof(SyncOp) }, Confidential = false, DisplayMessageKey = "display_SyncDomainController", HelpMessageKey = "help_SyncDomainController", Order=7)] - public String SyncDomainController + [ConfigurationProperty(Confidential = true, DisplayMessageKey = "display_DirectoryAdminPassword", HelpMessageKey = "help_DirectoryAdminPassword", Order = 2)] + public String DirectoryAdminPassword { get; set; } - [ConfigurationProperty(OperationTypes = new Type[] { typeof(SyncOp) }, Confidential = false, DisplayMessageKey = "display_SyncSearchContext", HelpMessageKey = "help_SyncSearchContext", Order=9)] - public String SyncSearchContext + [ConfigurationProperty(Confidential = false, DisplayMessageKey = "display_ObjectClass", HelpMessageKey = "help_ObjectClass", Order = 3)] + public String ObjectClass { get; set; } - [ConfigurationProperty(Confidential = false, DisplayMessageKey = "display_domainName", HelpMessageKey = "help_domainName", Order=1)] - public String DomainName + [ConfigurationProperty(Confidential = false, DisplayMessageKey = "display_SearchContainer", HelpMessageKey = "help_SearchContainer", Order = 4)] + public String SearchContainer { get; set; } - [ConfigurationProperty(Confidential = false, DisplayMessageKey = "display_DirectoryAdminName", HelpMessageKey = "help_DirectoryAdminName", Order=3)] - public String DirectoryAdminName - { get; set; } + [ConfigurationProperty(Confidential = false, DisplayMessageKey = "display_CreateHomeDirectory", HelpMessageKey = "help_CreateHomeDirectory", Order = 5)] + public bool CreateHomeDirectory { get; set; } - [ConfigurationProperty(Confidential = true, DisplayMessageKey = "display_DirectoryAdminPassword", HelpMessageKey = "help_DirectoryAdminPassword", Order=4)] - public String DirectoryAdminPassword + [ConfigurationProperty(Confidential = false, DisplayMessageKey = "display_LDAPHostName", HelpMessageKey = "help_LDAPHostName", Order = 6)] + public String LDAPHostName { get; set; } - [ConfigurationProperty(Confidential = false, DisplayMessageKey = "display_ObjectClass", HelpMessageKey = "help_ObjectClass", Order=10)] - public String ObjectClass - { get; set; } + [ConfigurationProperty(Confidential = false, DisplayMessageKey = "display_SearchChildDomains", HelpMessageKey = "help_SearchChildDomains", Order = 7)] + public bool SearchChildDomains { get; set; } - [ConfigurationProperty(Confidential = false, DisplayMessageKey = "display_CreateHomeDirectory", HelpMessageKey = "help_CreateHomeDirectory", Order=11)] - public bool CreateHomeDirectory { get; set; } + [ConfigurationProperty(Confidential = false, DisplayMessageKey = "display_domainName", HelpMessageKey = "help_domainName", Order = 8)] + public String DomainName + { get; set; } - [ConfigurationProperty(Confidential = false, DisplayMessageKey = "display_SearchContainer", HelpMessageKey = "help_SearchContainer", Order=2)] - public String SearchContainer + [ConfigurationProperty(OperationTypes = new Type[] { typeof(SyncOp) }, Confidential = false, DisplayMessageKey = "display_SyncGlobalCatalogServer", HelpMessageKey = "help_SyncGlobalCatalogServer", Order = 9)] + public String SyncGlobalCatalogServer { get; set; } - [ConfigurationProperty(Confidential = false, DisplayMessageKey = "display_SearchChildDomains", HelpMessageKey = "help_SearchChildDomains", Order=6)] - public bool SearchChildDomains {get;set;} + [ConfigurationProperty(OperationTypes = new Type[] { typeof(SyncOp) }, Confidential = false, DisplayMessageKey = "display_SyncDomainController", HelpMessageKey = "help_SyncDomainController", Order=10)] + public String SyncDomainController + { get; set; } - [ConfigurationProperty(Confidential = false, DisplayMessageKey = "display_LDAPHostName", HelpMessageKey = "help_LDAPHostName", Order=5)] - public String LDAPHostName + [ConfigurationProperty(OperationTypes = new Type[] { typeof(SyncOp) }, Confidential = false, DisplayMessageKey = "display_SyncSearchContext", HelpMessageKey = "help_SyncSearchContext", Order=11)] + public String SyncSearchContext { get; set; } public ActiveDirectoryConfiguration() { DomainName = ""; SearchContainer = ""; - DirectoryAdminName = "cn=DirectoryAdmin"; + DirectoryAdminName = "administrator"; ObjectClass = "User"; - CreateHomeDirectory = false; + CreateHomeDirectory = true; SearchChildDomains = false; LDAPHostName = ""; } diff --git a/ActiveDirectoryConnector/Messages.resx b/ActiveDirectoryConnector/Messages.resx index ab791a4c..c1094f5d 100644 --- a/ActiveDirectoryConnector/Messages.resx +++ b/ActiveDirectoryConnector/Messages.resx @@ -124,10 +124,10 @@ Create Home Directory - Directory Adminstrator's Account + Directory Adminstrator&apos;s Account - Directory Administrator's Password + Directory Administrator&apos;s Password Domain Name @@ -136,7 +136,7 @@ Active Directory Domain Controller Hostname - Object class for User Objects + Object Class for User Objects Search Child Domains diff --git a/ActiveDirectoryConnector/ObjectClasses.xml b/ActiveDirectoryConnector/ObjectClasses.xml index 4ddb14fe..911d7c36 100644 --- a/ActiveDirectoryConnector/ObjectClasses.xml +++ b/ActiveDirectoryConnector/ObjectClasses.xml @@ -65,7 +65,7 @@ - + @@ -73,7 +73,7 @@ - + From a4f91acc2e54c09b4347a2786da66788c518b88b Mon Sep 17 00:00:00 2001 From: dvernon Date: Tue, 9 Dec 2008 19:32:35 +0000 Subject: [PATCH 100/342] Changes for unit tests --- ActiveDirectoryConnectorTests/project.xml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/ActiveDirectoryConnectorTests/project.xml b/ActiveDirectoryConnectorTests/project.xml index 20e206c3..748bfeaf 100644 --- a/ActiveDirectoryConnectorTests/project.xml +++ b/ActiveDirectoryConnectorTests/project.xml @@ -4,9 +4,9 @@ - + - + From 40d734234bfdb01fc2eff8c8b78b1c561daca4d2 Mon Sep 17 00:00:00 2001 From: rcauble Date: Tue, 9 Dec 2008 22:33:20 +0000 Subject: [PATCH 101/342] Issue#355: changed serialization format to use flags --- FrameworkInternal/Resources.resx | 19 +++++----- FrameworkInternal/Serializer.cs | 43 ++++++++++++---------- FrameworkTests/ObjectSerializationTests.cs | 17 +++++++++ 3 files changed, 50 insertions(+), 29 deletions(-) diff --git a/FrameworkInternal/Resources.resx b/FrameworkInternal/Resources.resx index cca9ee27..5ddf45dc 100644 --- a/FrameworkInternal/Resources.resx +++ b/FrameworkInternal/Resources.resx @@ -374,18 +374,15 @@ OperationOptionInfo | SyncDeltaType | SyncToken | SyncDelta | QualifiedUid <!ATTLIST Throwable message CDATA #IMPLIED > -<!ELEMENT AttributeInfo EMPTY> +<!ELEMENT AttributeInfo (AttributeInfoFlag*)> <!ATTLIST AttributeInfo name CDATA #REQUIRED type CDATA #REQUIRED - required CDATA #IMPLIED - readable CDATA #IMPLIED - creatable CDATA #IMPLIED - updateable CDATA #IMPLIED - multivalue CDATA #IMPLIED - returnedbydefault CDATA #IMPLIED > - +<!ELEMENT AttributeInfoFlag EMPTY> +<!ATTLIST AttributeInfoFlag + value ( REQUIRED | MULTIVALUED | NOT_CREATABLE | NOT_UPDATEABLE | NOT_READABLE | NOT_RETURNED_BY_DEFAULT ) #REQUIRED +> <!ELEMENT ConnectorObject (ObjectClass,Attributes)> <!ELEMENT Attributes ((%attributeTypes;)*)> @@ -421,7 +418,11 @@ OperationOptionInfo | SyncDeltaType | SyncToken | SyncDelta | QualifiedUid name CDATA #REQUIRED type CDATA #REQUIRED > -<!ELEMENT SyncDeltaType (#PCDATA)> +<!ELEMENT SyncDeltaType EMPTY> +<!ATTLIST SyncDeltaType + value ( CREATE_OR_UPDATE | DELETE ) #REQUIRED +> + <!ELEMENT SyncToken (value)> <!ELEMENT SyncDelta (SyncDeltaType,SyncToken,Uid,ConnectorObject?)> <!ELEMENT QualifiedUid (ObjectClass,Uid)> diff --git a/FrameworkInternal/Serializer.cs b/FrameworkInternal/Serializer.cs index f9cce267..a30a6156 100644 --- a/FrameworkInternal/Serializer.cs +++ b/FrameworkInternal/Serializer.cs @@ -88,7 +88,7 @@ public EnumSerializationHandler (Type clazz, String name) public override object Deserialize(ObjectDecoder decoder) { - String val = decoder.ReadStringContents(); + String val = decoder.ReadStringField("value",null); Type enumClass = HandledObjectType; Object rv = Enum.Parse(enumClass, val); return rv; @@ -97,7 +97,7 @@ public override object Deserialize(ObjectDecoder decoder) { public override void Serialize(object obj, ObjectEncoder encoder) { Enum e = (Enum)obj; - encoder.WriteStringContents(Enum.GetName(e.GetType(),e)); + encoder.WriteStringField("value",Enum.GetName(e.GetType(),e)); } } @@ -1451,6 +1451,8 @@ static CommonObjectHandlers() { HANDLERS.Add( new ScriptContextHandler() ); HANDLERS.Add( new OperationOptionsHandler() ); HANDLERS.Add( new OperationOptionInfoHandler() ); + HANDLERS.Add( new EnumSerializationHandler(typeof(ConnectorAttributeInfo.Flags), + "AttributeInfoFlag")); HANDLERS.Add( new EnumSerializationHandler(typeof(SyncDeltaType), "SyncDeltaType") ); HANDLERS.Add( new SyncTokenHandler() ); @@ -1671,18 +1673,17 @@ public override Object Deserialize(ObjectDecoder decoder) { decoder.ReadStringField("name",null)); builder.ValueType=( decoder.ReadClassField("type",null)); - builder.Required=( - decoder.ReadBooleanField("required",false)); - builder.Readable=( - decoder.ReadBooleanField("readable",false)); - builder.Creatable=( - decoder.ReadBooleanField("creatable",false)); - builder.Updateable=( - decoder.ReadBooleanField("updateable",false)); - builder.MultiValued=( - decoder.ReadBooleanField("multivalue",false)); - builder.ReturnedByDefault=( - decoder.ReadBooleanField("returnedbydefault",true)); + ConnectorAttributeInfo.Flags flags = ConnectorAttributeInfo.Flags.NONE; + int count = decoder.GetNumSubObjects(); + for ( int i = 0; i < count; i++ ) { + object o = decoder.ReadObjectContents(i); + if ( o is ConnectorAttributeInfo.Flags ) { + ConnectorAttributeInfo.Flags f = + (ConnectorAttributeInfo.Flags)o; + flags |= f; + } + } + builder.InfoFlags = flags; return builder.Build(); } @@ -1690,12 +1691,14 @@ public override void Serialize(Object obj, ObjectEncoder encoder) { ConnectorAttributeInfo val = (ConnectorAttributeInfo)obj; encoder.WriteStringField("name", val.Name); encoder.WriteClassField("type", val.ValueType); - encoder.WriteBooleanField("required", val.IsRequired); - encoder.WriteBooleanField("readable", val.IsReadable); - encoder.WriteBooleanField("creatable", val.IsCreatable); - encoder.WriteBooleanField("updateable", val.IsUpdateable); - encoder.WriteBooleanField("multivalue", val.IsMultiValued); - encoder.WriteBooleanField("returnedbydefault", val.IsReturnedByDefault); + ConnectorAttributeInfo.Flags flags = val.InfoFlags; + foreach (Enum e in Enum.GetValues(typeof(ConnectorAttributeInfo.Flags))) { + ConnectorAttributeInfo.Flags flag = + (ConnectorAttributeInfo.Flags)e; + if ( (flags & flag) != 0 ) { + encoder.WriteObjectContents(flag); + } + } } } diff --git a/FrameworkTests/ObjectSerializationTests.cs b/FrameworkTests/ObjectSerializationTests.cs index dc4ebb02..f515a3ab 100644 --- a/FrameworkTests/ObjectSerializationTests.cs +++ b/FrameworkTests/ObjectSerializationTests.cs @@ -566,6 +566,23 @@ public void TestAttributeInfo() { Assert.IsTrue(v2.IsUpdateable); Assert.IsTrue(v2.IsCreatable); Assert.IsFalse(v2.IsReturnedByDefault); + + builder.InfoFlags = AllFlags(); + v1 = builder.Build(); + v2 = (ConnectorAttributeInfo)CloneObject(v1); + Assert.AreEqual(v1,v2); + } + + private ConnectorAttributeInfo.Flags AllFlags() + { + ConnectorAttributeInfo.Flags flags = + ConnectorAttributeInfo.Flags.NONE; + foreach (Enum e in Enum.GetValues(typeof(ConnectorAttributeInfo.Flags))) { + ConnectorAttributeInfo.Flags f = + (ConnectorAttributeInfo.Flags)e; + flags |= f; + } + return flags; } [Test] From c5a7d3bfbc189ac95f2daa4d18a329bacf2157a2 Mon Sep 17 00:00:00 2001 From: dvernon Date: Tue, 9 Dec 2008 22:45:43 +0000 Subject: [PATCH 102/342] Issue #367 - clean up connector messages --- ActiveDirectoryConnector/Messages.resx | 24 ++++++++++++------------ 1 file changed, 12 insertions(+), 12 deletions(-) diff --git a/ActiveDirectoryConnector/Messages.resx b/ActiveDirectoryConnector/Messages.resx index c1094f5d..ca71c015 100644 --- a/ActiveDirectoryConnector/Messages.resx +++ b/ActiveDirectoryConnector/Messages.resx @@ -118,16 +118,16 @@ System.Resources.ResXResourceWriter, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 - Windows Active Directory Connector. + Windows Active Directory Connector Create Home Directory - Directory Adminstrator&apos;s Account + Directory Adminstrator''s Account - Directory Administrator&apos;s Password + Directory Administrator''s Password Domain Name @@ -145,19 +145,19 @@ Search Container - ActiveSync Domain Controller + Sync Domain Controller - ActiveSync Global Catalog Server + Sync Global Catalog Server - ActiveSync Search Context + Sync Search Context Specify whether or not the home directory for the user will be created. - Enter the administrator user name with which the system should authenticate. The setting should be in the form 'domainname'\'username'. + Enter the administrator user name with which the system should authenticate. The setting can be either be a username or 'domainname'\'username'. Enter the password that should be used when authenticating. @@ -169,22 +169,22 @@ For cross-domain administration, enter the hostname, IP address, or domain name of the LDAP server. - Specify the class of object that will be managed on this resource. + Specify the Active Directory object class for user objects that will be managed on this resource. The default is User, and for most situations, this should be fine. - Select if you want searches of Active Directory to include child domains. In addition, the Search Container and Sync Search Context attributes must be set to the top of the parent domain, e.g. DC=mydomain,DC=com. + Select if you want searches of Active Directory to include child domains. In addition, the Search Container and Sync Search Context (see sync settings) attributes must be set to the top of the parent domain, e.g. DC=mydomain,DC=com. - Specify the container object from which users should be retrieved when loading from the resource. For example, if you want to retrieve users from the Users container, enter CN=Users,DC=MYDOMAIN,DC=COM. + Specify a container object which will be the default root of all searches. Unless a search explicitly passes in other criteria, only objects under this container will be searched. For example, if you want to retrieve users from the Users container, enter CN=Users,DC=MYDOMAIN,DC=COM. Domain controller to use during active sync. Only used if not searching child domains. - Name of the global catalog server. This is needed only if searching child domains + Name of the global catalog server. This is needed only if searching child domains. - Distinguished name of the object under which to search for changes during ActiveSync + Distinguished name of the object under which to search for changes during sync operation. Connector has not been configured From b80387d105eacea0f7003c1bcb5e0ef99886e794 Mon Sep 17 00:00:00 2001 From: tknappek Date: Wed, 10 Dec 2008 13:46:41 +0000 Subject: [PATCH 103/342] search implemented --- ExchangeConnector/ExchangeConnector.cs | 16 +- ExchangeConnector/ExchangeConnector.csproj | 3 + ExchangeConnector/ExchangeUtils.cs | 80 +++++- ExchangeConnector/LegacyExchangeConnector.cs | 148 ++++++++--- ExchangeConnector/Messages.resx | 258 +++++++++++++++++++ 5 files changed, 456 insertions(+), 49 deletions(-) create mode 100644 ExchangeConnector/Messages.resx diff --git a/ExchangeConnector/ExchangeConnector.cs b/ExchangeConnector/ExchangeConnector.cs index 1d1d80a4..df0a7d1c 100644 --- a/ExchangeConnector/ExchangeConnector.cs +++ b/ExchangeConnector/ExchangeConnector.cs @@ -45,18 +45,14 @@ using Org.IdentityConnectors.Common; using Org.IdentityConnectors.Framework.Common.Objects; using Org.IdentityConnectors.Framework.Spi; -using Org.IdentityConnectors.Framework.Spi.Operations; namespace Org.IdentityConnectors.Exchange { /// - /// MS Exchange extension of Active Directory connector - /// - //TODO: what about MessageCatalogPath inheritance? - [ConnectorClass("connector_displayName", - typeof(ExchangeConfiguration), - MessageCatalogPath = "Org.IdentityConnectors.Exchange.Messages" - )] + /// MS Exchange extension of Active Directory connector. + /// Full featured connector, see LegacyExchangeConnector for limited functionality connector. + /// LegacyExchangeConnector will be extension of this class, once ready. + /// public class ExchangeConnector : ActiveDirectoryConnector { private static readonly string CLASS = typeof(ExchangeConnector).ToString(); @@ -240,9 +236,9 @@ public override void Dispose() /// Defines the supported object classes by the connector, used for schema building /// /// List of supported object classes - protected override IList GetSupportedObjectClasses() + protected override ICollection GetSupportedObjectClasses() { - IList ocList = base.GetSupportedObjectClasses(); + ICollection ocList = base.GetSupportedObjectClasses(); Assertions.NullCheck(ocList, "ocList"); ocList.Add(MAILBOX); ocList.Add(MAILUSER); diff --git a/ExchangeConnector/ExchangeConnector.csproj b/ExchangeConnector/ExchangeConnector.csproj index 1f8ca22b..2bbf26d1 100644 --- a/ExchangeConnector/ExchangeConnector.csproj +++ b/ExchangeConnector/ExchangeConnector.csproj @@ -72,6 +72,9 @@ + + Designer + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + text/microsoft-resx + + + 2.0 + + + System.Resources.ResXResourceReader, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + System.Resources.ResXResourceWriter, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + Exchange Connector + + + Create Home Directory + + + Directory Adminstrator's Account + + + Directory Administrator's Password + + + Domain Name + + + Active Directory Domain Controller Hostname + + + Object class for User Objects + + + Search Child Domains + + + Search Container + + + ActiveSync Domain Controller + + + ActiveSync Global Catalog Server + + + ActiveSync Search Context + + + Specify whether or not the home directory for the user will be created. + + + Enter the administrator user name with which the system should authenticate. The setting should be in the form 'domainname'\'username'. + + + Enter the password that should be used when authenticating. + + + Name of the windows domain (e.g. windowsdomain.mycompany.com) + + + For cross-domain administration, enter the hostname, IP address, or domain name of the LDAP server. + + + Specify the class of object that will be managed on this resource. + + + Select if you want searches of Active Directory to include child domains. In addition, the Search Container and Sync Search Context attributes must be set to the top of the parent domain, e.g. DC=mydomain,DC=com. + + + Specify the container object from which users should be retrieved when loading from the resource. For example, if you want to retrieve users from the Users container, enter CN=Users,DC=MYDOMAIN,DC=COM. + + + Domain controller to use during active sync. Only used if not searching child domains. + + + Name of the global catalog server. This is needed only if searching child domains + + + Distinguished name of the object under which to search for changes during ActiveSync + + + Connector has not been configured + + + Delete is not supported for ObjectClass {0} + + + Invalid object class: {0} + + + Attribute {0} is not present in connector object. Cannot proceed with synchronization + + + The name operational attribute cannot be null + + + Sync operation is not available for ObjectClass {0} + + + Uid was not present + + + Invalid Object Class was specified in the connector configuration. Object Class \'{0}\' was not found in Active Directory + + + Could not add connector attribute to <null> search result + + + Could not add connector attribute to <null> directory entry + + + Expecting single value, but found multiple values for attribute {0} + + + ObjectClass \'{0}\' is not valid for this connector + + + Invalid credentials supplied for user {0} + + + Password expiration can only be reset by reseting the password + + + Active Directory does not support locking users. User may be unlocked only + + + An invalid searchscope was supplied: {0} + + + An execption occurred during validation of user {0}. The user was successfully authenticated, but the user's guid could not be determined. + + + An execption occurred during validation of user {0}. The user was successfully authenticated, but the user's sid could not be determined. + + + Directory administrator name not supplied. + + + Directory administrator password not supplied. + + + Domain name not supplied. + + + ObjectClass was not supplied. + + + Search Container was not supplied. + + \ No newline at end of file From b023af6ebb38b7c008bf745bbb7db4740a08683a Mon Sep 17 00:00:00 2001 From: tknappek Date: Wed, 10 Dec 2008 14:47:30 +0000 Subject: [PATCH 104/342] ExchageUtils now using CommonUtils --- ActiveDirectoryConnector/CommonUtils.cs | 23 +++++++++--------- ExchangeConnector/ExchangeUtils.cs | 32 ++++--------------------- 2 files changed, 15 insertions(+), 40 deletions(-) diff --git a/ActiveDirectoryConnector/CommonUtils.cs b/ActiveDirectoryConnector/CommonUtils.cs index 85edc223..b46e287c 100644 --- a/ActiveDirectoryConnector/CommonUtils.cs +++ b/ActiveDirectoryConnector/CommonUtils.cs @@ -39,29 +39,21 @@ */ using System; using System.Collections.Generic; -using System.Linq; -using System.Text; using System.Reflection; - -using System.Diagnostics; using System.IO; -using Microsoft.Win32; -using System.Xml.Serialization; using Org.IdentityConnectors.Common; using Org.IdentityConnectors.Framework.Common.Objects; -using Org.IdentityConnectors.Framework.Common.Objects.Filters; using Org.IdentityConnectors.Framework.Common.Serializer; -using Org.IdentityConnectors.Framework.Spi.Operations; namespace Org.IdentityConnectors.ActiveDirectory { - class CommonUtils + public class CommonUtils { /// /// reads the object class info definitions from xml /// ///Dictionary of object classes - internal static IDictionary GetOCInfo(string name) + protected static IDictionary GetOCInfo(string name) { Assembly assembly = Assembly.GetExecutingAssembly(); Stream stream = assembly.GetManifestResourceStream(name); @@ -70,8 +62,15 @@ internal static IDictionary GetOCInfo(string name) //we just read TextReader streamReader = new StreamReader(stream); - String xml = streamReader.ReadToEnd(); - streamReader.Close(); + String xml; + try + { + xml = streamReader.ReadToEnd(); + } + finally + { + streamReader.Close(); + } //read from xml var ret = (ICollection)SerializerUtil.DeserializeXmlObject(xml, true); diff --git a/ExchangeConnector/ExchangeUtils.cs b/ExchangeConnector/ExchangeUtils.cs index 9a85cbc2..d609a440 100644 --- a/ExchangeConnector/ExchangeUtils.cs +++ b/ExchangeConnector/ExchangeUtils.cs @@ -42,27 +42,25 @@ using System.Collections.Generic; using System.Diagnostics; using System.IO; -using System.Linq; using System.Management.Automation.Runspaces; using System.Reflection; using Microsoft.Win32; using System.Xml.Serialization; +using Org.IdentityConnectors.ActiveDirectory; using Org.IdentityConnectors.Common; using Org.IdentityConnectors.Framework.Common.Objects; -using Org.IdentityConnectors.Framework.Common.Objects.Filters; -using Org.IdentityConnectors.Framework.Common.Serializer; -using Org.IdentityConnectors.Framework.Spi.Operations; namespace Org.IdentityConnectors.Exchange { /// /// Description of ExchangeUtils. /// - public class ExchangeUtils + public class ExchangeUtils : CommonUtils { private static readonly string CLASS = typeof(ExchangeUtils).ToString(); + private const string OC_DEF_FILE = "Org.IdentityConnectors.Exchange.ObjectClasses.xml"; private const string EXCHANGE_REG_KEY = "Software\\Microsoft\\Exchange\\v8.0\\Setup\\"; private const string EXCHANGE_REG_VALUE = "MsiInstallPath"; @@ -137,29 +135,7 @@ internal static String GetRegistryStringValue(string keyName, string valName) ///Dictionary of object classes internal static IDictionary GetOCInfo() { - Assembly assembly = Assembly.GetExecutingAssembly(); - Stream stream = assembly.GetManifestResourceStream("Org.IdentityConnectors.Exchange.ObjectClasses.xml"); - - Assertions.NullCheck(stream, "stream"); - - //we just read - TextReader streamReader = new StreamReader(stream); - String xml = streamReader.ReadToEnd(); - streamReader.Close(); - - //read from xml - var ret = (ICollection)SerializerUtil.DeserializeXmlObject(xml, true); - - Assertions.NullCheck(ret, "ret"); - - //create map of object infos - var map = new Dictionary(ret.Count); - foreach (ObjectClassInfo o in ret) - { - map.Add(new ObjectClass(o.ObjectType.ToString()), o); - } - - return map; + return GetOCInfo(OC_DEF_FILE); } /// From 4bb9a1fbadc662c3a1d04d2cff48d03c7114d3e8 Mon Sep 17 00:00:00 2001 From: tknappek Date: Wed, 10 Dec 2008 15:00:56 +0000 Subject: [PATCH 105/342] visibility corrected --- ActiveDirectoryConnector/CommonUtils.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/ActiveDirectoryConnector/CommonUtils.cs b/ActiveDirectoryConnector/CommonUtils.cs index b46e287c..8720b8ba 100644 --- a/ActiveDirectoryConnector/CommonUtils.cs +++ b/ActiveDirectoryConnector/CommonUtils.cs @@ -53,7 +53,7 @@ public class CommonUtils /// reads the object class info definitions from xml /// ///Dictionary of object classes - protected static IDictionary GetOCInfo(string name) + protected internal static IDictionary GetOCInfo(string name) { Assembly assembly = Assembly.GetExecutingAssembly(); Stream stream = assembly.GetManifestResourceStream(name); From 730cee9dddf105723c1c068f06887f575de0d21f Mon Sep 17 00:00:00 2001 From: rcauble Date: Wed, 10 Dec 2008 15:59:34 +0000 Subject: [PATCH 106/342] corrected PASSWORD and ORGANGIZATIONS attribute info --- Framework/CommonObjects.cs | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/Framework/CommonObjects.cs b/Framework/CommonObjects.cs index bba30d19..e5b7abfe 100644 --- a/Framework/CommonObjects.cs +++ b/Framework/CommonObjects.cs @@ -1925,7 +1925,6 @@ public static class OperationalAttributeInfos { public static readonly ConnectorAttributeInfo PASSWORD = ConnectorAttributeInfoBuilder.Build( OperationalAttributes.PASSWORD_NAME, typeof(GuardedString), - ConnectorAttributeInfo.Flags.REQUIRED | ConnectorAttributeInfo.Flags.NOT_READABLE | ConnectorAttributeInfo.Flags.NOT_RETURNED_BY_DEFAULT); @@ -2169,7 +2168,7 @@ public static class PredefinedAttributeInfos { * values are the UID value of each organization that an account or person is * a member of. */ - public static readonly ConnectorAttributeInfo ORGANIZATIONS = + public static readonly ConnectorAttributeInfo ORGANIZATION = ConnectorAttributeInfoBuilder.Build(PredefinedAttributes.ORGANIZATION_NAME, typeof(String), ConnectorAttributeInfo.Flags.NOT_RETURNED_BY_DEFAULT); From 610d07136abb4dcbddcfc1dd458e07db8c69973c Mon Sep 17 00:00:00 2001 From: rcauble Date: Wed, 10 Dec 2008 17:24:41 +0000 Subject: [PATCH 107/342] Issue#353: removed reset password --- Framework/CommonObjects.cs | 49 ++------------------------------------ 1 file changed, 2 insertions(+), 47 deletions(-) diff --git a/Framework/CommonObjects.cs b/Framework/CommonObjects.cs index e5b7abfe..d722e1c4 100644 --- a/Framework/CommonObjects.cs +++ b/Framework/CommonObjects.cs @@ -376,20 +376,6 @@ public static GuardedString GetPasswordValue(ICollection att ConnectorAttribute pwd = Find(OperationalAttributes.PASSWORD_NAME, attrs); return (pwd == null) ? null : GetGuardedStringValue(pwd); } - /** - * Get the reset password value from the provided set of {@link Attribute}s. - * - * @param attrs - * Set of {@link Attribute}s that may contain the reset password - * {@link OperationalAttributes#RESET_PASSWORD_NAME} - * {@link Attribute}. - * @return null if it does not exist in the {@link Set} else - * the value. - */ - public static GuardedString GetResetPasswordValue(ICollection attrs) { - ConnectorAttribute pwd = Find(OperationalAttributes.RESET_PASSWORD_NAME, attrs); - return (pwd == null) ? null : GetGuardedStringValue(pwd); - } /** * Get the current password value from the provided set of {@link Attribute}s. @@ -651,8 +637,7 @@ internal ConnectorAttribute(string name, IList val) { throw new ArgumentException("Name must not be blank!"); } if (OperationalAttributes.PASSWORD_NAME.Equals(name) || - OperationalAttributes.CURRENT_PASSWORD_NAME.Equals(name) || - OperationalAttributes.RESET_PASSWORD_NAME.Equals(name)) { + OperationalAttributes.CURRENT_PASSWORD_NAME.Equals(name)) { // check the value.. if (val == null || val.Count != 1) { String MSG = "Must be a single value."; @@ -876,16 +861,6 @@ public static ConnectorAttribute BuildPassword(GuardedString password) { public static ConnectorAttribute BuildCurrentPassword(GuardedString password) { return Build(OperationalAttributes.CURRENT_PASSWORD_NAME, password); } - /** - * Builds the operational attribute reset password. - * - * @param password - * the string that represents a password. - * @return an attribute that represents a reset password operation. - */ - public static ConnectorAttribute BuildResetPassword(GuardedString password) { - return Build(OperationalAttributes.RESET_PASSWORD_NAME, password); - } public static ConnectorAttribute BuildPassword(SecureString password) { return Build(OperationalAttributes.PASSWORD_NAME, new GuardedString(password)); @@ -893,9 +868,6 @@ public static ConnectorAttribute BuildPassword(SecureString password) { public static ConnectorAttribute BuildCurrentPassword(SecureString password) { return Build(OperationalAttributes.CURRENT_PASSWORD_NAME, new GuardedString(password)); } - public static ConnectorAttribute BuildResetPassword(SecureString password) { - return Build(OperationalAttributes.RESET_PASSWORD_NAME, new GuardedString(password)); - } /** * Builds ant operational attribute that either represents the object is * enabled or sets in disabled depending on where its used for instance on @@ -1253,7 +1225,6 @@ internal ConnectorAttributeInfo(string name, Type type, throw new ArgumentException("Name must not be blank!"); } if ((OperationalAttributes.PASSWORD_NAME.Equals(name) || - OperationalAttributes.RESET_PASSWORD_NAME.Equals(name) || OperationalAttributes.CURRENT_PASSWORD_NAME.Equals(name)) && !typeof(GuardedString).Equals(type)) { String MSG = "Password based attributes must be of type GuardedString."; @@ -1938,17 +1909,7 @@ public static class OperationalAttributeInfos { OperationalAttributes.CURRENT_PASSWORD_NAME, typeof(GuardedString), ConnectorAttributeInfo.Flags.NOT_READABLE | ConnectorAttributeInfo.Flags.NOT_RETURNED_BY_DEFAULT); - - /** - * Used to do an administrator reset of the password. The value is the reset - * password value. - */ - public static readonly ConnectorAttributeInfo RESET_PASSWORD = - ConnectorAttributeInfoBuilder.Build( - OperationalAttributes.RESET_PASSWORD_NAME, typeof(GuardedString), - ConnectorAttributeInfo.Flags.NOT_READABLE | - ConnectorAttributeInfo.Flags.NOT_RETURNED_BY_DEFAULT); - + /** * Used to determine if a password is expired or to expire a password. */ @@ -2007,11 +1968,6 @@ public static class OperationalAttributes { * requires the current password. */ public static readonly string CURRENT_PASSWORD_NAME = ConnectorAttributeUtil.CreateSpecialName("CURRENT_PASSWORD"); - /** - * Used to do an administrator reset of the password. The value is the reset - * password value. - */ - public static readonly string RESET_PASSWORD_NAME = ConnectorAttributeUtil.CreateSpecialName("RESET_PASSWORD"); // ======================================================================= // Helper Methods.. @@ -2025,7 +1981,6 @@ public static class OperationalAttributes { PASSWORD_EXPIRATION_DATE_NAME, PASSWORD_NAME, CURRENT_PASSWORD_NAME, - RESET_PASSWORD_NAME, PASSWORD_EXPIRED_NAME ); From dc34bb4a77345a15fd81436fedf7fad9939bf151 Mon Sep 17 00:00:00 2001 From: rcauble Date: Wed, 10 Dec 2008 18:15:16 +0000 Subject: [PATCH 108/342] Issue#357 and #364: added objectclass to getLatestSyncToken and authenticate --- .../ActiveDirectoryConnector.cs | 6 +-- .../ActiveDirectoryConnectorTest.cs | 48 +++++++++---------- Framework/ApiOperations.cs | 4 +- Framework/SpiOperations.cs | 4 +- FrameworkInternal/Api.cs | 8 ++-- FrameworkInternal/ApiLocalOperations.cs | 9 ++-- FrameworkTests/ConnectorInfoManagerTests.cs | 2 +- TestBundleV1/TestConnector.cs | 2 +- 8 files changed, 42 insertions(+), 41 deletions(-) diff --git a/ActiveDirectoryConnector/ActiveDirectoryConnector.cs b/ActiveDirectoryConnector/ActiveDirectoryConnector.cs index b1863818..a23944c7 100644 --- a/ActiveDirectoryConnector/ActiveDirectoryConnector.cs +++ b/ActiveDirectoryConnector/ActiveDirectoryConnector.cs @@ -972,7 +972,7 @@ public virtual void Sync(ObjectClass objClass, SyncToken token, } - public virtual SyncToken GetLatestSyncToken() + public virtual SyncToken GetLatestSyncToken(ObjectClass objectClass) { string serverName = GetSyncServerName(); long highestCommittedUsn = 0; @@ -1082,7 +1082,7 @@ String GetSyncDeleteQuery(ActiveDirectorySyncToken adSyncToken) #region AuthenticateOp Members - public Uid Authenticate(string username, + public Uid Authenticate(ObjectClass objectClass, string username, Org.IdentityConnectors.Common.Security.GuardedString password, OperationOptions options) { @@ -1094,7 +1094,7 @@ public Uid Authenticate(string username, #region AttributeNormalizer Members - public virtual ConnectorAttribute NormalizeAttribute(ObjectClass oclass, ConnectorAttribute attribute) + public ConnectorAttribute NormalizeAttribute(ObjectClass oclass, ConnectorAttribute attribute) { // if this gets big, use deleagates, but for now, just // handle individual attirbutes; diff --git a/ActiveDirectoryConnectorTests/ActiveDirectoryConnectorTest.cs b/ActiveDirectoryConnectorTests/ActiveDirectoryConnectorTest.cs index e2f29d38..9042889b 100644 --- a/ActiveDirectoryConnectorTests/ActiveDirectoryConnectorTest.cs +++ b/ActiveDirectoryConnectorTests/ActiveDirectoryConnectorTest.cs @@ -1298,7 +1298,7 @@ public void TestUserPasswordChange() ObjectClass.ACCOUNT, createAttributes); // make sure authenticate works here - connector.Authenticate( + connector.Authenticate(ObjectClass.ACCOUNT, ConnectorAttributeUtil.GetAsStringValue(ConnectorAttributeUtil.Find("sAMAccountName", createAttributes)), gsCurrentPassword, null); @@ -1311,7 +1311,7 @@ public void TestUserPasswordChange() createUid, updateReplaceAttributes); // make sure authenticate works here - connector.Authenticate( + connector.Authenticate(ObjectClass.ACCOUNT, ConnectorAttributeUtil.GetAsStringValue(ConnectorAttributeUtil.Find("sAMAccountName", createAttributes)), gsNewPassword, null); @@ -1319,7 +1319,7 @@ public void TestUserPasswordChange() try { // make sure authenticate doesnt work with original password - connector.Authenticate( + connector.Authenticate(ObjectClass.ACCOUNT, ConnectorAttributeUtil.GetAsStringValue(ConnectorAttributeUtil.Find("sAMAccountName", createAttributes)), gsCurrentPassword, null); } @@ -1386,7 +1386,7 @@ public void TestAuthenticateUser() ObjectClass.ACCOUNT, createAttributes); // make sure authenticate works here - Uid authUid = connector.Authenticate( + Uid authUid = connector.Authenticate(ObjectClass.ACCOUNT, ConnectorAttributeUtil.GetAsStringValue(ConnectorAttributeUtil.Find("sAMAccountName", createAttributes)), gsCurrentPassword, null); @@ -1396,7 +1396,7 @@ public void TestAuthenticateUser() bool caughtException = false; try { - connector.Authenticate( + connector.Authenticate(ObjectClass.ACCOUNT, ConnectorAttributeUtil.GetAsStringValue(ConnectorAttributeUtil.Find("sAMAccountName", createAttributes)), GetGuardedString("boguspassword"), null); @@ -1417,14 +1417,14 @@ public void TestAuthenticateUser() createUid, updateReplaceAttributes); // make sure authenticate works here - new password - connector.Authenticate( + connector.Authenticate(ObjectClass.ACCOUNT, ConnectorAttributeUtil.GetAsStringValue(ConnectorAttributeUtil.Find("sAMAccountName", createAttributes)), gsNewPassword, null); // make sure it fails with the wrong password caughtException = false; try { - connector.Authenticate( + connector.Authenticate(ObjectClass.ACCOUNT, ConnectorAttributeUtil.GetAsStringValue(ConnectorAttributeUtil.Find("sAMAccountName", createAttributes)), GetGuardedString("bogusPassword"), null); } @@ -1446,7 +1446,7 @@ public void TestAuthenticateUser() try { // make sure authenticate fails with correct password - connector.Authenticate( + connector.Authenticate(ObjectClass.ACCOUNT, ConnectorAttributeUtil.GetAsStringValue(ConnectorAttributeUtil.Find("sAMAccountName", createAttributes)), gsNewPassword, null); } @@ -1462,7 +1462,7 @@ public void TestAuthenticateUser() try { // make sure authenticate fails with wrong password (invalid credentials exception) - connector.Authenticate( + connector.Authenticate(ObjectClass.ACCOUNT, ConnectorAttributeUtil.GetAsStringValue(ConnectorAttributeUtil.Find("sAMAccountName", createAttributes)), GetGuardedString("bogusPassword"), null); } @@ -1634,7 +1634,7 @@ public void TestPasswordExpiration() ObjectClass.ACCOUNT, createAttributes); // make sure authenticate works here - connector.Authenticate( + connector.Authenticate(ObjectClass.ACCOUNT, ConnectorAttributeUtil.GetAsStringValue(ConnectorAttributeUtil.Find("sAMAccountName", createAttributes)), gsCurrentPassword, null); @@ -1656,7 +1656,7 @@ public void TestPasswordExpiration() try { // make sure authenticate fails with correct password - connector.Authenticate( + connector.Authenticate(ObjectClass.ACCOUNT, ConnectorAttributeUtil.GetAsStringValue(ConnectorAttributeUtil.Find("sAMAccountName", createAttributes)), gsCurrentPassword, null); } @@ -1676,7 +1676,7 @@ public void TestPasswordExpiration() createUid, expirePasswordTomorrowAttrs); // make sure succeeds - connector.Authenticate( + connector.Authenticate(ObjectClass.ACCOUNT, ConnectorAttributeUtil.GetAsStringValue(ConnectorAttributeUtil.Find("sAMAccountName", createAttributes)), gsCurrentPassword, null); @@ -1713,7 +1713,7 @@ public void TestAccountLocked() ObjectClass.ACCOUNT, createAttributes); // make sure authenticate works here - connector.Authenticate( + connector.Authenticate(ObjectClass.ACCOUNT, ConnectorAttributeUtil.GetAsStringValue(ConnectorAttributeUtil.Find("sAMAccountName", createAttributes)), gsCurrentPassword, null); @@ -1723,7 +1723,7 @@ public void TestAccountLocked() // lock out by having unsucessful attempts. try { - connector.Authenticate( + connector.Authenticate(ObjectClass.ACCOUNT, ConnectorAttributeUtil.GetAsStringValue(ConnectorAttributeUtil.Find("sAMAccountName", createAttributes)), GetGuardedString("bogusPassword"), null); } @@ -1733,7 +1733,7 @@ public void TestAccountLocked() try { - connector.Authenticate( + connector.Authenticate(ObjectClass.ACCOUNT, ConnectorAttributeUtil.GetAsStringValue(ConnectorAttributeUtil.Find("sAMAccountName", createAttributes)), GetGuardedString("bogusPassword"), null); } @@ -1743,7 +1743,7 @@ public void TestAccountLocked() try { - connector.Authenticate( + connector.Authenticate(ObjectClass.ACCOUNT, ConnectorAttributeUtil.GetAsStringValue(ConnectorAttributeUtil.Find("sAMAccountName", createAttributes)), GetGuardedString("bogusPassword"), null); } @@ -1754,7 +1754,7 @@ public void TestAccountLocked() bool exceptionCaught = false; try { - connector.Authenticate( + connector.Authenticate(ObjectClass.ACCOUNT, ConnectorAttributeUtil.GetAsStringValue(ConnectorAttributeUtil.Find("sAMAccountName", createAttributes)), gsCurrentPassword, null); } @@ -1772,7 +1772,7 @@ public void TestAccountLocked() createUid, unlockAttrs); // make sure succeeds - connector.Authenticate( + connector.Authenticate(ObjectClass.ACCOUNT, ConnectorAttributeUtil.GetAsStringValue(ConnectorAttributeUtil.Find("sAMAccountName", createAttributes)), gsCurrentPassword, null); @@ -1826,7 +1826,7 @@ public void TestSync(bool searchChildDomains, String syncSearchContext) // do the first sync //connector.Sync(ObjectClass.ACCOUNT, syncHelper.Token, syncHelper.SyncHandler_Initial, null); - syncHelper.Init(connector.GetLatestSyncToken()); + syncHelper.Init(connector.GetLatestSyncToken(ObjectClass.ACCOUNT)); ICollection attributes = null; // create some users @@ -1844,7 +1844,7 @@ public void TestSync(bool searchChildDomains, String syncSearchContext) syncHelper.CheckAllSyncsProcessed(); // reset everything - syncHelper.Init(connector.GetLatestSyncToken()); + syncHelper.Init(connector.GetLatestSyncToken(ObjectClass.ACCOUNT)); // modify a user, then add some users, then modify one of the added users attributes = new List(); @@ -1872,7 +1872,7 @@ public void TestSync(bool searchChildDomains, String syncSearchContext) connector.Sync(ObjectClass.ACCOUNT, syncHelper._token, syncHelper.SyncHandler_ModifiedAccounts, null); syncHelper.CheckAllSyncsProcessed(); - syncHelper.Init(connector.GetLatestSyncToken()); + syncHelper.Init(connector.GetLatestSyncToken(ObjectClass.ACCOUNT)); // delete the user foreach (Uid uid in createdUids) { @@ -1891,7 +1891,7 @@ public void TestSync(bool searchChildDomains, String syncSearchContext) // now get the latest sync token, and it // should be greater or equal to the last one we saw - SyncToken latestToken = connector.GetLatestSyncToken(); + SyncToken latestToken = connector.GetLatestSyncToken(ObjectClass.ACCOUNT); Assert.Greater(GetUpdateUsnFromToken(latestToken), GetUpdateUsnFromToken(syncHelper._token)); Assert.GreaterOrEqual(GetDeleteUsnFromToken(latestToken), GetDeleteUsnFromToken(syncHelper._token)); } @@ -1914,11 +1914,11 @@ public void TestGetLastSyncToken() (ActiveDirectoryConfiguration)GetConfiguration(); configuration.SearchChildDomains = false; connector.Init(configuration); - SyncToken noGCToken = connector.GetLatestSyncToken(); + SyncToken noGCToken = connector.GetLatestSyncToken(ObjectClass.ACCOUNT); configuration.SearchChildDomains = true; connector.Init(configuration); - SyncToken GCToken = connector.GetLatestSyncToken(); + SyncToken GCToken = connector.GetLatestSyncToken(ObjectClass.ACCOUNT); } public long GetUpdateUsnFromToken(SyncToken token) diff --git a/Framework/ApiOperations.cs b/Framework/ApiOperations.cs index c9df6f33..1a40bff9 100644 --- a/Framework/ApiOperations.cs +++ b/Framework/ApiOperations.cs @@ -67,7 +67,7 @@ public interface AuthenticationApiOp : APIOperation { * iff the credentials do not pass authentication otherwise * nothing. */ - Uid Authenticate(string username, GuardedString password, OperationOptions options); + Uid Authenticate(ObjectClass objectClass, string username, GuardedString password, OperationOptions options); } /// @@ -259,7 +259,7 @@ void Sync(ObjectClass objClass, SyncToken token, * "now". * @return The latest token or null if there is no sync data. */ - SyncToken GetLatestSyncToken(); + SyncToken GetLatestSyncToken(ObjectClass objectClass); } /** diff --git a/Framework/SpiOperations.cs b/Framework/SpiOperations.cs index c40186de..3782eae8 100644 --- a/Framework/SpiOperations.cs +++ b/Framework/SpiOperations.cs @@ -77,7 +77,7 @@ public interface AuthenticateOp : SPIOperation { * iff native authentication fails. If a native exception if * available attempt to throw it. */ - Uid Authenticate(String username, GuardedString password, OperationOptions options); + Uid Authenticate(ObjectClass objectClass, String username, GuardedString password, OperationOptions options); } /** @@ -268,7 +268,7 @@ void Sync(ObjectClass objClass, SyncToken token, * "now". * @return The latest token or null if there is no sync data. */ - SyncToken GetLatestSyncToken(); + SyncToken GetLatestSyncToken(ObjectClass objectClass); } /** diff --git a/FrameworkInternal/Api.cs b/FrameworkInternal/Api.cs index f66fb76b..e9d728cd 100644 --- a/FrameworkInternal/Api.cs +++ b/FrameworkInternal/Api.cs @@ -597,10 +597,10 @@ public Uid RemoveAttributeValues( /** * {@inheritDoc} */ - public Uid Authenticate(String username, GuardedString password, OperationOptions options) { + public Uid Authenticate(ObjectClass objectClass,String username, GuardedString password, OperationOptions options) { return ((AuthenticationApiOp) this .GetOperationCheckSupported(SafeType.Get())).Authenticate( - username, password, options); + objectClass,username, password, options); } /** @@ -651,9 +651,9 @@ public void Sync(ObjectClass objClass, SyncToken token, .Sync(objClass, token, handler, options); } - public SyncToken GetLatestSyncToken() { + public SyncToken GetLatestSyncToken(ObjectClass objectClass) { return ((SyncApiOp)this.GetOperationCheckSupported(SafeType.Get())) - .GetLatestSyncToken(); + .GetLatestSyncToken(objectClass); } private APIOperation GetOperationCheckSupported(SafeType api) { diff --git a/FrameworkInternal/ApiLocalOperations.cs b/FrameworkInternal/ApiLocalOperations.cs index f34866b0..0d2203b7 100644 --- a/FrameworkInternal/ApiLocalOperations.cs +++ b/FrameworkInternal/ApiLocalOperations.cs @@ -279,14 +279,15 @@ public AuthenticationImpl(ConnectorOperationalContext context, * * @see Authentication#authenticate(String, String) */ - public Uid Authenticate(String username, GuardedString password, OperationOptions options) { + public Uid Authenticate(ObjectClass objectClass, String username, GuardedString password, OperationOptions options) { + Assertions.NullCheck(objectClass,"objectClass"); Assertions.NullCheck(username, "username"); Assertions.NullCheck(password, "password"); //convert null into empty if ( options == null ) { options = new OperationOptionsBuilder().Build(); } - return ((AuthenticateOp) GetConnector()).Authenticate(username, password,options); + return ((AuthenticateOp) GetConnector()).Authenticate(objectClass,username, password,options); } } #endregion @@ -1140,9 +1141,9 @@ public void Sync(ObjectClass objClass, SyncToken token, handler = new NormalizingSyncResultsHandler(handler,normalizer).Handle; ((SyncOp)GetConnector()).Sync(objClass, token, handler, options); } - public SyncToken GetLatestSyncToken() + public SyncToken GetLatestSyncToken(ObjectClass objectClass) { - return ((SyncOp) GetConnector()).GetLatestSyncToken(); + return ((SyncOp) GetConnector()).GetLatestSyncToken(objectClass); } } #endregion diff --git a/FrameworkTests/ConnectorInfoManagerTests.cs b/FrameworkTests/ConnectorInfoManagerTests.cs index e2b73cd5..1aa52f52 100644 --- a/FrameworkTests/ConnectorInfoManagerTests.cs +++ b/FrameworkTests/ConnectorInfoManagerTests.cs @@ -285,7 +285,7 @@ public void TestSyncWithManyResults() { ConnectorFacadeFactory facf = ConnectorFacadeFactory.GetInstance(); ConnectorFacade facade = facf.NewInstance(api); - SyncToken latest = facade.GetLatestSyncToken(); + SyncToken latest = facade.GetLatestSyncToken(ObjectClass.ACCOUNT); Assert.AreEqual("mylatest",latest.Value); IList results = new List(); diff --git a/TestBundleV1/TestConnector.cs b/TestBundleV1/TestConnector.cs index 59746a12..4551ba5e 100644 --- a/TestBundleV1/TestConnector.cs +++ b/TestBundleV1/TestConnector.cs @@ -211,7 +211,7 @@ public void Sync(ObjectClass objClass, SyncToken token, } } - public SyncToken GetLatestSyncToken() + public SyncToken GetLatestSyncToken(ObjectClass objectClass) { return new SyncToken("mylatest"); } From aea39681cf8de20daaef37751ec7d5fc90da3457 Mon Sep 17 00:00:00 2001 From: rcauble Date: Thu, 11 Dec 2008 22:18:42 +0000 Subject: [PATCH 109/342] Issue#344: fixed on C# side --- .../ActiveDirectoryConnector.cs | 2 +- Framework/Api.cs | 2 + Framework/Spi.cs | 2 +- FrameworkInternal/Api.cs | 13 ++++++ FrameworkInternal/ApiLocal.cs | 42 +++++++++++-------- FrameworkInternal/Resources.resx | 3 ++ FrameworkTests/ConnectorInfoManagerTests.cs | 2 + TestBundleV1/TestConnector.cs | 2 +- TestBundleV2/TestConnector.cs | 2 +- 9 files changed, 48 insertions(+), 22 deletions(-) diff --git a/ActiveDirectoryConnector/ActiveDirectoryConnector.cs b/ActiveDirectoryConnector/ActiveDirectoryConnector.cs index a23944c7..209df03d 100644 --- a/ActiveDirectoryConnector/ActiveDirectoryConnector.cs +++ b/ActiveDirectoryConnector/ActiveDirectoryConnector.cs @@ -69,7 +69,7 @@ public enum UpdateType { /// [ConnectorClass("connector_displayName", typeof(ActiveDirectoryConfiguration), - MessageCatalogPath = "Org.IdentityConnectors.ActiveDirectory.Messages" + MessageCatalogPaths = new String[]{"Org.IdentityConnectors.ActiveDirectory.Messages"} )] public class ActiveDirectoryConnector : CreateOp, Connector, SchemaOp, DeleteOp, SearchOp, TestOp, UpdateAttributeValuesOp, ScriptOnResourceOp, SyncOp, diff --git a/Framework/Api.cs b/Framework/Api.cs index 87d5e1d4..cf45cb7a 100644 --- a/Framework/Api.cs +++ b/Framework/Api.cs @@ -228,6 +228,8 @@ public interface ConnectorInfo { */ string GetConnectorDisplayName(); + ConnectorMessages Messages { get; } + ConnectorKey ConnectorKey { get; } /** diff --git a/Framework/Spi.cs b/Framework/Spi.cs index dda4f9db..9b5148ed 100644 --- a/Framework/Spi.cs +++ b/Framework/Spi.cs @@ -124,7 +124,7 @@ public SafeType ConnectorConfigurationType { } } - public string MessageCatalogPath {get;set;} + public string [] MessageCatalogPaths {get;set;} } #endregion diff --git a/FrameworkInternal/Api.cs b/FrameworkInternal/Api.cs index e9d728cd..5bf6ca17 100644 --- a/FrameworkInternal/Api.cs +++ b/FrameworkInternal/Api.cs @@ -55,6 +55,7 @@ using System.Linq; using System.Collections.Generic; using System.Globalization; +using System.Resources; using System.Reflection; using System.Reflection.Emit; using System.Diagnostics; @@ -373,6 +374,9 @@ public String Format(String key, String dflt, params object [] args) { if ( catalog != null ) { message = CollectionUtil.GetValue(catalog,key,null); } + if ( message == null ) { + message = GetFrameworkMessage(locale,key); + } if ( message == null ) { return dflt; } @@ -383,6 +387,15 @@ public String Format(String key, String dflt, params object [] args) { } } + private String GetFrameworkMessage(CultureInfo culture, String key) + { + ResourceManager manager = + new ResourceManager("Org.IdentityConnectors.Resources", + typeof(ConnectorMessagesImpl).Assembly); + String contents = (String)manager.GetObject(key,culture); + return contents; + } + public IDictionary> Catalogs { get { return _catalogs; diff --git a/FrameworkInternal/ApiLocal.cs b/FrameworkInternal/ApiLocal.cs index f0fc8fe2..38482ed3 100644 --- a/FrameworkInternal/ApiLocal.cs +++ b/FrameworkInternal/ApiLocal.cs @@ -415,7 +415,7 @@ private LocalConnectorInfoImpl CreateConnectorInfo(Assembly assembly, rv.ConnectorDisplayNameKey = connectorDisplayNameKey; rv.ConnectorKey = key; rv.DefaultAPIConfiguration = CreateDefaultAPIConfiguration(rv); - rv.Messages = LoadMessages(assembly,rv,attribute.MessageCatalogPath); + rv.Messages = LoadMessages(assembly,rv,attribute.MessageCatalogPaths); return rv; } @@ -478,28 +478,34 @@ private CultureInfo [] GetLocalizedCultures(Assembly assembly) { private ConnectorMessagesImpl LoadMessages(Assembly assembly, LocalConnectorInfoImpl info, - String nameBase) { - if ( StringUtil.IsBlank(nameBase) ) { + String [] nameBases) { + if ( nameBases == null || nameBases.Length == 0 ) { String pkage = info.ConnectorClass.RawType.Namespace; - nameBase = pkage+".Messages"; + nameBases = new String[]{pkage+".Messages"}; } ConnectorMessagesImpl rv = new ConnectorMessagesImpl(); CultureInfo [] cultures = GetLocalizedCultures(assembly); - ResourceManager manager = new ResourceManager(nameBase,assembly); - foreach (CultureInfo culture in cultures) { - ResourceSet resourceSet = manager.GetResourceSet(culture,true,false); - if ( resourceSet != null ) { - IDictionary temp = new - Dictionary(); - foreach (System.Collections.DictionaryEntry entry in resourceSet) { - String key = ""+entry.Key; - String val = ""+entry.Value; - temp[key] = val; - } - rv.Catalogs[culture] = temp; - } - } + for ( int i = nameBases.Length - 1; i >= 0; i-- ) { + String nameBase = nameBases[i]; + ResourceManager manager = new ResourceManager(nameBase,assembly); + foreach (CultureInfo culture in cultures) { + ResourceSet resourceSet = manager.GetResourceSet(culture,true,false); + if ( resourceSet != null ) { + IDictionary temp = + CollectionUtil.GetValue(rv.Catalogs,culture,null); + if ( temp == null ) { + temp = new Dictionary(); + rv.Catalogs[culture] = temp; + } + foreach (System.Collections.DictionaryEntry entry in resourceSet) { + String key = ""+entry.Key; + String val = ""+entry.Value; + temp[key] = val; + } + } + } + } return rv; } diff --git a/FrameworkInternal/Resources.resx b/FrameworkInternal/Resources.resx index 5ddf45dc..2cf0d0d5 100644 --- a/FrameworkInternal/Resources.resx +++ b/FrameworkInternal/Resources.resx @@ -473,4 +473,7 @@ OperationOptionInfo | SyncDeltaType | SyncToken | SyncDelta | QualifiedUid <!ELEMENT EchoMessage (value,objectXml?)> <!ELEMENT objectXml (#PCDATA)> + + Test Framework Value + \ No newline at end of file diff --git a/FrameworkTests/ConnectorInfoManagerTests.cs b/FrameworkTests/ConnectorInfoManagerTests.cs index 1aa52f52..db4eaf2e 100644 --- a/FrameworkTests/ConnectorInfoManagerTests.cs +++ b/FrameworkTests/ConnectorInfoManagerTests.cs @@ -131,6 +131,8 @@ public void TestAPIConfiguration() Thread.CurrentThread.CurrentUICulture = new CultureInfo("en"); Assert.AreEqual("Help for test field.",property.GetHelpMessage(null)); Assert.AreEqual("Display for test field.",property.GetDisplayName(null)); + Assert.AreEqual("Test Framework Value", + info.Messages.Format("TEST_FRAMEWORK_KEY", "empty")); CultureInfo eslocale = new CultureInfo("es"); Thread.CurrentThread.CurrentUICulture = eslocale; diff --git a/TestBundleV1/TestConnector.cs b/TestBundleV1/TestConnector.cs index 4551ba5e..4b934efc 100644 --- a/TestBundleV1/TestConnector.cs +++ b/TestBundleV1/TestConnector.cs @@ -110,7 +110,7 @@ public int GetConnectionNumber() { [ConnectorClass("TestConnector", typeof(TstConnectorConfig), - MessageCatalogPath="TestBundleV1.Messages" + MessageCatalogPaths= new String[]{"TestBundleV1.Messages"} )] public class TstConnector : CreateOp, PoolableConnector, SchemaOp, SearchOp, SyncOp { diff --git a/TestBundleV2/TestConnector.cs b/TestBundleV2/TestConnector.cs index 3c6b05fa..9ba84e73 100644 --- a/TestBundleV2/TestConnector.cs +++ b/TestBundleV2/TestConnector.cs @@ -54,7 +54,7 @@ public override void Validate() { [ConnectorClass("TestConnector", typeof(TstConnectorConfig), - MessageCatalogPath="TestBundleV2.Messages" + MessageCatalogPaths=new String[]{"TestBundleV2.Messages"} )] public class TstConnector : CreateOp, Connector, SchemaOp { From c4e62239229ea18510c39f9147154a7fe84928d1 Mon Sep 17 00:00:00 2001 From: rcauble Date: Fri, 12 Dec 2008 19:54:21 +0000 Subject: [PATCH 110/342] Issue#309: update copyright --- .../ActiveDirectoryConfiguration.cs | 41 +++++----------- .../ActiveDirectoryConnector.cs | 43 +++++----------- .../ActiveDirectoryConnector.csproj | 43 ++++++---------- .../ActiveDirectoryFilterTranslator.cs | 41 +++++----------- .../ActiveDirectorySyncToken.cs | 44 +++++------------ .../ActiveDirectoryUtils.cs | 41 +++++----------- ActiveDirectoryConnector/AssemblyInfo.cs | 41 +++++----------- ActiveDirectoryConnector/CommonUtils.cs | 43 +++++----------- .../CustomAttributeHandlers.cs | 41 +++++----------- .../PasswordChangeHandler.cs | 44 +++++------------ .../TerminalServicesUtils.cs | 41 +++++----------- .../UserAccountControl.cs | 41 +++++----------- .../ActiveDirectoryConnectorTest.cs | 41 +++++----------- .../ActiveDirectoryConnectorTests.csproj | 43 ++++++---------- .../Properties/AssemblyInfo.cs | 41 +++++----------- BooScriptExecutorFactory/AssemblyInfo.cs | 41 +++++----------- .../BooScriptExecutorFactory.cs | 41 +++++----------- .../BooScriptExecutorFactory.csproj | 43 ++++++---------- Common/AssemblyInfo.cs | 41 +++++----------- Common/Assertions.cs | 41 +++++----------- Common/CollectionUtil.cs | 41 +++++----------- Common/Common.csproj | 43 ++++++---------- Common/DateTimeUtil.cs | 41 +++++----------- Common/FrameworkInternalBridge.cs | 41 +++++----------- Common/IOUtil.cs | 41 +++++----------- Common/Locale.cs | 41 +++++----------- Common/Pair.cs | 41 +++++----------- Common/Pooling.cs | 41 +++++----------- Common/Proxy.cs | 41 +++++----------- Common/ReflectionUtil.cs | 41 +++++----------- Common/SafeType.cs | 42 +++++----------- Common/Script.cs | 41 +++++----------- Common/Security.cs | 41 +++++----------- Common/StringUtil.cs | 41 +++++----------- Common/TraceUtil.cs | 41 +++++----------- Common/XmlUtil.cs | 41 +++++----------- Console/AssemblyInfo.cs | 41 +++++----------- Console/Console.csproj | 43 ++++++---------- Console/MainForm.cs | 41 +++++----------- Console/Program.cs | 41 +++++----------- ExchangeConnector/ExchangeConfiguration.cs | 45 +++++------------ ExchangeConnector/ExchangeConnector.cs | 49 ++++++------------- ExchangeConnector/ExchangeConnector.csproj | 25 +++++++++- ExchangeConnector/ExchangeUtils.cs | 41 +++++----------- ExchangeConnector/LegacyExchangeConnector.cs | 47 ++++++------------ ExchangeConnector/Properties/AssemblyInfo.cs | 26 +++++++++- ExchangeConnector/RunSpaceInstance.cs | 42 +++++----------- Framework/Api.cs | 41 +++++----------- Framework/ApiOperations.cs | 41 +++++----------- Framework/AssemblyInfo.cs | 41 +++++----------- Framework/Common.cs | 41 +++++----------- Framework/CommonExceptions.cs | 41 +++++----------- Framework/CommonObjects.cs | 41 +++++----------- Framework/CommonObjectsFilter.cs | 41 +++++----------- Framework/CommonSerializer.cs | 41 +++++----------- Framework/Framework.csproj | 43 ++++++---------- Framework/Spi.cs | 41 +++++----------- Framework/SpiOperations.cs | 41 +++++----------- Framework/Test.cs | 41 +++++----------- FrameworkInternal/Api.cs | 41 +++++----------- FrameworkInternal/ApiLocal.cs | 41 +++++----------- FrameworkInternal/ApiLocalOperations.cs | 41 +++++----------- FrameworkInternal/ApiRemote.cs | 41 +++++----------- FrameworkInternal/ApiRemoteMessages.cs | 41 +++++----------- FrameworkInternal/AssemblyInfo.cs | 41 +++++----------- FrameworkInternal/FrameworkInternal.csproj | 43 ++++++---------- FrameworkInternal/Security.cs | 41 +++++----------- FrameworkInternal/Serializer.cs | 41 +++++----------- FrameworkInternal/SerializerBinary.cs | 41 +++++----------- FrameworkInternal/SerializerXml.cs | 41 +++++----------- FrameworkInternal/Server.cs | 41 +++++----------- FrameworkInternal/Test.cs | 41 +++++----------- FrameworkTests/AssemblyInfo.cs | 41 +++++----------- FrameworkTests/CollectionUtilTests.cs | 41 +++++----------- FrameworkTests/ConnectorInfoManagerTests.cs | 41 +++++----------- FrameworkTests/FilterTranslatorTests.cs | 41 +++++----------- FrameworkTests/FrameworkTests.csproj | 43 ++++++---------- FrameworkTests/GuardedStringTests.cs | 41 +++++----------- FrameworkTests/LocaleTests.cs | 41 +++++----------- FrameworkTests/ObjectNormalizerFacadeTests.cs | 41 +++++----------- FrameworkTests/ObjectPoolTests.cs | 41 +++++----------- FrameworkTests/ObjectSerializationTests.cs | 41 +++++----------- FrameworkTests/ProxyTests.cs | 41 +++++----------- FrameworkTests/SafeTypeTest.cs | 42 +++++----------- FrameworkTests/ScriptTests.cs | 41 +++++----------- FrameworkTests/TestHelperTests.cs | 41 +++++----------- FrameworkTests/TestUtil.cs | 41 +++++----------- FrameworkTests/UpdateImplTests.cs | 41 +++++----------- Service/AssemblyInfo.cs | 41 +++++----------- Service/Program.cs | 41 +++++----------- Service/ProjectInstaller.cs | 41 +++++----------- Service/Service.cs | 41 +++++----------- Service/Service.csproj | 43 ++++++---------- ServiceInstall/ExtBuild.proj | 39 +++++---------- ServiceInstall/ServiceInstall.wixproj | 43 ++++++---------- ShellScriptExecutorFactory/AssemblyInfo.cs | 41 +++++----------- .../ShellScriptExecutorFactory.cs | 41 +++++----------- .../ShellScriptExecutorFactory.csproj | 43 ++++++---------- TestBundleV1/AssemblyInfo.cs | 41 +++++----------- TestBundleV1/TestBundleV1.csproj | 43 ++++++---------- TestBundleV1/TestConnector.cs | 41 +++++----------- TestBundleV2/AssemblyInfo.cs | 41 +++++----------- TestBundleV2/TestBundleV2.csproj | 43 ++++++---------- TestBundleV2/TestConnector.cs | 41 +++++----------- 104 files changed, 1309 insertions(+), 2979 deletions(-) diff --git a/ActiveDirectoryConnector/ActiveDirectoryConfiguration.cs b/ActiveDirectoryConnector/ActiveDirectoryConfiguration.cs index 701eb9ba..d58cd36a 100644 --- a/ActiveDirectoryConnector/ActiveDirectoryConfiguration.cs +++ b/ActiveDirectoryConnector/ActiveDirectoryConfiguration.cs @@ -1,41 +1,24 @@ /* - * Copyright 2008 Sun Microsystems, Inc. All rights reserved. - * - * U.S. Government Rights - Commercial software. Government users - * are subject to the Sun Microsystems, Inc. standard license agreement - * and applicable provisions of the FAR and its supplements. - * - * Use is subject to license terms. - * - * This distribution may include materials developed by third parties. - * Sun, Sun Microsystems, the Sun logo, Java and Project Identity - * Connectors are trademarks or registered trademarks of Sun - * Microsystems, Inc. or its subsidiaries in the U.S. and other - * countries. - * - * UNIX is a registered trademark in the U.S. and other countries, - * exclusively licensed through X/Open Company, Ltd. - * - * ----------- + * ==================== * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER. * - * Copyright 2008 Sun Microsystems, Inc. All rights reserved. + * Copyright 2007-2008 Sun Microsystems, Inc. All rights reserved. * - * The contents of this file are subject to the terms of the Common Development - * and Distribution License(CDDL) (the License). You may not use this file - * except in compliance with the License. + * The contents of this file are subject to the terms of the Common Development + * and Distribution License("CDDL") (the "License"). You may not use this file + * except in compliance with the License. * - * You can obtain a copy of the License at - * http://identityconnectors.dev.java.net/CDDLv1.0.html - * See the License for the specific language governing permissions and - * limitations under the License. + * You can obtain a copy of the License at + * http://IdentityConnectors.dev.java.net/legal/license.txt + * See the License for the specific language governing permissions and limitations + * under the License. * - * When distributing the Covered Code, include this CDDL Header Notice in each - * file and include the License file at identityconnectors/legal/license.txt. + * When distributing the Covered Code, include this CDDL Header Notice in each file + * and include the License file at identityconnectors/legal/license.txt. * If applicable, add the following below this CDDL Header, with the fields * enclosed by brackets [] replaced by your own identifying information: * "Portions Copyrighted [year] [name of copyright owner]" - * ----------- + * ==================== */ using System; using System.Collections.Generic; diff --git a/ActiveDirectoryConnector/ActiveDirectoryConnector.cs b/ActiveDirectoryConnector/ActiveDirectoryConnector.cs index 209df03d..2bbb0f85 100644 --- a/ActiveDirectoryConnector/ActiveDirectoryConnector.cs +++ b/ActiveDirectoryConnector/ActiveDirectoryConnector.cs @@ -1,41 +1,24 @@ -/* - * Copyright 2008 Sun Microsystems, Inc. All rights reserved. - * - * U.S. Government Rights - Commercial software. Government users - * are subject to the Sun Microsystems, Inc. standard license agreement - * and applicable provisions of the FAR and its supplements. - * - * Use is subject to license terms. - * - * This distribution may include materials developed by third parties. - * Sun, Sun Microsystems, the Sun logo, Java and Project Identity - * Connectors are trademarks or registered trademarks of Sun - * Microsystems, Inc. or its subsidiaries in the U.S. and other - * countries. - * - * UNIX is a registered trademark in the U.S. and other countries, - * exclusively licensed through X/Open Company, Ltd. - * - * ----------- +/* + * ==================== * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER. * - * Copyright 2008 Sun Microsystems, Inc. All rights reserved. + * Copyright 2007-2008 Sun Microsystems, Inc. All rights reserved. * - * The contents of this file are subject to the terms of the Common Development - * and Distribution License(CDDL) (the License). You may not use this file - * except in compliance with the License. + * The contents of this file are subject to the terms of the Common Development + * and Distribution License("CDDL") (the "License"). You may not use this file + * except in compliance with the License. * - * You can obtain a copy of the License at - * http://identityconnectors.dev.java.net/CDDLv1.0.html - * See the License for the specific language governing permissions and - * limitations under the License. + * You can obtain a copy of the License at + * http://IdentityConnectors.dev.java.net/legal/license.txt + * See the License for the specific language governing permissions and limitations + * under the License. * - * When distributing the Covered Code, include this CDDL Header Notice in each - * file and include the License file at identityconnectors/legal/license.txt. + * When distributing the Covered Code, include this CDDL Header Notice in each file + * and include the License file at identityconnectors/legal/license.txt. * If applicable, add the following below this CDDL Header, with the fields * enclosed by brackets [] replaced by your own identifying information: * "Portions Copyrighted [year] [name of copyright owner]" - * ----------- + * ==================== */ using System; using System.Reflection; diff --git a/ActiveDirectoryConnector/ActiveDirectoryConnector.csproj b/ActiveDirectoryConnector/ActiveDirectoryConnector.csproj index 0ff408bc..2ffc1c15 100644 --- a/ActiveDirectoryConnector/ActiveDirectoryConnector.csproj +++ b/ActiveDirectoryConnector/ActiveDirectoryConnector.csproj @@ -1,39 +1,24 @@ - @@ -132,4 +117,4 @@ - \ No newline at end of file + diff --git a/ActiveDirectoryConnector/ActiveDirectoryFilterTranslator.cs b/ActiveDirectoryConnector/ActiveDirectoryFilterTranslator.cs index d4cb058f..2b0978d7 100644 --- a/ActiveDirectoryConnector/ActiveDirectoryFilterTranslator.cs +++ b/ActiveDirectoryConnector/ActiveDirectoryFilterTranslator.cs @@ -1,41 +1,24 @@ /* - * Copyright 2008 Sun Microsystems, Inc. All rights reserved. - * - * U.S. Government Rights - Commercial software. Government users - * are subject to the Sun Microsystems, Inc. standard license agreement - * and applicable provisions of the FAR and its supplements. - * - * Use is subject to license terms. - * - * This distribution may include materials developed by third parties. - * Sun, Sun Microsystems, the Sun logo, Java and Project Identity - * Connectors are trademarks or registered trademarks of Sun - * Microsystems, Inc. or its subsidiaries in the U.S. and other - * countries. - * - * UNIX is a registered trademark in the U.S. and other countries, - * exclusively licensed through X/Open Company, Ltd. - * - * ----------- + * ==================== * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER. * - * Copyright 2008 Sun Microsystems, Inc. All rights reserved. + * Copyright 2007-2008 Sun Microsystems, Inc. All rights reserved. * - * The contents of this file are subject to the terms of the Common Development - * and Distribution License(CDDL) (the License). You may not use this file - * except in compliance with the License. + * The contents of this file are subject to the terms of the Common Development + * and Distribution License("CDDL") (the "License"). You may not use this file + * except in compliance with the License. * - * You can obtain a copy of the License at - * http://identityconnectors.dev.java.net/CDDLv1.0.html - * See the License for the specific language governing permissions and - * limitations under the License. + * You can obtain a copy of the License at + * http://IdentityConnectors.dev.java.net/legal/license.txt + * See the License for the specific language governing permissions and limitations + * under the License. * - * When distributing the Covered Code, include this CDDL Header Notice in each - * file and include the License file at identityconnectors/legal/license.txt. + * When distributing the Covered Code, include this CDDL Header Notice in each file + * and include the License file at identityconnectors/legal/license.txt. * If applicable, add the following below this CDDL Header, with the fields * enclosed by brackets [] replaced by your own identifying information: * "Portions Copyrighted [year] [name of copyright owner]" - * ----------- + * ==================== */ using System; using System.Collections.Generic; diff --git a/ActiveDirectoryConnector/ActiveDirectorySyncToken.cs b/ActiveDirectoryConnector/ActiveDirectorySyncToken.cs index 04a6f80c..9449ba52 100644 --- a/ActiveDirectoryConnector/ActiveDirectorySyncToken.cs +++ b/ActiveDirectoryConnector/ActiveDirectorySyncToken.cs @@ -1,43 +1,25 @@ -/* - * Copyright 2008 Sun Microsystems, Inc. All rights reserved. - * - * U.S. Government Rights - Commercial software. Government users - * are subject to the Sun Microsystems, Inc. standard license agreement - * and applicable provisions of the FAR and its supplements. - * - * Use is subject to license terms. - * - * This distribution may include materials developed by third parties. - * Sun, Sun Microsystems, the Sun logo, Java and Project Identity - * Connectors are trademarks or registered trademarks of Sun - * Microsystems, Inc. or its subsidiaries in the U.S. and other - * countries. - * - * UNIX is a registered trademark in the U.S. and other countries, - * exclusively licensed through X/Open Company, Ltd. - * - * ----------- +/* + * ==================== * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER. * - * Copyright 2008 Sun Microsystems, Inc. All rights reserved. + * Copyright 2007-2008 Sun Microsystems, Inc. All rights reserved. * - * The contents of this file are subject to the terms of the Common Development - * and Distribution License(CDDL) (the License). You may not use this file - * except in compliance with the License. + * The contents of this file are subject to the terms of the Common Development + * and Distribution License("CDDL") (the "License"). You may not use this file + * except in compliance with the License. * - * You can obtain a copy of the License at - * http://identityconnectors.dev.java.net/CDDLv1.0.html - * See the License for the specific language governing permissions and - * limitations under the License. + * You can obtain a copy of the License at + * http://IdentityConnectors.dev.java.net/legal/license.txt + * See the License for the specific language governing permissions and limitations + * under the License. * - * When distributing the Covered Code, include this CDDL Header Notice in each - * file and include the License file at identityconnectors/legal/license.txt. + * When distributing the Covered Code, include this CDDL Header Notice in each file + * and include the License file at identityconnectors/legal/license.txt. * If applicable, add the following below this CDDL Header, with the fields * enclosed by brackets [] replaced by your own identifying information: * "Portions Copyrighted [year] [name of copyright owner]" - * ----------- + * ==================== */ - using System; using System.Collections.Generic; using System.Linq; diff --git a/ActiveDirectoryConnector/ActiveDirectoryUtils.cs b/ActiveDirectoryConnector/ActiveDirectoryUtils.cs index 4cd98a53..1627ee7f 100644 --- a/ActiveDirectoryConnector/ActiveDirectoryUtils.cs +++ b/ActiveDirectoryConnector/ActiveDirectoryUtils.cs @@ -1,41 +1,24 @@ /* - * Copyright 2008 Sun Microsystems, Inc. All rights reserved. - * - * U.S. Government Rights - Commercial software. Government users - * are subject to the Sun Microsystems, Inc. standard license agreement - * and applicable provisions of the FAR and its supplements. - * - * Use is subject to license terms. - * - * This distribution may include materials developed by third parties. - * Sun, Sun Microsystems, the Sun logo, Java and Project Identity - * Connectors are trademarks or registered trademarks of Sun - * Microsystems, Inc. or its subsidiaries in the U.S. and other - * countries. - * - * UNIX is a registered trademark in the U.S. and other countries, - * exclusively licensed through X/Open Company, Ltd. - * - * ----------- + * ==================== * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER. * - * Copyright 2008 Sun Microsystems, Inc. All rights reserved. + * Copyright 2007-2008 Sun Microsystems, Inc. All rights reserved. * - * The contents of this file are subject to the terms of the Common Development - * and Distribution License(CDDL) (the License). You may not use this file - * except in compliance with the License. + * The contents of this file are subject to the terms of the Common Development + * and Distribution License("CDDL") (the "License"). You may not use this file + * except in compliance with the License. * - * You can obtain a copy of the License at - * http://identityconnectors.dev.java.net/CDDLv1.0.html - * See the License for the specific language governing permissions and - * limitations under the License. + * You can obtain a copy of the License at + * http://IdentityConnectors.dev.java.net/legal/license.txt + * See the License for the specific language governing permissions and limitations + * under the License. * - * When distributing the Covered Code, include this CDDL Header Notice in each - * file and include the License file at identityconnectors/legal/license.txt. + * When distributing the Covered Code, include this CDDL Header Notice in each file + * and include the License file at identityconnectors/legal/license.txt. * If applicable, add the following below this CDDL Header, with the fields * enclosed by brackets [] replaced by your own identifying information: * "Portions Copyrighted [year] [name of copyright owner]" - * ----------- + * ==================== */ using System; using System.Collections.Generic; diff --git a/ActiveDirectoryConnector/AssemblyInfo.cs b/ActiveDirectoryConnector/AssemblyInfo.cs index fa480ab5..65843b5d 100644 --- a/ActiveDirectoryConnector/AssemblyInfo.cs +++ b/ActiveDirectoryConnector/AssemblyInfo.cs @@ -1,41 +1,24 @@ /* - * Copyright 2008 Sun Microsystems, Inc. All rights reserved. - * - * U.S. Government Rights - Commercial software. Government users - * are subject to the Sun Microsystems, Inc. standard license agreement - * and applicable provisions of the FAR and its supplements. - * - * Use is subject to license terms. - * - * This distribution may include materials developed by third parties. - * Sun, Sun Microsystems, the Sun logo, Java and Project Identity - * Connectors are trademarks or registered trademarks of Sun - * Microsystems, Inc. or its subsidiaries in the U.S. and other - * countries. - * - * UNIX is a registered trademark in the U.S. and other countries, - * exclusively licensed through X/Open Company, Ltd. - * - * ----------- + * ==================== * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER. * - * Copyright 2008 Sun Microsystems, Inc. All rights reserved. + * Copyright 2007-2008 Sun Microsystems, Inc. All rights reserved. * - * The contents of this file are subject to the terms of the Common Development - * and Distribution License(CDDL) (the License). You may not use this file - * except in compliance with the License. + * The contents of this file are subject to the terms of the Common Development + * and Distribution License("CDDL") (the "License"). You may not use this file + * except in compliance with the License. * - * You can obtain a copy of the License at - * http://identityconnectors.dev.java.net/CDDLv1.0.html - * See the License for the specific language governing permissions and - * limitations under the License. + * You can obtain a copy of the License at + * http://IdentityConnectors.dev.java.net/legal/license.txt + * See the License for the specific language governing permissions and limitations + * under the License. * - * When distributing the Covered Code, include this CDDL Header Notice in each - * file and include the License file at identityconnectors/legal/license.txt. + * When distributing the Covered Code, include this CDDL Header Notice in each file + * and include the License file at identityconnectors/legal/license.txt. * If applicable, add the following below this CDDL Header, with the fields * enclosed by brackets [] replaced by your own identifying information: * "Portions Copyrighted [year] [name of copyright owner]" - * ----------- + * ==================== */ #region Using directives diff --git a/ActiveDirectoryConnector/CommonUtils.cs b/ActiveDirectoryConnector/CommonUtils.cs index 8720b8ba..d42bb8cd 100644 --- a/ActiveDirectoryConnector/CommonUtils.cs +++ b/ActiveDirectoryConnector/CommonUtils.cs @@ -1,41 +1,24 @@ -/* - * Copyright 2008 Sun Microsystems, Inc. All rights reserved. - * - * U.S. Government Rights - Commercial software. Government users - * are subject to the Sun Microsystems, Inc. standard license agreement - * and applicable provisions of the FAR and its supplements. - * - * Use is subject to license terms. - * - * This distribution may include materials developed by third parties. - * Sun, Sun Microsystems, the Sun logo, Java and Project Identity - * Connectors are trademarks or registered trademarks of Sun - * Microsystems, Inc. or its subsidiaries in the U.S. and other - * countries. - * - * UNIX is a registered trademark in the U.S. and other countries, - * exclusively licensed through X/Open Company, Ltd. - * - * ----------- +/* + * ==================== * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER. * - * Copyright 2008 Sun Microsystems, Inc. All rights reserved. + * Copyright 2007-2008 Sun Microsystems, Inc. All rights reserved. * - * The contents of this file are subject to the terms of the Common Development - * and Distribution License(CDDL) (the License). You may not use this file - * except in compliance with the License. + * The contents of this file are subject to the terms of the Common Development + * and Distribution License("CDDL") (the "License"). You may not use this file + * except in compliance with the License. * - * You can obtain a copy of the License at - * http://identityconnectors.dev.java.net/CDDLv1.0.html - * See the License for the specific language governing permissions and - * limitations under the License. + * You can obtain a copy of the License at + * http://IdentityConnectors.dev.java.net/legal/license.txt + * See the License for the specific language governing permissions and limitations + * under the License. * - * When distributing the Covered Code, include this CDDL Header Notice in each - * file and include the License file at identityconnectors/legal/license.txt. + * When distributing the Covered Code, include this CDDL Header Notice in each file + * and include the License file at identityconnectors/legal/license.txt. * If applicable, add the following below this CDDL Header, with the fields * enclosed by brackets [] replaced by your own identifying information: * "Portions Copyrighted [year] [name of copyright owner]" - * ----------- + * ==================== */ using System; using System.Collections.Generic; diff --git a/ActiveDirectoryConnector/CustomAttributeHandlers.cs b/ActiveDirectoryConnector/CustomAttributeHandlers.cs index 068f14bd..911bfb1e 100644 --- a/ActiveDirectoryConnector/CustomAttributeHandlers.cs +++ b/ActiveDirectoryConnector/CustomAttributeHandlers.cs @@ -1,41 +1,24 @@ /* - * Copyright 2008 Sun Microsystems, Inc. All rights reserved. - * - * U.S. Government Rights - Commercial software. Government users - * are subject to the Sun Microsystems, Inc. standard license agreement - * and applicable provisions of the FAR and its supplements. - * - * Use is subject to license terms. - * - * This distribution may include materials developed by third parties. - * Sun, Sun Microsystems, the Sun logo, Java and Project Identity - * Connectors are trademarks or registered trademarks of Sun - * Microsystems, Inc. or its subsidiaries in the U.S. and other - * countries. - * - * UNIX is a registered trademark in the U.S. and other countries, - * exclusively licensed through X/Open Company, Ltd. - * - * ----------- + * ==================== * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER. * - * Copyright 2008 Sun Microsystems, Inc. All rights reserved. + * Copyright 2007-2008 Sun Microsystems, Inc. All rights reserved. * - * The contents of this file are subject to the terms of the Common Development - * and Distribution License(CDDL) (the License). You may not use this file - * except in compliance with the License. + * The contents of this file are subject to the terms of the Common Development + * and Distribution License("CDDL") (the "License"). You may not use this file + * except in compliance with the License. * - * You can obtain a copy of the License at - * http://identityconnectors.dev.java.net/CDDLv1.0.html - * See the License for the specific language governing permissions and - * limitations under the License. + * You can obtain a copy of the License at + * http://IdentityConnectors.dev.java.net/legal/license.txt + * See the License for the specific language governing permissions and limitations + * under the License. * - * When distributing the Covered Code, include this CDDL Header Notice in each - * file and include the License file at identityconnectors/legal/license.txt. + * When distributing the Covered Code, include this CDDL Header Notice in each file + * and include the License file at identityconnectors/legal/license.txt. * If applicable, add the following below this CDDL Header, with the fields * enclosed by brackets [] replaced by your own identifying information: * "Portions Copyrighted [year] [name of copyright owner]" - * ----------- + * ==================== */ using System; using System.Collections.Generic; diff --git a/ActiveDirectoryConnector/PasswordChangeHandler.cs b/ActiveDirectoryConnector/PasswordChangeHandler.cs index f9f74f47..98b3efbc 100644 --- a/ActiveDirectoryConnector/PasswordChangeHandler.cs +++ b/ActiveDirectoryConnector/PasswordChangeHandler.cs @@ -1,43 +1,25 @@ -/* - * Copyright 2008 Sun Microsystems, Inc. All rights reserved. - * - * U.S. Government Rights - Commercial software. Government users - * are subject to the Sun Microsystems, Inc. standard license agreement - * and applicable provisions of the FAR and its supplements. - * - * Use is subject to license terms. - * - * This distribution may include materials developed by third parties. - * Sun, Sun Microsystems, the Sun logo, Java and Project Identity - * Connectors are trademarks or registered trademarks of Sun - * Microsystems, Inc. or its subsidiaries in the U.S. and other - * countries. - * - * UNIX is a registered trademark in the U.S. and other countries, - * exclusively licensed through X/Open Company, Ltd. - * - * ----------- +/* + * ==================== * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER. * - * Copyright 2008 Sun Microsystems, Inc. All rights reserved. + * Copyright 2007-2008 Sun Microsystems, Inc. All rights reserved. * - * The contents of this file are subject to the terms of the Common Development - * and Distribution License(CDDL) (the License). You may not use this file - * except in compliance with the License. + * The contents of this file are subject to the terms of the Common Development + * and Distribution License("CDDL") (the "License"). You may not use this file + * except in compliance with the License. * - * You can obtain a copy of the License at - * http://identityconnectors.dev.java.net/CDDLv1.0.html - * See the License for the specific language governing permissions and - * limitations under the License. + * You can obtain a copy of the License at + * http://IdentityConnectors.dev.java.net/legal/license.txt + * See the License for the specific language governing permissions and limitations + * under the License. * - * When distributing the Covered Code, include this CDDL Header Notice in each - * file and include the License file at identityconnectors/legal/license.txt. + * When distributing the Covered Code, include this CDDL Header Notice in each file + * and include the License file at identityconnectors/legal/license.txt. * If applicable, add the following below this CDDL Header, with the fields * enclosed by brackets [] replaced by your own identifying information: * "Portions Copyrighted [year] [name of copyright owner]" - * ----------- + * ==================== */ - using System; using System.Collections.Generic; using System.Linq; diff --git a/ActiveDirectoryConnector/TerminalServicesUtils.cs b/ActiveDirectoryConnector/TerminalServicesUtils.cs index 53f326cb..599506d2 100644 --- a/ActiveDirectoryConnector/TerminalServicesUtils.cs +++ b/ActiveDirectoryConnector/TerminalServicesUtils.cs @@ -1,41 +1,24 @@ /* - * Copyright 2008 Sun Microsystems, Inc. All rights reserved. - * - * U.S. Government Rights - Commercial software. Government users - * are subject to the Sun Microsystems, Inc. standard license agreement - * and applicable provisions of the FAR and its supplements. - * - * Use is subject to license terms. - * - * This distribution may include materials developed by third parties. - * Sun, Sun Microsystems, the Sun logo, Java and Project Identity - * Connectors are trademarks or registered trademarks of Sun - * Microsystems, Inc. or its subsidiaries in the U.S. and other - * countries. - * - * UNIX is a registered trademark in the U.S. and other countries, - * exclusively licensed through X/Open Company, Ltd. - * - * ----------- + * ==================== * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER. * - * Copyright 2008 Sun Microsystems, Inc. All rights reserved. + * Copyright 2007-2008 Sun Microsystems, Inc. All rights reserved. * - * The contents of this file are subject to the terms of the Common Development - * and Distribution License(CDDL) (the License). You may not use this file - * except in compliance with the License. + * The contents of this file are subject to the terms of the Common Development + * and Distribution License("CDDL") (the "License"). You may not use this file + * except in compliance with the License. * - * You can obtain a copy of the License at - * http://identityconnectors.dev.java.net/CDDLv1.0.html - * See the License for the specific language governing permissions and - * limitations under the License. + * You can obtain a copy of the License at + * http://IdentityConnectors.dev.java.net/legal/license.txt + * See the License for the specific language governing permissions and limitations + * under the License. * - * When distributing the Covered Code, include this CDDL Header Notice in each - * file and include the License file at identityconnectors/legal/license.txt. + * When distributing the Covered Code, include this CDDL Header Notice in each file + * and include the License file at identityconnectors/legal/license.txt. * If applicable, add the following below this CDDL Header, with the fields * enclosed by brackets [] replaced by your own identifying information: * "Portions Copyrighted [year] [name of copyright owner]" - * ----------- + * ==================== */ using System; using System.Collections.Generic; diff --git a/ActiveDirectoryConnector/UserAccountControl.cs b/ActiveDirectoryConnector/UserAccountControl.cs index 5934c206..074de031 100644 --- a/ActiveDirectoryConnector/UserAccountControl.cs +++ b/ActiveDirectoryConnector/UserAccountControl.cs @@ -1,41 +1,24 @@ /* - * Copyright 2008 Sun Microsystems, Inc. All rights reserved. - * - * U.S. Government Rights - Commercial software. Government users - * are subject to the Sun Microsystems, Inc. standard license agreement - * and applicable provisions of the FAR and its supplements. - * - * Use is subject to license terms. - * - * This distribution may include materials developed by third parties. - * Sun, Sun Microsystems, the Sun logo, Java and Project Identity - * Connectors are trademarks or registered trademarks of Sun - * Microsystems, Inc. or its subsidiaries in the U.S. and other - * countries. - * - * UNIX is a registered trademark in the U.S. and other countries, - * exclusively licensed through X/Open Company, Ltd. - * - * ----------- + * ==================== * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER. * - * Copyright 2008 Sun Microsystems, Inc. All rights reserved. + * Copyright 2007-2008 Sun Microsystems, Inc. All rights reserved. * - * The contents of this file are subject to the terms of the Common Development - * and Distribution License(CDDL) (the License). You may not use this file - * except in compliance with the License. + * The contents of this file are subject to the terms of the Common Development + * and Distribution License("CDDL") (the "License"). You may not use this file + * except in compliance with the License. * - * You can obtain a copy of the License at - * http://identityconnectors.dev.java.net/CDDLv1.0.html - * See the License for the specific language governing permissions and - * limitations under the License. + * You can obtain a copy of the License at + * http://IdentityConnectors.dev.java.net/legal/license.txt + * See the License for the specific language governing permissions and limitations + * under the License. * - * When distributing the Covered Code, include this CDDL Header Notice in each - * file and include the License file at identityconnectors/legal/license.txt. + * When distributing the Covered Code, include this CDDL Header Notice in each file + * and include the License file at identityconnectors/legal/license.txt. * If applicable, add the following below this CDDL Header, with the fields * enclosed by brackets [] replaced by your own identifying information: * "Portions Copyrighted [year] [name of copyright owner]" - * ----------- + * ==================== */ using System; using System.Collections.Generic; diff --git a/ActiveDirectoryConnectorTests/ActiveDirectoryConnectorTest.cs b/ActiveDirectoryConnectorTests/ActiveDirectoryConnectorTest.cs index 9042889b..b70889c6 100644 --- a/ActiveDirectoryConnectorTests/ActiveDirectoryConnectorTest.cs +++ b/ActiveDirectoryConnectorTests/ActiveDirectoryConnectorTest.cs @@ -1,41 +1,24 @@ /* - * Copyright 2008 Sun Microsystems, Inc. All rights reserved. - * - * U.S. Government Rights - Commercial software. Government users - * are subject to the Sun Microsystems, Inc. standard license agreement - * and applicable provisions of the FAR and its supplements. - * - * Use is subject to license terms. - * - * This distribution may include materials developed by third parties. - * Sun, Sun Microsystems, the Sun logo, Java and Project Identity - * Connectors are trademarks or registered trademarks of Sun - * Microsystems, Inc. or its subsidiaries in the U.S. and other - * countries. - * - * UNIX is a registered trademark in the U.S. and other countries, - * exclusively licensed through X/Open Company, Ltd. - * - * ----------- + * ==================== * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER. * - * Copyright 2008 Sun Microsystems, Inc. All rights reserved. + * Copyright 2007-2008 Sun Microsystems, Inc. All rights reserved. * - * The contents of this file are subject to the terms of the Common Development - * and Distribution License(CDDL) (the License). You may not use this file - * except in compliance with the License. + * The contents of this file are subject to the terms of the Common Development + * and Distribution License("CDDL") (the "License"). You may not use this file + * except in compliance with the License. * - * You can obtain a copy of the License at - * http://identityconnectors.dev.java.net/CDDLv1.0.html - * See the License for the specific language governing permissions and - * limitations under the License. + * You can obtain a copy of the License at + * http://IdentityConnectors.dev.java.net/legal/license.txt + * See the License for the specific language governing permissions and limitations + * under the License. * - * When distributing the Covered Code, include this CDDL Header Notice in each - * file and include the License file at identityconnectors/legal/license.txt. + * When distributing the Covered Code, include this CDDL Header Notice in each file + * and include the License file at identityconnectors/legal/license.txt. * If applicable, add the following below this CDDL Header, with the fields * enclosed by brackets [] replaced by your own identifying information: * "Portions Copyrighted [year] [name of copyright owner]" - * ----------- + * ==================== */ using System; using System.Collections.Generic; diff --git a/ActiveDirectoryConnectorTests/ActiveDirectoryConnectorTests.csproj b/ActiveDirectoryConnectorTests/ActiveDirectoryConnectorTests.csproj index be5c653c..29bfa34e 100644 --- a/ActiveDirectoryConnectorTests/ActiveDirectoryConnectorTests.csproj +++ b/ActiveDirectoryConnectorTests/ActiveDirectoryConnectorTests.csproj @@ -1,39 +1,24 @@ - @@ -152,4 +137,4 @@ - \ No newline at end of file + diff --git a/ActiveDirectoryConnectorTests/Properties/AssemblyInfo.cs b/ActiveDirectoryConnectorTests/Properties/AssemblyInfo.cs index aee55be5..5b458efe 100644 --- a/ActiveDirectoryConnectorTests/Properties/AssemblyInfo.cs +++ b/ActiveDirectoryConnectorTests/Properties/AssemblyInfo.cs @@ -1,41 +1,24 @@ /* - * Copyright 2008 Sun Microsystems, Inc. All rights reserved. - * - * U.S. Government Rights - Commercial software. Government users - * are subject to the Sun Microsystems, Inc. standard license agreement - * and applicable provisions of the FAR and its supplements. - * - * Use is subject to license terms. - * - * This distribution may include materials developed by third parties. - * Sun, Sun Microsystems, the Sun logo, Java and Project Identity - * Connectors are trademarks or registered trademarks of Sun - * Microsystems, Inc. or its subsidiaries in the U.S. and other - * countries. - * - * UNIX is a registered trademark in the U.S. and other countries, - * exclusively licensed through X/Open Company, Ltd. - * - * ----------- + * ==================== * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER. * - * Copyright 2008 Sun Microsystems, Inc. All rights reserved. + * Copyright 2007-2008 Sun Microsystems, Inc. All rights reserved. * - * The contents of this file are subject to the terms of the Common Development - * and Distribution License(CDDL) (the License). You may not use this file - * except in compliance with the License. + * The contents of this file are subject to the terms of the Common Development + * and Distribution License("CDDL") (the "License"). You may not use this file + * except in compliance with the License. * - * You can obtain a copy of the License at - * http://identityconnectors.dev.java.net/CDDLv1.0.html - * See the License for the specific language governing permissions and - * limitations under the License. + * You can obtain a copy of the License at + * http://IdentityConnectors.dev.java.net/legal/license.txt + * See the License for the specific language governing permissions and limitations + * under the License. * - * When distributing the Covered Code, include this CDDL Header Notice in each - * file and include the License file at identityconnectors/legal/license.txt. + * When distributing the Covered Code, include this CDDL Header Notice in each file + * and include the License file at identityconnectors/legal/license.txt. * If applicable, add the following below this CDDL Header, with the fields * enclosed by brackets [] replaced by your own identifying information: * "Portions Copyrighted [year] [name of copyright owner]" - * ----------- + * ==================== */ using System.Reflection; using System.Runtime.CompilerServices; diff --git a/BooScriptExecutorFactory/AssemblyInfo.cs b/BooScriptExecutorFactory/AssemblyInfo.cs index cfe5e33f..95eb3457 100644 --- a/BooScriptExecutorFactory/AssemblyInfo.cs +++ b/BooScriptExecutorFactory/AssemblyInfo.cs @@ -1,41 +1,24 @@ /* - * Copyright 2008 Sun Microsystems, Inc. All rights reserved. - * - * U.S. Government Rights - Commercial software. Government users - * are subject to the Sun Microsystems, Inc. standard license agreement - * and applicable provisions of the FAR and its supplements. - * - * Use is subject to license terms. - * - * This distribution may include materials developed by third parties. - * Sun, Sun Microsystems, the Sun logo, Java and Project Identity - * Connectors are trademarks or registered trademarks of Sun - * Microsystems, Inc. or its subsidiaries in the U.S. and other - * countries. - * - * UNIX is a registered trademark in the U.S. and other countries, - * exclusively licensed through X/Open Company, Ltd. - * - * ----------- + * ==================== * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER. * - * Copyright 2008 Sun Microsystems, Inc. All rights reserved. + * Copyright 2007-2008 Sun Microsystems, Inc. All rights reserved. * - * The contents of this file are subject to the terms of the Common Development - * and Distribution License(CDDL) (the License). You may not use this file - * except in compliance with the License. + * The contents of this file are subject to the terms of the Common Development + * and Distribution License("CDDL") (the "License"). You may not use this file + * except in compliance with the License. * - * You can obtain a copy of the License at - * http://identityconnectors.dev.java.net/CDDLv1.0.html - * See the License for the specific language governing permissions and - * limitations under the License. + * You can obtain a copy of the License at + * http://IdentityConnectors.dev.java.net/legal/license.txt + * See the License for the specific language governing permissions and limitations + * under the License. * - * When distributing the Covered Code, include this CDDL Header Notice in each - * file and include the License file at identityconnectors/legal/license.txt. + * When distributing the Covered Code, include this CDDL Header Notice in each file + * and include the License file at identityconnectors/legal/license.txt. * If applicable, add the following below this CDDL Header, with the fields * enclosed by brackets [] replaced by your own identifying information: * "Portions Copyrighted [year] [name of copyright owner]" - * ----------- + * ==================== */ #region Using directives diff --git a/BooScriptExecutorFactory/BooScriptExecutorFactory.cs b/BooScriptExecutorFactory/BooScriptExecutorFactory.cs index ff6a3983..ff822ba7 100644 --- a/BooScriptExecutorFactory/BooScriptExecutorFactory.cs +++ b/BooScriptExecutorFactory/BooScriptExecutorFactory.cs @@ -1,41 +1,24 @@ /* - * Copyright 2008 Sun Microsystems, Inc. All rights reserved. - * - * U.S. Government Rights - Commercial software. Government users - * are subject to the Sun Microsystems, Inc. standard license agreement - * and applicable provisions of the FAR and its supplements. - * - * Use is subject to license terms. - * - * This distribution may include materials developed by third parties. - * Sun, Sun Microsystems, the Sun logo, Java and Project Identity - * Connectors are trademarks or registered trademarks of Sun - * Microsystems, Inc. or its subsidiaries in the U.S. and other - * countries. - * - * UNIX is a registered trademark in the U.S. and other countries, - * exclusively licensed through X/Open Company, Ltd. - * - * ----------- + * ==================== * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER. * - * Copyright 2008 Sun Microsystems, Inc. All rights reserved. + * Copyright 2007-2008 Sun Microsystems, Inc. All rights reserved. * - * The contents of this file are subject to the terms of the Common Development - * and Distribution License(CDDL) (the License). You may not use this file - * except in compliance with the License. + * The contents of this file are subject to the terms of the Common Development + * and Distribution License("CDDL") (the "License"). You may not use this file + * except in compliance with the License. * - * You can obtain a copy of the License at - * http://identityconnectors.dev.java.net/CDDLv1.0.html - * See the License for the specific language governing permissions and - * limitations under the License. + * You can obtain a copy of the License at + * http://IdentityConnectors.dev.java.net/legal/license.txt + * See the License for the specific language governing permissions and limitations + * under the License. * - * When distributing the Covered Code, include this CDDL Header Notice in each - * file and include the License file at identityconnectors/legal/license.txt. + * When distributing the Covered Code, include this CDDL Header Notice in each file + * and include the License file at identityconnectors/legal/license.txt. * If applicable, add the following below this CDDL Header, with the fields * enclosed by brackets [] replaced by your own identifying information: * "Portions Copyrighted [year] [name of copyright owner]" - * ----------- + * ==================== */ using System; using System.Reflection; diff --git a/BooScriptExecutorFactory/BooScriptExecutorFactory.csproj b/BooScriptExecutorFactory/BooScriptExecutorFactory.csproj index 93243c07..469a1f3b 100644 --- a/BooScriptExecutorFactory/BooScriptExecutorFactory.csproj +++ b/BooScriptExecutorFactory/BooScriptExecutorFactory.csproj @@ -1,39 +1,24 @@ - @@ -107,4 +92,4 @@ - \ No newline at end of file + diff --git a/Common/AssemblyInfo.cs b/Common/AssemblyInfo.cs index ef767baf..59a263db 100644 --- a/Common/AssemblyInfo.cs +++ b/Common/AssemblyInfo.cs @@ -1,41 +1,24 @@ /* - * Copyright 2008 Sun Microsystems, Inc. All rights reserved. - * - * U.S. Government Rights - Commercial software. Government users - * are subject to the Sun Microsystems, Inc. standard license agreement - * and applicable provisions of the FAR and its supplements. - * - * Use is subject to license terms. - * - * This distribution may include materials developed by third parties. - * Sun, Sun Microsystems, the Sun logo, Java and Project Identity - * Connectors are trademarks or registered trademarks of Sun - * Microsystems, Inc. or its subsidiaries in the U.S. and other - * countries. - * - * UNIX is a registered trademark in the U.S. and other countries, - * exclusively licensed through X/Open Company, Ltd. - * - * ----------- + * ==================== * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER. * - * Copyright 2008 Sun Microsystems, Inc. All rights reserved. + * Copyright 2007-2008 Sun Microsystems, Inc. All rights reserved. * - * The contents of this file are subject to the terms of the Common Development - * and Distribution License(CDDL) (the License). You may not use this file - * except in compliance with the License. + * The contents of this file are subject to the terms of the Common Development + * and Distribution License("CDDL") (the "License"). You may not use this file + * except in compliance with the License. * - * You can obtain a copy of the License at - * http://identityconnectors.dev.java.net/CDDLv1.0.html - * See the License for the specific language governing permissions and - * limitations under the License. + * You can obtain a copy of the License at + * http://IdentityConnectors.dev.java.net/legal/license.txt + * See the License for the specific language governing permissions and limitations + * under the License. * - * When distributing the Covered Code, include this CDDL Header Notice in each - * file and include the License file at identityconnectors/legal/license.txt. + * When distributing the Covered Code, include this CDDL Header Notice in each file + * and include the License file at identityconnectors/legal/license.txt. * If applicable, add the following below this CDDL Header, with the fields * enclosed by brackets [] replaced by your own identifying information: * "Portions Copyrighted [year] [name of copyright owner]" - * ----------- + * ==================== */ #region Using directives diff --git a/Common/Assertions.cs b/Common/Assertions.cs index 585d6c93..9c927a7c 100644 --- a/Common/Assertions.cs +++ b/Common/Assertions.cs @@ -1,41 +1,24 @@ /* - * Copyright 2008 Sun Microsystems, Inc. All rights reserved. - * - * U.S. Government Rights - Commercial software. Government users - * are subject to the Sun Microsystems, Inc. standard license agreement - * and applicable provisions of the FAR and its supplements. - * - * Use is subject to license terms. - * - * This distribution may include materials developed by third parties. - * Sun, Sun Microsystems, the Sun logo, Java and Project Identity - * Connectors are trademarks or registered trademarks of Sun - * Microsystems, Inc. or its subsidiaries in the U.S. and other - * countries. - * - * UNIX is a registered trademark in the U.S. and other countries, - * exclusively licensed through X/Open Company, Ltd. - * - * ----------- + * ==================== * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER. * - * Copyright 2008 Sun Microsystems, Inc. All rights reserved. + * Copyright 2007-2008 Sun Microsystems, Inc. All rights reserved. * - * The contents of this file are subject to the terms of the Common Development - * and Distribution License(CDDL) (the License). You may not use this file - * except in compliance with the License. + * The contents of this file are subject to the terms of the Common Development + * and Distribution License("CDDL") (the "License"). You may not use this file + * except in compliance with the License. * - * You can obtain a copy of the License at - * http://identityconnectors.dev.java.net/CDDLv1.0.html - * See the License for the specific language governing permissions and - * limitations under the License. + * You can obtain a copy of the License at + * http://IdentityConnectors.dev.java.net/legal/license.txt + * See the License for the specific language governing permissions and limitations + * under the License. * - * When distributing the Covered Code, include this CDDL Header Notice in each - * file and include the License file at identityconnectors/legal/license.txt. + * When distributing the Covered Code, include this CDDL Header Notice in each file + * and include the License file at identityconnectors/legal/license.txt. * If applicable, add the following below this CDDL Header, with the fields * enclosed by brackets [] replaced by your own identifying information: * "Portions Copyrighted [year] [name of copyright owner]" - * ----------- + * ==================== */ using System; diff --git a/Common/CollectionUtil.cs b/Common/CollectionUtil.cs index 346d3cb6..5e57742e 100644 --- a/Common/CollectionUtil.cs +++ b/Common/CollectionUtil.cs @@ -1,41 +1,24 @@ /* - * Copyright 2008 Sun Microsystems, Inc. All rights reserved. - * - * U.S. Government Rights - Commercial software. Government users - * are subject to the Sun Microsystems, Inc. standard license agreement - * and applicable provisions of the FAR and its supplements. - * - * Use is subject to license terms. - * - * This distribution may include materials developed by third parties. - * Sun, Sun Microsystems, the Sun logo, Java and Project Identity - * Connectors are trademarks or registered trademarks of Sun - * Microsystems, Inc. or its subsidiaries in the U.S. and other - * countries. - * - * UNIX is a registered trademark in the U.S. and other countries, - * exclusively licensed through X/Open Company, Ltd. - * - * ----------- + * ==================== * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER. * - * Copyright 2008 Sun Microsystems, Inc. All rights reserved. + * Copyright 2007-2008 Sun Microsystems, Inc. All rights reserved. * - * The contents of this file are subject to the terms of the Common Development - * and Distribution License(CDDL) (the License). You may not use this file - * except in compliance with the License. + * The contents of this file are subject to the terms of the Common Development + * and Distribution License("CDDL") (the "License"). You may not use this file + * except in compliance with the License. * - * You can obtain a copy of the License at - * http://identityconnectors.dev.java.net/CDDLv1.0.html - * See the License for the specific language governing permissions and - * limitations under the License. + * You can obtain a copy of the License at + * http://IdentityConnectors.dev.java.net/legal/license.txt + * See the License for the specific language governing permissions and limitations + * under the License. * - * When distributing the Covered Code, include this CDDL Header Notice in each - * file and include the License file at identityconnectors/legal/license.txt. + * When distributing the Covered Code, include this CDDL Header Notice in each file + * and include the License file at identityconnectors/legal/license.txt. * If applicable, add the following below this CDDL Header, with the fields * enclosed by brackets [] replaced by your own identifying information: * "Portions Copyrighted [year] [name of copyright owner]" - * ----------- + * ==================== */ using System; using System.Runtime.CompilerServices; diff --git a/Common/Common.csproj b/Common/Common.csproj index 3f7f269d..abf5a07e 100644 --- a/Common/Common.csproj +++ b/Common/Common.csproj @@ -1,39 +1,24 @@ - @@ -106,4 +91,4 @@ - \ No newline at end of file + diff --git a/Common/DateTimeUtil.cs b/Common/DateTimeUtil.cs index 26d8814a..55c086b7 100644 --- a/Common/DateTimeUtil.cs +++ b/Common/DateTimeUtil.cs @@ -1,41 +1,24 @@ /* - * Copyright 2008 Sun Microsystems, Inc. All rights reserved. - * - * U.S. Government Rights - Commercial software. Government users - * are subject to the Sun Microsystems, Inc. standard license agreement - * and applicable provisions of the FAR and its supplements. - * - * Use is subject to license terms. - * - * This distribution may include materials developed by third parties. - * Sun, Sun Microsystems, the Sun logo, Java and Project Identity - * Connectors are trademarks or registered trademarks of Sun - * Microsystems, Inc. or its subsidiaries in the U.S. and other - * countries. - * - * UNIX is a registered trademark in the U.S. and other countries, - * exclusively licensed through X/Open Company, Ltd. - * - * ----------- + * ==================== * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER. * - * Copyright 2008 Sun Microsystems, Inc. All rights reserved. + * Copyright 2007-2008 Sun Microsystems, Inc. All rights reserved. * - * The contents of this file are subject to the terms of the Common Development - * and Distribution License(CDDL) (the License). You may not use this file - * except in compliance with the License. + * The contents of this file are subject to the terms of the Common Development + * and Distribution License("CDDL") (the "License"). You may not use this file + * except in compliance with the License. * - * You can obtain a copy of the License at - * http://identityconnectors.dev.java.net/CDDLv1.0.html - * See the License for the specific language governing permissions and - * limitations under the License. + * You can obtain a copy of the License at + * http://IdentityConnectors.dev.java.net/legal/license.txt + * See the License for the specific language governing permissions and limitations + * under the License. * - * When distributing the Covered Code, include this CDDL Header Notice in each - * file and include the License file at identityconnectors/legal/license.txt. + * When distributing the Covered Code, include this CDDL Header Notice in each file + * and include the License file at identityconnectors/legal/license.txt. * If applicable, add the following below this CDDL Header, with the fields * enclosed by brackets [] replaced by your own identifying information: * "Portions Copyrighted [year] [name of copyright owner]" - * ----------- + * ==================== */ using System; diff --git a/Common/FrameworkInternalBridge.cs b/Common/FrameworkInternalBridge.cs index d9523d98..a72c0e71 100644 --- a/Common/FrameworkInternalBridge.cs +++ b/Common/FrameworkInternalBridge.cs @@ -1,41 +1,24 @@ /* - * Copyright 2008 Sun Microsystems, Inc. All rights reserved. - * - * U.S. Government Rights - Commercial software. Government users - * are subject to the Sun Microsystems, Inc. standard license agreement - * and applicable provisions of the FAR and its supplements. - * - * Use is subject to license terms. - * - * This distribution may include materials developed by third parties. - * Sun, Sun Microsystems, the Sun logo, Java and Project Identity - * Connectors are trademarks or registered trademarks of Sun - * Microsystems, Inc. or its subsidiaries in the U.S. and other - * countries. - * - * UNIX is a registered trademark in the U.S. and other countries, - * exclusively licensed through X/Open Company, Ltd. - * - * ----------- + * ==================== * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER. * - * Copyright 2008 Sun Microsystems, Inc. All rights reserved. + * Copyright 2007-2008 Sun Microsystems, Inc. All rights reserved. * - * The contents of this file are subject to the terms of the Common Development - * and Distribution License(CDDL) (the License). You may not use this file - * except in compliance with the License. + * The contents of this file are subject to the terms of the Common Development + * and Distribution License("CDDL") (the "License"). You may not use this file + * except in compliance with the License. * - * You can obtain a copy of the License at - * http://identityconnectors.dev.java.net/CDDLv1.0.html - * See the License for the specific language governing permissions and - * limitations under the License. + * You can obtain a copy of the License at + * http://IdentityConnectors.dev.java.net/legal/license.txt + * See the License for the specific language governing permissions and limitations + * under the License. * - * When distributing the Covered Code, include this CDDL Header Notice in each - * file and include the License file at identityconnectors/legal/license.txt. + * When distributing the Covered Code, include this CDDL Header Notice in each file + * and include the License file at identityconnectors/legal/license.txt. * If applicable, add the following below this CDDL Header, with the fields * enclosed by brackets [] replaced by your own identifying information: * "Portions Copyrighted [year] [name of copyright owner]" - * ----------- + * ==================== */ using System; using System.Reflection; diff --git a/Common/IOUtil.cs b/Common/IOUtil.cs index 3e72ef9d..41483294 100644 --- a/Common/IOUtil.cs +++ b/Common/IOUtil.cs @@ -1,41 +1,24 @@ /* - * Copyright 2008 Sun Microsystems, Inc. All rights reserved. - * - * U.S. Government Rights - Commercial software. Government users - * are subject to the Sun Microsystems, Inc. standard license agreement - * and applicable provisions of the FAR and its supplements. - * - * Use is subject to license terms. - * - * This distribution may include materials developed by third parties. - * Sun, Sun Microsystems, the Sun logo, Java and Project Identity - * Connectors are trademarks or registered trademarks of Sun - * Microsystems, Inc. or its subsidiaries in the U.S. and other - * countries. - * - * UNIX is a registered trademark in the U.S. and other countries, - * exclusively licensed through X/Open Company, Ltd. - * - * ----------- + * ==================== * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER. * - * Copyright 2008 Sun Microsystems, Inc. All rights reserved. + * Copyright 2007-2008 Sun Microsystems, Inc. All rights reserved. * - * The contents of this file are subject to the terms of the Common Development - * and Distribution License(CDDL) (the License). You may not use this file - * except in compliance with the License. + * The contents of this file are subject to the terms of the Common Development + * and Distribution License("CDDL") (the "License"). You may not use this file + * except in compliance with the License. * - * You can obtain a copy of the License at - * http://identityconnectors.dev.java.net/CDDLv1.0.html - * See the License for the specific language governing permissions and - * limitations under the License. + * You can obtain a copy of the License at + * http://IdentityConnectors.dev.java.net/legal/license.txt + * See the License for the specific language governing permissions and limitations + * under the License. * - * When distributing the Covered Code, include this CDDL Header Notice in each - * file and include the License file at identityconnectors/legal/license.txt. + * When distributing the Covered Code, include this CDDL Header Notice in each file + * and include the License file at identityconnectors/legal/license.txt. * If applicable, add the following below this CDDL Header, with the fields * enclosed by brackets [] replaced by your own identifying information: * "Portions Copyrighted [year] [name of copyright owner]" - * ----------- + * ==================== */ using System; using System.Net; diff --git a/Common/Locale.cs b/Common/Locale.cs index 8bb9232f..b1620142 100644 --- a/Common/Locale.cs +++ b/Common/Locale.cs @@ -1,41 +1,24 @@ /* - * Copyright 2008 Sun Microsystems, Inc. All rights reserved. - * - * U.S. Government Rights - Commercial software. Government users - * are subject to the Sun Microsystems, Inc. standard license agreement - * and applicable provisions of the FAR and its supplements. - * - * Use is subject to license terms. - * - * This distribution may include materials developed by third parties. - * Sun, Sun Microsystems, the Sun logo, Java and Project Identity - * Connectors are trademarks or registered trademarks of Sun - * Microsystems, Inc. or its subsidiaries in the U.S. and other - * countries. - * - * UNIX is a registered trademark in the U.S. and other countries, - * exclusively licensed through X/Open Company, Ltd. - * - * ----------- + * ==================== * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER. * - * Copyright 2008 Sun Microsystems, Inc. All rights reserved. + * Copyright 2007-2008 Sun Microsystems, Inc. All rights reserved. * - * The contents of this file are subject to the terms of the Common Development - * and Distribution License(CDDL) (the License). You may not use this file - * except in compliance with the License. + * The contents of this file are subject to the terms of the Common Development + * and Distribution License("CDDL") (the "License"). You may not use this file + * except in compliance with the License. * - * You can obtain a copy of the License at - * http://identityconnectors.dev.java.net/CDDLv1.0.html - * See the License for the specific language governing permissions and - * limitations under the License. + * You can obtain a copy of the License at + * http://IdentityConnectors.dev.java.net/legal/license.txt + * See the License for the specific language governing permissions and limitations + * under the License. * - * When distributing the Covered Code, include this CDDL Header Notice in each - * file and include the License file at identityconnectors/legal/license.txt. + * When distributing the Covered Code, include this CDDL Header Notice in each file + * and include the License file at identityconnectors/legal/license.txt. * If applicable, add the following below this CDDL Header, with the fields * enclosed by brackets [] replaced by your own identifying information: * "Portions Copyrighted [year] [name of copyright owner]" - * ----------- + * ==================== */ using System; using System.Collections.Generic; diff --git a/Common/Pair.cs b/Common/Pair.cs index 77658393..cad448f7 100644 --- a/Common/Pair.cs +++ b/Common/Pair.cs @@ -1,41 +1,24 @@ /* - * Copyright 2008 Sun Microsystems, Inc. All rights reserved. - * - * U.S. Government Rights - Commercial software. Government users - * are subject to the Sun Microsystems, Inc. standard license agreement - * and applicable provisions of the FAR and its supplements. - * - * Use is subject to license terms. - * - * This distribution may include materials developed by third parties. - * Sun, Sun Microsystems, the Sun logo, Java and Project Identity - * Connectors are trademarks or registered trademarks of Sun - * Microsystems, Inc. or its subsidiaries in the U.S. and other - * countries. - * - * UNIX is a registered trademark in the U.S. and other countries, - * exclusively licensed through X/Open Company, Ltd. - * - * ----------- + * ==================== * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER. * - * Copyright 2008 Sun Microsystems, Inc. All rights reserved. + * Copyright 2007-2008 Sun Microsystems, Inc. All rights reserved. * - * The contents of this file are subject to the terms of the Common Development - * and Distribution License(CDDL) (the License). You may not use this file - * except in compliance with the License. + * The contents of this file are subject to the terms of the Common Development + * and Distribution License("CDDL") (the "License"). You may not use this file + * except in compliance with the License. * - * You can obtain a copy of the License at - * http://identityconnectors.dev.java.net/CDDLv1.0.html - * See the License for the specific language governing permissions and - * limitations under the License. + * You can obtain a copy of the License at + * http://IdentityConnectors.dev.java.net/legal/license.txt + * See the License for the specific language governing permissions and limitations + * under the License. * - * When distributing the Covered Code, include this CDDL Header Notice in each - * file and include the License file at identityconnectors/legal/license.txt. + * When distributing the Covered Code, include this CDDL Header Notice in each file + * and include the License file at identityconnectors/legal/license.txt. * If applicable, add the following below this CDDL Header, with the fields * enclosed by brackets [] replaced by your own identifying information: * "Portions Copyrighted [year] [name of copyright owner]" - * ----------- + * ==================== */ using System; diff --git a/Common/Pooling.cs b/Common/Pooling.cs index ce8cf214..063c8610 100644 --- a/Common/Pooling.cs +++ b/Common/Pooling.cs @@ -1,41 +1,24 @@ /* - * Copyright 2008 Sun Microsystems, Inc. All rights reserved. - * - * U.S. Government Rights - Commercial software. Government users - * are subject to the Sun Microsystems, Inc. standard license agreement - * and applicable provisions of the FAR and its supplements. - * - * Use is subject to license terms. - * - * This distribution may include materials developed by third parties. - * Sun, Sun Microsystems, the Sun logo, Java and Project Identity - * Connectors are trademarks or registered trademarks of Sun - * Microsystems, Inc. or its subsidiaries in the U.S. and other - * countries. - * - * UNIX is a registered trademark in the U.S. and other countries, - * exclusively licensed through X/Open Company, Ltd. - * - * ----------- + * ==================== * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER. * - * Copyright 2008 Sun Microsystems, Inc. All rights reserved. + * Copyright 2007-2008 Sun Microsystems, Inc. All rights reserved. * - * The contents of this file are subject to the terms of the Common Development - * and Distribution License(CDDL) (the License). You may not use this file - * except in compliance with the License. + * The contents of this file are subject to the terms of the Common Development + * and Distribution License("CDDL") (the "License"). You may not use this file + * except in compliance with the License. * - * You can obtain a copy of the License at - * http://identityconnectors.dev.java.net/CDDLv1.0.html - * See the License for the specific language governing permissions and - * limitations under the License. + * You can obtain a copy of the License at + * http://IdentityConnectors.dev.java.net/legal/license.txt + * See the License for the specific language governing permissions and limitations + * under the License. * - * When distributing the Covered Code, include this CDDL Header Notice in each - * file and include the License file at identityconnectors/legal/license.txt. + * When distributing the Covered Code, include this CDDL Header Notice in each file + * and include the License file at identityconnectors/legal/license.txt. * If applicable, add the following below this CDDL Header, with the fields * enclosed by brackets [] replaced by your own identifying information: * "Portions Copyrighted [year] [name of copyright owner]" - * ----------- + * ==================== */ using System; diff --git a/Common/Proxy.cs b/Common/Proxy.cs index 33c6cc30..2534fdd0 100644 --- a/Common/Proxy.cs +++ b/Common/Proxy.cs @@ -1,41 +1,24 @@ /* - * Copyright 2008 Sun Microsystems, Inc. All rights reserved. - * - * U.S. Government Rights - Commercial software. Government users - * are subject to the Sun Microsystems, Inc. standard license agreement - * and applicable provisions of the FAR and its supplements. - * - * Use is subject to license terms. - * - * This distribution may include materials developed by third parties. - * Sun, Sun Microsystems, the Sun logo, Java and Project Identity - * Connectors are trademarks or registered trademarks of Sun - * Microsystems, Inc. or its subsidiaries in the U.S. and other - * countries. - * - * UNIX is a registered trademark in the U.S. and other countries, - * exclusively licensed through X/Open Company, Ltd. - * - * ----------- + * ==================== * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER. * - * Copyright 2008 Sun Microsystems, Inc. All rights reserved. + * Copyright 2007-2008 Sun Microsystems, Inc. All rights reserved. * - * The contents of this file are subject to the terms of the Common Development - * and Distribution License(CDDL) (the License). You may not use this file - * except in compliance with the License. + * The contents of this file are subject to the terms of the Common Development + * and Distribution License("CDDL") (the "License"). You may not use this file + * except in compliance with the License. * - * You can obtain a copy of the License at - * http://identityconnectors.dev.java.net/CDDLv1.0.html - * See the License for the specific language governing permissions and - * limitations under the License. + * You can obtain a copy of the License at + * http://IdentityConnectors.dev.java.net/legal/license.txt + * See the License for the specific language governing permissions and limitations + * under the License. * - * When distributing the Covered Code, include this CDDL Header Notice in each - * file and include the License file at identityconnectors/legal/license.txt. + * When distributing the Covered Code, include this CDDL Header Notice in each file + * and include the License file at identityconnectors/legal/license.txt. * If applicable, add the following below this CDDL Header, with the fields * enclosed by brackets [] replaced by your own identifying information: * "Portions Copyrighted [year] [name of copyright owner]" - * ----------- + * ==================== */ using System; using System.Reflection; diff --git a/Common/ReflectionUtil.cs b/Common/ReflectionUtil.cs index f66d2490..38a455a3 100644 --- a/Common/ReflectionUtil.cs +++ b/Common/ReflectionUtil.cs @@ -1,41 +1,24 @@ /* - * Copyright 2008 Sun Microsystems, Inc. All rights reserved. - * - * U.S. Government Rights - Commercial software. Government users - * are subject to the Sun Microsystems, Inc. standard license agreement - * and applicable provisions of the FAR and its supplements. - * - * Use is subject to license terms. - * - * This distribution may include materials developed by third parties. - * Sun, Sun Microsystems, the Sun logo, Java and Project Identity - * Connectors are trademarks or registered trademarks of Sun - * Microsystems, Inc. or its subsidiaries in the U.S. and other - * countries. - * - * UNIX is a registered trademark in the U.S. and other countries, - * exclusively licensed through X/Open Company, Ltd. - * - * ----------- + * ==================== * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER. * - * Copyright 2008 Sun Microsystems, Inc. All rights reserved. + * Copyright 2007-2008 Sun Microsystems, Inc. All rights reserved. * - * The contents of this file are subject to the terms of the Common Development - * and Distribution License(CDDL) (the License). You may not use this file - * except in compliance with the License. + * The contents of this file are subject to the terms of the Common Development + * and Distribution License("CDDL") (the "License"). You may not use this file + * except in compliance with the License. * - * You can obtain a copy of the License at - * http://identityconnectors.dev.java.net/CDDLv1.0.html - * See the License for the specific language governing permissions and - * limitations under the License. + * You can obtain a copy of the License at + * http://IdentityConnectors.dev.java.net/legal/license.txt + * See the License for the specific language governing permissions and limitations + * under the License. * - * When distributing the Covered Code, include this CDDL Header Notice in each - * file and include the License file at identityconnectors/legal/license.txt. + * When distributing the Covered Code, include this CDDL Header Notice in each file + * and include the License file at identityconnectors/legal/license.txt. * If applicable, add the following below this CDDL Header, with the fields * enclosed by brackets [] replaced by your own identifying information: * "Portions Copyrighted [year] [name of copyright owner]" - * ----------- + * ==================== */ using System; using System.Reflection; diff --git a/Common/SafeType.cs b/Common/SafeType.cs index 690e2b1f..31c4621e 100644 --- a/Common/SafeType.cs +++ b/Common/SafeType.cs @@ -1,43 +1,25 @@ /* - * Copyright 2008 Sun Microsystems, Inc. All rights reserved. - * - * U.S. Government Rights - Commercial software. Government users - * are subject to the Sun Microsystems, Inc. standard license agreement - * and applicable provisions of the FAR and its supplements. - * - * Use is subject to license terms. - * - * This distribution may include materials developed by third parties. - * Sun, Sun Microsystems, the Sun logo, Java and Project Identity - * Connectors are trademarks or registered trademarks of Sun - * Microsystems, Inc. or its subsidiaries in the U.S. and other - * countries. - * - * UNIX is a registered trademark in the U.S. and other countries, - * exclusively licensed through X/Open Company, Ltd. - * - * ----------- + * ==================== * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER. * - * Copyright 2008 Sun Microsystems, Inc. All rights reserved. + * Copyright 2007-2008 Sun Microsystems, Inc. All rights reserved. * - * The contents of this file are subject to the terms of the Common Development - * and Distribution License(CDDL) (the License). You may not use this file - * except in compliance with the License. + * The contents of this file are subject to the terms of the Common Development + * and Distribution License("CDDL") (the "License"). You may not use this file + * except in compliance with the License. * - * You can obtain a copy of the License at - * http://identityconnectors.dev.java.net/CDDLv1.0.html - * See the License for the specific language governing permissions and - * limitations under the License. + * You can obtain a copy of the License at + * http://IdentityConnectors.dev.java.net/legal/license.txt + * See the License for the specific language governing permissions and limitations + * under the License. * - * When distributing the Covered Code, include this CDDL Header Notice in each - * file and include the License file at identityconnectors/legal/license.txt. + * When distributing the Covered Code, include this CDDL Header Notice in each file + * and include the License file at identityconnectors/legal/license.txt. * If applicable, add the following below this CDDL Header, with the fields * enclosed by brackets [] replaced by your own identifying information: * "Portions Copyrighted [year] [name of copyright owner]" - * ----------- + * ==================== */ - using System; using System.Reflection; diff --git a/Common/Script.cs b/Common/Script.cs index cfe7ea8a..4d1bbc36 100644 --- a/Common/Script.cs +++ b/Common/Script.cs @@ -1,41 +1,24 @@ /* - * Copyright 2008 Sun Microsystems, Inc. All rights reserved. - * - * U.S. Government Rights - Commercial software. Government users - * are subject to the Sun Microsystems, Inc. standard license agreement - * and applicable provisions of the FAR and its supplements. - * - * Use is subject to license terms. - * - * This distribution may include materials developed by third parties. - * Sun, Sun Microsystems, the Sun logo, Java and Project Identity - * Connectors are trademarks or registered trademarks of Sun - * Microsystems, Inc. or its subsidiaries in the U.S. and other - * countries. - * - * UNIX is a registered trademark in the U.S. and other countries, - * exclusively licensed through X/Open Company, Ltd. - * - * ----------- + * ==================== * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER. * - * Copyright 2008 Sun Microsystems, Inc. All rights reserved. + * Copyright 2007-2008 Sun Microsystems, Inc. All rights reserved. * - * The contents of this file are subject to the terms of the Common Development - * and Distribution License(CDDL) (the License). You may not use this file - * except in compliance with the License. + * The contents of this file are subject to the terms of the Common Development + * and Distribution License("CDDL") (the "License"). You may not use this file + * except in compliance with the License. * - * You can obtain a copy of the License at - * http://identityconnectors.dev.java.net/CDDLv1.0.html - * See the License for the specific language governing permissions and - * limitations under the License. + * You can obtain a copy of the License at + * http://IdentityConnectors.dev.java.net/legal/license.txt + * See the License for the specific language governing permissions and limitations + * under the License. * - * When distributing the Covered Code, include this CDDL Header Notice in each - * file and include the License file at identityconnectors/legal/license.txt. + * When distributing the Covered Code, include this CDDL Header Notice in each file + * and include the License file at identityconnectors/legal/license.txt. * If applicable, add the following below this CDDL Header, with the fields * enclosed by brackets [] replaced by your own identifying information: * "Portions Copyrighted [year] [name of copyright owner]" - * ----------- + * ==================== */ using System; using System.IO; diff --git a/Common/Security.cs b/Common/Security.cs index 48234821..4d7473bf 100644 --- a/Common/Security.cs +++ b/Common/Security.cs @@ -1,41 +1,24 @@ /* - * Copyright 2008 Sun Microsystems, Inc. All rights reserved. - * - * U.S. Government Rights - Commercial software. Government users - * are subject to the Sun Microsystems, Inc. standard license agreement - * and applicable provisions of the FAR and its supplements. - * - * Use is subject to license terms. - * - * This distribution may include materials developed by third parties. - * Sun, Sun Microsystems, the Sun logo, Java and Project Identity - * Connectors are trademarks or registered trademarks of Sun - * Microsystems, Inc. or its subsidiaries in the U.S. and other - * countries. - * - * UNIX is a registered trademark in the U.S. and other countries, - * exclusively licensed through X/Open Company, Ltd. - * - * ----------- + * ==================== * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER. * - * Copyright 2008 Sun Microsystems, Inc. All rights reserved. + * Copyright 2007-2008 Sun Microsystems, Inc. All rights reserved. * - * The contents of this file are subject to the terms of the Common Development - * and Distribution License(CDDL) (the License). You may not use this file - * except in compliance with the License. + * The contents of this file are subject to the terms of the Common Development + * and Distribution License("CDDL") (the "License"). You may not use this file + * except in compliance with the License. * - * You can obtain a copy of the License at - * http://identityconnectors.dev.java.net/CDDLv1.0.html - * See the License for the specific language governing permissions and - * limitations under the License. + * You can obtain a copy of the License at + * http://IdentityConnectors.dev.java.net/legal/license.txt + * See the License for the specific language governing permissions and limitations + * under the License. * - * When distributing the Covered Code, include this CDDL Header Notice in each - * file and include the License file at identityconnectors/legal/license.txt. + * When distributing the Covered Code, include this CDDL Header Notice in each file + * and include the License file at identityconnectors/legal/license.txt. * If applicable, add the following below this CDDL Header, with the fields * enclosed by brackets [] replaced by your own identifying information: * "Portions Copyrighted [year] [name of copyright owner]" - * ----------- + * ==================== */ using System; using System.Security; diff --git a/Common/StringUtil.cs b/Common/StringUtil.cs index 0db4931e..2fef0f7b 100644 --- a/Common/StringUtil.cs +++ b/Common/StringUtil.cs @@ -1,41 +1,24 @@ /* - * Copyright 2008 Sun Microsystems, Inc. All rights reserved. - * - * U.S. Government Rights - Commercial software. Government users - * are subject to the Sun Microsystems, Inc. standard license agreement - * and applicable provisions of the FAR and its supplements. - * - * Use is subject to license terms. - * - * This distribution may include materials developed by third parties. - * Sun, Sun Microsystems, the Sun logo, Java and Project Identity - * Connectors are trademarks or registered trademarks of Sun - * Microsystems, Inc. or its subsidiaries in the U.S. and other - * countries. - * - * UNIX is a registered trademark in the U.S. and other countries, - * exclusively licensed through X/Open Company, Ltd. - * - * ----------- + * ==================== * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER. * - * Copyright 2008 Sun Microsystems, Inc. All rights reserved. + * Copyright 2007-2008 Sun Microsystems, Inc. All rights reserved. * - * The contents of this file are subject to the terms of the Common Development - * and Distribution License(CDDL) (the License). You may not use this file - * except in compliance with the License. + * The contents of this file are subject to the terms of the Common Development + * and Distribution License("CDDL") (the "License"). You may not use this file + * except in compliance with the License. * - * You can obtain a copy of the License at - * http://identityconnectors.dev.java.net/CDDLv1.0.html - * See the License for the specific language governing permissions and - * limitations under the License. + * You can obtain a copy of the License at + * http://IdentityConnectors.dev.java.net/legal/license.txt + * See the License for the specific language governing permissions and limitations + * under the License. * - * When distributing the Covered Code, include this CDDL Header Notice in each - * file and include the License file at identityconnectors/legal/license.txt. + * When distributing the Covered Code, include this CDDL Header Notice in each file + * and include the License file at identityconnectors/legal/license.txt. * If applicable, add the following below this CDDL Header, with the fields * enclosed by brackets [] replaced by your own identifying information: * "Portions Copyrighted [year] [name of copyright owner]" - * ----------- + * ==================== */ using System; using Org.IdentityConnectors.Common.Security; diff --git a/Common/TraceUtil.cs b/Common/TraceUtil.cs index 7163dca9..d12f5e70 100644 --- a/Common/TraceUtil.cs +++ b/Common/TraceUtil.cs @@ -1,41 +1,24 @@ /* - * Copyright 2008 Sun Microsystems, Inc. All rights reserved. - * - * U.S. Government Rights - Commercial software. Government users - * are subject to the Sun Microsystems, Inc. standard license agreement - * and applicable provisions of the FAR and its supplements. - * - * Use is subject to license terms. - * - * This distribution may include materials developed by third parties. - * Sun, Sun Microsystems, the Sun logo, Java and Project Identity - * Connectors are trademarks or registered trademarks of Sun - * Microsystems, Inc. or its subsidiaries in the U.S. and other - * countries. - * - * UNIX is a registered trademark in the U.S. and other countries, - * exclusively licensed through X/Open Company, Ltd. - * - * ----------- + * ==================== * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER. * - * Copyright 2008 Sun Microsystems, Inc. All rights reserved. + * Copyright 2007-2008 Sun Microsystems, Inc. All rights reserved. * - * The contents of this file are subject to the terms of the Common Development - * and Distribution License(CDDL) (the License). You may not use this file - * except in compliance with the License. + * The contents of this file are subject to the terms of the Common Development + * and Distribution License("CDDL") (the "License"). You may not use this file + * except in compliance with the License. * - * You can obtain a copy of the License at - * http://identityconnectors.dev.java.net/CDDLv1.0.html - * See the License for the specific language governing permissions and - * limitations under the License. + * You can obtain a copy of the License at + * http://IdentityConnectors.dev.java.net/legal/license.txt + * See the License for the specific language governing permissions and limitations + * under the License. * - * When distributing the Covered Code, include this CDDL Header Notice in each - * file and include the License file at identityconnectors/legal/license.txt. + * When distributing the Covered Code, include this CDDL Header Notice in each file + * and include the License file at identityconnectors/legal/license.txt. * If applicable, add the following below this CDDL Header, with the fields * enclosed by brackets [] replaced by your own identifying information: * "Portions Copyrighted [year] [name of copyright owner]" - * ----------- + * ==================== */ using System; using System.Diagnostics; diff --git a/Common/XmlUtil.cs b/Common/XmlUtil.cs index 9da3656d..30ea8040 100644 --- a/Common/XmlUtil.cs +++ b/Common/XmlUtil.cs @@ -1,41 +1,24 @@ /* - * Copyright 2008 Sun Microsystems, Inc. All rights reserved. - * - * U.S. Government Rights - Commercial software. Government users - * are subject to the Sun Microsystems, Inc. standard license agreement - * and applicable provisions of the FAR and its supplements. - * - * Use is subject to license terms. - * - * This distribution may include materials developed by third parties. - * Sun, Sun Microsystems, the Sun logo, Java and Project Identity - * Connectors are trademarks or registered trademarks of Sun - * Microsystems, Inc. or its subsidiaries in the U.S. and other - * countries. - * - * UNIX is a registered trademark in the U.S. and other countries, - * exclusively licensed through X/Open Company, Ltd. - * - * ----------- + * ==================== * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER. * - * Copyright 2008 Sun Microsystems, Inc. All rights reserved. + * Copyright 2007-2008 Sun Microsystems, Inc. All rights reserved. * - * The contents of this file are subject to the terms of the Common Development - * and Distribution License(CDDL) (the License). You may not use this file - * except in compliance with the License. + * The contents of this file are subject to the terms of the Common Development + * and Distribution License("CDDL") (the "License"). You may not use this file + * except in compliance with the License. * - * You can obtain a copy of the License at - * http://identityconnectors.dev.java.net/CDDLv1.0.html - * See the License for the specific language governing permissions and - * limitations under the License. + * You can obtain a copy of the License at + * http://IdentityConnectors.dev.java.net/legal/license.txt + * See the License for the specific language governing permissions and limitations + * under the License. * - * When distributing the Covered Code, include this CDDL Header Notice in each - * file and include the License file at identityconnectors/legal/license.txt. + * When distributing the Covered Code, include this CDDL Header Notice in each file + * and include the License file at identityconnectors/legal/license.txt. * If applicable, add the following below this CDDL Header, with the fields * enclosed by brackets [] replaced by your own identifying information: * "Portions Copyrighted [year] [name of copyright owner]" - * ----------- + * ==================== */ using System; using System.Xml; diff --git a/Console/AssemblyInfo.cs b/Console/AssemblyInfo.cs index 3580eb04..f8507a15 100644 --- a/Console/AssemblyInfo.cs +++ b/Console/AssemblyInfo.cs @@ -1,41 +1,24 @@ /* - * Copyright 2008 Sun Microsystems, Inc. All rights reserved. - * - * U.S. Government Rights - Commercial software. Government users - * are subject to the Sun Microsystems, Inc. standard license agreement - * and applicable provisions of the FAR and its supplements. - * - * Use is subject to license terms. - * - * This distribution may include materials developed by third parties. - * Sun, Sun Microsystems, the Sun logo, Java and Project Identity - * Connectors are trademarks or registered trademarks of Sun - * Microsystems, Inc. or its subsidiaries in the U.S. and other - * countries. - * - * UNIX is a registered trademark in the U.S. and other countries, - * exclusively licensed through X/Open Company, Ltd. - * - * ----------- + * ==================== * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER. * - * Copyright 2008 Sun Microsystems, Inc. All rights reserved. + * Copyright 2007-2008 Sun Microsystems, Inc. All rights reserved. * - * The contents of this file are subject to the terms of the Common Development - * and Distribution License(CDDL) (the License). You may not use this file - * except in compliance with the License. + * The contents of this file are subject to the terms of the Common Development + * and Distribution License("CDDL") (the "License"). You may not use this file + * except in compliance with the License. * - * You can obtain a copy of the License at - * http://identityconnectors.dev.java.net/CDDLv1.0.html - * See the License for the specific language governing permissions and - * limitations under the License. + * You can obtain a copy of the License at + * http://IdentityConnectors.dev.java.net/legal/license.txt + * See the License for the specific language governing permissions and limitations + * under the License. * - * When distributing the Covered Code, include this CDDL Header Notice in each - * file and include the License file at identityconnectors/legal/license.txt. + * When distributing the Covered Code, include this CDDL Header Notice in each file + * and include the License file at identityconnectors/legal/license.txt. * If applicable, add the following below this CDDL Header, with the fields * enclosed by brackets [] replaced by your own identifying information: * "Portions Copyrighted [year] [name of copyright owner]" - * ----------- + * ==================== */ #region Using directives diff --git a/Console/Console.csproj b/Console/Console.csproj index f4c25752..1192908e 100644 --- a/Console/Console.csproj +++ b/Console/Console.csproj @@ -1,39 +1,24 @@ - @@ -102,4 +87,4 @@ Framework - \ No newline at end of file + diff --git a/Console/MainForm.cs b/Console/MainForm.cs index d7ba3af2..ca92a32a 100644 --- a/Console/MainForm.cs +++ b/Console/MainForm.cs @@ -1,41 +1,24 @@ /* - * Copyright 2008 Sun Microsystems, Inc. All rights reserved. - * - * U.S. Government Rights - Commercial software. Government users - * are subject to the Sun Microsystems, Inc. standard license agreement - * and applicable provisions of the FAR and its supplements. - * - * Use is subject to license terms. - * - * This distribution may include materials developed by third parties. - * Sun, Sun Microsystems, the Sun logo, Java and Project Identity - * Connectors are trademarks or registered trademarks of Sun - * Microsystems, Inc. or its subsidiaries in the U.S. and other - * countries. - * - * UNIX is a registered trademark in the U.S. and other countries, - * exclusively licensed through X/Open Company, Ltd. - * - * ----------- + * ==================== * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER. * - * Copyright 2008 Sun Microsystems, Inc. All rights reserved. + * Copyright 2007-2008 Sun Microsystems, Inc. All rights reserved. * - * The contents of this file are subject to the terms of the Common Development - * and Distribution License(CDDL) (the License). You may not use this file - * except in compliance with the License. + * The contents of this file are subject to the terms of the Common Development + * and Distribution License("CDDL") (the "License"). You may not use this file + * except in compliance with the License. * - * You can obtain a copy of the License at - * http://identityconnectors.dev.java.net/CDDLv1.0.html - * See the License for the specific language governing permissions and - * limitations under the License. + * You can obtain a copy of the License at + * http://IdentityConnectors.dev.java.net/legal/license.txt + * See the License for the specific language governing permissions and limitations + * under the License. * - * When distributing the Covered Code, include this CDDL Header Notice in each - * file and include the License file at identityconnectors/legal/license.txt. + * When distributing the Covered Code, include this CDDL Header Notice in each file + * and include the License file at identityconnectors/legal/license.txt. * If applicable, add the following below this CDDL Header, with the fields * enclosed by brackets [] replaced by your own identifying information: * "Portions Copyrighted [year] [name of copyright owner]" - * ----------- + * ==================== */ using System; using System.Collections.Generic; diff --git a/Console/Program.cs b/Console/Program.cs index ce4f71a5..23030249 100644 --- a/Console/Program.cs +++ b/Console/Program.cs @@ -1,41 +1,24 @@ /* - * Copyright 2008 Sun Microsystems, Inc. All rights reserved. - * - * U.S. Government Rights - Commercial software. Government users - * are subject to the Sun Microsystems, Inc. standard license agreement - * and applicable provisions of the FAR and its supplements. - * - * Use is subject to license terms. - * - * This distribution may include materials developed by third parties. - * Sun, Sun Microsystems, the Sun logo, Java and Project Identity - * Connectors are trademarks or registered trademarks of Sun - * Microsystems, Inc. or its subsidiaries in the U.S. and other - * countries. - * - * UNIX is a registered trademark in the U.S. and other countries, - * exclusively licensed through X/Open Company, Ltd. - * - * ----------- + * ==================== * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER. * - * Copyright 2008 Sun Microsystems, Inc. All rights reserved. + * Copyright 2007-2008 Sun Microsystems, Inc. All rights reserved. * - * The contents of this file are subject to the terms of the Common Development - * and Distribution License(CDDL) (the License). You may not use this file - * except in compliance with the License. + * The contents of this file are subject to the terms of the Common Development + * and Distribution License("CDDL") (the "License"). You may not use this file + * except in compliance with the License. * - * You can obtain a copy of the License at - * http://identityconnectors.dev.java.net/CDDLv1.0.html - * See the License for the specific language governing permissions and - * limitations under the License. + * You can obtain a copy of the License at + * http://IdentityConnectors.dev.java.net/legal/license.txt + * See the License for the specific language governing permissions and limitations + * under the License. * - * When distributing the Covered Code, include this CDDL Header Notice in each - * file and include the License file at identityconnectors/legal/license.txt. + * When distributing the Covered Code, include this CDDL Header Notice in each file + * and include the License file at identityconnectors/legal/license.txt. * If applicable, add the following below this CDDL Header, with the fields * enclosed by brackets [] replaced by your own identifying information: * "Portions Copyrighted [year] [name of copyright owner]" - * ----------- + * ==================== */ using System; using System.Windows.Forms; diff --git a/ExchangeConnector/ExchangeConfiguration.cs b/ExchangeConnector/ExchangeConfiguration.cs index afaf33f7..9d5e5477 100644 --- a/ExchangeConnector/ExchangeConfiguration.cs +++ b/ExchangeConnector/ExchangeConfiguration.cs @@ -1,44 +1,25 @@ -/* - * Copyright 2008 Sun Microsystems, Inc. All rights reserved. - * - * U.S. Government Rights - Commercial software. Government users - * are subject to the Sun Microsystems, Inc. standard license agreement - * and applicable provisions of the FAR and its supplements. - * - * Use is subject to license terms. - * - * This distribution may include materials developed by third parties. - * Sun, Sun Microsystems, the Sun logo, Java and Project Identity - * Connectors are trademarks or registered trademarks of Sun - * Microsystems, Inc. or its subsidiaries in the U.S. and other - * countries. - * - * UNIX is a registered trademark in the U.S. and other countries, - * exclusively licensed through X/Open Company, Ltd. - * - * ----------- +/* + * ==================== * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER. * - * Copyright 2008 Sun Microsystems, Inc. All rights reserved. + * Copyright 2007-2008 Sun Microsystems, Inc. All rights reserved. * - * The contents of this file are subject to the terms of the Common Development - * and Distribution License(CDDL) (the License). You may not use this file - * except in compliance with the License. + * The contents of this file are subject to the terms of the Common Development + * and Distribution License("CDDL") (the "License"). You may not use this file + * except in compliance with the License. * - * You can obtain a copy of the License at - * http://identityconnectors.dev.java.net/CDDLv1.0.html - * See the License for the specific language governing permissions and - * limitations under the License. + * You can obtain a copy of the License at + * http://IdentityConnectors.dev.java.net/legal/license.txt + * See the License for the specific language governing permissions and limitations + * under the License. * - * When distributing the Covered Code, include this CDDL Header Notice in each - * file and include the License file at identityconnectors/legal/license.txt. + * When distributing the Covered Code, include this CDDL Header Notice in each file + * and include the License file at identityconnectors/legal/license.txt. * If applicable, add the following below this CDDL Header, with the fields * enclosed by brackets [] replaced by your own identifying information: * "Portions Copyrighted [year] [name of copyright owner]" - * ----------- + * ==================== */ - -using Org.IdentityConnectors.Framework.Common.Exceptions; using System; using Org.IdentityConnectors.ActiveDirectory; using Org.IdentityConnectors.Framework.Spi; diff --git a/ExchangeConnector/ExchangeConnector.cs b/ExchangeConnector/ExchangeConnector.cs index df0a7d1c..8af0c7b3 100644 --- a/ExchangeConnector/ExchangeConnector.cs +++ b/ExchangeConnector/ExchangeConnector.cs @@ -1,41 +1,24 @@ -/* - * Copyright 2008 Sun Microsystems, Inc. All rights reserved. - * - * U.S. Government Rights - Commercial software. Government users - * are subject to the Sun Microsystems, Inc. standard license agreement - * and applicable provisions of the FAR and its supplements. - * - * Use is subject to license terms. - * - * This distribution may include materials developed by third parties. - * Sun, Sun Microsystems, the Sun logo, Java and Project Identity - * Connectors are trademarks or registered trademarks of Sun - * Microsystems, Inc. or its subsidiaries in the U.S. and other - * countries. - * - * UNIX is a registered trademark in the U.S. and other countries, - * exclusively licensed through X/Open Company, Ltd. - * - * ----------- +/* + * ==================== * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER. * - * Copyright 2008 Sun Microsystems, Inc. All rights reserved. + * Copyright 2007-2008 Sun Microsystems, Inc. All rights reserved. * - * The contents of this file are subject to the terms of the Common Development - * and Distribution License(CDDL) (the License). You may not use this file - * except in compliance with the License. + * The contents of this file are subject to the terms of the Common Development + * and Distribution License("CDDL") (the "License"). You may not use this file + * except in compliance with the License. * - * You can obtain a copy of the License at - * http://identityconnectors.dev.java.net/CDDLv1.0.html - * See the License for the specific language governing permissions and - * limitations under the License. + * You can obtain a copy of the License at + * http://IdentityConnectors.dev.java.net/legal/license.txt + * See the License for the specific language governing permissions and limitations + * under the License. * - * When distributing the Covered Code, include this CDDL Header Notice in each - * file and include the License file at identityconnectors/legal/license.txt. - * If applicable, add the following below this CDDL Header, with the fields - * enclosed by brackets [] replaced by your own identifying information: + * When distributing the Covered Code, include this CDDL Header Notice in each file + * and include the License file at identityconnectors/legal/license.txt. + * If applicable, add the following below this CDDL Header, with the fields + * enclosed by brackets [] replaced by your own identifying information: * "Portions Copyrighted [year] [name of copyright owner]" - * ----------- + * ==================== */ using System; using System.Collections.Generic; @@ -316,4 +299,4 @@ internal string NameParameter } } } - \ No newline at end of file + diff --git a/ExchangeConnector/ExchangeConnector.csproj b/ExchangeConnector/ExchangeConnector.csproj index 2bbf26d1..7ba60d80 100644 --- a/ExchangeConnector/ExchangeConnector.csproj +++ b/ExchangeConnector/ExchangeConnector.csproj @@ -1,4 +1,25 @@ - + Debug @@ -84,4 +105,4 @@ --> - \ No newline at end of file + diff --git a/ExchangeConnector/ExchangeUtils.cs b/ExchangeConnector/ExchangeUtils.cs index d609a440..6f4bcaf8 100644 --- a/ExchangeConnector/ExchangeUtils.cs +++ b/ExchangeConnector/ExchangeUtils.cs @@ -1,41 +1,24 @@ /* - * Copyright 2008 Sun Microsystems, Inc. All rights reserved. - * - * U.S. Government Rights - Commercial software. Government users - * are subject to the Sun Microsystems, Inc. standard license agreement - * and applicable provisions of the FAR and its supplements. - * - * Use is subject to license terms. - * - * This distribution may include materials developed by third parties. - * Sun, Sun Microsystems, the Sun logo, Java and Project Identity - * Connectors are trademarks or registered trademarks of Sun - * Microsystems, Inc. or its subsidiaries in the U.S. and other - * countries. - * - * UNIX is a registered trademark in the U.S. and other countries, - * exclusively licensed through X/Open Company, Ltd. - * - * ----------- + * ==================== * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER. * - * Copyright 2008 Sun Microsystems, Inc. All rights reserved. + * Copyright 2007-2008 Sun Microsystems, Inc. All rights reserved. * - * The contents of this file are subject to the terms of the Common Development - * and Distribution License(CDDL) (the License). You may not use this file - * except in compliance with the License. + * The contents of this file are subject to the terms of the Common Development + * and Distribution License("CDDL") (the "License"). You may not use this file + * except in compliance with the License. * - * You can obtain a copy of the License at - * http://identityconnectors.dev.java.net/CDDLv1.0.html - * See the License for the specific language governing permissions and - * limitations under the License. + * You can obtain a copy of the License at + * http://IdentityConnectors.dev.java.net/legal/license.txt + * See the License for the specific language governing permissions and limitations + * under the License. * - * When distributing the Covered Code, include this CDDL Header Notice in each - * file and include the License file at identityconnectors/legal/license.txt. + * When distributing the Covered Code, include this CDDL Header Notice in each file + * and include the License file at identityconnectors/legal/license.txt. * If applicable, add the following below this CDDL Header, with the fields * enclosed by brackets [] replaced by your own identifying information: * "Portions Copyrighted [year] [name of copyright owner]" - * ----------- + * ==================== */ using System; using System.Collections; diff --git a/ExchangeConnector/LegacyExchangeConnector.cs b/ExchangeConnector/LegacyExchangeConnector.cs index 0a4e0925..9d39edf2 100644 --- a/ExchangeConnector/LegacyExchangeConnector.cs +++ b/ExchangeConnector/LegacyExchangeConnector.cs @@ -1,41 +1,24 @@ -/* - * Copyright 2008 Sun Microsystems, Inc. All rights reserved. - * - * U.S. Government Rights - Commercial software. Government users - * are subject to the Sun Microsystems, Inc. standard license agreement - * and applicable provisions of the FAR and its supplements. - * - * Use is subject to license terms. - * - * This distribution may include materials developed by third parties. - * Sun, Sun Microsystems, the Sun logo, Java and Project Identity - * Connectors are trademarks or registered trademarks of Sun - * Microsystems, Inc. or its subsidiaries in the U.S. and other - * countries. - * - * UNIX is a registered trademark in the U.S. and other countries, - * exclusively licensed through X/Open Company, Ltd. - * - * ----------- +/* + * ==================== * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER. * - * Copyright 2008 Sun Microsystems, Inc. All rights reserved. + * Copyright 2007-2008 Sun Microsystems, Inc. All rights reserved. * - * The contents of this file are subject to the terms of the Common Development - * and Distribution License(CDDL) (the License). You may not use this file - * except in compliance with the License. + * The contents of this file are subject to the terms of the Common Development + * and Distribution License("CDDL") (the "License"). You may not use this file + * except in compliance with the License. * - * You can obtain a copy of the License at - * http://identityconnectors.dev.java.net/CDDLv1.0.html - * See the License for the specific language governing permissions and - * limitations under the License. + * You can obtain a copy of the License at + * http://IdentityConnectors.dev.java.net/legal/license.txt + * See the License for the specific language governing permissions and limitations + * under the License. * - * When distributing the Covered Code, include this CDDL Header Notice in each - * file and include the License file at identityconnectors/legal/license.txt. - * If applicable, add the following below this CDDL Header, with the fields - * enclosed by brackets [] replaced by your own identifying information: + * When distributing the Covered Code, include this CDDL Header Notice in each file + * and include the License file at identityconnectors/legal/license.txt. + * If applicable, add the following below this CDDL Header, with the fields + * enclosed by brackets [] replaced by your own identifying information: * "Portions Copyrighted [year] [name of copyright owner]" - * ----------- + * ==================== */ using System.Collections.Generic; using System.Diagnostics; diff --git a/ExchangeConnector/Properties/AssemblyInfo.cs b/ExchangeConnector/Properties/AssemblyInfo.cs index 9e58746d..d1492654 100644 --- a/ExchangeConnector/Properties/AssemblyInfo.cs +++ b/ExchangeConnector/Properties/AssemblyInfo.cs @@ -1,4 +1,26 @@ -using System.Reflection; +/* + * ==================== + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER. + * + * Copyright 2007-2008 Sun Microsystems, Inc. All rights reserved. + * + * The contents of this file are subject to the terms of the Common Development + * and Distribution License("CDDL") (the "License"). You may not use this file + * except in compliance with the License. + * + * You can obtain a copy of the License at + * http://IdentityConnectors.dev.java.net/legal/license.txt + * See the License for the specific language governing permissions and limitations + * under the License. + * + * When distributing the Covered Code, include this CDDL Header Notice in each file + * and include the License file at identityconnectors/legal/license.txt. + * If applicable, add the following below this CDDL Header, with the fields + * enclosed by brackets [] replaced by your own identifying information: + * "Portions Copyrighted [year] [name of copyright owner]" + * ==================== + */ +using System.Reflection; using System.Runtime.CompilerServices; using System.Runtime.InteropServices; @@ -10,7 +32,7 @@ [assembly: AssemblyConfiguration("")] [assembly: AssemblyCompany("IDM")] [assembly: AssemblyProduct("ExchangeConnector")] -[assembly: AssemblyCopyright("Copyright © IDM 2008")] +[assembly: AssemblyCopyright("Copyright IDM 2008")] [assembly: AssemblyTrademark("")] [assembly: AssemblyCulture("")] diff --git a/ExchangeConnector/RunSpaceInstance.cs b/ExchangeConnector/RunSpaceInstance.cs index 06b23be1..21a0ff76 100644 --- a/ExchangeConnector/RunSpaceInstance.cs +++ b/ExchangeConnector/RunSpaceInstance.cs @@ -1,43 +1,25 @@ /* - * Copyright 2008 Sun Microsystems, Inc. All rights reserved. - * - * U.S. Government Rights - Commercial software. Government users - * are subject to the Sun Microsystems, Inc. standard license agreement - * and applicable provisions of the FAR and its supplements. - * - * Use is subject to license terms. - * - * This distribution may include materials developed by third parties. - * Sun, Sun Microsystems, the Sun logo, Java and Project Identity - * Connectors are trademarks or registered trademarks of Sun - * Microsystems, Inc. or its subsidiaries in the U.S. and other - * countries. - * - * UNIX is a registered trademark in the U.S. and other countries, - * exclusively licensed through X/Open Company, Ltd. - * - * ----------- + * ==================== * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER. * - * Copyright 2008 Sun Microsystems, Inc. All rights reserved. + * Copyright 2007-2008 Sun Microsystems, Inc. All rights reserved. * - * The contents of this file are subject to the terms of the Common Development - * and Distribution License(CDDL) (the License). You may not use this file - * except in compliance with the License. + * The contents of this file are subject to the terms of the Common Development + * and Distribution License("CDDL") (the "License"). You may not use this file + * except in compliance with the License. * - * You can obtain a copy of the License at - * http://identityconnectors.dev.java.net/CDDLv1.0.html - * See the License for the specific language governing permissions and - * limitations under the License. + * You can obtain a copy of the License at + * http://IdentityConnectors.dev.java.net/legal/license.txt + * See the License for the specific language governing permissions and limitations + * under the License. * - * When distributing the Covered Code, include this CDDL Header Notice in each - * file and include the License file at identityconnectors/legal/license.txt. + * When distributing the Covered Code, include this CDDL Header Notice in each file + * and include the License file at identityconnectors/legal/license.txt. * If applicable, add the following below this CDDL Header, with the fields * enclosed by brackets [] replaced by your own identifying information: * "Portions Copyrighted [year] [name of copyright owner]" - * ----------- + * ==================== */ - using System; using System.Collections; using System.Collections.Generic; diff --git a/Framework/Api.cs b/Framework/Api.cs index cf45cb7a..c8368e89 100644 --- a/Framework/Api.cs +++ b/Framework/Api.cs @@ -1,41 +1,24 @@ /* - * Copyright 2008 Sun Microsystems, Inc. All rights reserved. - * - * U.S. Government Rights - Commercial software. Government users - * are subject to the Sun Microsystems, Inc. standard license agreement - * and applicable provisions of the FAR and its supplements. - * - * Use is subject to license terms. - * - * This distribution may include materials developed by third parties. - * Sun, Sun Microsystems, the Sun logo, Java and Project Identity - * Connectors are trademarks or registered trademarks of Sun - * Microsystems, Inc. or its subsidiaries in the U.S. and other - * countries. - * - * UNIX is a registered trademark in the U.S. and other countries, - * exclusively licensed through X/Open Company, Ltd. - * - * ----------- + * ==================== * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER. * - * Copyright 2008 Sun Microsystems, Inc. All rights reserved. + * Copyright 2007-2008 Sun Microsystems, Inc. All rights reserved. * - * The contents of this file are subject to the terms of the Common Development - * and Distribution License(CDDL) (the License). You may not use this file - * except in compliance with the License. + * The contents of this file are subject to the terms of the Common Development + * and Distribution License("CDDL") (the "License"). You may not use this file + * except in compliance with the License. * - * You can obtain a copy of the License at - * http://identityconnectors.dev.java.net/CDDLv1.0.html - * See the License for the specific language governing permissions and - * limitations under the License. + * You can obtain a copy of the License at + * http://IdentityConnectors.dev.java.net/legal/license.txt + * See the License for the specific language governing permissions and limitations + * under the License. * - * When distributing the Covered Code, include this CDDL Header Notice in each - * file and include the License file at identityconnectors/legal/license.txt. + * When distributing the Covered Code, include this CDDL Header Notice in each file + * and include the License file at identityconnectors/legal/license.txt. * If applicable, add the following below this CDDL Header, with the fields * enclosed by brackets [] replaced by your own identifying information: * "Portions Copyrighted [year] [name of copyright owner]" - * ----------- + * ==================== */ using System; using System.Reflection; diff --git a/Framework/ApiOperations.cs b/Framework/ApiOperations.cs index 1a40bff9..bf1ce08f 100644 --- a/Framework/ApiOperations.cs +++ b/Framework/ApiOperations.cs @@ -1,41 +1,24 @@ /* - * Copyright 2008 Sun Microsystems, Inc. All rights reserved. - * - * U.S. Government Rights - Commercial software. Government users - * are subject to the Sun Microsystems, Inc. standard license agreement - * and applicable provisions of the FAR and its supplements. - * - * Use is subject to license terms. - * - * This distribution may include materials developed by third parties. - * Sun, Sun Microsystems, the Sun logo, Java and Project Identity - * Connectors are trademarks or registered trademarks of Sun - * Microsystems, Inc. or its subsidiaries in the U.S. and other - * countries. - * - * UNIX is a registered trademark in the U.S. and other countries, - * exclusively licensed through X/Open Company, Ltd. - * - * ----------- + * ==================== * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER. * - * Copyright 2008 Sun Microsystems, Inc. All rights reserved. + * Copyright 2007-2008 Sun Microsystems, Inc. All rights reserved. * - * The contents of this file are subject to the terms of the Common Development - * and Distribution License(CDDL) (the License). You may not use this file - * except in compliance with the License. + * The contents of this file are subject to the terms of the Common Development + * and Distribution License("CDDL") (the "License"). You may not use this file + * except in compliance with the License. * - * You can obtain a copy of the License at - * http://identityconnectors.dev.java.net/CDDLv1.0.html - * See the License for the specific language governing permissions and - * limitations under the License. + * You can obtain a copy of the License at + * http://IdentityConnectors.dev.java.net/legal/license.txt + * See the License for the specific language governing permissions and limitations + * under the License. * - * When distributing the Covered Code, include this CDDL Header Notice in each - * file and include the License file at identityconnectors/legal/license.txt. + * When distributing the Covered Code, include this CDDL Header Notice in each file + * and include the License file at identityconnectors/legal/license.txt. * If applicable, add the following below this CDDL Header, with the fields * enclosed by brackets [] replaced by your own identifying information: * "Portions Copyrighted [year] [name of copyright owner]" - * ----------- + * ==================== */ using System; using System.Reflection; diff --git a/Framework/AssemblyInfo.cs b/Framework/AssemblyInfo.cs index c4afaf29..c33629ae 100644 --- a/Framework/AssemblyInfo.cs +++ b/Framework/AssemblyInfo.cs @@ -1,41 +1,24 @@ /* - * Copyright 2008 Sun Microsystems, Inc. All rights reserved. - * - * U.S. Government Rights - Commercial software. Government users - * are subject to the Sun Microsystems, Inc. standard license agreement - * and applicable provisions of the FAR and its supplements. - * - * Use is subject to license terms. - * - * This distribution may include materials developed by third parties. - * Sun, Sun Microsystems, the Sun logo, Java and Project Identity - * Connectors are trademarks or registered trademarks of Sun - * Microsystems, Inc. or its subsidiaries in the U.S. and other - * countries. - * - * UNIX is a registered trademark in the U.S. and other countries, - * exclusively licensed through X/Open Company, Ltd. - * - * ----------- + * ==================== * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER. * - * Copyright 2008 Sun Microsystems, Inc. All rights reserved. + * Copyright 2007-2008 Sun Microsystems, Inc. All rights reserved. * - * The contents of this file are subject to the terms of the Common Development - * and Distribution License(CDDL) (the License). You may not use this file - * except in compliance with the License. + * The contents of this file are subject to the terms of the Common Development + * and Distribution License("CDDL") (the "License"). You may not use this file + * except in compliance with the License. * - * You can obtain a copy of the License at - * http://identityconnectors.dev.java.net/CDDLv1.0.html - * See the License for the specific language governing permissions and - * limitations under the License. + * You can obtain a copy of the License at + * http://IdentityConnectors.dev.java.net/legal/license.txt + * See the License for the specific language governing permissions and limitations + * under the License. * - * When distributing the Covered Code, include this CDDL Header Notice in each - * file and include the License file at identityconnectors/legal/license.txt. + * When distributing the Covered Code, include this CDDL Header Notice in each file + * and include the License file at identityconnectors/legal/license.txt. * If applicable, add the following below this CDDL Header, with the fields * enclosed by brackets [] replaced by your own identifying information: * "Portions Copyrighted [year] [name of copyright owner]" - * ----------- + * ==================== */ #region Using directives diff --git a/Framework/Common.cs b/Framework/Common.cs index 9547d7fa..2da8fa0d 100644 --- a/Framework/Common.cs +++ b/Framework/Common.cs @@ -1,41 +1,24 @@ /* - * Copyright 2008 Sun Microsystems, Inc. All rights reserved. - * - * U.S. Government Rights - Commercial software. Government users - * are subject to the Sun Microsystems, Inc. standard license agreement - * and applicable provisions of the FAR and its supplements. - * - * Use is subject to license terms. - * - * This distribution may include materials developed by third parties. - * Sun, Sun Microsystems, the Sun logo, Java and Project Identity - * Connectors are trademarks or registered trademarks of Sun - * Microsystems, Inc. or its subsidiaries in the U.S. and other - * countries. - * - * UNIX is a registered trademark in the U.S. and other countries, - * exclusively licensed through X/Open Company, Ltd. - * - * ----------- + * ==================== * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER. * - * Copyright 2008 Sun Microsystems, Inc. All rights reserved. + * Copyright 2007-2008 Sun Microsystems, Inc. All rights reserved. * - * The contents of this file are subject to the terms of the Common Development - * and Distribution License(CDDL) (the License). You may not use this file - * except in compliance with the License. + * The contents of this file are subject to the terms of the Common Development + * and Distribution License("CDDL") (the "License"). You may not use this file + * except in compliance with the License. * - * You can obtain a copy of the License at - * http://identityconnectors.dev.java.net/CDDLv1.0.html - * See the License for the specific language governing permissions and - * limitations under the License. + * You can obtain a copy of the License at + * http://IdentityConnectors.dev.java.net/legal/license.txt + * See the License for the specific language governing permissions and limitations + * under the License. * - * When distributing the Covered Code, include this CDDL Header Notice in each - * file and include the License file at identityconnectors/legal/license.txt. + * When distributing the Covered Code, include this CDDL Header Notice in each file + * and include the License file at identityconnectors/legal/license.txt. * If applicable, add the following below this CDDL Header, with the fields * enclosed by brackets [] replaced by your own identifying information: * "Portions Copyrighted [year] [name of copyright owner]" - * ----------- + * ==================== */ using System; using System.Reflection; diff --git a/Framework/CommonExceptions.cs b/Framework/CommonExceptions.cs index 6c2d0175..08043091 100644 --- a/Framework/CommonExceptions.cs +++ b/Framework/CommonExceptions.cs @@ -1,41 +1,24 @@ /* - * Copyright 2008 Sun Microsystems, Inc. All rights reserved. - * - * U.S. Government Rights - Commercial software. Government users - * are subject to the Sun Microsystems, Inc. standard license agreement - * and applicable provisions of the FAR and its supplements. - * - * Use is subject to license terms. - * - * This distribution may include materials developed by third parties. - * Sun, Sun Microsystems, the Sun logo, Java and Project Identity - * Connectors are trademarks or registered trademarks of Sun - * Microsystems, Inc. or its subsidiaries in the U.S. and other - * countries. - * - * UNIX is a registered trademark in the U.S. and other countries, - * exclusively licensed through X/Open Company, Ltd. - * - * ----------- + * ==================== * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER. * - * Copyright 2008 Sun Microsystems, Inc. All rights reserved. + * Copyright 2007-2008 Sun Microsystems, Inc. All rights reserved. * - * The contents of this file are subject to the terms of the Common Development - * and Distribution License(CDDL) (the License). You may not use this file - * except in compliance with the License. + * The contents of this file are subject to the terms of the Common Development + * and Distribution License("CDDL") (the "License"). You may not use this file + * except in compliance with the License. * - * You can obtain a copy of the License at - * http://identityconnectors.dev.java.net/CDDLv1.0.html - * See the License for the specific language governing permissions and - * limitations under the License. + * You can obtain a copy of the License at + * http://IdentityConnectors.dev.java.net/legal/license.txt + * See the License for the specific language governing permissions and limitations + * under the License. * - * When distributing the Covered Code, include this CDDL Header Notice in each - * file and include the License file at identityconnectors/legal/license.txt. + * When distributing the Covered Code, include this CDDL Header Notice in each file + * and include the License file at identityconnectors/legal/license.txt. * If applicable, add the following below this CDDL Header, with the fields * enclosed by brackets [] replaced by your own identifying information: * "Portions Copyrighted [year] [name of copyright owner]" - * ----------- + * ==================== */ using System; using Org.IdentityConnectors.Framework.Common.Objects; diff --git a/Framework/CommonObjects.cs b/Framework/CommonObjects.cs index d722e1c4..2abefcb4 100644 --- a/Framework/CommonObjects.cs +++ b/Framework/CommonObjects.cs @@ -1,41 +1,24 @@ /* - * Copyright 2008 Sun Microsystems, Inc. All rights reserved. - * - * U.S. Government Rights - Commercial software. Government users - * are subject to the Sun Microsystems, Inc. standard license agreement - * and applicable provisions of the FAR and its supplements. - * - * Use is subject to license terms. - * - * This distribution may include materials developed by third parties. - * Sun, Sun Microsystems, the Sun logo, Java and Project Identity - * Connectors are trademarks or registered trademarks of Sun - * Microsystems, Inc. or its subsidiaries in the U.S. and other - * countries. - * - * UNIX is a registered trademark in the U.S. and other countries, - * exclusively licensed through X/Open Company, Ltd. - * - * ----------- + * ==================== * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER. * - * Copyright 2008 Sun Microsystems, Inc. All rights reserved. + * Copyright 2007-2008 Sun Microsystems, Inc. All rights reserved. * - * The contents of this file are subject to the terms of the Common Development - * and Distribution License(CDDL) (the License). You may not use this file - * except in compliance with the License. + * The contents of this file are subject to the terms of the Common Development + * and Distribution License("CDDL") (the "License"). You may not use this file + * except in compliance with the License. * - * You can obtain a copy of the License at - * http://identityconnectors.dev.java.net/CDDLv1.0.html - * See the License for the specific language governing permissions and - * limitations under the License. + * You can obtain a copy of the License at + * http://IdentityConnectors.dev.java.net/legal/license.txt + * See the License for the specific language governing permissions and limitations + * under the License. * - * When distributing the Covered Code, include this CDDL Header Notice in each - * file and include the License file at identityconnectors/legal/license.txt. + * When distributing the Covered Code, include this CDDL Header Notice in each file + * and include the License file at identityconnectors/legal/license.txt. * If applicable, add the following below this CDDL Header, with the fields * enclosed by brackets [] replaced by your own identifying information: * "Portions Copyrighted [year] [name of copyright owner]" - * ----------- + * ==================== */ using System; using System.Security; diff --git a/Framework/CommonObjectsFilter.cs b/Framework/CommonObjectsFilter.cs index 94f44eba..2e32a68f 100644 --- a/Framework/CommonObjectsFilter.cs +++ b/Framework/CommonObjectsFilter.cs @@ -1,41 +1,24 @@ /* - * Copyright 2008 Sun Microsystems, Inc. All rights reserved. - * - * U.S. Government Rights - Commercial software. Government users - * are subject to the Sun Microsystems, Inc. standard license agreement - * and applicable provisions of the FAR and its supplements. - * - * Use is subject to license terms. - * - * This distribution may include materials developed by third parties. - * Sun, Sun Microsystems, the Sun logo, Java and Project Identity - * Connectors are trademarks or registered trademarks of Sun - * Microsystems, Inc. or its subsidiaries in the U.S. and other - * countries. - * - * UNIX is a registered trademark in the U.S. and other countries, - * exclusively licensed through X/Open Company, Ltd. - * - * ----------- + * ==================== * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER. * - * Copyright 2008 Sun Microsystems, Inc. All rights reserved. + * Copyright 2007-2008 Sun Microsystems, Inc. All rights reserved. * - * The contents of this file are subject to the terms of the Common Development - * and Distribution License(CDDL) (the License). You may not use this file - * except in compliance with the License. + * The contents of this file are subject to the terms of the Common Development + * and Distribution License("CDDL") (the "License"). You may not use this file + * except in compliance with the License. * - * You can obtain a copy of the License at - * http://identityconnectors.dev.java.net/CDDLv1.0.html - * See the License for the specific language governing permissions and - * limitations under the License. + * You can obtain a copy of the License at + * http://IdentityConnectors.dev.java.net/legal/license.txt + * See the License for the specific language governing permissions and limitations + * under the License. * - * When distributing the Covered Code, include this CDDL Header Notice in each - * file and include the License file at identityconnectors/legal/license.txt. + * When distributing the Covered Code, include this CDDL Header Notice in each file + * and include the License file at identityconnectors/legal/license.txt. * If applicable, add the following below this CDDL Header, with the fields * enclosed by brackets [] replaced by your own identifying information: * "Portions Copyrighted [year] [name of copyright owner]" - * ----------- + * ==================== */ using System; using System.Text; diff --git a/Framework/CommonSerializer.cs b/Framework/CommonSerializer.cs index 67f48ef3..261429cb 100644 --- a/Framework/CommonSerializer.cs +++ b/Framework/CommonSerializer.cs @@ -1,41 +1,24 @@ /* - * Copyright 2008 Sun Microsystems, Inc. All rights reserved. - * - * U.S. Government Rights - Commercial software. Government users - * are subject to the Sun Microsystems, Inc. standard license agreement - * and applicable provisions of the FAR and its supplements. - * - * Use is subject to license terms. - * - * This distribution may include materials developed by third parties. - * Sun, Sun Microsystems, the Sun logo, Java and Project Identity - * Connectors are trademarks or registered trademarks of Sun - * Microsystems, Inc. or its subsidiaries in the U.S. and other - * countries. - * - * UNIX is a registered trademark in the U.S. and other countries, - * exclusively licensed through X/Open Company, Ltd. - * - * ----------- + * ==================== * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER. * - * Copyright 2008 Sun Microsystems, Inc. All rights reserved. + * Copyright 2007-2008 Sun Microsystems, Inc. All rights reserved. * - * The contents of this file are subject to the terms of the Common Development - * and Distribution License(CDDL) (the License). You may not use this file - * except in compliance with the License. + * The contents of this file are subject to the terms of the Common Development + * and Distribution License("CDDL") (the "License"). You may not use this file + * except in compliance with the License. * - * You can obtain a copy of the License at - * http://identityconnectors.dev.java.net/CDDLv1.0.html - * See the License for the specific language governing permissions and - * limitations under the License. + * You can obtain a copy of the License at + * http://IdentityConnectors.dev.java.net/legal/license.txt + * See the License for the specific language governing permissions and limitations + * under the License. * - * When distributing the Covered Code, include this CDDL Header Notice in each - * file and include the License file at identityconnectors/legal/license.txt. + * When distributing the Covered Code, include this CDDL Header Notice in each file + * and include the License file at identityconnectors/legal/license.txt. * If applicable, add the following below this CDDL Header, with the fields * enclosed by brackets [] replaced by your own identifying information: * "Portions Copyrighted [year] [name of copyright owner]" - * ----------- + * ==================== */ using System; using System.IO; diff --git a/Framework/Framework.csproj b/Framework/Framework.csproj index 6e80fea5..4cecaa9d 100644 --- a/Framework/Framework.csproj +++ b/Framework/Framework.csproj @@ -1,39 +1,24 @@ - @@ -111,4 +96,4 @@ - \ No newline at end of file + diff --git a/Framework/Spi.cs b/Framework/Spi.cs index 9b5148ed..e76f3c7e 100644 --- a/Framework/Spi.cs +++ b/Framework/Spi.cs @@ -1,41 +1,24 @@ /* - * Copyright 2008 Sun Microsystems, Inc. All rights reserved. - * - * U.S. Government Rights - Commercial software. Government users - * are subject to the Sun Microsystems, Inc. standard license agreement - * and applicable provisions of the FAR and its supplements. - * - * Use is subject to license terms. - * - * This distribution may include materials developed by third parties. - * Sun, Sun Microsystems, the Sun logo, Java and Project Identity - * Connectors are trademarks or registered trademarks of Sun - * Microsystems, Inc. or its subsidiaries in the U.S. and other - * countries. - * - * UNIX is a registered trademark in the U.S. and other countries, - * exclusively licensed through X/Open Company, Ltd. - * - * ----------- + * ==================== * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER. * - * Copyright 2008 Sun Microsystems, Inc. All rights reserved. + * Copyright 2007-2008 Sun Microsystems, Inc. All rights reserved. * - * The contents of this file are subject to the terms of the Common Development - * and Distribution License(CDDL) (the License). You may not use this file - * except in compliance with the License. + * The contents of this file are subject to the terms of the Common Development + * and Distribution License("CDDL") (the "License"). You may not use this file + * except in compliance with the License. * - * You can obtain a copy of the License at - * http://identityconnectors.dev.java.net/CDDLv1.0.html - * See the License for the specific language governing permissions and - * limitations under the License. + * You can obtain a copy of the License at + * http://IdentityConnectors.dev.java.net/legal/license.txt + * See the License for the specific language governing permissions and limitations + * under the License. * - * When distributing the Covered Code, include this CDDL Header Notice in each - * file and include the License file at identityconnectors/legal/license.txt. + * When distributing the Covered Code, include this CDDL Header Notice in each file + * and include the License file at identityconnectors/legal/license.txt. * If applicable, add the following below this CDDL Header, with the fields * enclosed by brackets [] replaced by your own identifying information: * "Portions Copyrighted [year] [name of copyright owner]" - * ----------- + * ==================== */ using System; using System.Globalization; diff --git a/Framework/SpiOperations.cs b/Framework/SpiOperations.cs index 3782eae8..236f1124 100644 --- a/Framework/SpiOperations.cs +++ b/Framework/SpiOperations.cs @@ -1,41 +1,24 @@ /* - * Copyright 2008 Sun Microsystems, Inc. All rights reserved. - * - * U.S. Government Rights - Commercial software. Government users - * are subject to the Sun Microsystems, Inc. standard license agreement - * and applicable provisions of the FAR and its supplements. - * - * Use is subject to license terms. - * - * This distribution may include materials developed by third parties. - * Sun, Sun Microsystems, the Sun logo, Java and Project Identity - * Connectors are trademarks or registered trademarks of Sun - * Microsystems, Inc. or its subsidiaries in the U.S. and other - * countries. - * - * UNIX is a registered trademark in the U.S. and other countries, - * exclusively licensed through X/Open Company, Ltd. - * - * ----------- + * ==================== * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER. * - * Copyright 2008 Sun Microsystems, Inc. All rights reserved. + * Copyright 2007-2008 Sun Microsystems, Inc. All rights reserved. * - * The contents of this file are subject to the terms of the Common Development - * and Distribution License(CDDL) (the License). You may not use this file - * except in compliance with the License. + * The contents of this file are subject to the terms of the Common Development + * and Distribution License("CDDL") (the "License"). You may not use this file + * except in compliance with the License. * - * You can obtain a copy of the License at - * http://identityconnectors.dev.java.net/CDDLv1.0.html - * See the License for the specific language governing permissions and - * limitations under the License. + * You can obtain a copy of the License at + * http://IdentityConnectors.dev.java.net/legal/license.txt + * See the License for the specific language governing permissions and limitations + * under the License. * - * When distributing the Covered Code, include this CDDL Header Notice in each - * file and include the License file at identityconnectors/legal/license.txt. + * When distributing the Covered Code, include this CDDL Header Notice in each file + * and include the License file at identityconnectors/legal/license.txt. * If applicable, add the following below this CDDL Header, with the fields * enclosed by brackets [] replaced by your own identifying information: * "Portions Copyrighted [year] [name of copyright owner]" - * ----------- + * ==================== */ using System; using System.Collections.Generic; diff --git a/Framework/Test.cs b/Framework/Test.cs index 38321ed7..ea50962a 100644 --- a/Framework/Test.cs +++ b/Framework/Test.cs @@ -1,41 +1,24 @@ /* - * Copyright 2008 Sun Microsystems, Inc. All rights reserved. - * - * U.S. Government Rights - Commercial software. Government users - * are subject to the Sun Microsystems, Inc. standard license agreement - * and applicable provisions of the FAR and its supplements. - * - * Use is subject to license terms. - * - * This distribution may include materials developed by third parties. - * Sun, Sun Microsystems, the Sun logo, Java and Project Identity - * Connectors are trademarks or registered trademarks of Sun - * Microsystems, Inc. or its subsidiaries in the U.S. and other - * countries. - * - * UNIX is a registered trademark in the U.S. and other countries, - * exclusively licensed through X/Open Company, Ltd. - * - * ----------- + * ==================== * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER. * - * Copyright 2008 Sun Microsystems, Inc. All rights reserved. + * Copyright 2007-2008 Sun Microsystems, Inc. All rights reserved. * - * The contents of this file are subject to the terms of the Common Development - * and Distribution License(CDDL) (the License). You may not use this file - * except in compliance with the License. + * The contents of this file are subject to the terms of the Common Development + * and Distribution License("CDDL") (the "License"). You may not use this file + * except in compliance with the License. * - * You can obtain a copy of the License at - * http://identityconnectors.dev.java.net/CDDLv1.0.html - * See the License for the specific language governing permissions and - * limitations under the License. + * You can obtain a copy of the License at + * http://IdentityConnectors.dev.java.net/legal/license.txt + * See the License for the specific language governing permissions and limitations + * under the License. * - * When distributing the Covered Code, include this CDDL Header Notice in each - * file and include the License file at identityconnectors/legal/license.txt. + * When distributing the Covered Code, include this CDDL Header Notice in each file + * and include the License file at identityconnectors/legal/license.txt. * If applicable, add the following below this CDDL Header, with the fields * enclosed by brackets [] replaced by your own identifying information: * "Portions Copyrighted [year] [name of copyright owner]" - * ----------- + * ==================== */ using System; diff --git a/FrameworkInternal/Api.cs b/FrameworkInternal/Api.cs index 5bf6ca17..c9a02e16 100644 --- a/FrameworkInternal/Api.cs +++ b/FrameworkInternal/Api.cs @@ -1,41 +1,24 @@ /* - * Copyright 2008 Sun Microsystems, Inc. All rights reserved. - * - * U.S. Government Rights - Commercial software. Government users - * are subject to the Sun Microsystems, Inc. standard license agreement - * and applicable provisions of the FAR and its supplements. - * - * Use is subject to license terms. - * - * This distribution may include materials developed by third parties. - * Sun, Sun Microsystems, the Sun logo, Java and Project Identity - * Connectors are trademarks or registered trademarks of Sun - * Microsystems, Inc. or its subsidiaries in the U.S. and other - * countries. - * - * UNIX is a registered trademark in the U.S. and other countries, - * exclusively licensed through X/Open Company, Ltd. - * - * ----------- + * ==================== * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER. * - * Copyright 2008 Sun Microsystems, Inc. All rights reserved. + * Copyright 2007-2008 Sun Microsystems, Inc. All rights reserved. * - * The contents of this file are subject to the terms of the Common Development - * and Distribution License(CDDL) (the License). You may not use this file - * except in compliance with the License. + * The contents of this file are subject to the terms of the Common Development + * and Distribution License("CDDL") (the "License"). You may not use this file + * except in compliance with the License. * - * You can obtain a copy of the License at - * http://identityconnectors.dev.java.net/CDDLv1.0.html - * See the License for the specific language governing permissions and - * limitations under the License. + * You can obtain a copy of the License at + * http://IdentityConnectors.dev.java.net/legal/license.txt + * See the License for the specific language governing permissions and limitations + * under the License. * - * When distributing the Covered Code, include this CDDL Header Notice in each - * file and include the License file at identityconnectors/legal/license.txt. + * When distributing the Covered Code, include this CDDL Header Notice in each file + * and include the License file at identityconnectors/legal/license.txt. * If applicable, add the following below this CDDL Header, with the fields * enclosed by brackets [] replaced by your own identifying information: * "Portions Copyrighted [year] [name of copyright owner]" - * ----------- + * ==================== */ using System; diff --git a/FrameworkInternal/ApiLocal.cs b/FrameworkInternal/ApiLocal.cs index 38482ed3..bfba6d38 100644 --- a/FrameworkInternal/ApiLocal.cs +++ b/FrameworkInternal/ApiLocal.cs @@ -1,41 +1,24 @@ /* - * Copyright 2008 Sun Microsystems, Inc. All rights reserved. - * - * U.S. Government Rights - Commercial software. Government users - * are subject to the Sun Microsystems, Inc. standard license agreement - * and applicable provisions of the FAR and its supplements. - * - * Use is subject to license terms. - * - * This distribution may include materials developed by third parties. - * Sun, Sun Microsystems, the Sun logo, Java and Project Identity - * Connectors are trademarks or registered trademarks of Sun - * Microsystems, Inc. or its subsidiaries in the U.S. and other - * countries. - * - * UNIX is a registered trademark in the U.S. and other countries, - * exclusively licensed through X/Open Company, Ltd. - * - * ----------- + * ==================== * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER. * - * Copyright 2008 Sun Microsystems, Inc. All rights reserved. + * Copyright 2007-2008 Sun Microsystems, Inc. All rights reserved. * - * The contents of this file are subject to the terms of the Common Development - * and Distribution License(CDDL) (the License). You may not use this file - * except in compliance with the License. + * The contents of this file are subject to the terms of the Common Development + * and Distribution License("CDDL") (the "License"). You may not use this file + * except in compliance with the License. * - * You can obtain a copy of the License at - * http://identityconnectors.dev.java.net/CDDLv1.0.html - * See the License for the specific language governing permissions and - * limitations under the License. + * You can obtain a copy of the License at + * http://IdentityConnectors.dev.java.net/legal/license.txt + * See the License for the specific language governing permissions and limitations + * under the License. * - * When distributing the Covered Code, include this CDDL Header Notice in each - * file and include the License file at identityconnectors/legal/license.txt. + * When distributing the Covered Code, include this CDDL Header Notice in each file + * and include the License file at identityconnectors/legal/license.txt. * If applicable, add the following below this CDDL Header, with the fields * enclosed by brackets [] replaced by your own identifying information: * "Portions Copyrighted [year] [name of copyright owner]" - * ----------- + * ==================== */ using System; using System.Diagnostics; diff --git a/FrameworkInternal/ApiLocalOperations.cs b/FrameworkInternal/ApiLocalOperations.cs index 0d2203b7..6ed4c594 100644 --- a/FrameworkInternal/ApiLocalOperations.cs +++ b/FrameworkInternal/ApiLocalOperations.cs @@ -1,41 +1,24 @@ /* - * Copyright 2008 Sun Microsystems, Inc. All rights reserved. - * - * U.S. Government Rights - Commercial software. Government users - * are subject to the Sun Microsystems, Inc. standard license agreement - * and applicable provisions of the FAR and its supplements. - * - * Use is subject to license terms. - * - * This distribution may include materials developed by third parties. - * Sun, Sun Microsystems, the Sun logo, Java and Project Identity - * Connectors are trademarks or registered trademarks of Sun - * Microsystems, Inc. or its subsidiaries in the U.S. and other - * countries. - * - * UNIX is a registered trademark in the U.S. and other countries, - * exclusively licensed through X/Open Company, Ltd. - * - * ----------- + * ==================== * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER. * - * Copyright 2008 Sun Microsystems, Inc. All rights reserved. + * Copyright 2007-2008 Sun Microsystems, Inc. All rights reserved. * - * The contents of this file are subject to the terms of the Common Development - * and Distribution License(CDDL) (the License). You may not use this file - * except in compliance with the License. + * The contents of this file are subject to the terms of the Common Development + * and Distribution License("CDDL") (the "License"). You may not use this file + * except in compliance with the License. * - * You can obtain a copy of the License at - * http://identityconnectors.dev.java.net/CDDLv1.0.html - * See the License for the specific language governing permissions and - * limitations under the License. + * You can obtain a copy of the License at + * http://IdentityConnectors.dev.java.net/legal/license.txt + * See the License for the specific language governing permissions and limitations + * under the License. * - * When distributing the Covered Code, include this CDDL Header Notice in each - * file and include the License file at identityconnectors/legal/license.txt. + * When distributing the Covered Code, include this CDDL Header Notice in each file + * and include the License file at identityconnectors/legal/license.txt. * If applicable, add the following below this CDDL Header, with the fields * enclosed by brackets [] replaced by your own identifying information: * "Portions Copyrighted [year] [name of copyright owner]" - * ----------- + * ==================== */ using System; diff --git a/FrameworkInternal/ApiRemote.cs b/FrameworkInternal/ApiRemote.cs index 93b51b5f..b9ba8dbe 100644 --- a/FrameworkInternal/ApiRemote.cs +++ b/FrameworkInternal/ApiRemote.cs @@ -1,41 +1,24 @@ /* - * Copyright 2008 Sun Microsystems, Inc. All rights reserved. - * - * U.S. Government Rights - Commercial software. Government users - * are subject to the Sun Microsystems, Inc. standard license agreement - * and applicable provisions of the FAR and its supplements. - * - * Use is subject to license terms. - * - * This distribution may include materials developed by third parties. - * Sun, Sun Microsystems, the Sun logo, Java and Project Identity - * Connectors are trademarks or registered trademarks of Sun - * Microsystems, Inc. or its subsidiaries in the U.S. and other - * countries. - * - * UNIX is a registered trademark in the U.S. and other countries, - * exclusively licensed through X/Open Company, Ltd. - * - * ----------- + * ==================== * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER. * - * Copyright 2008 Sun Microsystems, Inc. All rights reserved. + * Copyright 2007-2008 Sun Microsystems, Inc. All rights reserved. * - * The contents of this file are subject to the terms of the Common Development - * and Distribution License(CDDL) (the License). You may not use this file - * except in compliance with the License. + * The contents of this file are subject to the terms of the Common Development + * and Distribution License("CDDL") (the "License"). You may not use this file + * except in compliance with the License. * - * You can obtain a copy of the License at - * http://identityconnectors.dev.java.net/CDDLv1.0.html - * See the License for the specific language governing permissions and - * limitations under the License. + * You can obtain a copy of the License at + * http://IdentityConnectors.dev.java.net/legal/license.txt + * See the License for the specific language governing permissions and limitations + * under the License. * - * When distributing the Covered Code, include this CDDL Header Notice in each - * file and include the License file at identityconnectors/legal/license.txt. + * When distributing the Covered Code, include this CDDL Header Notice in each file + * and include the License file at identityconnectors/legal/license.txt. * If applicable, add the following below this CDDL Header, with the fields * enclosed by brackets [] replaced by your own identifying information: * "Portions Copyrighted [year] [name of copyright owner]" - * ----------- + * ==================== */ using System; using System.Collections.Generic; diff --git a/FrameworkInternal/ApiRemoteMessages.cs b/FrameworkInternal/ApiRemoteMessages.cs index 0ecef654..62c58ab0 100644 --- a/FrameworkInternal/ApiRemoteMessages.cs +++ b/FrameworkInternal/ApiRemoteMessages.cs @@ -1,41 +1,24 @@ /* - * Copyright 2008 Sun Microsystems, Inc. All rights reserved. - * - * U.S. Government Rights - Commercial software. Government users - * are subject to the Sun Microsystems, Inc. standard license agreement - * and applicable provisions of the FAR and its supplements. - * - * Use is subject to license terms. - * - * This distribution may include materials developed by third parties. - * Sun, Sun Microsystems, the Sun logo, Java and Project Identity - * Connectors are trademarks or registered trademarks of Sun - * Microsystems, Inc. or its subsidiaries in the U.S. and other - * countries. - * - * UNIX is a registered trademark in the U.S. and other countries, - * exclusively licensed through X/Open Company, Ltd. - * - * ----------- + * ==================== * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER. * - * Copyright 2008 Sun Microsystems, Inc. All rights reserved. + * Copyright 2007-2008 Sun Microsystems, Inc. All rights reserved. * - * The contents of this file are subject to the terms of the Common Development - * and Distribution License(CDDL) (the License). You may not use this file - * except in compliance with the License. + * The contents of this file are subject to the terms of the Common Development + * and Distribution License("CDDL") (the "License"). You may not use this file + * except in compliance with the License. * - * You can obtain a copy of the License at - * http://identityconnectors.dev.java.net/CDDLv1.0.html - * See the License for the specific language governing permissions and - * limitations under the License. + * You can obtain a copy of the License at + * http://IdentityConnectors.dev.java.net/legal/license.txt + * See the License for the specific language governing permissions and limitations + * under the License. * - * When distributing the Covered Code, include this CDDL Header Notice in each - * file and include the License file at identityconnectors/legal/license.txt. + * When distributing the Covered Code, include this CDDL Header Notice in each file + * and include the License file at identityconnectors/legal/license.txt. * If applicable, add the following below this CDDL Header, with the fields * enclosed by brackets [] replaced by your own identifying information: * "Portions Copyrighted [year] [name of copyright owner]" - * ----------- + * ==================== */ using System; using System.Collections.Generic; diff --git a/FrameworkInternal/AssemblyInfo.cs b/FrameworkInternal/AssemblyInfo.cs index b304c494..5c1c75d7 100644 --- a/FrameworkInternal/AssemblyInfo.cs +++ b/FrameworkInternal/AssemblyInfo.cs @@ -1,41 +1,24 @@ /* - * Copyright 2008 Sun Microsystems, Inc. All rights reserved. - * - * U.S. Government Rights - Commercial software. Government users - * are subject to the Sun Microsystems, Inc. standard license agreement - * and applicable provisions of the FAR and its supplements. - * - * Use is subject to license terms. - * - * This distribution may include materials developed by third parties. - * Sun, Sun Microsystems, the Sun logo, Java and Project Identity - * Connectors are trademarks or registered trademarks of Sun - * Microsystems, Inc. or its subsidiaries in the U.S. and other - * countries. - * - * UNIX is a registered trademark in the U.S. and other countries, - * exclusively licensed through X/Open Company, Ltd. - * - * ----------- + * ==================== * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER. * - * Copyright 2008 Sun Microsystems, Inc. All rights reserved. + * Copyright 2007-2008 Sun Microsystems, Inc. All rights reserved. * - * The contents of this file are subject to the terms of the Common Development - * and Distribution License(CDDL) (the License). You may not use this file - * except in compliance with the License. + * The contents of this file are subject to the terms of the Common Development + * and Distribution License("CDDL") (the "License"). You may not use this file + * except in compliance with the License. * - * You can obtain a copy of the License at - * http://identityconnectors.dev.java.net/CDDLv1.0.html - * See the License for the specific language governing permissions and - * limitations under the License. + * You can obtain a copy of the License at + * http://IdentityConnectors.dev.java.net/legal/license.txt + * See the License for the specific language governing permissions and limitations + * under the License. * - * When distributing the Covered Code, include this CDDL Header Notice in each - * file and include the License file at identityconnectors/legal/license.txt. + * When distributing the Covered Code, include this CDDL Header Notice in each file + * and include the License file at identityconnectors/legal/license.txt. * If applicable, add the following below this CDDL Header, with the fields * enclosed by brackets [] replaced by your own identifying information: * "Portions Copyrighted [year] [name of copyright owner]" - * ----------- + * ==================== */ #region Using directives diff --git a/FrameworkInternal/FrameworkInternal.csproj b/FrameworkInternal/FrameworkInternal.csproj index 2fdb219e..34ca21fd 100644 --- a/FrameworkInternal/FrameworkInternal.csproj +++ b/FrameworkInternal/FrameworkInternal.csproj @@ -1,39 +1,24 @@ - @@ -115,4 +100,4 @@ Framework - \ No newline at end of file + diff --git a/FrameworkInternal/Security.cs b/FrameworkInternal/Security.cs index 25b92852..9520c701 100644 --- a/FrameworkInternal/Security.cs +++ b/FrameworkInternal/Security.cs @@ -1,41 +1,24 @@ /* - * Copyright 2008 Sun Microsystems, Inc. All rights reserved. - * - * U.S. Government Rights - Commercial software. Government users - * are subject to the Sun Microsystems, Inc. standard license agreement - * and applicable provisions of the FAR and its supplements. - * - * Use is subject to license terms. - * - * This distribution may include materials developed by third parties. - * Sun, Sun Microsystems, the Sun logo, Java and Project Identity - * Connectors are trademarks or registered trademarks of Sun - * Microsystems, Inc. or its subsidiaries in the U.S. and other - * countries. - * - * UNIX is a registered trademark in the U.S. and other countries, - * exclusively licensed through X/Open Company, Ltd. - * - * ----------- + * ==================== * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER. * - * Copyright 2008 Sun Microsystems, Inc. All rights reserved. + * Copyright 2007-2008 Sun Microsystems, Inc. All rights reserved. * - * The contents of this file are subject to the terms of the Common Development - * and Distribution License(CDDL) (the License). You may not use this file - * except in compliance with the License. + * The contents of this file are subject to the terms of the Common Development + * and Distribution License("CDDL") (the "License"). You may not use this file + * except in compliance with the License. * - * You can obtain a copy of the License at - * http://identityconnectors.dev.java.net/CDDLv1.0.html - * See the License for the specific language governing permissions and - * limitations under the License. + * You can obtain a copy of the License at + * http://IdentityConnectors.dev.java.net/legal/license.txt + * See the License for the specific language governing permissions and limitations + * under the License. * - * When distributing the Covered Code, include this CDDL Header Notice in each - * file and include the License file at identityconnectors/legal/license.txt. + * When distributing the Covered Code, include this CDDL Header Notice in each file + * and include the License file at identityconnectors/legal/license.txt. * If applicable, add the following below this CDDL Header, with the fields * enclosed by brackets [] replaced by your own identifying information: * "Portions Copyrighted [year] [name of copyright owner]" - * ----------- + * ==================== */ using System; diff --git a/FrameworkInternal/Serializer.cs b/FrameworkInternal/Serializer.cs index a30a6156..095902c3 100644 --- a/FrameworkInternal/Serializer.cs +++ b/FrameworkInternal/Serializer.cs @@ -1,41 +1,24 @@ /* - * Copyright 2008 Sun Microsystems, Inc. All rights reserved. - * - * U.S. Government Rights - Commercial software. Government users - * are subject to the Sun Microsystems, Inc. standard license agreement - * and applicable provisions of the FAR and its supplements. - * - * Use is subject to license terms. - * - * This distribution may include materials developed by third parties. - * Sun, Sun Microsystems, the Sun logo, Java and Project Identity - * Connectors are trademarks or registered trademarks of Sun - * Microsystems, Inc. or its subsidiaries in the U.S. and other - * countries. - * - * UNIX is a registered trademark in the U.S. and other countries, - * exclusively licensed through X/Open Company, Ltd. - * - * ----------- + * ==================== * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER. * - * Copyright 2008 Sun Microsystems, Inc. All rights reserved. + * Copyright 2007-2008 Sun Microsystems, Inc. All rights reserved. * - * The contents of this file are subject to the terms of the Common Development - * and Distribution License(CDDL) (the License). You may not use this file - * except in compliance with the License. + * The contents of this file are subject to the terms of the Common Development + * and Distribution License("CDDL") (the "License"). You may not use this file + * except in compliance with the License. * - * You can obtain a copy of the License at - * http://identityconnectors.dev.java.net/CDDLv1.0.html - * See the License for the specific language governing permissions and - * limitations under the License. + * You can obtain a copy of the License at + * http://IdentityConnectors.dev.java.net/legal/license.txt + * See the License for the specific language governing permissions and limitations + * under the License. * - * When distributing the Covered Code, include this CDDL Header Notice in each - * file and include the License file at identityconnectors/legal/license.txt. + * When distributing the Covered Code, include this CDDL Header Notice in each file + * and include the License file at identityconnectors/legal/license.txt. * If applicable, add the following below this CDDL Header, with the fields * enclosed by brackets [] replaced by your own identifying information: * "Portions Copyrighted [year] [name of copyright owner]" - * ----------- + * ==================== */ using System; using System.IO; diff --git a/FrameworkInternal/SerializerBinary.cs b/FrameworkInternal/SerializerBinary.cs index 9b9b52f5..c98692ac 100644 --- a/FrameworkInternal/SerializerBinary.cs +++ b/FrameworkInternal/SerializerBinary.cs @@ -1,41 +1,24 @@ /* - * Copyright 2008 Sun Microsystems, Inc. All rights reserved. - * - * U.S. Government Rights - Commercial software. Government users - * are subject to the Sun Microsystems, Inc. standard license agreement - * and applicable provisions of the FAR and its supplements. - * - * Use is subject to license terms. - * - * This distribution may include materials developed by third parties. - * Sun, Sun Microsystems, the Sun logo, Java and Project Identity - * Connectors are trademarks or registered trademarks of Sun - * Microsystems, Inc. or its subsidiaries in the U.S. and other - * countries. - * - * UNIX is a registered trademark in the U.S. and other countries, - * exclusively licensed through X/Open Company, Ltd. - * - * ----------- + * ==================== * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER. * - * Copyright 2008 Sun Microsystems, Inc. All rights reserved. + * Copyright 2007-2008 Sun Microsystems, Inc. All rights reserved. * - * The contents of this file are subject to the terms of the Common Development - * and Distribution License(CDDL) (the License). You may not use this file - * except in compliance with the License. + * The contents of this file are subject to the terms of the Common Development + * and Distribution License("CDDL") (the "License"). You may not use this file + * except in compliance with the License. * - * You can obtain a copy of the License at - * http://identityconnectors.dev.java.net/CDDLv1.0.html - * See the License for the specific language governing permissions and - * limitations under the License. + * You can obtain a copy of the License at + * http://IdentityConnectors.dev.java.net/legal/license.txt + * See the License for the specific language governing permissions and limitations + * under the License. * - * When distributing the Covered Code, include this CDDL Header Notice in each - * file and include the License file at identityconnectors/legal/license.txt. + * When distributing the Covered Code, include this CDDL Header Notice in each file + * and include the License file at identityconnectors/legal/license.txt. * If applicable, add the following below this CDDL Header, with the fields * enclosed by brackets [] replaced by your own identifying information: * "Portions Copyrighted [year] [name of copyright owner]" - * ----------- + * ==================== */ using System; using System.Collections.Generic; diff --git a/FrameworkInternal/SerializerXml.cs b/FrameworkInternal/SerializerXml.cs index e780625c..44556848 100644 --- a/FrameworkInternal/SerializerXml.cs +++ b/FrameworkInternal/SerializerXml.cs @@ -1,41 +1,24 @@ /* - * Copyright 2008 Sun Microsystems, Inc. All rights reserved. - * - * U.S. Government Rights - Commercial software. Government users - * are subject to the Sun Microsystems, Inc. standard license agreement - * and applicable provisions of the FAR and its supplements. - * - * Use is subject to license terms. - * - * This distribution may include materials developed by third parties. - * Sun, Sun Microsystems, the Sun logo, Java and Project Identity - * Connectors are trademarks or registered trademarks of Sun - * Microsystems, Inc. or its subsidiaries in the U.S. and other - * countries. - * - * UNIX is a registered trademark in the U.S. and other countries, - * exclusively licensed through X/Open Company, Ltd. - * - * ----------- + * ==================== * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER. * - * Copyright 2008 Sun Microsystems, Inc. All rights reserved. + * Copyright 2007-2008 Sun Microsystems, Inc. All rights reserved. * - * The contents of this file are subject to the terms of the Common Development - * and Distribution License(CDDL) (the License). You may not use this file - * except in compliance with the License. + * The contents of this file are subject to the terms of the Common Development + * and Distribution License("CDDL") (the "License"). You may not use this file + * except in compliance with the License. * - * You can obtain a copy of the License at - * http://identityconnectors.dev.java.net/CDDLv1.0.html - * See the License for the specific language governing permissions and - * limitations under the License. + * You can obtain a copy of the License at + * http://IdentityConnectors.dev.java.net/legal/license.txt + * See the License for the specific language governing permissions and limitations + * under the License. * - * When distributing the Covered Code, include this CDDL Header Notice in each - * file and include the License file at identityconnectors/legal/license.txt. + * When distributing the Covered Code, include this CDDL Header Notice in each file + * and include the License file at identityconnectors/legal/license.txt. * If applicable, add the following below this CDDL Header, with the fields * enclosed by brackets [] replaced by your own identifying information: * "Portions Copyrighted [year] [name of copyright owner]" - * ----------- + * ==================== */ using System; using System.Collections.Generic; diff --git a/FrameworkInternal/Server.cs b/FrameworkInternal/Server.cs index 39c0d4cf..70f3a7a3 100644 --- a/FrameworkInternal/Server.cs +++ b/FrameworkInternal/Server.cs @@ -1,41 +1,24 @@ /* - * Copyright 2008 Sun Microsystems, Inc. All rights reserved. - * - * U.S. Government Rights - Commercial software. Government users - * are subject to the Sun Microsystems, Inc. standard license agreement - * and applicable provisions of the FAR and its supplements. - * - * Use is subject to license terms. - * - * This distribution may include materials developed by third parties. - * Sun, Sun Microsystems, the Sun logo, Java and Project Identity - * Connectors are trademarks or registered trademarks of Sun - * Microsystems, Inc. or its subsidiaries in the U.S. and other - * countries. - * - * UNIX is a registered trademark in the U.S. and other countries, - * exclusively licensed through X/Open Company, Ltd. - * - * ----------- + * ==================== * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER. * - * Copyright 2008 Sun Microsystems, Inc. All rights reserved. + * Copyright 2007-2008 Sun Microsystems, Inc. All rights reserved. * - * The contents of this file are subject to the terms of the Common Development - * and Distribution License(CDDL) (the License). You may not use this file - * except in compliance with the License. + * The contents of this file are subject to the terms of the Common Development + * and Distribution License("CDDL") (the "License"). You may not use this file + * except in compliance with the License. * - * You can obtain a copy of the License at - * http://identityconnectors.dev.java.net/CDDLv1.0.html - * See the License for the specific language governing permissions and - * limitations under the License. + * You can obtain a copy of the License at + * http://IdentityConnectors.dev.java.net/legal/license.txt + * See the License for the specific language governing permissions and limitations + * under the License. * - * When distributing the Covered Code, include this CDDL Header Notice in each - * file and include the License file at identityconnectors/legal/license.txt. + * When distributing the Covered Code, include this CDDL Header Notice in each file + * and include the License file at identityconnectors/legal/license.txt. * If applicable, add the following below this CDDL Header, with the fields * enclosed by brackets [] replaced by your own identifying information: * "Portions Copyrighted [year] [name of copyright owner]" - * ----------- + * ==================== */ using System; using System.Collections.Generic; diff --git a/FrameworkInternal/Test.cs b/FrameworkInternal/Test.cs index 8ac3be7b..553155d3 100644 --- a/FrameworkInternal/Test.cs +++ b/FrameworkInternal/Test.cs @@ -1,41 +1,24 @@ /* - * Copyright 2008 Sun Microsystems, Inc. All rights reserved. - * - * U.S. Government Rights - Commercial software. Government users - * are subject to the Sun Microsystems, Inc. standard license agreement - * and applicable provisions of the FAR and its supplements. - * - * Use is subject to license terms. - * - * This distribution may include materials developed by third parties. - * Sun, Sun Microsystems, the Sun logo, Java and Project Identity - * Connectors are trademarks or registered trademarks of Sun - * Microsystems, Inc. or its subsidiaries in the U.S. and other - * countries. - * - * UNIX is a registered trademark in the U.S. and other countries, - * exclusively licensed through X/Open Company, Ltd. - * - * ----------- + * ==================== * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER. * - * Copyright 2008 Sun Microsystems, Inc. All rights reserved. + * Copyright 2007-2008 Sun Microsystems, Inc. All rights reserved. * - * The contents of this file are subject to the terms of the Common Development - * and Distribution License(CDDL) (the License). You may not use this file - * except in compliance with the License. + * The contents of this file are subject to the terms of the Common Development + * and Distribution License("CDDL") (the "License"). You may not use this file + * except in compliance with the License. * - * You can obtain a copy of the License at - * http://identityconnectors.dev.java.net/CDDLv1.0.html - * See the License for the specific language governing permissions and - * limitations under the License. + * You can obtain a copy of the License at + * http://IdentityConnectors.dev.java.net/legal/license.txt + * See the License for the specific language governing permissions and limitations + * under the License. * - * When distributing the Covered Code, include this CDDL Header Notice in each - * file and include the License file at identityconnectors/legal/license.txt. + * When distributing the Covered Code, include this CDDL Header Notice in each file + * and include the License file at identityconnectors/legal/license.txt. * If applicable, add the following below this CDDL Header, with the fields * enclosed by brackets [] replaced by your own identifying information: * "Portions Copyrighted [year] [name of copyright owner]" - * ----------- + * ==================== */ using System; using System.Text; diff --git a/FrameworkTests/AssemblyInfo.cs b/FrameworkTests/AssemblyInfo.cs index 3143253c..61f6c635 100644 --- a/FrameworkTests/AssemblyInfo.cs +++ b/FrameworkTests/AssemblyInfo.cs @@ -1,41 +1,24 @@ /* - * Copyright 2008 Sun Microsystems, Inc. All rights reserved. - * - * U.S. Government Rights - Commercial software. Government users - * are subject to the Sun Microsystems, Inc. standard license agreement - * and applicable provisions of the FAR and its supplements. - * - * Use is subject to license terms. - * - * This distribution may include materials developed by third parties. - * Sun, Sun Microsystems, the Sun logo, Java and Project Identity - * Connectors are trademarks or registered trademarks of Sun - * Microsystems, Inc. or its subsidiaries in the U.S. and other - * countries. - * - * UNIX is a registered trademark in the U.S. and other countries, - * exclusively licensed through X/Open Company, Ltd. - * - * ----------- + * ==================== * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER. * - * Copyright 2008 Sun Microsystems, Inc. All rights reserved. + * Copyright 2007-2008 Sun Microsystems, Inc. All rights reserved. * - * The contents of this file are subject to the terms of the Common Development - * and Distribution License(CDDL) (the License). You may not use this file - * except in compliance with the License. + * The contents of this file are subject to the terms of the Common Development + * and Distribution License("CDDL") (the "License"). You may not use this file + * except in compliance with the License. * - * You can obtain a copy of the License at - * http://identityconnectors.dev.java.net/CDDLv1.0.html - * See the License for the specific language governing permissions and - * limitations under the License. + * You can obtain a copy of the License at + * http://IdentityConnectors.dev.java.net/legal/license.txt + * See the License for the specific language governing permissions and limitations + * under the License. * - * When distributing the Covered Code, include this CDDL Header Notice in each - * file and include the License file at identityconnectors/legal/license.txt. + * When distributing the Covered Code, include this CDDL Header Notice in each file + * and include the License file at identityconnectors/legal/license.txt. * If applicable, add the following below this CDDL Header, with the fields * enclosed by brackets [] replaced by your own identifying information: * "Portions Copyrighted [year] [name of copyright owner]" - * ----------- + * ==================== */ #region Using directives diff --git a/FrameworkTests/CollectionUtilTests.cs b/FrameworkTests/CollectionUtilTests.cs index bac30fe1..289a91ca 100644 --- a/FrameworkTests/CollectionUtilTests.cs +++ b/FrameworkTests/CollectionUtilTests.cs @@ -1,41 +1,24 @@ /* - * Copyright 2008 Sun Microsystems, Inc. All rights reserved. - * - * U.S. Government Rights - Commercial software. Government users - * are subject to the Sun Microsystems, Inc. standard license agreement - * and applicable provisions of the FAR and its supplements. - * - * Use is subject to license terms. - * - * This distribution may include materials developed by third parties. - * Sun, Sun Microsystems, the Sun logo, Java and Project Identity - * Connectors are trademarks or registered trademarks of Sun - * Microsystems, Inc. or its subsidiaries in the U.S. and other - * countries. - * - * UNIX is a registered trademark in the U.S. and other countries, - * exclusively licensed through X/Open Company, Ltd. - * - * ----------- + * ==================== * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER. * - * Copyright 2008 Sun Microsystems, Inc. All rights reserved. + * Copyright 2007-2008 Sun Microsystems, Inc. All rights reserved. * - * The contents of this file are subject to the terms of the Common Development - * and Distribution License(CDDL) (the License). You may not use this file - * except in compliance with the License. + * The contents of this file are subject to the terms of the Common Development + * and Distribution License("CDDL") (the "License"). You may not use this file + * except in compliance with the License. * - * You can obtain a copy of the License at - * http://identityconnectors.dev.java.net/CDDLv1.0.html - * See the License for the specific language governing permissions and - * limitations under the License. + * You can obtain a copy of the License at + * http://IdentityConnectors.dev.java.net/legal/license.txt + * See the License for the specific language governing permissions and limitations + * under the License. * - * When distributing the Covered Code, include this CDDL Header Notice in each - * file and include the License file at identityconnectors/legal/license.txt. + * When distributing the Covered Code, include this CDDL Header Notice in each file + * and include the License file at identityconnectors/legal/license.txt. * If applicable, add the following below this CDDL Header, with the fields * enclosed by brackets [] replaced by your own identifying information: * "Portions Copyrighted [year] [name of copyright owner]" - * ----------- + * ==================== */ using System; using System.Collections.Generic; diff --git a/FrameworkTests/ConnectorInfoManagerTests.cs b/FrameworkTests/ConnectorInfoManagerTests.cs index db4eaf2e..16b8f43f 100644 --- a/FrameworkTests/ConnectorInfoManagerTests.cs +++ b/FrameworkTests/ConnectorInfoManagerTests.cs @@ -1,41 +1,24 @@ /* - * Copyright 2008 Sun Microsystems, Inc. All rights reserved. - * - * U.S. Government Rights - Commercial software. Government users - * are subject to the Sun Microsystems, Inc. standard license agreement - * and applicable provisions of the FAR and its supplements. - * - * Use is subject to license terms. - * - * This distribution may include materials developed by third parties. - * Sun, Sun Microsystems, the Sun logo, Java and Project Identity - * Connectors are trademarks or registered trademarks of Sun - * Microsystems, Inc. or its subsidiaries in the U.S. and other - * countries. - * - * UNIX is a registered trademark in the U.S. and other countries, - * exclusively licensed through X/Open Company, Ltd. - * - * ----------- + * ==================== * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER. * - * Copyright 2008 Sun Microsystems, Inc. All rights reserved. + * Copyright 2007-2008 Sun Microsystems, Inc. All rights reserved. * - * The contents of this file are subject to the terms of the Common Development - * and Distribution License(CDDL) (the License). You may not use this file - * except in compliance with the License. + * The contents of this file are subject to the terms of the Common Development + * and Distribution License("CDDL") (the "License"). You may not use this file + * except in compliance with the License. * - * You can obtain a copy of the License at - * http://identityconnectors.dev.java.net/CDDLv1.0.html - * See the License for the specific language governing permissions and - * limitations under the License. + * You can obtain a copy of the License at + * http://IdentityConnectors.dev.java.net/legal/license.txt + * See the License for the specific language governing permissions and limitations + * under the License. * - * When distributing the Covered Code, include this CDDL Header Notice in each - * file and include the License file at identityconnectors/legal/license.txt. + * When distributing the Covered Code, include this CDDL Header Notice in each file + * and include the License file at identityconnectors/legal/license.txt. * If applicable, add the following below this CDDL Header, with the fields * enclosed by brackets [] replaced by your own identifying information: * "Portions Copyrighted [year] [name of copyright owner]" - * ----------- + * ==================== */ using System; using NUnit.Framework; diff --git a/FrameworkTests/FilterTranslatorTests.cs b/FrameworkTests/FilterTranslatorTests.cs index f6b5453f..f545c6b2 100644 --- a/FrameworkTests/FilterTranslatorTests.cs +++ b/FrameworkTests/FilterTranslatorTests.cs @@ -1,41 +1,24 @@ /* - * Copyright 2008 Sun Microsystems, Inc. All rights reserved. - * - * U.S. Government Rights - Commercial software. Government users - * are subject to the Sun Microsystems, Inc. standard license agreement - * and applicable provisions of the FAR and its supplements. - * - * Use is subject to license terms. - * - * This distribution may include materials developed by third parties. - * Sun, Sun Microsystems, the Sun logo, Java and Project Identity - * Connectors are trademarks or registered trademarks of Sun - * Microsystems, Inc. or its subsidiaries in the U.S. and other - * countries. - * - * UNIX is a registered trademark in the U.S. and other countries, - * exclusively licensed through X/Open Company, Ltd. - * - * ----------- + * ==================== * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER. * - * Copyright 2008 Sun Microsystems, Inc. All rights reserved. + * Copyright 2007-2008 Sun Microsystems, Inc. All rights reserved. * - * The contents of this file are subject to the terms of the Common Development - * and Distribution License(CDDL) (the License). You may not use this file - * except in compliance with the License. + * The contents of this file are subject to the terms of the Common Development + * and Distribution License("CDDL") (the "License"). You may not use this file + * except in compliance with the License. * - * You can obtain a copy of the License at - * http://identityconnectors.dev.java.net/CDDLv1.0.html - * See the License for the specific language governing permissions and - * limitations under the License. + * You can obtain a copy of the License at + * http://IdentityConnectors.dev.java.net/legal/license.txt + * See the License for the specific language governing permissions and limitations + * under the License. * - * When distributing the Covered Code, include this CDDL Header Notice in each - * file and include the License file at identityconnectors/legal/license.txt. + * When distributing the Covered Code, include this CDDL Header Notice in each file + * and include the License file at identityconnectors/legal/license.txt. * If applicable, add the following below this CDDL Header, with the fields * enclosed by brackets [] replaced by your own identifying information: * "Portions Copyrighted [year] [name of copyright owner]" - * ----------- + * ==================== */ using System; using System.Collections.Generic; diff --git a/FrameworkTests/FrameworkTests.csproj b/FrameworkTests/FrameworkTests.csproj index ef56ea0f..8137402f 100644 --- a/FrameworkTests/FrameworkTests.csproj +++ b/FrameworkTests/FrameworkTests.csproj @@ -1,39 +1,24 @@ - @@ -165,4 +150,4 @@ - \ No newline at end of file + diff --git a/FrameworkTests/GuardedStringTests.cs b/FrameworkTests/GuardedStringTests.cs index 2310bf7b..789cfc60 100644 --- a/FrameworkTests/GuardedStringTests.cs +++ b/FrameworkTests/GuardedStringTests.cs @@ -1,41 +1,24 @@ /* - * Copyright 2008 Sun Microsystems, Inc. All rights reserved. - * - * U.S. Government Rights - Commercial software. Government users - * are subject to the Sun Microsystems, Inc. standard license agreement - * and applicable provisions of the FAR and its supplements. - * - * Use is subject to license terms. - * - * This distribution may include materials developed by third parties. - * Sun, Sun Microsystems, the Sun logo, Java and Project Identity - * Connectors are trademarks or registered trademarks of Sun - * Microsystems, Inc. or its subsidiaries in the U.S. and other - * countries. - * - * UNIX is a registered trademark in the U.S. and other countries, - * exclusively licensed through X/Open Company, Ltd. - * - * ----------- + * ==================== * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER. * - * Copyright 2008 Sun Microsystems, Inc. All rights reserved. + * Copyright 2007-2008 Sun Microsystems, Inc. All rights reserved. * - * The contents of this file are subject to the terms of the Common Development - * and Distribution License(CDDL) (the License). You may not use this file - * except in compliance with the License. + * The contents of this file are subject to the terms of the Common Development + * and Distribution License("CDDL") (the "License"). You may not use this file + * except in compliance with the License. * - * You can obtain a copy of the License at - * http://identityconnectors.dev.java.net/CDDLv1.0.html - * See the License for the specific language governing permissions and - * limitations under the License. + * You can obtain a copy of the License at + * http://IdentityConnectors.dev.java.net/legal/license.txt + * See the License for the specific language governing permissions and limitations + * under the License. * - * When distributing the Covered Code, include this CDDL Header Notice in each - * file and include the License file at identityconnectors/legal/license.txt. + * When distributing the Covered Code, include this CDDL Header Notice in each file + * and include the License file at identityconnectors/legal/license.txt. * If applicable, add the following below this CDDL Header, with the fields * enclosed by brackets [] replaced by your own identifying information: * "Portions Copyrighted [year] [name of copyright owner]" - * ----------- + * ==================== */ using System; using System.Security; diff --git a/FrameworkTests/LocaleTests.cs b/FrameworkTests/LocaleTests.cs index 9d8c1a38..0176a1af 100644 --- a/FrameworkTests/LocaleTests.cs +++ b/FrameworkTests/LocaleTests.cs @@ -1,41 +1,24 @@ /* - * Copyright 2008 Sun Microsystems, Inc. All rights reserved. - * - * U.S. Government Rights - Commercial software. Government users - * are subject to the Sun Microsystems, Inc. standard license agreement - * and applicable provisions of the FAR and its supplements. - * - * Use is subject to license terms. - * - * This distribution may include materials developed by third parties. - * Sun, Sun Microsystems, the Sun logo, Java and Project Identity - * Connectors are trademarks or registered trademarks of Sun - * Microsystems, Inc. or its subsidiaries in the U.S. and other - * countries. - * - * UNIX is a registered trademark in the U.S. and other countries, - * exclusively licensed through X/Open Company, Ltd. - * - * ----------- + * ==================== * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER. * - * Copyright 2008 Sun Microsystems, Inc. All rights reserved. + * Copyright 2007-2008 Sun Microsystems, Inc. All rights reserved. * - * The contents of this file are subject to the terms of the Common Development - * and Distribution License(CDDL) (the License). You may not use this file - * except in compliance with the License. + * The contents of this file are subject to the terms of the Common Development + * and Distribution License("CDDL") (the "License"). You may not use this file + * except in compliance with the License. * - * You can obtain a copy of the License at - * http://identityconnectors.dev.java.net/CDDLv1.0.html - * See the License for the specific language governing permissions and - * limitations under the License. + * You can obtain a copy of the License at + * http://IdentityConnectors.dev.java.net/legal/license.txt + * See the License for the specific language governing permissions and limitations + * under the License. * - * When distributing the Covered Code, include this CDDL Header Notice in each - * file and include the License file at identityconnectors/legal/license.txt. + * When distributing the Covered Code, include this CDDL Header Notice in each file + * and include the License file at identityconnectors/legal/license.txt. * If applicable, add the following below this CDDL Header, with the fields * enclosed by brackets [] replaced by your own identifying information: * "Portions Copyrighted [year] [name of copyright owner]" - * ----------- + * ==================== */ using System; using NUnit.Framework; diff --git a/FrameworkTests/ObjectNormalizerFacadeTests.cs b/FrameworkTests/ObjectNormalizerFacadeTests.cs index 2d98b2b1..475f770e 100644 --- a/FrameworkTests/ObjectNormalizerFacadeTests.cs +++ b/FrameworkTests/ObjectNormalizerFacadeTests.cs @@ -1,41 +1,24 @@ /* - * Copyright 2008 Sun Microsystems, Inc. All rights reserved. - * - * U.S. Government Rights - Commercial software. Government users - * are subject to the Sun Microsystems, Inc. standard license agreement - * and applicable provisions of the FAR and its supplements. - * - * Use is subject to license terms. - * - * This distribution may include materials developed by third parties. - * Sun, Sun Microsystems, the Sun logo, Java and Project Identity - * Connectors are trademarks or registered trademarks of Sun - * Microsystems, Inc. or its subsidiaries in the U.S. and other - * countries. - * - * UNIX is a registered trademark in the U.S. and other countries, - * exclusively licensed through X/Open Company, Ltd. - * - * ----------- + * ==================== * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER. * - * Copyright 2008 Sun Microsystems, Inc. All rights reserved. + * Copyright 2007-2008 Sun Microsystems, Inc. All rights reserved. * - * The contents of this file are subject to the terms of the Common Development - * and Distribution License(CDDL) (the License). You may not use this file - * except in compliance with the License. + * The contents of this file are subject to the terms of the Common Development + * and Distribution License("CDDL") (the "License"). You may not use this file + * except in compliance with the License. * - * You can obtain a copy of the License at - * http://identityconnectors.dev.java.net/CDDLv1.0.html - * See the License for the specific language governing permissions and - * limitations under the License. + * You can obtain a copy of the License at + * http://IdentityConnectors.dev.java.net/legal/license.txt + * See the License for the specific language governing permissions and limitations + * under the License. * - * When distributing the Covered Code, include this CDDL Header Notice in each - * file and include the License file at identityconnectors/legal/license.txt. + * When distributing the Covered Code, include this CDDL Header Notice in each file + * and include the License file at identityconnectors/legal/license.txt. * If applicable, add the following below this CDDL Header, with the fields * enclosed by brackets [] replaced by your own identifying information: * "Portions Copyrighted [year] [name of copyright owner]" - * ----------- + * ==================== */ using System; using NUnit.Framework; diff --git a/FrameworkTests/ObjectPoolTests.cs b/FrameworkTests/ObjectPoolTests.cs index 3251267d..3c09f8ec 100644 --- a/FrameworkTests/ObjectPoolTests.cs +++ b/FrameworkTests/ObjectPoolTests.cs @@ -1,41 +1,24 @@ /* - * Copyright 2008 Sun Microsystems, Inc. All rights reserved. - * - * U.S. Government Rights - Commercial software. Government users - * are subject to the Sun Microsystems, Inc. standard license agreement - * and applicable provisions of the FAR and its supplements. - * - * Use is subject to license terms. - * - * This distribution may include materials developed by third parties. - * Sun, Sun Microsystems, the Sun logo, Java and Project Identity - * Connectors are trademarks or registered trademarks of Sun - * Microsystems, Inc. or its subsidiaries in the U.S. and other - * countries. - * - * UNIX is a registered trademark in the U.S. and other countries, - * exclusively licensed through X/Open Company, Ltd. - * - * ----------- + * ==================== * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER. * - * Copyright 2008 Sun Microsystems, Inc. All rights reserved. + * Copyright 2007-2008 Sun Microsystems, Inc. All rights reserved. * - * The contents of this file are subject to the terms of the Common Development - * and Distribution License(CDDL) (the License). You may not use this file - * except in compliance with the License. + * The contents of this file are subject to the terms of the Common Development + * and Distribution License("CDDL") (the "License"). You may not use this file + * except in compliance with the License. * - * You can obtain a copy of the License at - * http://identityconnectors.dev.java.net/CDDLv1.0.html - * See the License for the specific language governing permissions and - * limitations under the License. + * You can obtain a copy of the License at + * http://IdentityConnectors.dev.java.net/legal/license.txt + * See the License for the specific language governing permissions and limitations + * under the License. * - * When distributing the Covered Code, include this CDDL Header Notice in each - * file and include the License file at identityconnectors/legal/license.txt. + * When distributing the Covered Code, include this CDDL Header Notice in each file + * and include the License file at identityconnectors/legal/license.txt. * If applicable, add the following below this CDDL Header, with the fields * enclosed by brackets [] replaced by your own identifying information: * "Portions Copyrighted [year] [name of copyright owner]" - * ----------- + * ==================== */ using System; using System.Threading; diff --git a/FrameworkTests/ObjectSerializationTests.cs b/FrameworkTests/ObjectSerializationTests.cs index f515a3ab..f0a91182 100644 --- a/FrameworkTests/ObjectSerializationTests.cs +++ b/FrameworkTests/ObjectSerializationTests.cs @@ -1,41 +1,24 @@ /* - * Copyright 2008 Sun Microsystems, Inc. All rights reserved. - * - * U.S. Government Rights - Commercial software. Government users - * are subject to the Sun Microsystems, Inc. standard license agreement - * and applicable provisions of the FAR and its supplements. - * - * Use is subject to license terms. - * - * This distribution may include materials developed by third parties. - * Sun, Sun Microsystems, the Sun logo, Java and Project Identity - * Connectors are trademarks or registered trademarks of Sun - * Microsystems, Inc. or its subsidiaries in the U.S. and other - * countries. - * - * UNIX is a registered trademark in the U.S. and other countries, - * exclusively licensed through X/Open Company, Ltd. - * - * ----------- + * ==================== * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER. * - * Copyright 2008 Sun Microsystems, Inc. All rights reserved. + * Copyright 2007-2008 Sun Microsystems, Inc. All rights reserved. * - * The contents of this file are subject to the terms of the Common Development - * and Distribution License(CDDL) (the License). You may not use this file - * except in compliance with the License. + * The contents of this file are subject to the terms of the Common Development + * and Distribution License("CDDL") (the "License"). You may not use this file + * except in compliance with the License. * - * You can obtain a copy of the License at - * http://identityconnectors.dev.java.net/CDDLv1.0.html - * See the License for the specific language governing permissions and - * limitations under the License. + * You can obtain a copy of the License at + * http://IdentityConnectors.dev.java.net/legal/license.txt + * See the License for the specific language governing permissions and limitations + * under the License. * - * When distributing the Covered Code, include this CDDL Header Notice in each - * file and include the License file at identityconnectors/legal/license.txt. + * When distributing the Covered Code, include this CDDL Header Notice in each file + * and include the License file at identityconnectors/legal/license.txt. * If applicable, add the following below this CDDL Header, with the fields * enclosed by brackets [] replaced by your own identifying information: * "Portions Copyrighted [year] [name of copyright owner]" - * ----------- + * ==================== */ using System; using System.IO; diff --git a/FrameworkTests/ProxyTests.cs b/FrameworkTests/ProxyTests.cs index 81762048..863ba446 100644 --- a/FrameworkTests/ProxyTests.cs +++ b/FrameworkTests/ProxyTests.cs @@ -1,41 +1,24 @@ /* - * Copyright 2008 Sun Microsystems, Inc. All rights reserved. - * - * U.S. Government Rights - Commercial software. Government users - * are subject to the Sun Microsystems, Inc. standard license agreement - * and applicable provisions of the FAR and its supplements. - * - * Use is subject to license terms. - * - * This distribution may include materials developed by third parties. - * Sun, Sun Microsystems, the Sun logo, Java and Project Identity - * Connectors are trademarks or registered trademarks of Sun - * Microsystems, Inc. or its subsidiaries in the U.S. and other - * countries. - * - * UNIX is a registered trademark in the U.S. and other countries, - * exclusively licensed through X/Open Company, Ltd. - * - * ----------- + * ==================== * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER. * - * Copyright 2008 Sun Microsystems, Inc. All rights reserved. + * Copyright 2007-2008 Sun Microsystems, Inc. All rights reserved. * - * The contents of this file are subject to the terms of the Common Development - * and Distribution License(CDDL) (the License). You may not use this file - * except in compliance with the License. + * The contents of this file are subject to the terms of the Common Development + * and Distribution License("CDDL") (the "License"). You may not use this file + * except in compliance with the License. * - * You can obtain a copy of the License at - * http://identityconnectors.dev.java.net/CDDLv1.0.html - * See the License for the specific language governing permissions and - * limitations under the License. + * You can obtain a copy of the License at + * http://IdentityConnectors.dev.java.net/legal/license.txt + * See the License for the specific language governing permissions and limitations + * under the License. * - * When distributing the Covered Code, include this CDDL Header Notice in each - * file and include the License file at identityconnectors/legal/license.txt. + * When distributing the Covered Code, include this CDDL Header Notice in each file + * and include the License file at identityconnectors/legal/license.txt. * If applicable, add the following below this CDDL Header, with the fields * enclosed by brackets [] replaced by your own identifying information: * "Portions Copyrighted [year] [name of copyright owner]" - * ----------- + * ==================== */ using System; using NUnit.Framework; diff --git a/FrameworkTests/SafeTypeTest.cs b/FrameworkTests/SafeTypeTest.cs index 0533b2d5..49567e30 100644 --- a/FrameworkTests/SafeTypeTest.cs +++ b/FrameworkTests/SafeTypeTest.cs @@ -1,43 +1,25 @@ /* - * Copyright 2008 Sun Microsystems, Inc. All rights reserved. - * - * U.S. Government Rights - Commercial software. Government users - * are subject to the Sun Microsystems, Inc. standard license agreement - * and applicable provisions of the FAR and its supplements. - * - * Use is subject to license terms. - * - * This distribution may include materials developed by third parties. - * Sun, Sun Microsystems, the Sun logo, Java and Project Identity - * Connectors are trademarks or registered trademarks of Sun - * Microsystems, Inc. or its subsidiaries in the U.S. and other - * countries. - * - * UNIX is a registered trademark in the U.S. and other countries, - * exclusively licensed through X/Open Company, Ltd. - * - * ----------- + * ==================== * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER. * - * Copyright 2008 Sun Microsystems, Inc. All rights reserved. + * Copyright 2007-2008 Sun Microsystems, Inc. All rights reserved. * - * The contents of this file are subject to the terms of the Common Development - * and Distribution License(CDDL) (the License). You may not use this file - * except in compliance with the License. + * The contents of this file are subject to the terms of the Common Development + * and Distribution License("CDDL") (the "License"). You may not use this file + * except in compliance with the License. * - * You can obtain a copy of the License at - * http://identityconnectors.dev.java.net/CDDLv1.0.html - * See the License for the specific language governing permissions and - * limitations under the License. + * You can obtain a copy of the License at + * http://IdentityConnectors.dev.java.net/legal/license.txt + * See the License for the specific language governing permissions and limitations + * under the License. * - * When distributing the Covered Code, include this CDDL Header Notice in each - * file and include the License file at identityconnectors/legal/license.txt. + * When distributing the Covered Code, include this CDDL Header Notice in each file + * and include the License file at identityconnectors/legal/license.txt. * If applicable, add the following below this CDDL Header, with the fields * enclosed by brackets [] replaced by your own identifying information: * "Portions Copyrighted [year] [name of copyright owner]" - * ----------- + * ==================== */ - using System; using NUnit.Framework; using NUnit.Framework.SyntaxHelpers; diff --git a/FrameworkTests/ScriptTests.cs b/FrameworkTests/ScriptTests.cs index fdd33cd6..a22d85b9 100644 --- a/FrameworkTests/ScriptTests.cs +++ b/FrameworkTests/ScriptTests.cs @@ -1,41 +1,24 @@ /* - * Copyright 2008 Sun Microsystems, Inc. All rights reserved. - * - * U.S. Government Rights - Commercial software. Government users - * are subject to the Sun Microsystems, Inc. standard license agreement - * and applicable provisions of the FAR and its supplements. - * - * Use is subject to license terms. - * - * This distribution may include materials developed by third parties. - * Sun, Sun Microsystems, the Sun logo, Java and Project Identity - * Connectors are trademarks or registered trademarks of Sun - * Microsystems, Inc. or its subsidiaries in the U.S. and other - * countries. - * - * UNIX is a registered trademark in the U.S. and other countries, - * exclusively licensed through X/Open Company, Ltd. - * - * ----------- + * ==================== * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER. * - * Copyright 2008 Sun Microsystems, Inc. All rights reserved. + * Copyright 2007-2008 Sun Microsystems, Inc. All rights reserved. * - * The contents of this file are subject to the terms of the Common Development - * and Distribution License(CDDL) (the License). You may not use this file - * except in compliance with the License. + * The contents of this file are subject to the terms of the Common Development + * and Distribution License("CDDL") (the "License"). You may not use this file + * except in compliance with the License. * - * You can obtain a copy of the License at - * http://identityconnectors.dev.java.net/CDDLv1.0.html - * See the License for the specific language governing permissions and - * limitations under the License. + * You can obtain a copy of the License at + * http://IdentityConnectors.dev.java.net/legal/license.txt + * See the License for the specific language governing permissions and limitations + * under the License. * - * When distributing the Covered Code, include this CDDL Header Notice in each - * file and include the License file at identityconnectors/legal/license.txt. + * When distributing the Covered Code, include this CDDL Header Notice in each file + * and include the License file at identityconnectors/legal/license.txt. * If applicable, add the following below this CDDL Header, with the fields * enclosed by brackets [] replaced by your own identifying information: * "Portions Copyrighted [year] [name of copyright owner]" - * ----------- + * ==================== */ using System; using System.Collections.Generic; diff --git a/FrameworkTests/TestHelperTests.cs b/FrameworkTests/TestHelperTests.cs index 76da0a73..5d79c29d 100644 --- a/FrameworkTests/TestHelperTests.cs +++ b/FrameworkTests/TestHelperTests.cs @@ -1,41 +1,24 @@ /* - * Copyright 2008 Sun Microsystems, Inc. All rights reserved. - * - * U.S. Government Rights - Commercial software. Government users - * are subject to the Sun Microsystems, Inc. standard license agreement - * and applicable provisions of the FAR and its supplements. - * - * Use is subject to license terms. - * - * This distribution may include materials developed by third parties. - * Sun, Sun Microsystems, the Sun logo, Java and Project Identity - * Connectors are trademarks or registered trademarks of Sun - * Microsystems, Inc. or its subsidiaries in the U.S. and other - * countries. - * - * UNIX is a registered trademark in the U.S. and other countries, - * exclusively licensed through X/Open Company, Ltd. - * - * ----------- + * ==================== * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER. * - * Copyright 2008 Sun Microsystems, Inc. All rights reserved. + * Copyright 2007-2008 Sun Microsystems, Inc. All rights reserved. * - * The contents of this file are subject to the terms of the Common Development - * and Distribution License(CDDL) (the License). You may not use this file - * except in compliance with the License. + * The contents of this file are subject to the terms of the Common Development + * and Distribution License("CDDL") (the "License"). You may not use this file + * except in compliance with the License. * - * You can obtain a copy of the License at - * http://identityconnectors.dev.java.net/CDDLv1.0.html - * See the License for the specific language governing permissions and - * limitations under the License. + * You can obtain a copy of the License at + * http://IdentityConnectors.dev.java.net/legal/license.txt + * See the License for the specific language governing permissions and limitations + * under the License. * - * When distributing the Covered Code, include this CDDL Header Notice in each - * file and include the License file at identityconnectors/legal/license.txt. + * When distributing the Covered Code, include this CDDL Header Notice in each file + * and include the License file at identityconnectors/legal/license.txt. * If applicable, add the following below this CDDL Header, with the fields * enclosed by brackets [] replaced by your own identifying information: * "Portions Copyrighted [year] [name of copyright owner]" - * ----------- + * ==================== */ using System; using System.IO; diff --git a/FrameworkTests/TestUtil.cs b/FrameworkTests/TestUtil.cs index 586a763b..309f8b91 100644 --- a/FrameworkTests/TestUtil.cs +++ b/FrameworkTests/TestUtil.cs @@ -1,41 +1,24 @@ /* - * Copyright 2008 Sun Microsystems, Inc. All rights reserved. - * - * U.S. Government Rights - Commercial software. Government users - * are subject to the Sun Microsystems, Inc. standard license agreement - * and applicable provisions of the FAR and its supplements. - * - * Use is subject to license terms. - * - * This distribution may include materials developed by third parties. - * Sun, Sun Microsystems, the Sun logo, Java and Project Identity - * Connectors are trademarks or registered trademarks of Sun - * Microsystems, Inc. or its subsidiaries in the U.S. and other - * countries. - * - * UNIX is a registered trademark in the U.S. and other countries, - * exclusively licensed through X/Open Company, Ltd. - * - * ----------- + * ==================== * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER. * - * Copyright 2008 Sun Microsystems, Inc. All rights reserved. + * Copyright 2007-2008 Sun Microsystems, Inc. All rights reserved. * - * The contents of this file are subject to the terms of the Common Development - * and Distribution License(CDDL) (the License). You may not use this file - * except in compliance with the License. + * The contents of this file are subject to the terms of the Common Development + * and Distribution License("CDDL") (the "License"). You may not use this file + * except in compliance with the License. * - * You can obtain a copy of the License at - * http://identityconnectors.dev.java.net/CDDLv1.0.html - * See the License for the specific language governing permissions and - * limitations under the License. + * You can obtain a copy of the License at + * http://IdentityConnectors.dev.java.net/legal/license.txt + * See the License for the specific language governing permissions and limitations + * under the License. * - * When distributing the Covered Code, include this CDDL Header Notice in each - * file and include the License file at identityconnectors/legal/license.txt. + * When distributing the Covered Code, include this CDDL Header Notice in each file + * and include the License file at identityconnectors/legal/license.txt. * If applicable, add the following below this CDDL Header, with the fields * enclosed by brackets [] replaced by your own identifying information: * "Portions Copyrighted [year] [name of copyright owner]" - * ----------- + * ==================== */ using System; using System.Diagnostics; diff --git a/FrameworkTests/UpdateImplTests.cs b/FrameworkTests/UpdateImplTests.cs index 41527258..f71f7c52 100644 --- a/FrameworkTests/UpdateImplTests.cs +++ b/FrameworkTests/UpdateImplTests.cs @@ -1,41 +1,24 @@ /* - * Copyright 2008 Sun Microsystems, Inc. All rights reserved. - * - * U.S. Government Rights - Commercial software. Government users - * are subject to the Sun Microsystems, Inc. standard license agreement - * and applicable provisions of the FAR and its supplements. - * - * Use is subject to license terms. - * - * This distribution may include materials developed by third parties. - * Sun, Sun Microsystems, the Sun logo, Java and Project Identity - * Connectors are trademarks or registered trademarks of Sun - * Microsystems, Inc. or its subsidiaries in the U.S. and other - * countries. - * - * UNIX is a registered trademark in the U.S. and other countries, - * exclusively licensed through X/Open Company, Ltd. - * - * ----------- + * ==================== * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER. * - * Copyright 2008 Sun Microsystems, Inc. All rights reserved. + * Copyright 2007-2008 Sun Microsystems, Inc. All rights reserved. * - * The contents of this file are subject to the terms of the Common Development - * and Distribution License(CDDL) (the License). You may not use this file - * except in compliance with the License. + * The contents of this file are subject to the terms of the Common Development + * and Distribution License("CDDL") (the "License"). You may not use this file + * except in compliance with the License. * - * You can obtain a copy of the License at - * http://identityconnectors.dev.java.net/CDDLv1.0.html - * See the License for the specific language governing permissions and - * limitations under the License. + * You can obtain a copy of the License at + * http://IdentityConnectors.dev.java.net/legal/license.txt + * See the License for the specific language governing permissions and limitations + * under the License. * - * When distributing the Covered Code, include this CDDL Header Notice in each - * file and include the License file at identityconnectors/legal/license.txt. + * When distributing the Covered Code, include this CDDL Header Notice in each file + * and include the License file at identityconnectors/legal/license.txt. * If applicable, add the following below this CDDL Header, with the fields * enclosed by brackets [] replaced by your own identifying information: * "Portions Copyrighted [year] [name of copyright owner]" - * ----------- + * ==================== */ using System; using System.Security; diff --git a/Service/AssemblyInfo.cs b/Service/AssemblyInfo.cs index 5ba7de71..b5a7e910 100644 --- a/Service/AssemblyInfo.cs +++ b/Service/AssemblyInfo.cs @@ -1,41 +1,24 @@ /* - * Copyright 2008 Sun Microsystems, Inc. All rights reserved. - * - * U.S. Government Rights - Commercial software. Government users - * are subject to the Sun Microsystems, Inc. standard license agreement - * and applicable provisions of the FAR and its supplements. - * - * Use is subject to license terms. - * - * This distribution may include materials developed by third parties. - * Sun, Sun Microsystems, the Sun logo, Java and Project Identity - * Connectors are trademarks or registered trademarks of Sun - * Microsystems, Inc. or its subsidiaries in the U.S. and other - * countries. - * - * UNIX is a registered trademark in the U.S. and other countries, - * exclusively licensed through X/Open Company, Ltd. - * - * ----------- + * ==================== * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER. * - * Copyright 2008 Sun Microsystems, Inc. All rights reserved. + * Copyright 2007-2008 Sun Microsystems, Inc. All rights reserved. * - * The contents of this file are subject to the terms of the Common Development - * and Distribution License(CDDL) (the License). You may not use this file - * except in compliance with the License. + * The contents of this file are subject to the terms of the Common Development + * and Distribution License("CDDL") (the "License"). You may not use this file + * except in compliance with the License. * - * You can obtain a copy of the License at - * http://identityconnectors.dev.java.net/CDDLv1.0.html - * See the License for the specific language governing permissions and - * limitations under the License. + * You can obtain a copy of the License at + * http://IdentityConnectors.dev.java.net/legal/license.txt + * See the License for the specific language governing permissions and limitations + * under the License. * - * When distributing the Covered Code, include this CDDL Header Notice in each - * file and include the License file at identityconnectors/legal/license.txt. + * When distributing the Covered Code, include this CDDL Header Notice in each file + * and include the License file at identityconnectors/legal/license.txt. * If applicable, add the following below this CDDL Header, with the fields * enclosed by brackets [] replaced by your own identifying information: * "Portions Copyrighted [year] [name of copyright owner]" - * ----------- + * ==================== */ #region Using directives diff --git a/Service/Program.cs b/Service/Program.cs index d7074c2f..aff6237a 100644 --- a/Service/Program.cs +++ b/Service/Program.cs @@ -1,41 +1,24 @@ /* - * Copyright 2008 Sun Microsystems, Inc. All rights reserved. - * - * U.S. Government Rights - Commercial software. Government users - * are subject to the Sun Microsystems, Inc. standard license agreement - * and applicable provisions of the FAR and its supplements. - * - * Use is subject to license terms. - * - * This distribution may include materials developed by third parties. - * Sun, Sun Microsystems, the Sun logo, Java and Project Identity - * Connectors are trademarks or registered trademarks of Sun - * Microsystems, Inc. or its subsidiaries in the U.S. and other - * countries. - * - * UNIX is a registered trademark in the U.S. and other countries, - * exclusively licensed through X/Open Company, Ltd. - * - * ----------- + * ==================== * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER. * - * Copyright 2008 Sun Microsystems, Inc. All rights reserved. + * Copyright 2007-2008 Sun Microsystems, Inc. All rights reserved. * - * The contents of this file are subject to the terms of the Common Development - * and Distribution License(CDDL) (the License). You may not use this file - * except in compliance with the License. + * The contents of this file are subject to the terms of the Common Development + * and Distribution License("CDDL") (the "License"). You may not use this file + * except in compliance with the License. * - * You can obtain a copy of the License at - * http://identityconnectors.dev.java.net/CDDLv1.0.html - * See the License for the specific language governing permissions and - * limitations under the License. + * You can obtain a copy of the License at + * http://IdentityConnectors.dev.java.net/legal/license.txt + * See the License for the specific language governing permissions and limitations + * under the License. * - * When distributing the Covered Code, include this CDDL Header Notice in each - * file and include the License file at identityconnectors/legal/license.txt. + * When distributing the Covered Code, include this CDDL Header Notice in each file + * and include the License file at identityconnectors/legal/license.txt. * If applicable, add the following below this CDDL Header, with the fields * enclosed by brackets [] replaced by your own identifying information: * "Portions Copyrighted [year] [name of copyright owner]" - * ----------- + * ==================== */ using System; using System.ComponentModel; diff --git a/Service/ProjectInstaller.cs b/Service/ProjectInstaller.cs index f8282687..c119abc6 100644 --- a/Service/ProjectInstaller.cs +++ b/Service/ProjectInstaller.cs @@ -1,41 +1,24 @@ /* - * Copyright 2008 Sun Microsystems, Inc. All rights reserved. - * - * U.S. Government Rights - Commercial software. Government users - * are subject to the Sun Microsystems, Inc. standard license agreement - * and applicable provisions of the FAR and its supplements. - * - * Use is subject to license terms. - * - * This distribution may include materials developed by third parties. - * Sun, Sun Microsystems, the Sun logo, Java and Project Identity - * Connectors are trademarks or registered trademarks of Sun - * Microsystems, Inc. or its subsidiaries in the U.S. and other - * countries. - * - * UNIX is a registered trademark in the U.S. and other countries, - * exclusively licensed through X/Open Company, Ltd. - * - * ----------- + * ==================== * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER. * - * Copyright 2008 Sun Microsystems, Inc. All rights reserved. + * Copyright 2007-2008 Sun Microsystems, Inc. All rights reserved. * - * The contents of this file are subject to the terms of the Common Development - * and Distribution License(CDDL) (the License). You may not use this file - * except in compliance with the License. + * The contents of this file are subject to the terms of the Common Development + * and Distribution License("CDDL") (the "License"). You may not use this file + * except in compliance with the License. * - * You can obtain a copy of the License at - * http://identityconnectors.dev.java.net/CDDLv1.0.html - * See the License for the specific language governing permissions and - * limitations under the License. + * You can obtain a copy of the License at + * http://IdentityConnectors.dev.java.net/legal/license.txt + * See the License for the specific language governing permissions and limitations + * under the License. * - * When distributing the Covered Code, include this CDDL Header Notice in each - * file and include the License file at identityconnectors/legal/license.txt. + * When distributing the Covered Code, include this CDDL Header Notice in each file + * and include the License file at identityconnectors/legal/license.txt. * If applicable, add the following below this CDDL Header, with the fields * enclosed by brackets [] replaced by your own identifying information: * "Portions Copyrighted [year] [name of copyright owner]" - * ----------- + * ==================== */ using System; using System.Collections.Generic; diff --git a/Service/Service.cs b/Service/Service.cs index 5693b123..2855a94e 100644 --- a/Service/Service.cs +++ b/Service/Service.cs @@ -1,41 +1,24 @@ /* - * Copyright 2008 Sun Microsystems, Inc. All rights reserved. - * - * U.S. Government Rights - Commercial software. Government users - * are subject to the Sun Microsystems, Inc. standard license agreement - * and applicable provisions of the FAR and its supplements. - * - * Use is subject to license terms. - * - * This distribution may include materials developed by third parties. - * Sun, Sun Microsystems, the Sun logo, Java and Project Identity - * Connectors are trademarks or registered trademarks of Sun - * Microsystems, Inc. or its subsidiaries in the U.S. and other - * countries. - * - * UNIX is a registered trademark in the U.S. and other countries, - * exclusively licensed through X/Open Company, Ltd. - * - * ----------- + * ==================== * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER. * - * Copyright 2008 Sun Microsystems, Inc. All rights reserved. + * Copyright 2007-2008 Sun Microsystems, Inc. All rights reserved. * - * The contents of this file are subject to the terms of the Common Development - * and Distribution License(CDDL) (the License). You may not use this file - * except in compliance with the License. + * The contents of this file are subject to the terms of the Common Development + * and Distribution License("CDDL") (the "License"). You may not use this file + * except in compliance with the License. * - * You can obtain a copy of the License at - * http://identityconnectors.dev.java.net/CDDLv1.0.html - * See the License for the specific language governing permissions and - * limitations under the License. + * You can obtain a copy of the License at + * http://IdentityConnectors.dev.java.net/legal/license.txt + * See the License for the specific language governing permissions and limitations + * under the License. * - * When distributing the Covered Code, include this CDDL Header Notice in each - * file and include the License file at identityconnectors/legal/license.txt. + * When distributing the Covered Code, include this CDDL Header Notice in each file + * and include the License file at identityconnectors/legal/license.txt. * If applicable, add the following below this CDDL Header, with the fields * enclosed by brackets [] replaced by your own identifying information: * "Portions Copyrighted [year] [name of copyright owner]" - * ----------- + * ==================== */ using System; using System.Collections.Generic; diff --git a/Service/Service.csproj b/Service/Service.csproj index 2d7b5c92..78cc3b47 100644 --- a/Service/Service.csproj +++ b/Service/Service.csproj @@ -1,39 +1,24 @@ - @@ -121,4 +106,4 @@ Framework - \ No newline at end of file + diff --git a/ServiceInstall/ExtBuild.proj b/ServiceInstall/ExtBuild.proj index f14e633c..df976a43 100644 --- a/ServiceInstall/ExtBuild.proj +++ b/ServiceInstall/ExtBuild.proj @@ -1,39 +1,24 @@ diff --git a/ServiceInstall/ServiceInstall.wixproj b/ServiceInstall/ServiceInstall.wixproj index a0db0748..3012a219 100644 --- a/ServiceInstall/ServiceInstall.wixproj +++ b/ServiceInstall/ServiceInstall.wixproj @@ -1,39 +1,24 @@ - @@ -79,4 +64,4 @@ - \ No newline at end of file + diff --git a/ShellScriptExecutorFactory/AssemblyInfo.cs b/ShellScriptExecutorFactory/AssemblyInfo.cs index 7a2dd083..4e907090 100644 --- a/ShellScriptExecutorFactory/AssemblyInfo.cs +++ b/ShellScriptExecutorFactory/AssemblyInfo.cs @@ -1,41 +1,24 @@ /* - * Copyright 2008 Sun Microsystems, Inc. All rights reserved. - * - * U.S. Government Rights - Commercial software. Government users - * are subject to the Sun Microsystems, Inc. standard license agreement - * and applicable provisions of the FAR and its supplements. - * - * Use is subject to license terms. - * - * This distribution may include materials developed by third parties. - * Sun, Sun Microsystems, the Sun logo, Java and Project Identity - * Connectors are trademarks or registered trademarks of Sun - * Microsystems, Inc. or its subsidiaries in the U.S. and other - * countries. - * - * UNIX is a registered trademark in the U.S. and other countries, - * exclusively licensed through X/Open Company, Ltd. - * - * ----------- + * ==================== * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER. * - * Copyright 2008 Sun Microsystems, Inc. All rights reserved. + * Copyright 2007-2008 Sun Microsystems, Inc. All rights reserved. * - * The contents of this file are subject to the terms of the Common Development - * and Distribution License(CDDL) (the License). You may not use this file - * except in compliance with the License. + * The contents of this file are subject to the terms of the Common Development + * and Distribution License("CDDL") (the "License"). You may not use this file + * except in compliance with the License. * - * You can obtain a copy of the License at - * http://identityconnectors.dev.java.net/CDDLv1.0.html - * See the License for the specific language governing permissions and - * limitations under the License. + * You can obtain a copy of the License at + * http://IdentityConnectors.dev.java.net/legal/license.txt + * See the License for the specific language governing permissions and limitations + * under the License. * - * When distributing the Covered Code, include this CDDL Header Notice in each - * file and include the License file at identityconnectors/legal/license.txt. + * When distributing the Covered Code, include this CDDL Header Notice in each file + * and include the License file at identityconnectors/legal/license.txt. * If applicable, add the following below this CDDL Header, with the fields * enclosed by brackets [] replaced by your own identifying information: * "Portions Copyrighted [year] [name of copyright owner]" - * ----------- + * ==================== */ #region Using directives diff --git a/ShellScriptExecutorFactory/ShellScriptExecutorFactory.cs b/ShellScriptExecutorFactory/ShellScriptExecutorFactory.cs index 3c2ea830..60be5ff6 100644 --- a/ShellScriptExecutorFactory/ShellScriptExecutorFactory.cs +++ b/ShellScriptExecutorFactory/ShellScriptExecutorFactory.cs @@ -1,41 +1,24 @@ /* - * Copyright 2008 Sun Microsystems, Inc. All rights reserved. - * - * U.S. Government Rights - Commercial software. Government users - * are subject to the Sun Microsystems, Inc. standard license agreement - * and applicable provisions of the FAR and its supplements. - * - * Use is subject to license terms. - * - * This distribution may include materials developed by third parties. - * Sun, Sun Microsystems, the Sun logo, Java and Project Identity - * Connectors are trademarks or registered trademarks of Sun - * Microsystems, Inc. or its subsidiaries in the U.S. and other - * countries. - * - * UNIX is a registered trademark in the U.S. and other countries, - * exclusively licensed through X/Open Company, Ltd. - * - * ----------- + * ==================== * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER. * - * Copyright 2008 Sun Microsystems, Inc. All rights reserved. + * Copyright 2007-2008 Sun Microsystems, Inc. All rights reserved. * - * The contents of this file are subject to the terms of the Common Development - * and Distribution License(CDDL) (the License). You may not use this file - * except in compliance with the License. + * The contents of this file are subject to the terms of the Common Development + * and Distribution License("CDDL") (the "License"). You may not use this file + * except in compliance with the License. * - * You can obtain a copy of the License at - * http://identityconnectors.dev.java.net/CDDLv1.0.html - * See the License for the specific language governing permissions and - * limitations under the License. + * You can obtain a copy of the License at + * http://IdentityConnectors.dev.java.net/legal/license.txt + * See the License for the specific language governing permissions and limitations + * under the License. * - * When distributing the Covered Code, include this CDDL Header Notice in each - * file and include the License file at identityconnectors/legal/license.txt. + * When distributing the Covered Code, include this CDDL Header Notice in each file + * and include the License file at identityconnectors/legal/license.txt. * If applicable, add the following below this CDDL Header, with the fields * enclosed by brackets [] replaced by your own identifying information: * "Portions Copyrighted [year] [name of copyright owner]" - * ----------- + * ==================== */ using System; using System.IO; diff --git a/ShellScriptExecutorFactory/ShellScriptExecutorFactory.csproj b/ShellScriptExecutorFactory/ShellScriptExecutorFactory.csproj index 07a41624..0779540f 100644 --- a/ShellScriptExecutorFactory/ShellScriptExecutorFactory.csproj +++ b/ShellScriptExecutorFactory/ShellScriptExecutorFactory.csproj @@ -1,39 +1,24 @@ - @@ -93,4 +78,4 @@ - \ No newline at end of file + diff --git a/TestBundleV1/AssemblyInfo.cs b/TestBundleV1/AssemblyInfo.cs index 33e53d26..f1b27d89 100644 --- a/TestBundleV1/AssemblyInfo.cs +++ b/TestBundleV1/AssemblyInfo.cs @@ -1,41 +1,24 @@ /* - * Copyright 2008 Sun Microsystems, Inc. All rights reserved. - * - * U.S. Government Rights - Commercial software. Government users - * are subject to the Sun Microsystems, Inc. standard license agreement - * and applicable provisions of the FAR and its supplements. - * - * Use is subject to license terms. - * - * This distribution may include materials developed by third parties. - * Sun, Sun Microsystems, the Sun logo, Java and Project Identity - * Connectors are trademarks or registered trademarks of Sun - * Microsystems, Inc. or its subsidiaries in the U.S. and other - * countries. - * - * UNIX is a registered trademark in the U.S. and other countries, - * exclusively licensed through X/Open Company, Ltd. - * - * ----------- + * ==================== * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER. * - * Copyright 2008 Sun Microsystems, Inc. All rights reserved. + * Copyright 2007-2008 Sun Microsystems, Inc. All rights reserved. * - * The contents of this file are subject to the terms of the Common Development - * and Distribution License(CDDL) (the License). You may not use this file - * except in compliance with the License. + * The contents of this file are subject to the terms of the Common Development + * and Distribution License("CDDL") (the "License"). You may not use this file + * except in compliance with the License. * - * You can obtain a copy of the License at - * http://identityconnectors.dev.java.net/CDDLv1.0.html - * See the License for the specific language governing permissions and - * limitations under the License. + * You can obtain a copy of the License at + * http://IdentityConnectors.dev.java.net/legal/license.txt + * See the License for the specific language governing permissions and limitations + * under the License. * - * When distributing the Covered Code, include this CDDL Header Notice in each - * file and include the License file at identityconnectors/legal/license.txt. + * When distributing the Covered Code, include this CDDL Header Notice in each file + * and include the License file at identityconnectors/legal/license.txt. * If applicable, add the following below this CDDL Header, with the fields * enclosed by brackets [] replaced by your own identifying information: * "Portions Copyrighted [year] [name of copyright owner]" - * ----------- + * ==================== */ #region Using directives diff --git a/TestBundleV1/TestBundleV1.csproj b/TestBundleV1/TestBundleV1.csproj index 2ad8340c..0c67a441 100644 --- a/TestBundleV1/TestBundleV1.csproj +++ b/TestBundleV1/TestBundleV1.csproj @@ -1,39 +1,24 @@ - @@ -103,4 +88,4 @@ - \ No newline at end of file + diff --git a/TestBundleV1/TestConnector.cs b/TestBundleV1/TestConnector.cs index 4b934efc..abc8f446 100644 --- a/TestBundleV1/TestConnector.cs +++ b/TestBundleV1/TestConnector.cs @@ -1,41 +1,24 @@ /* - * Copyright 2008 Sun Microsystems, Inc. All rights reserved. - * - * U.S. Government Rights - Commercial software. Government users - * are subject to the Sun Microsystems, Inc. standard license agreement - * and applicable provisions of the FAR and its supplements. - * - * Use is subject to license terms. - * - * This distribution may include materials developed by third parties. - * Sun, Sun Microsystems, the Sun logo, Java and Project Identity - * Connectors are trademarks or registered trademarks of Sun - * Microsystems, Inc. or its subsidiaries in the U.S. and other - * countries. - * - * UNIX is a registered trademark in the U.S. and other countries, - * exclusively licensed through X/Open Company, Ltd. - * - * ----------- + * ==================== * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER. * - * Copyright 2008 Sun Microsystems, Inc. All rights reserved. + * Copyright 2007-2008 Sun Microsystems, Inc. All rights reserved. * - * The contents of this file are subject to the terms of the Common Development - * and Distribution License(CDDL) (the License). You may not use this file - * except in compliance with the License. + * The contents of this file are subject to the terms of the Common Development + * and Distribution License("CDDL") (the "License"). You may not use this file + * except in compliance with the License. * - * You can obtain a copy of the License at - * http://identityconnectors.dev.java.net/CDDLv1.0.html - * See the License for the specific language governing permissions and - * limitations under the License. + * You can obtain a copy of the License at + * http://IdentityConnectors.dev.java.net/legal/license.txt + * See the License for the specific language governing permissions and limitations + * under the License. * - * When distributing the Covered Code, include this CDDL Header Notice in each - * file and include the License file at identityconnectors/legal/license.txt. + * When distributing the Covered Code, include this CDDL Header Notice in each file + * and include the License file at identityconnectors/legal/license.txt. * If applicable, add the following below this CDDL Header, with the fields * enclosed by brackets [] replaced by your own identifying information: * "Portions Copyrighted [year] [name of copyright owner]" - * ----------- + * ==================== */ using System; using System.Collections.Generic; diff --git a/TestBundleV2/AssemblyInfo.cs b/TestBundleV2/AssemblyInfo.cs index eebe80c0..9c8de8e4 100644 --- a/TestBundleV2/AssemblyInfo.cs +++ b/TestBundleV2/AssemblyInfo.cs @@ -1,41 +1,24 @@ /* - * Copyright 2008 Sun Microsystems, Inc. All rights reserved. - * - * U.S. Government Rights - Commercial software. Government users - * are subject to the Sun Microsystems, Inc. standard license agreement - * and applicable provisions of the FAR and its supplements. - * - * Use is subject to license terms. - * - * This distribution may include materials developed by third parties. - * Sun, Sun Microsystems, the Sun logo, Java and Project Identity - * Connectors are trademarks or registered trademarks of Sun - * Microsystems, Inc. or its subsidiaries in the U.S. and other - * countries. - * - * UNIX is a registered trademark in the U.S. and other countries, - * exclusively licensed through X/Open Company, Ltd. - * - * ----------- + * ==================== * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER. * - * Copyright 2008 Sun Microsystems, Inc. All rights reserved. + * Copyright 2007-2008 Sun Microsystems, Inc. All rights reserved. * - * The contents of this file are subject to the terms of the Common Development - * and Distribution License(CDDL) (the License). You may not use this file - * except in compliance with the License. + * The contents of this file are subject to the terms of the Common Development + * and Distribution License("CDDL") (the "License"). You may not use this file + * except in compliance with the License. * - * You can obtain a copy of the License at - * http://identityconnectors.dev.java.net/CDDLv1.0.html - * See the License for the specific language governing permissions and - * limitations under the License. + * You can obtain a copy of the License at + * http://IdentityConnectors.dev.java.net/legal/license.txt + * See the License for the specific language governing permissions and limitations + * under the License. * - * When distributing the Covered Code, include this CDDL Header Notice in each - * file and include the License file at identityconnectors/legal/license.txt. + * When distributing the Covered Code, include this CDDL Header Notice in each file + * and include the License file at identityconnectors/legal/license.txt. * If applicable, add the following below this CDDL Header, with the fields * enclosed by brackets [] replaced by your own identifying information: * "Portions Copyrighted [year] [name of copyright owner]" - * ----------- + * ==================== */ #region Using directives diff --git a/TestBundleV2/TestBundleV2.csproj b/TestBundleV2/TestBundleV2.csproj index 34a8ea60..2b6e3cfe 100644 --- a/TestBundleV2/TestBundleV2.csproj +++ b/TestBundleV2/TestBundleV2.csproj @@ -1,39 +1,24 @@ - @@ -94,4 +79,4 @@ - \ No newline at end of file + diff --git a/TestBundleV2/TestConnector.cs b/TestBundleV2/TestConnector.cs index 9ba84e73..cce29c4a 100644 --- a/TestBundleV2/TestConnector.cs +++ b/TestBundleV2/TestConnector.cs @@ -1,41 +1,24 @@ /* - * Copyright 2008 Sun Microsystems, Inc. All rights reserved. - * - * U.S. Government Rights - Commercial software. Government users - * are subject to the Sun Microsystems, Inc. standard license agreement - * and applicable provisions of the FAR and its supplements. - * - * Use is subject to license terms. - * - * This distribution may include materials developed by third parties. - * Sun, Sun Microsystems, the Sun logo, Java and Project Identity - * Connectors are trademarks or registered trademarks of Sun - * Microsystems, Inc. or its subsidiaries in the U.S. and other - * countries. - * - * UNIX is a registered trademark in the U.S. and other countries, - * exclusively licensed through X/Open Company, Ltd. - * - * ----------- + * ==================== * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER. * - * Copyright 2008 Sun Microsystems, Inc. All rights reserved. + * Copyright 2007-2008 Sun Microsystems, Inc. All rights reserved. * - * The contents of this file are subject to the terms of the Common Development - * and Distribution License(CDDL) (the License). You may not use this file - * except in compliance with the License. + * The contents of this file are subject to the terms of the Common Development + * and Distribution License("CDDL") (the "License"). You may not use this file + * except in compliance with the License. * - * You can obtain a copy of the License at - * http://identityconnectors.dev.java.net/CDDLv1.0.html - * See the License for the specific language governing permissions and - * limitations under the License. + * You can obtain a copy of the License at + * http://IdentityConnectors.dev.java.net/legal/license.txt + * See the License for the specific language governing permissions and limitations + * under the License. * - * When distributing the Covered Code, include this CDDL Header Notice in each - * file and include the License file at identityconnectors/legal/license.txt. + * When distributing the Covered Code, include this CDDL Header Notice in each file + * and include the License file at identityconnectors/legal/license.txt. * If applicable, add the following below this CDDL Header, with the fields * enclosed by brackets [] replaced by your own identifying information: * "Portions Copyrighted [year] [name of copyright owner]" - * ----------- + * ==================== */ using System; using System.Collections.Generic; From ec559e20c0a296ebdc7d177f17fb54c8c5a9e1a2 Mon Sep 17 00:00:00 2001 From: rcauble Date: Fri, 12 Dec 2008 20:56:14 +0000 Subject: [PATCH 111/342] Issue#344: i18n name for ObjectClass --- Framework/CommonObjects.cs | 10 ++++++++++ FrameworkInternal/Resources.resx | 12 ++++++++++++ 2 files changed, 22 insertions(+) diff --git a/Framework/CommonObjects.cs b/Framework/CommonObjects.cs index 2abefcb4..fa2cec1a 100644 --- a/Framework/CommonObjects.cs +++ b/Framework/CommonObjects.cs @@ -1692,6 +1692,16 @@ public String GetObjectClassValue() { return _type; } + /** + * Convenience method to build the display name key for + * an object class. + * + * @return The display name key. + */ + public String GetDisplayNameKey() { + return "MESSAGE_OBJECT_CLASS_"+_type.ToUpper(); + } + public override int GetHashCode() { return _type.GetHashCode(); } diff --git a/FrameworkInternal/Resources.resx b/FrameworkInternal/Resources.resx index 2cf0d0d5..cbe9265b 100644 --- a/FrameworkInternal/Resources.resx +++ b/FrameworkInternal/Resources.resx @@ -476,4 +476,16 @@ OperationOptionInfo | SyncDeltaType | SyncToken | SyncDelta | QualifiedUid Test Framework Value + + Account + + + Person + + + Group + + + Organization + \ No newline at end of file From 9f3834e372f15c663270e08773465ac8f6ebb627 Mon Sep 17 00:00:00 2001 From: rcauble Date: Fri, 12 Dec 2008 21:36:17 +0000 Subject: [PATCH 112/342] Issue#350: Configuration properties should have a way to specify required --- Framework/Api.cs | 6 ++++++ Framework/Spi.cs | 5 +++++ FrameworkInternal/Api.cs | 5 +++++ FrameworkInternal/ApiLocal.cs | 4 +++- FrameworkInternal/Resources.resx | 1 + FrameworkInternal/Serializer.cs | 3 +++ FrameworkTests/ObjectSerializationTests.cs | 2 ++ 7 files changed, 25 insertions(+), 1 deletion(-) diff --git a/Framework/Api.cs b/Framework/Api.cs index c8368e89..d8e0cf23 100644 --- a/Framework/Api.cs +++ b/Framework/Api.cs @@ -127,6 +127,12 @@ public interface ConfigurationProperty { */ bool IsConfidential { get; } + /** + * Is this a required property + * @return True if the property is required + */ + bool IsRequired { get; } + /** * Set of operations for which this property must be specified. * This is used for the case where a connector may or may not diff --git a/Framework/Spi.cs b/Framework/Spi.cs index e76f3c7e..974246d0 100644 --- a/Framework/Spi.cs +++ b/Framework/Spi.cs @@ -139,6 +139,10 @@ public class ConfigurationPropertyAttribute : System.Attribute { /// public bool Confidential {get;set;} /// + /// Is this a required property? + /// + public bool Required {get;set;} + /// /// Change the default help message key. /// public string HelpMessageKey {get;set;} @@ -182,6 +186,7 @@ public SafeType [] Operations { public ConfigurationPropertyAttribute() { Order = 1; Confidential = false; + Required = false; HelpMessageKey = null; DisplayMessageKey = null; OperationTypes = new Type[0]; diff --git a/FrameworkInternal/Api.cs b/FrameworkInternal/Api.cs index c9a02e16..09f7f320 100644 --- a/FrameworkInternal/Api.cs +++ b/FrameworkInternal/Api.cs @@ -86,6 +86,8 @@ public string GetDisplayName(string def) { public Type ValueType { get; set; } public bool IsConfidential { get; set; } + + public bool IsRequired { get; set; } public override int GetHashCode() { @@ -113,6 +115,9 @@ public override bool Equals(Object o) { if (IsConfidential != other.IsConfidential) { return false; } + if (IsRequired != other.IsRequired) { + return false; + } if (!CollectionUtil.Equals(ValueType,other.ValueType)) { return false; } diff --git a/FrameworkInternal/ApiLocal.cs b/FrameworkInternal/ApiLocal.cs index bfba6d38..6f78e65e 100644 --- a/FrameworkInternal/ApiLocal.cs +++ b/FrameworkInternal/ApiLocal.cs @@ -188,6 +188,7 @@ public static ConfigurationPropertiesImpl String helpKey = name + ".help"; String displKey = name + ".display"; bool confidential = false; + bool required = false; if (options != null) { // determine the display and help keys.. if (!StringUtil.IsBlank(options.HelpMessageKey)) { @@ -198,7 +199,7 @@ public static ConfigurationPropertiesImpl } // determine the order.. order = options.Order; - + required = options.Required; confidential = options.Confidential; } Type type = desc.PropertyType; @@ -211,6 +212,7 @@ public static ConfigurationPropertiesImpl ConfigurationPropertyImpl prop = new ConfigurationPropertyImpl(); prop.IsConfidential=confidential; + prop.IsRequired=required; prop.DisplayMessageKey=displKey; prop.HelpMessageKey=helpKey; prop.Name=name; diff --git a/FrameworkInternal/Resources.resx b/FrameworkInternal/Resources.resx index cbe9265b..9d8ea6ed 100644 --- a/FrameworkInternal/Resources.resx +++ b/FrameworkInternal/Resources.resx @@ -261,6 +261,7 @@ OperationOptionInfo | SyncDeltaType | SyncToken | SyncDelta | QualifiedUid <!ATTLIST ConfigurationProperty order CDATA #IMPLIED confidential CDATA #IMPLIED + required CDATA #IMPLIED name CDATA #REQUIRED helpMessageKey CDATA #REQUIRED displayMessageKey CDATA #REQUIRED diff --git a/FrameworkInternal/Serializer.cs b/FrameworkInternal/Serializer.cs index 095902c3..2fdc9a6a 100644 --- a/FrameworkInternal/Serializer.cs +++ b/FrameworkInternal/Serializer.cs @@ -1098,6 +1098,7 @@ public override Object Deserialize(ObjectDecoder decoder) { ConfigurationPropertyImpl rv = new ConfigurationPropertyImpl(); rv.Order=(decoder.ReadIntField("order",0)); rv.IsConfidential=(decoder.ReadBooleanField("confidential",false)); + rv.IsRequired=decoder.ReadBooleanField("required",false); rv.Name=(decoder.ReadStringField("name",null)); rv.HelpMessageKey=( decoder.ReadStringField("helpMessageKey",null)); @@ -1127,6 +1128,8 @@ public override void Serialize(Object obj, ObjectEncoder encoder) { val.Order); encoder.WriteBooleanField("confidential", val.IsConfidential); + encoder.WriteBooleanField("required", + val.IsRequired); encoder.WriteStringField("name", val.Name); encoder.WriteStringField("helpMessageKey", diff --git a/FrameworkTests/ObjectSerializationTests.cs b/FrameworkTests/ObjectSerializationTests.cs index f0a91182..637d68f2 100644 --- a/FrameworkTests/ObjectSerializationTests.cs +++ b/FrameworkTests/ObjectSerializationTests.cs @@ -392,6 +392,7 @@ public void TestConfigurationProperty() { ConfigurationPropertyImpl v1 = new ConfigurationPropertyImpl(); v1.Order = (1); v1.IsConfidential=(true); + v1.IsRequired=true; v1.Name=("foo"); v1.HelpMessageKey=("help key"); v1.DisplayMessageKey=("display key"); @@ -403,6 +404,7 @@ public void TestConfigurationProperty() { CloneObject(v1); Assert.AreEqual(1, v2.Order); Assert.IsTrue(v2.IsConfidential); + Assert.IsTrue(v2.IsRequired); Assert.AreEqual("foo", v2.Name); Assert.AreEqual("help key", v2.HelpMessageKey); Assert.AreEqual("display key", v2.DisplayMessageKey); From 32aec2334cc615badae45aaa3864ceff3c44977d Mon Sep 17 00:00:00 2001 From: dvernon Date: Tue, 16 Dec 2008 19:32:11 +0000 Subject: [PATCH 113/342] Changes for IDM unit tests --- .../ActiveDirectoryConnector.cs | 34 +++++++++++++++++-- .../ActiveDirectoryUtils.cs | 7 ++++ ActiveDirectoryConnector/Messages.resx | 3 ++ ActiveDirectoryConnector/ObjectClasses.xml | 2 +- .../ActiveDirectoryConnectorTest.cs | 21 ++++++++++-- 5 files changed, 61 insertions(+), 6 deletions(-) diff --git a/ActiveDirectoryConnector/ActiveDirectoryConnector.cs b/ActiveDirectoryConnector/ActiveDirectoryConnector.cs index 2bbb0f85..f406525a 100644 --- a/ActiveDirectoryConnector/ActiveDirectoryConnector.cs +++ b/ActiveDirectoryConnector/ActiveDirectoryConnector.cs @@ -81,8 +81,12 @@ public class ActiveDirectoryConnector : CreateOp, Connector, SchemaOp, DeleteOp, public static readonly string ATT_DESCRIPTION = "description"; public static readonly string ATT_SHORT_NAME = "name"; public static readonly string ATT_DISPLAY_NAME = "displayName"; + public static readonly string ATT_USER_ACOUNT_CONTROL = "userAccountControl"; public static readonly string OBJECTCLASS_OU = "Organizational Unit"; + public static readonly ObjectClass ouObjectClass = new ObjectClass(OBJECTCLASS_OU); + private static readonly string OLD_SEARCH_FILTER_STRING = "Search Filter String"; + private static readonly string OLD_SEARCH_FILTER = "searchFilter"; ActiveDirectoryConfiguration _configuration = null; ActiveDirectoryUtils _utils = null; @@ -398,7 +402,7 @@ public virtual void ExecuteQuery(ObjectClass oclass, string query, ResultsHandler handler, OperationOptions options) { try - { + { bool useGC = false; if (_configuration.SearchChildDomains) { @@ -410,6 +414,31 @@ public virtual void ExecuteQuery(ObjectClass oclass, string query, SearchScope searchScope = GetADSearchScopeFromOptions(options); string searchContext = GetADSearchContextFromOptions(options); + // for backward compatibility, support old query style from resource adapters + // but log a warning + if((query == null) || (query.Length == 0)) { + if ((options != null) && (options.Options != null)) + { + Object oldStyleQuery = null; + if (options.Options.Keys.Contains(OLD_SEARCH_FILTER_STRING)) + { + oldStyleQuery = options.Options[OLD_SEARCH_FILTER_STRING]; + } + else if (options.Options.Keys.Contains(OLD_SEARCH_FILTER)) + { + oldStyleQuery = options.Options[OLD_SEARCH_FILTER]; + } + if ((oldStyleQuery != null) && (oldStyleQuery is string)) + { + query = (string)oldStyleQuery; + Trace.TraceWarning(_configuration.ConnectorMessages.Format( + "warn_CompatibilityModeQuery", + "Using Identity Manger Resource Adapter style query ''{0}''. This should be updated to use the new connector query syntax.", + ((query != null) && (query.Length > 0)) ? query : "")); + } + } + } + ExecuteQuery(oclass, query, handler, options, false, null, _configuration.LDAPHostName, useGC, searchContext, searchScope); } @@ -658,8 +687,9 @@ private ICollection GetAttributesToReturn(ObjectClass oclass, OperationO attributeNames = AttributesReturnedByDefault[oclass]; } - // Uid is always returned + // Uid and name are always returned attributeNames.Add(Uid.NAME); + attributeNames.Add(Name.NAME); return attributeNames; } diff --git a/ActiveDirectoryConnector/ActiveDirectoryUtils.cs b/ActiveDirectoryConnector/ActiveDirectoryUtils.cs index 1627ee7f..814b4366 100644 --- a/ActiveDirectoryConnector/ActiveDirectoryUtils.cs +++ b/ActiveDirectoryConnector/ActiveDirectoryUtils.cs @@ -238,6 +238,9 @@ internal void UpdateADObject(ObjectClass oclass, gsCurrentPassword, gsNewPassword); } + + UserAccountControl.Set(directoryEntry.Properties[ActiveDirectoryConnector.ATT_USER_ACOUNT_CONTROL], + UserAccountControl.PASSWD_NOTREQD, false); directoryEntry.CommitChanges(); } @@ -252,6 +255,10 @@ internal void UpdateADObject(ObjectClass oclass, directoryEntry.CommitChanges(); } + UserAccountControl.Set(directoryEntry.Properties[ActiveDirectoryConnector.ATT_USER_ACOUNT_CONTROL], + UserAccountControl.PASSWD_NOTREQD, false); + + directoryEntry.CommitChanges(); HandleNameChange(type, directoryEntry, attributes); HandleContainerChange(type, directoryEntry, attributes, config); diff --git a/ActiveDirectoryConnector/Messages.resx b/ActiveDirectoryConnector/Messages.resx index ca71c015..1c5b7bb7 100644 --- a/ActiveDirectoryConnector/Messages.resx +++ b/ActiveDirectoryConnector/Messages.resx @@ -255,4 +255,7 @@ Search Container was not supplied. + + Using Identity Manger Resource Adapter style query '{0}'. This should be updated to use the new connector query syntax. + \ No newline at end of file diff --git a/ActiveDirectoryConnector/ObjectClasses.xml b/ActiveDirectoryConnector/ObjectClasses.xml index 911d7c36..86cf2941 100644 --- a/ActiveDirectoryConnector/ObjectClasses.xml +++ b/ActiveDirectoryConnector/ObjectClasses.xml @@ -104,7 +104,7 @@ - + diff --git a/ActiveDirectoryConnectorTests/ActiveDirectoryConnectorTest.cs b/ActiveDirectoryConnectorTests/ActiveDirectoryConnectorTest.cs index b70889c6..8b9d562a 100644 --- a/ActiveDirectoryConnectorTests/ActiveDirectoryConnectorTest.cs +++ b/ActiveDirectoryConnectorTests/ActiveDirectoryConnectorTest.cs @@ -65,6 +65,9 @@ public class ActiveDirectoryConnectorTest public static readonly string CONFIG_PROPERTY_GC_DOMAIN_CONTROLLER = "config_sync_gc_domain_controller"; public static readonly string TEST_PARAM_SHARED_HOME_FOLDER = "test_param_shared_home_folder"; + // having troubles with duplicate random numbers + public static List randomList = new List(); + [Test] public void TestConfiguration() { ActiveDirectoryConfiguration config = new ActiveDirectoryConfiguration(); @@ -2711,14 +2714,26 @@ public GuardedString GetGuardedString(string regularString) int GetRandomNumber() { - const int randomRange = 100000; - int number = _rand.Next(randomRange); + const int randomRange = 10000000; + + int number = 0; + + // having trouble with duplicate random numbers, so try a few hundred + // times to get a unique one before giving up. + for(int i=0;i<500;i++) { + number = _rand.Next(randomRange); #if DEBUG // make sure the debug numbers are in a different // range than release ones to eliminate conflicts during // the build where both configurations are run concurrently - number += randomRange; + number += randomRange; #endif + if(!(randomList.Contains(number))) { + randomList.Add(number); + break; + } + } + return number; } From 68f4ecaecd75418cd16693169a79b5d27a518941 Mon Sep 17 00:00:00 2001 From: dvernon Date: Tue, 16 Dec 2008 20:22:12 +0000 Subject: [PATCH 114/342] Changes for IDM unit tests --- .../ActiveDirectoryConnector.cs | 40 ++++++++++++------- .../CustomAttributeHandlers.cs | 7 +++- 2 files changed, 31 insertions(+), 16 deletions(-) diff --git a/ActiveDirectoryConnector/ActiveDirectoryConnector.cs b/ActiveDirectoryConnector/ActiveDirectoryConnector.cs index f406525a..0610756f 100644 --- a/ActiveDirectoryConnector/ActiveDirectoryConnector.cs +++ b/ActiveDirectoryConnector/ActiveDirectoryConnector.cs @@ -1109,29 +1109,41 @@ public Uid Authenticate(ObjectClass objectClass, string username, public ConnectorAttribute NormalizeAttribute(ObjectClass oclass, ConnectorAttribute attribute) { - // if this gets big, use deleagates, but for now, just - // handle individual attirbutes; + // if this gets big, use delegates, but for now, just + // handle individual attributes; if (attribute is Uid) { - StringBuilder normalizedUidValue = new StringBuilder(); String uidValue = ((Uid)attribute).GetUidValue(); // convert to upper case if (uidValue != null) { - uidValue = uidValue.ToUpper(); - - // now remove spaces - foreach(Char nextChar in uidValue) { - if(!nextChar.Equals(" ")) { - normalizedUidValue.Append(nextChar); + StringBuilder normalizedUidValue = new StringBuilder(); + + if (oclass.Equals(ObjectClass.ACCOUNT)) + { + // convert to upper case + uidValue = uidValue.ToUpper(); + + // now remove spaces + foreach (Char nextChar in uidValue) + { + if (!nextChar.Equals(" ")) + { + normalizedUidValue.Append(nextChar); + } } - } - return new Uid(normalizedUidValue.ToString()); + return new Uid(normalizedUidValue.ToString()); + } + else + { + // the uid is a dn + return new Uid(ActiveDirectoryUtils.NormalizeLdapString(uidValue)); + } } - else - { - return attribute; + else + { + return attribute; } } else if (attribute is Name) diff --git a/ActiveDirectoryConnector/CustomAttributeHandlers.cs b/ActiveDirectoryConnector/CustomAttributeHandlers.cs index 911bfb1e..101e8275 100644 --- a/ActiveDirectoryConnector/CustomAttributeHandlers.cs +++ b/ActiveDirectoryConnector/CustomAttributeHandlers.cs @@ -794,7 +794,7 @@ private ConnectorAttribute GetCaFromDe_OpAtt_Name( throw new ConnectorException("There should be exactly one value for the name attribute"); } - return ConnectorAttributeBuilder.Build(Name.NAME, ActiveDirectoryUtils.NormalizeLdapString(value)); + return ConnectorAttributeBuilder.Build(Name.NAME, /*ActiveDirectoryUtils.NormalizeLdapString(*/value/*)*/); } private ConnectorAttribute GetCaFromDe_OpAtt_Uid( @@ -820,7 +820,10 @@ private ConnectorAttribute GetCaFromDe_OpAtt_Uid( else { ConnectorAttribute name = GetCaFromDe_OpAtt_Name(oclass, attributeName, searchResult); - value = name.Value; + if ((name.Value != null) && (name.Value.Count != 0)) + { + value.Add(ActiveDirectoryUtils.NormalizeLdapString((String)name.Value[0])); + } } return ConnectorAttributeBuilder.Build(Uid.NAME, value); From 9ad1107cef6b6d574532c16abd4e7f6e63873102 Mon Sep 17 00:00:00 2001 From: dvernon Date: Tue, 16 Dec 2008 21:53:50 +0000 Subject: [PATCH 115/342] Issue #372 - Requiring admin, password, search container, and domain --- .../ActiveDirectoryConfiguration.cs | 12 ++++++++---- 1 file changed, 8 insertions(+), 4 deletions(-) diff --git a/ActiveDirectoryConnector/ActiveDirectoryConfiguration.cs b/ActiveDirectoryConnector/ActiveDirectoryConfiguration.cs index d58cd36a..20d662b5 100644 --- a/ActiveDirectoryConnector/ActiveDirectoryConfiguration.cs +++ b/ActiveDirectoryConnector/ActiveDirectoryConfiguration.cs @@ -32,11 +32,13 @@ namespace Org.IdentityConnectors.ActiveDirectory { public class ActiveDirectoryConfiguration : Org.IdentityConnectors.Framework.Spi.AbstractConfiguration { - [ConfigurationProperty(Confidential = false, DisplayMessageKey = "display_DirectoryAdminName", HelpMessageKey = "help_DirectoryAdminName", Order = 1)] + [ConfigurationProperty(Confidential = false, DisplayMessageKey = "display_DirectoryAdminName", + Required = true, HelpMessageKey = "help_DirectoryAdminName", Order = 1)] public String DirectoryAdminName { get; set; } - [ConfigurationProperty(Confidential = true, DisplayMessageKey = "display_DirectoryAdminPassword", HelpMessageKey = "help_DirectoryAdminPassword", Order = 2)] + [ConfigurationProperty(Confidential = true, DisplayMessageKey = "display_DirectoryAdminPassword", + Required = true, HelpMessageKey = "help_DirectoryAdminPassword", Order = 2)] public String DirectoryAdminPassword { get; set; } @@ -44,7 +46,8 @@ public String DirectoryAdminPassword public String ObjectClass { get; set; } - [ConfigurationProperty(Confidential = false, DisplayMessageKey = "display_SearchContainer", HelpMessageKey = "help_SearchContainer", Order = 4)] + [ConfigurationProperty(Confidential = false, DisplayMessageKey = "display_SearchContainer", + Required=true, HelpMessageKey = "help_SearchContainer", Order = 4)] public String SearchContainer { get; set; } @@ -58,7 +61,8 @@ public String LDAPHostName [ConfigurationProperty(Confidential = false, DisplayMessageKey = "display_SearchChildDomains", HelpMessageKey = "help_SearchChildDomains", Order = 7)] public bool SearchChildDomains { get; set; } - [ConfigurationProperty(Confidential = false, DisplayMessageKey = "display_domainName", HelpMessageKey = "help_domainName", Order = 8)] + [ConfigurationProperty(Confidential = false, DisplayMessageKey = "display_domainName", + Required = true, HelpMessageKey = "help_domainName", Order = 8)] public String DomainName { get; set; } From baa45b290285f726bba9aefbcff1a2a8f49cacde Mon Sep 17 00:00:00 2001 From: dvernon Date: Tue, 16 Dec 2008 22:36:19 +0000 Subject: [PATCH 116/342] Issue #373 - Adding exception for when the the search container does not exist and test is called. --- .../ActiveDirectoryConnector.cs | 41 +++++++++++++++---- ActiveDirectoryConnector/Messages.resx | 3 ++ 2 files changed, 35 insertions(+), 9 deletions(-) diff --git a/ActiveDirectoryConnector/ActiveDirectoryConnector.cs b/ActiveDirectoryConnector/ActiveDirectoryConnector.cs index 0610756f..f33afab1 100644 --- a/ActiveDirectoryConnector/ActiveDirectoryConnector.cs +++ b/ActiveDirectoryConnector/ActiveDirectoryConnector.cs @@ -529,15 +529,7 @@ private void ExecuteQuery(ObjectClass oclass, string query, } string path; - - if (useGlobalCatalog) - { - path = ActiveDirectoryUtils.GetGCPath(serverName, searchRoot); - } - else - { - path = ActiveDirectoryUtils.GetLDAPPath(serverName, searchRoot); - } + path = GetSearchContainerPath(useGlobalCatalog, serverName, searchRoot); Trace.TraceInformation("Search: Getting root node for search"); DirectoryEntry searchRootEntry = new DirectoryEntry(path, @@ -674,6 +666,27 @@ private void ExecuteQuery(ObjectClass oclass, string query, } } + private string GetSearchContainerPath() + { + return GetSearchContainerPath(UseGlobalCatalog(), _configuration.LDAPHostName, _configuration.SearchContainer); + } + + private string GetSearchContainerPath(bool useGC, string hostname, string searchContainer) + { + String path; + + if (useGC) + { + path = ActiveDirectoryUtils.GetGCPath(hostname, searchContainer); + } + else + { + path = ActiveDirectoryUtils.GetLDAPPath(hostname, searchContainer); + } + + return path; + } + private ICollection GetAttributesToReturn(ObjectClass oclass, OperationOptions options) { ICollection attributeNames = null; @@ -731,6 +744,16 @@ public virtual void Test() "Invalid Object Class was specified in the connector configuration. Object Class \'{0}\' was not found in Active Directory", _configuration.ObjectClass)); } + + // see if search container is valid + if (!DirectoryEntry.Exists(GetSearchContainerPath())) + { + throw new ConnectorException( + _configuration.ConnectorMessages.Format( + "ex_InvalidSearchContainerInConfiguration", + "An invalid search container was supplied: {0}", + _configuration.SearchContainer)); + } } #endregion diff --git a/ActiveDirectoryConnector/Messages.resx b/ActiveDirectoryConnector/Messages.resx index 1c5b7bb7..d685fb80 100644 --- a/ActiveDirectoryConnector/Messages.resx +++ b/ActiveDirectoryConnector/Messages.resx @@ -258,4 +258,7 @@ Using Identity Manger Resource Adapter style query '{0}'. This should be updated to use the new connector query syntax. + + An invalid search container was supplied: {0} + \ No newline at end of file From b1527591eabc02c6f3d397173b7e4b9a34a456a5 Mon Sep 17 00:00:00 2001 From: dvernon Date: Thu, 18 Dec 2008 22:03:31 +0000 Subject: [PATCH 117/342] Issue #376 - Updating ObjectClasses.xml to conform to the new dtd. --- ActiveDirectoryConnector/ObjectClasses.xml | 286 +++++++++++++++------ 1 file changed, 211 insertions(+), 75 deletions(-) diff --git a/ActiveDirectoryConnector/ObjectClasses.xml b/ActiveDirectoryConnector/ObjectClasses.xml index 86cf2941..7dd21635 100644 --- a/ActiveDirectoryConnector/ObjectClasses.xml +++ b/ActiveDirectoryConnector/ObjectClasses.xml @@ -39,88 +39,224 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + - - - - - - - - - - - - - - - + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + - - - - - + + + + + + + + + + + + + + + + + + + From d1a31e1e3d310f616d97cc756c64967d3b03210f Mon Sep 17 00:00:00 2001 From: dvernon Date: Fri, 19 Dec 2008 00:25:08 +0000 Subject: [PATCH 118/342] Fixing contract tests --- ActiveDirectoryConnector/CustomAttributeHandlers.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/ActiveDirectoryConnector/CustomAttributeHandlers.cs b/ActiveDirectoryConnector/CustomAttributeHandlers.cs index 101e8275..50c175a8 100644 --- a/ActiveDirectoryConnector/CustomAttributeHandlers.cs +++ b/ActiveDirectoryConnector/CustomAttributeHandlers.cs @@ -794,7 +794,7 @@ private ConnectorAttribute GetCaFromDe_OpAtt_Name( throw new ConnectorException("There should be exactly one value for the name attribute"); } - return ConnectorAttributeBuilder.Build(Name.NAME, /*ActiveDirectoryUtils.NormalizeLdapString(*/value/*)*/); + return ConnectorAttributeBuilder.Build(Name.NAME, ActiveDirectoryUtils.NormalizeLdapString(value)); } private ConnectorAttribute GetCaFromDe_OpAtt_Uid( From ffb1b7894564b2014e0addf37ae18731e83f41a3 Mon Sep 17 00:00:00 2001 From: petrjung Date: Mon, 22 Dec 2008 13:55:08 +0000 Subject: [PATCH 119/342] issue 375 C# framework and ConnectorServer need to be published --- Service/Service.csproj | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/Service/Service.csproj b/Service/Service.csproj index 78cc3b47..4692550b 100644 --- a/Service/Service.csproj +++ b/Service/Service.csproj @@ -66,6 +66,7 @@ 4096 + @@ -92,6 +93,14 @@ + + + + + + + + {F140E8DA-52B4-4159-992A-9DA10EA8EEFB} From 197d3dafae428b6ff3187a4662009742fd4cc06e Mon Sep 17 00:00:00 2001 From: petrjung Date: Mon, 22 Dec 2008 14:24:34 +0000 Subject: [PATCH 120/342] issue 375, C# framework and ConnectorServer need to be published --- Service/Service.csproj | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/Service/Service.csproj b/Service/Service.csproj index 4692550b..1bbd2847 100644 --- a/Service/Service.csproj +++ b/Service/Service.csproj @@ -99,7 +99,8 @@ - + + From 207a732346a42e8625d9b066689452250962f925 Mon Sep 17 00:00:00 2001 From: tknappek Date: Mon, 22 Dec 2008 14:33:04 +0000 Subject: [PATCH 121/342] updated to follow the latest framework changes --- ExchangeConnector/ExchangeConnector.cs | 4 +- ExchangeConnector/LegacyExchangeConnector.cs | 17 ++- ExchangeConnector/Messages.resx | 137 +------------------ 3 files changed, 14 insertions(+), 144 deletions(-) diff --git a/ExchangeConnector/ExchangeConnector.cs b/ExchangeConnector/ExchangeConnector.cs index 8af0c7b3..9ca43a69 100644 --- a/ExchangeConnector/ExchangeConnector.cs +++ b/ExchangeConnector/ExchangeConnector.cs @@ -147,10 +147,10 @@ public override void Sync(ObjectClass objClass, SyncToken token, /// Implementation of SynOp.GetLatestSyncToken /// /// - public override SyncToken GetLatestSyncToken() + public override SyncToken GetLatestSyncToken(ObjectClass oclass) { //TODO: Implement GetLatestSyncToken - return base.GetLatestSyncToken(); + return base.GetLatestSyncToken(oclass); } /// diff --git a/ExchangeConnector/LegacyExchangeConnector.cs b/ExchangeConnector/LegacyExchangeConnector.cs index 9d39edf2..8b0aab56 100644 --- a/ExchangeConnector/LegacyExchangeConnector.cs +++ b/ExchangeConnector/LegacyExchangeConnector.cs @@ -39,7 +39,8 @@ namespace Org.IdentityConnectors.Exchange /// [ConnectorClass("connector_displayName", typeof(ExchangeConfiguration), - MessageCatalogPath = "Org.IdentityConnectors.ActiveDirectory.Messages" + MessageCatalogPaths = new[] { "Org.IdentityConnectors.Exchange.Messages", + "Org.IdentityConnectors.ActiveDirectory.Messages" } )] public class LegacyExchangeConnector : ActiveDirectoryConnector { @@ -60,13 +61,17 @@ public class LegacyExchangeConnector : ActiveDirectoryConnector }; private static readonly ConnectorAttributeInfo ATTINFO_RECIPIENT_TYPE = - ConnectorAttributeInfoBuilder.Build(ATT_RECIPIENT_TYPE, typeof(string), true, true, true, false); + ConnectorAttributeInfoBuilder.Build(ATT_RECIPIENT_TYPE, typeof(string), ConnectorAttributeInfo.Flags.REQUIRED | + ConnectorAttributeInfo.Flags.NOT_UPDATEABLE | ConnectorAttributeInfo.Flags.NOT_RETURNED_BY_DEFAULT); + + private static readonly ConnectorAttributeInfo ATTINFO_EXTERNAL_MAIL = - ConnectorAttributeInfoBuilder.Build(ATT_EXTERNAL_MAIL, typeof(string), false); + ConnectorAttributeInfoBuilder.Build(ATT_EXTERNAL_MAIL, typeof(string), ConnectorAttributeInfo.Flags.NOT_RETURNED_BY_DEFAULT | + ConnectorAttributeInfo.Flags.MULTIVALUED); private static readonly ConnectorAttributeInfo ATTINFO_DATABASE = - ConnectorAttributeInfoBuilder.Build(ATT_DATABASE, typeof(string), false, true, true, false); + ConnectorAttributeInfoBuilder.Build(ATT_DATABASE, typeof(string), ConnectorAttributeInfo.Flags.NOT_RETURNED_BY_DEFAULT); private const string RCPT_TYPE_MAIL_BOX = "mailbox"; private const string RCPT_TYPE_MAIL_USER = "mailuser"; @@ -218,10 +223,10 @@ public override void Sync(ObjectClass objClass, SyncToken token, /// Implementation of SynOp.GetLatestSyncToken /// /// - public override SyncToken GetLatestSyncToken() + public override SyncToken GetLatestSyncToken(ObjectClass oclass) { //TODO: Implement GetLatestSyncToken - return base.GetLatestSyncToken(); + return base.GetLatestSyncToken(oclass); } /// diff --git a/ExchangeConnector/Messages.resx b/ExchangeConnector/Messages.resx index 8ab7757c..3960efa6 100644 --- a/ExchangeConnector/Messages.resx +++ b/ExchangeConnector/Messages.resx @@ -1,4 +1,4 @@ - + + + + + + + diff --git a/DotNetConnectors.sln b/DotNetConnectors.sln index 14b67eb8..c59e1e95 100644 --- a/DotNetConnectors.sln +++ b/DotNetConnectors.sln @@ -1,7 +1,6 @@  Microsoft Visual Studio Solution File, Format Version 10.00 # Visual Studio 2008 -# SharpDevelop 3.0.0.3437 Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Service", "Service\Service.csproj", "{A9D6374A-D51F-4FA3-8C02-5B1D23FAA82E}" EndProject Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Framework", "Framework\Framework.csproj", "{8B24461B-456A-4032-89A1-CD418F7B5B62}" @@ -14,7 +13,7 @@ Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "TestBundleV1", "TestBundleV EndProject Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "TestBundleV2", "TestBundleV2\TestBundleV2.csproj", "{3E737796-3A83-4924-9FF1-DC542F21F59E}" EndProject -Project("{CFEE4113-1246-4D54-95CB-156813CB8593}") = "ServiceInstall", "ServiceInstall\ServiceInstall.wixproj", "{1CBA8F74-050C-432B-8437-08BD13BDC684}" +Project("{930C7802-8A8C-48F9-8165-68863BCCD9DD}") = "ServiceInstall", "ServiceInstall\ServiceInstall.wixproj", "{1CBA8F74-050C-432B-8437-08BD13BDC684}" EndProject Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Console", "Console\Console.csproj", "{D09E6A1F-55F6-4661-8E5A-C485E4B08BB3}" EndProject @@ -34,69 +33,58 @@ Global Release|Any CPU = Release|Any CPU EndGlobalSection GlobalSection(ProjectConfigurationPlatforms) = postSolution - {A9D6374A-D51F-4FA3-8C02-5B1D23FAA82E}.Debug.ActiveCfg = Debug - {A9D6374A-D51F-4FA3-8C02-5B1D23FAA82E}.Debug.Build.0 = Debug {A9D6374A-D51F-4FA3-8C02-5B1D23FAA82E}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {A9D6374A-D51F-4FA3-8C02-5B1D23FAA82E}.Release.ActiveCfg = Release - {A9D6374A-D51F-4FA3-8C02-5B1D23FAA82E}.Release.Build.0 = Release - {A9D6374A-D51F-4FA3-8C02-5B1D23FAA82E}.Release|Any CPU.ActiveCfg = Release|Any CPU - {D09E6A1F-55F6-4661-8E5A-C485E4B08BB3}.Debug.ActiveCfg = Debug - {D09E6A1F-55F6-4661-8E5A-C485E4B08BB3}.Debug.Build.0 = Debug - {D09E6A1F-55F6-4661-8E5A-C485E4B08BB3}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {D09E6A1F-55F6-4661-8E5A-C485E4B08BB3}.Release.ActiveCfg = Release - {D09E6A1F-55F6-4661-8E5A-C485E4B08BB3}.Release.Build.0 = Release - {D09E6A1F-55F6-4661-8E5A-C485E4B08BB3}.Release|Any CPU.ActiveCfg = Release|Any CPU - {D09E6A1F-55F6-4661-8E5A-C485E4B08BB3}.Debug|Any CPU.Build.0 = Debug|Any CPU - {D09E6A1F-55F6-4661-8E5A-C485E4B08BB3}.Release|Any CPU.Build.0 = Release|Any CPU {A9D6374A-D51F-4FA3-8C02-5B1D23FAA82E}.Debug|Any CPU.Build.0 = Debug|Any CPU + {A9D6374A-D51F-4FA3-8C02-5B1D23FAA82E}.Release|Any CPU.ActiveCfg = Release|Any CPU {A9D6374A-D51F-4FA3-8C02-5B1D23FAA82E}.Release|Any CPU.Build.0 = Release|Any CPU - {8B24461B-456A-4032-89A1-CD418F7B5B62}.Debug.Build.0 = Debug - {8B24461B-456A-4032-89A1-CD418F7B5B62}.Debug.ActiveCfg = Debug - {8B24461B-456A-4032-89A1-CD418F7B5B62}.Release.Build.0 = Release - {8B24461B-456A-4032-89A1-CD418F7B5B62}.Release.ActiveCfg = Release - {8B24461B-456A-4032-89A1-CD418F7B5B62}.Debug|Any CPU.Build.0 = Debug|Any CPU {8B24461B-456A-4032-89A1-CD418F7B5B62}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {8B24461B-456A-4032-89A1-CD418F7B5B62}.Release|Any CPU.Build.0 = Release|Any CPU + {8B24461B-456A-4032-89A1-CD418F7B5B62}.Debug|Any CPU.Build.0 = Debug|Any CPU {8B24461B-456A-4032-89A1-CD418F7B5B62}.Release|Any CPU.ActiveCfg = Release|Any CPU - {F140E8DA-52B4-4159-992A-9DA10EA8EEFB}.Debug|Any CPU.Build.0 = Debug|Any CPU + {8B24461B-456A-4032-89A1-CD418F7B5B62}.Release|Any CPU.Build.0 = Release|Any CPU {F140E8DA-52B4-4159-992A-9DA10EA8EEFB}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {F140E8DA-52B4-4159-992A-9DA10EA8EEFB}.Release|Any CPU.Build.0 = Release|Any CPU + {F140E8DA-52B4-4159-992A-9DA10EA8EEFB}.Debug|Any CPU.Build.0 = Debug|Any CPU {F140E8DA-52B4-4159-992A-9DA10EA8EEFB}.Release|Any CPU.ActiveCfg = Release|Any CPU - {32804F5A-812C-4FA6-835C-BDAE5B24D355}.Debug|Any CPU.Build.0 = Debug|Any CPU + {F140E8DA-52B4-4159-992A-9DA10EA8EEFB}.Release|Any CPU.Build.0 = Release|Any CPU {32804F5A-812C-4FA6-835C-BDAE5B24D355}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {32804F5A-812C-4FA6-835C-BDAE5B24D355}.Release|Any CPU.Build.0 = Release|Any CPU + {32804F5A-812C-4FA6-835C-BDAE5B24D355}.Debug|Any CPU.Build.0 = Debug|Any CPU {32804F5A-812C-4FA6-835C-BDAE5B24D355}.Release|Any CPU.ActiveCfg = Release|Any CPU - {0BC2A013-56FE-46DD-90FC-2D2D57748FB6}.Debug|Any CPU.Build.0 = Debug|Any CPU + {32804F5A-812C-4FA6-835C-BDAE5B24D355}.Release|Any CPU.Build.0 = Release|Any CPU {0BC2A013-56FE-46DD-90FC-2D2D57748FB6}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {0BC2A013-56FE-46DD-90FC-2D2D57748FB6}.Release|Any CPU.Build.0 = Release|Any CPU + {0BC2A013-56FE-46DD-90FC-2D2D57748FB6}.Debug|Any CPU.Build.0 = Debug|Any CPU {0BC2A013-56FE-46DD-90FC-2D2D57748FB6}.Release|Any CPU.ActiveCfg = Release|Any CPU - {3E737796-3A83-4924-9FF1-DC542F21F59E}.Debug|Any CPU.Build.0 = Debug|Any CPU + {0BC2A013-56FE-46DD-90FC-2D2D57748FB6}.Release|Any CPU.Build.0 = Release|Any CPU {3E737796-3A83-4924-9FF1-DC542F21F59E}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {3E737796-3A83-4924-9FF1-DC542F21F59E}.Release|Any CPU.Build.0 = Release|Any CPU + {3E737796-3A83-4924-9FF1-DC542F21F59E}.Debug|Any CPU.Build.0 = Debug|Any CPU {3E737796-3A83-4924-9FF1-DC542F21F59E}.Release|Any CPU.ActiveCfg = Release|Any CPU - {1CBA8F74-050C-432B-8437-08BD13BDC684}.Debug|Any CPU.Build.0 = Debug|Any CPU - {1CBA8F74-050C-432B-8437-08BD13BDC684}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {1CBA8F74-050C-432B-8437-08BD13BDC684}.Release|Any CPU.Build.0 = Release|Any CPU - {1CBA8F74-050C-432B-8437-08BD13BDC684}.Release|Any CPU.ActiveCfg = Release|Any CPU - {5B011775-B121-4EEE-A410-BA2D2F5BFB8B}.Debug|Any CPU.Build.0 = Debug|Any CPU + {3E737796-3A83-4924-9FF1-DC542F21F59E}.Release|Any CPU.Build.0 = Release|Any CPU + {1CBA8F74-050C-432B-8437-08BD13BDC684}.Debug|Any CPU.ActiveCfg = Debug|x86 + {1CBA8F74-050C-432B-8437-08BD13BDC684}.Release|Any CPU.ActiveCfg = Release|x86 + {D09E6A1F-55F6-4661-8E5A-C485E4B08BB3}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {D09E6A1F-55F6-4661-8E5A-C485E4B08BB3}.Debug|Any CPU.Build.0 = Debug|Any CPU + {D09E6A1F-55F6-4661-8E5A-C485E4B08BB3}.Release|Any CPU.ActiveCfg = Release|Any CPU + {D09E6A1F-55F6-4661-8E5A-C485E4B08BB3}.Release|Any CPU.Build.0 = Release|Any CPU {5B011775-B121-4EEE-A410-BA2D2F5BFB8B}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {5B011775-B121-4EEE-A410-BA2D2F5BFB8B}.Release|Any CPU.Build.0 = Release|Any CPU + {5B011775-B121-4EEE-A410-BA2D2F5BFB8B}.Debug|Any CPU.Build.0 = Debug|Any CPU {5B011775-B121-4EEE-A410-BA2D2F5BFB8B}.Release|Any CPU.ActiveCfg = Release|Any CPU - {0747C440-70E4-4E63-9F9D-03B3A010C991}.Debug|Any CPU.Build.0 = Debug|Any CPU + {5B011775-B121-4EEE-A410-BA2D2F5BFB8B}.Release|Any CPU.Build.0 = Release|Any CPU {0747C440-70E4-4E63-9F9D-03B3A010C991}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {0747C440-70E4-4E63-9F9D-03B3A010C991}.Release|Any CPU.Build.0 = Release|Any CPU + {0747C440-70E4-4E63-9F9D-03B3A010C991}.Debug|Any CPU.Build.0 = Debug|Any CPU {0747C440-70E4-4E63-9F9D-03B3A010C991}.Release|Any CPU.ActiveCfg = Release|Any CPU - {BDF495CA-0FCD-4E51-A871-D467CDE3B43E}.Debug|Any CPU.Build.0 = Debug|Any CPU - {BDF495CA-0FCD-4E51-A871-D467CDE3B43E}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {BDF495CA-0FCD-4E51-A871-D467CDE3B43E}.Release|Any CPU.Build.0 = Release|Any CPU - {BDF495CA-0FCD-4E51-A871-D467CDE3B43E}.Release|Any CPU.ActiveCfg = Release|Any CPU - {4700690A-2D29-40A0-86AC-E5A9F71A479A}.Debug|Any CPU.Build.0 = Debug|Any CPU + {0747C440-70E4-4E63-9F9D-03B3A010C991}.Release|Any CPU.Build.0 = Release|Any CPU {4700690A-2D29-40A0-86AC-E5A9F71A479A}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {4700690A-2D29-40A0-86AC-E5A9F71A479A}.Release|Any CPU.Build.0 = Release|Any CPU + {4700690A-2D29-40A0-86AC-E5A9F71A479A}.Debug|Any CPU.Build.0 = Debug|Any CPU {4700690A-2D29-40A0-86AC-E5A9F71A479A}.Release|Any CPU.ActiveCfg = Release|Any CPU - {DB8F7DA9-11A2-4BEB-8C14-25D8229EBE7E}.Debug|Any CPU.Build.0 = Debug|Any CPU + {4700690A-2D29-40A0-86AC-E5A9F71A479A}.Release|Any CPU.Build.0 = Release|Any CPU + {BDF495CA-0FCD-4E51-A871-D467CDE3B43E}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {BDF495CA-0FCD-4E51-A871-D467CDE3B43E}.Debug|Any CPU.Build.0 = Debug|Any CPU + {BDF495CA-0FCD-4E51-A871-D467CDE3B43E}.Release|Any CPU.ActiveCfg = Release|Any CPU + {BDF495CA-0FCD-4E51-A871-D467CDE3B43E}.Release|Any CPU.Build.0 = Release|Any CPU {DB8F7DA9-11A2-4BEB-8C14-25D8229EBE7E}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {DB8F7DA9-11A2-4BEB-8C14-25D8229EBE7E}.Release|Any CPU.Build.0 = Release|Any CPU + {DB8F7DA9-11A2-4BEB-8C14-25D8229EBE7E}.Debug|Any CPU.Build.0 = Debug|Any CPU {DB8F7DA9-11A2-4BEB-8C14-25D8229EBE7E}.Release|Any CPU.ActiveCfg = Release|Any CPU + {DB8F7DA9-11A2-4BEB-8C14-25D8229EBE7E}.Release|Any CPU.Build.0 = Release|Any CPU + EndGlobalSection + GlobalSection(SolutionProperties) = preSolution + HideSolutionNode = FALSE EndGlobalSection EndGlobal diff --git a/ExchangeConnector/build.xml b/ExchangeConnector/build.xml new file mode 100644 index 00000000..f51519a7 --- /dev/null +++ b/ExchangeConnector/build.xml @@ -0,0 +1,28 @@ + + + + + + + diff --git a/ServiceInstall/ServiceInstall.wixproj b/ServiceInstall/ServiceInstall.wixproj index 3012a219..4af8a460 100644 --- a/ServiceInstall/ServiceInstall.wixproj +++ b/ServiceInstall/ServiceInstall.wixproj @@ -1,4 +1,4 @@ - + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file From 5b975be6976ef05614fee9eab91bd9470e35a623 Mon Sep 17 00:00:00 2001 From: petrjung Date: Mon, 22 Dec 2008 18:25:26 +0000 Subject: [PATCH 126/342] issue 323 Contract tests need to be run for Active Directory during the Hudson build, revert unwanted changes --- DotNetConnectors.sln | 90 --------------------------- ServiceInstall/ServiceInstall.wixproj | 8 +-- 2 files changed, 3 insertions(+), 95 deletions(-) delete mode 100644 DotNetConnectors.sln diff --git a/DotNetConnectors.sln b/DotNetConnectors.sln deleted file mode 100644 index c59e1e95..00000000 --- a/DotNetConnectors.sln +++ /dev/null @@ -1,90 +0,0 @@ - -Microsoft Visual Studio Solution File, Format Version 10.00 -# Visual Studio 2008 -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Service", "Service\Service.csproj", "{A9D6374A-D51F-4FA3-8C02-5B1D23FAA82E}" -EndProject -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Framework", "Framework\Framework.csproj", "{8B24461B-456A-4032-89A1-CD418F7B5B62}" -EndProject -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Common", "Common\Common.csproj", "{F140E8DA-52B4-4159-992A-9DA10EA8EEFB}" -EndProject -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "FrameworkTests", "FrameworkTests\FrameworkTests.csproj", "{32804F5A-812C-4FA6-835C-BDAE5B24D355}" -EndProject -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "TestBundleV1", "TestBundleV1\TestBundleV1.csproj", "{0BC2A013-56FE-46DD-90FC-2D2D57748FB6}" -EndProject -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "TestBundleV2", "TestBundleV2\TestBundleV2.csproj", "{3E737796-3A83-4924-9FF1-DC542F21F59E}" -EndProject -Project("{930C7802-8A8C-48F9-8165-68863BCCD9DD}") = "ServiceInstall", "ServiceInstall\ServiceInstall.wixproj", "{1CBA8F74-050C-432B-8437-08BD13BDC684}" -EndProject -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Console", "Console\Console.csproj", "{D09E6A1F-55F6-4661-8E5A-C485E4B08BB3}" -EndProject -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "FrameworkInternal", "FrameworkInternal\FrameworkInternal.csproj", "{5B011775-B121-4EEE-A410-BA2D2F5BFB8B}" -EndProject -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "BooScriptExecutorFactory", "BooScriptExecutorFactory\BooScriptExecutorFactory.csproj", "{0747C440-70E4-4E63-9F9D-03B3A010C991}" -EndProject -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "ShellScriptExecutorFactory", "ShellScriptExecutorFactory\ShellScriptExecutorFactory.csproj", "{4700690A-2D29-40A0-86AC-E5A9F71A479A}" -EndProject -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "ActiveDirectoryConnector", "ActiveDirectoryConnector\ActiveDirectoryConnector.csproj", "{BDF495CA-0FCD-4E51-A871-D467CDE3B43E}" -EndProject -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "ActiveDirectoryConnectorTests", "ActiveDirectoryConnectorTests\ActiveDirectoryConnectorTests.csproj", "{DB8F7DA9-11A2-4BEB-8C14-25D8229EBE7E}" -EndProject -Global - GlobalSection(SolutionConfigurationPlatforms) = preSolution - Debug|Any CPU = Debug|Any CPU - Release|Any CPU = Release|Any CPU - EndGlobalSection - GlobalSection(ProjectConfigurationPlatforms) = postSolution - {A9D6374A-D51F-4FA3-8C02-5B1D23FAA82E}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {A9D6374A-D51F-4FA3-8C02-5B1D23FAA82E}.Debug|Any CPU.Build.0 = Debug|Any CPU - {A9D6374A-D51F-4FA3-8C02-5B1D23FAA82E}.Release|Any CPU.ActiveCfg = Release|Any CPU - {A9D6374A-D51F-4FA3-8C02-5B1D23FAA82E}.Release|Any CPU.Build.0 = Release|Any CPU - {8B24461B-456A-4032-89A1-CD418F7B5B62}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {8B24461B-456A-4032-89A1-CD418F7B5B62}.Debug|Any CPU.Build.0 = Debug|Any CPU - {8B24461B-456A-4032-89A1-CD418F7B5B62}.Release|Any CPU.ActiveCfg = Release|Any CPU - {8B24461B-456A-4032-89A1-CD418F7B5B62}.Release|Any CPU.Build.0 = Release|Any CPU - {F140E8DA-52B4-4159-992A-9DA10EA8EEFB}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {F140E8DA-52B4-4159-992A-9DA10EA8EEFB}.Debug|Any CPU.Build.0 = Debug|Any CPU - {F140E8DA-52B4-4159-992A-9DA10EA8EEFB}.Release|Any CPU.ActiveCfg = Release|Any CPU - {F140E8DA-52B4-4159-992A-9DA10EA8EEFB}.Release|Any CPU.Build.0 = Release|Any CPU - {32804F5A-812C-4FA6-835C-BDAE5B24D355}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {32804F5A-812C-4FA6-835C-BDAE5B24D355}.Debug|Any CPU.Build.0 = Debug|Any CPU - {32804F5A-812C-4FA6-835C-BDAE5B24D355}.Release|Any CPU.ActiveCfg = Release|Any CPU - {32804F5A-812C-4FA6-835C-BDAE5B24D355}.Release|Any CPU.Build.0 = Release|Any CPU - {0BC2A013-56FE-46DD-90FC-2D2D57748FB6}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {0BC2A013-56FE-46DD-90FC-2D2D57748FB6}.Debug|Any CPU.Build.0 = Debug|Any CPU - {0BC2A013-56FE-46DD-90FC-2D2D57748FB6}.Release|Any CPU.ActiveCfg = Release|Any CPU - {0BC2A013-56FE-46DD-90FC-2D2D57748FB6}.Release|Any CPU.Build.0 = Release|Any CPU - {3E737796-3A83-4924-9FF1-DC542F21F59E}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {3E737796-3A83-4924-9FF1-DC542F21F59E}.Debug|Any CPU.Build.0 = Debug|Any CPU - {3E737796-3A83-4924-9FF1-DC542F21F59E}.Release|Any CPU.ActiveCfg = Release|Any CPU - {3E737796-3A83-4924-9FF1-DC542F21F59E}.Release|Any CPU.Build.0 = Release|Any CPU - {1CBA8F74-050C-432B-8437-08BD13BDC684}.Debug|Any CPU.ActiveCfg = Debug|x86 - {1CBA8F74-050C-432B-8437-08BD13BDC684}.Release|Any CPU.ActiveCfg = Release|x86 - {D09E6A1F-55F6-4661-8E5A-C485E4B08BB3}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {D09E6A1F-55F6-4661-8E5A-C485E4B08BB3}.Debug|Any CPU.Build.0 = Debug|Any CPU - {D09E6A1F-55F6-4661-8E5A-C485E4B08BB3}.Release|Any CPU.ActiveCfg = Release|Any CPU - {D09E6A1F-55F6-4661-8E5A-C485E4B08BB3}.Release|Any CPU.Build.0 = Release|Any CPU - {5B011775-B121-4EEE-A410-BA2D2F5BFB8B}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {5B011775-B121-4EEE-A410-BA2D2F5BFB8B}.Debug|Any CPU.Build.0 = Debug|Any CPU - {5B011775-B121-4EEE-A410-BA2D2F5BFB8B}.Release|Any CPU.ActiveCfg = Release|Any CPU - {5B011775-B121-4EEE-A410-BA2D2F5BFB8B}.Release|Any CPU.Build.0 = Release|Any CPU - {0747C440-70E4-4E63-9F9D-03B3A010C991}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {0747C440-70E4-4E63-9F9D-03B3A010C991}.Debug|Any CPU.Build.0 = Debug|Any CPU - {0747C440-70E4-4E63-9F9D-03B3A010C991}.Release|Any CPU.ActiveCfg = Release|Any CPU - {0747C440-70E4-4E63-9F9D-03B3A010C991}.Release|Any CPU.Build.0 = Release|Any CPU - {4700690A-2D29-40A0-86AC-E5A9F71A479A}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {4700690A-2D29-40A0-86AC-E5A9F71A479A}.Debug|Any CPU.Build.0 = Debug|Any CPU - {4700690A-2D29-40A0-86AC-E5A9F71A479A}.Release|Any CPU.ActiveCfg = Release|Any CPU - {4700690A-2D29-40A0-86AC-E5A9F71A479A}.Release|Any CPU.Build.0 = Release|Any CPU - {BDF495CA-0FCD-4E51-A871-D467CDE3B43E}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {BDF495CA-0FCD-4E51-A871-D467CDE3B43E}.Debug|Any CPU.Build.0 = Debug|Any CPU - {BDF495CA-0FCD-4E51-A871-D467CDE3B43E}.Release|Any CPU.ActiveCfg = Release|Any CPU - {BDF495CA-0FCD-4E51-A871-D467CDE3B43E}.Release|Any CPU.Build.0 = Release|Any CPU - {DB8F7DA9-11A2-4BEB-8C14-25D8229EBE7E}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {DB8F7DA9-11A2-4BEB-8C14-25D8229EBE7E}.Debug|Any CPU.Build.0 = Debug|Any CPU - {DB8F7DA9-11A2-4BEB-8C14-25D8229EBE7E}.Release|Any CPU.ActiveCfg = Release|Any CPU - {DB8F7DA9-11A2-4BEB-8C14-25D8229EBE7E}.Release|Any CPU.Build.0 = Release|Any CPU - EndGlobalSection - GlobalSection(SolutionProperties) = preSolution - HideSolutionNode = FALSE - EndGlobalSection -EndGlobal diff --git a/ServiceInstall/ServiceInstall.wixproj b/ServiceInstall/ServiceInstall.wixproj index 4af8a460..3012a219 100644 --- a/ServiceInstall/ServiceInstall.wixproj +++ b/ServiceInstall/ServiceInstall.wixproj @@ -1,4 +1,4 @@ - + + + {BDF495CA-0FCD-4E51-A871-D467CDE3B43E} + Debug + AnyCPU + Library + Org.IdentityConnectors.ActiveDirectory + ActiveDirectory.Connector + v3.5 + + + prompt + 4 + AnyCPU + bin\Debug\ + True + Full + False + True + DEBUG;TRACE + + + pdbonly + bin\Release\ + TRACE + prompt + 4 + AnyCPU + False + True + False + + + + + + 3.5 + + + + + 3.5 + + + + 3.5 + + + + + + Code + + + + + + + + + + + + + + {F140E8DA-52B4-4159-992A-9DA10EA8EEFB} + Common + + + {8B24461B-456A-4032-89A1-CD418F7B5B62} + Framework + + + {97d25db0-0363-11cf-abc4-02608c9e7553} + 1 + 0 + 0 + tlbimp + False + + + {97d25db0-0363-11cf-abc4-02608c9e7553} + 1 + 0 + 0 + tlbimp + False + + + + + Designer + + + + + + diff --git a/DotNetConnectors.sln/ActiveDirectoryConnector/ActiveDirectoryFilterTranslator.cs b/DotNetConnectors.sln/ActiveDirectoryConnector/ActiveDirectoryFilterTranslator.cs new file mode 100644 index 00000000..2b0978d7 --- /dev/null +++ b/DotNetConnectors.sln/ActiveDirectoryConnector/ActiveDirectoryFilterTranslator.cs @@ -0,0 +1,479 @@ +/* + * ==================== + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER. + * + * Copyright 2007-2008 Sun Microsystems, Inc. All rights reserved. + * + * The contents of this file are subject to the terms of the Common Development + * and Distribution License("CDDL") (the "License"). You may not use this file + * except in compliance with the License. + * + * You can obtain a copy of the License at + * http://IdentityConnectors.dev.java.net/legal/license.txt + * See the License for the specific language governing permissions and limitations + * under the License. + * + * When distributing the Covered Code, include this CDDL Header Notice in each file + * and include the License file at identityconnectors/legal/license.txt. + * If applicable, add the following below this CDDL Header, with the fields + * enclosed by brackets [] replaced by your own identifying information: + * "Portions Copyrighted [year] [name of copyright owner]" + * ==================== + */ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using Org.IdentityConnectors.Framework.Common.Objects.Filters; +using Org.IdentityConnectors.Framework.Common.Objects; +using Org.IdentityConnectors.Common; +using System.Diagnostics; +using Org.IdentityConnectors.Framework.Common.Exceptions; + +namespace Org.IdentityConnectors.ActiveDirectory +{ + /// + /// This was taken from the LDAP filter translator (java) and ported to + /// C#. There are a few changes, but not many ... that will change over + /// time of course. + /// + public class ActiveDirectoryFilterTranslator : AbstractFilterTranslator + { + protected override String CreateAndExpression(String leftExpression, + String rightExpression) { + StringBuilder builder = new StringBuilder(); + builder.Append("(&"); + builder.Append(leftExpression); + builder.Append(rightExpression); + builder.Append(')'); + return builder.ToString(); + } + + protected override String CreateOrExpression(String leftExpression, + String rightExpression) { + StringBuilder builder = new StringBuilder(); + builder.Append("(|"); + builder.Append(leftExpression); + builder.Append(rightExpression); + builder.Append(')'); + return builder.ToString(); + } + + protected override String CreateContainsExpression(ContainsFilter filter, + Boolean not) { + String[] attrNames = GetLdapNamesForAttribute(filter.GetAttribute()); + if (attrNames == null) { + return null; + } + + StringBuilder builder = new StringBuilder(); + if (not) { + builder.Append("!("); + } + if (attrNames.Length == 1) { + builder.Append('('); + builder.Append(attrNames[0]); + builder.Append("=*"); + int len = builder.Length; + GetLdapFilterValue(builder, attrNames[0], filter.GetValue()); + // Build (attr=*) rather than (attr=**) for zero-length values. + if (builder.Length != len) { + builder.Append('*'); + } + builder.Append(')'); + } else { + builder.Append("(|"); + foreach (String attrName in attrNames) { + builder.Append('('); + builder.Append(attrName); + builder.Append("=*"); + int len = builder.Length; + GetLdapFilterValue(builder, attrName, filter.GetValue()); + // Build (attr=*) rather than (attr=**) for zero-length values. + if (builder.Length != len) { + builder.Append('*'); + } + builder.Append(')'); + } + builder.Append(')'); + } + if (not) { + builder.Append(')'); + } + return builder.ToString(); + } + + protected override String CreateStartsWithExpression(StartsWithFilter filter, + Boolean not) { + String[] attrNames = GetLdapNamesForAttribute(filter.GetAttribute()); + if (attrNames == null) { + return null; + } + + StringBuilder builder = new StringBuilder(); + if (not) { + builder.Append("!("); + } + if (attrNames.Length == 1) { + builder.Append('('); + builder.Append(attrNames[0]); + builder.Append('='); + GetLdapFilterValue(builder, attrNames[0], filter.GetValue()); + builder.Append("*)"); + } else { + builder.Append("(|"); + foreach (String attrName in attrNames) { + builder.Append('('); + builder.Append(attrName); + builder.Append('='); + GetLdapFilterValue(builder, attrName, filter.GetValue()); + builder.Append("*)"); + } + builder.Append(')'); + } + if (not) { + builder.Append(')'); + } + return builder.ToString(); + } + + protected override String CreateEndsWithExpression(EndsWithFilter filter, + Boolean not) { + String[] attrNames = GetLdapNamesForAttribute(filter.GetAttribute()); + if (attrNames == null) { + return null; + } + + StringBuilder builder = new StringBuilder(); + if (not) { + builder.Append("!("); + } + if (attrNames.Length == 1) { + builder.Append('('); + builder.Append(attrNames[0]); + builder.Append("=*"); + GetLdapFilterValue(builder, attrNames[0], filter.GetValue()); + builder.Append(')'); + } else { + builder.Append("(|"); + foreach (String attrName in attrNames) { + builder.Append('('); + builder.Append(attrName); + builder.Append("=*"); + GetLdapFilterValue(builder, attrName, filter.GetValue()); + builder.Append(')'); + } + builder.Append(')'); + } + if (not) { + builder.Append(')'); + } + return builder.ToString(); + } + + protected override String CreateEqualsExpression(EqualsFilter filter, Boolean not) { + // The LDAP equality filter matches any one attribute value, + // whereas the connector EqualsFilter matches an attribute and + // its values exactly. + if (not) { + return null; + } + + ConnectorAttribute attr = filter.GetAttribute(); + // if there is only one thing to search on, and it's + // a uid we need to convert the uid to something we + // can search on. NOTE: only handling the case where + // we are doing an equality search, and only one item + // is in the equality search ... It's all that makes + // sense for uid. + if (attr is Uid) + { + String attrValue = ((Uid)attr).GetUidValue(); + if (LooksLikeGUID(attrValue)) + { + String searchGuid = GetUidSearchString(((Uid)attr).GetUidValue()); + attr = new Uid(searchGuid); + } else { + attr = new Name(attrValue); + } + + } + + + String[] attrNames = GetLdapNamesForAttribute(attr); + if (attrNames == null) { + return null; + } + + StringBuilder builder = new StringBuilder(); + + if (attr.Value == null) { + return null; + } + if (attr.Value.Count == 1) { + BuildEqualityFilter(builder, attrNames, + attr.Value[0]); + } else { + builder.Append("(&"); + foreach (Object value in attr.Value) { + BuildEqualityFilter(builder, attrNames, value); + } + builder.Append(')'); + } + + return builder.ToString(); + } + + protected override String CreateGreaterThanExpression(GreaterThanFilter filter, + Boolean not) { + // Note that (!(a > X)) is only the same as (a <= X) if every object + // has a value of a. + if (not) { + return null; + } + + String[] attrNames = GetLdapNamesForAttribute(filter.GetAttribute()); + if (attrNames == null) { + return null; + } + + StringBuilder builder = new StringBuilder(); + BuildGreaterOrEqualFilter(builder, attrNames, filter.GetValue()); + return builder.ToString(); + } + + protected override String CreateGreaterThanOrEqualExpression( + GreaterThanOrEqualFilter filter, Boolean not) { + String[] attrNames = GetLdapNamesForAttribute(filter.GetAttribute()); + if (attrNames == null) { + return null; + } + + StringBuilder builder = new StringBuilder(); + if (not) { + builder.Append("!("); + } + BuildGreaterOrEqualFilter(builder, attrNames, filter.GetValue()); + if (not) { + builder.Append(')'); + } + return builder.ToString(); + } + + protected override String CreateLessThanExpression(LessThanFilter filter, + Boolean not) { + // Note that (!(a < X)) is only the same as (a >= X) if every object + // has a value of a. + if (not) { + return null; + } + + String[] attrNames = GetLdapNamesForAttribute(filter.GetAttribute()); + if (attrNames == null) { + return null; + } + + StringBuilder builder = new StringBuilder(); + BuildLessOrEqualFilter(builder, attrNames, filter.GetValue()); + return builder.ToString(); + } + + protected override String CreateLessThanOrEqualExpression( + LessThanOrEqualFilter filter, Boolean not) { + String[] attrNames = GetLdapNamesForAttribute(filter.GetAttribute()); + if (attrNames == null) { + return null; + } + + StringBuilder builder = new StringBuilder(); + if (not) { + builder.Append("!("); + } + BuildLessOrEqualFilter(builder, attrNames, filter.GetValue()); + if (not) { + builder.Append(')'); + } + return builder.ToString(); + } + + /** + * Get the string representation of an attribute value suitable for + * embedding in an LDAP search filter (RFC 2254 / RFC 4515). + * + * @param builder A string builder on to which a suitably escaped attribute + * value will be appended. + * + * @param value The attribute value to be embedded. + */ + static void GetLdapFilterValue(StringBuilder builder, + String AttributeName, Object value) { + // at this point, this can probably go away + // it was here to properyly escape queries, but + // it doesn't seem that they need escaping. + if (value == null) + { + return; + } + else + { + builder.Append(value); + } + } + + /** + * Get the LDAP name or names for a given connector attribute used in a + * search filter. + * + * @param attr The connector attribute used in a search filter. + * + * @return The name or names of the corresponding LDAP attribute. + * Returns null if the attribute cannot be specified in an LDAP + * filter. + */ + + protected virtual String[] GetLdapNamesForAttribute(ConnectorAttribute attr) { + // Special processing for certain connector attributes. + String[] attrNames = null; + if (attr is Uid) { + /* + attrNames = new String[] { + configCache.getConfiguration().getUuidAttribute() }; + */ + attrNames = new String[] { "objectGUID" }; + } else if (attr is Name) { + /* + attrNames = configCache.getNamingAttributes(); + */ + attrNames = new String [] { "distinguishedName" }; + } else if (attr.Is(OperationalAttributes.PASSWORD_NAME)) { + /* + attrNames = new String[] { + configCache.getConfiguration().getPasswordAttribute() + }; + */ + attrNames = new String[] { "userPassword" }; + } else if (ConnectorAttributeUtil.IsSpecial(attr)) { + return null; + } else { + attrNames = new String[] { attr.Name }; + } + + return attrNames; + } + + static void BuildEqualityFilter(StringBuilder builder, + String[] attrNames, + Object attrValue) { + if (attrNames.Length == 1) { + builder.Append('('); + builder.Append(attrNames[0]); + builder.Append('='); + GetLdapFilterValue(builder, attrNames[0], attrValue); + builder.Append(')'); + } else { + builder.Append("(|"); + foreach (String attrName in attrNames) { + builder.Append('('); + builder.Append(attrName); + builder.Append('='); + GetLdapFilterValue(builder, attrName, attrValue); + builder.Append(')'); + } + builder.Append(')'); + } + } + + static void BuildGreaterOrEqualFilter(StringBuilder builder, + String[] attrNames, + Object attrValue) { + if (attrNames.Length == 1) { + builder.Append('('); + builder.Append(attrNames[0]); + builder.Append(">="); + GetLdapFilterValue(builder, attrNames[0], attrValue); + builder.Append(')'); + } else { + builder.Append("(|"); + foreach (String attrName in attrNames) { + builder.Append('('); + builder.Append(attrName); + builder.Append(">="); + GetLdapFilterValue(builder, attrName, attrValue); + builder.Append(')'); + } + builder.Append(')'); + } + } + + static void BuildLessOrEqualFilter(StringBuilder builder, + String[] attrNames, + Object attrValue) { + if (attrNames.Length == 1) { + builder.Append('('); + builder.Append(attrNames[0]); + builder.Append("<="); + GetLdapFilterValue(builder, attrNames[0], attrValue); + builder.Append(')'); + } else { + builder.Append("(|"); + foreach (String attrName in attrNames) { + builder.Append('('); + builder.Append(attrName); + builder.Append("<="); + GetLdapFilterValue(builder, attrName, attrValue); + builder.Append(')'); + } + builder.Append(')'); + } + } + + // This is a special case for IDM backward compatibility for + // non account objects. If it doesn't look like a UID, just + // assume it's a dn + static internal bool LooksLikeGUID(string value) + { + string[] uidStringParts = value.Split('='); + if ((uidStringParts.Length != 2) || (uidStringParts[1] == null)) + { + // This is a special case for IDM backward compatibility for + // non account objects. If it doesn't look like a UID, just + // assume it's a dn + return false; + } + + return true; + } + + // This is called to fix up UID values which are in the + // format , but need to be in the + // format \\xx\\xx\\xx... + static internal string GetUidSearchString(string uidString) + { + // be tolerant of whitespace between '<' and "GUID", + // and between "GUID" and '=' and between '=' and + // start of guidstring, and between start of guidstring + // and '>' + string uidSearchString = ""; + string[] uidStringParts = uidString.Split('='); + if ((uidStringParts.Length != 2) || (uidStringParts[1] == null)) + { + throw new ConnectorException("Uid is not in the expected format"); + } + uidSearchString = uidStringParts[1].Trim(); + + // take off the final '>' + uidSearchString = uidSearchString.Substring(0, uidSearchString.IndexOf('>')); + + // now put the '\' characters in + string escapedSearchString = ""; + for(int position = 0;position < uidSearchString.Length;position++) { + if(position % 2 == 0) { + escapedSearchString += "\\"; + } + escapedSearchString += uidSearchString[position]; + } + + return escapedSearchString; + } + } +} diff --git a/DotNetConnectors.sln/ActiveDirectoryConnector/ActiveDirectorySyncToken.cs b/DotNetConnectors.sln/ActiveDirectoryConnector/ActiveDirectorySyncToken.cs new file mode 100644 index 00000000..9449ba52 --- /dev/null +++ b/DotNetConnectors.sln/ActiveDirectoryConnector/ActiveDirectorySyncToken.cs @@ -0,0 +1,89 @@ +/* + * ==================== + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER. + * + * Copyright 2007-2008 Sun Microsystems, Inc. All rights reserved. + * + * The contents of this file are subject to the terms of the Common Development + * and Distribution License("CDDL") (the "License"). You may not use this file + * except in compliance with the License. + * + * You can obtain a copy of the License at + * http://IdentityConnectors.dev.java.net/legal/license.txt + * See the License for the specific language governing permissions and limitations + * under the License. + * + * When distributing the Covered Code, include this CDDL Header Notice in each file + * and include the License file at identityconnectors/legal/license.txt. + * If applicable, add the following below this CDDL Header, with the fields + * enclosed by brackets [] replaced by your own identifying information: + * "Portions Copyrighted [year] [name of copyright owner]" + * ==================== + */ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using Org.IdentityConnectors.Framework.Common.Objects; +using Org.IdentityConnectors.Framework.Common.Exceptions; + +namespace Org.IdentityConnectors.ActiveDirectory +{ + public class ActiveDirectorySyncToken + { + internal long LastModifiedUsn { get; set; } + internal long LastDeleteUsn { get; set; } + internal bool UseGlobalCatalog { get; set; } + internal string SyncServer { get; set; } + + public ActiveDirectorySyncToken(SyncToken token, string serverName, bool useGlobalCatalog) + : this(token == null ? null : (string)token.Value, serverName, useGlobalCatalog) + { + } + + public ActiveDirectorySyncToken(String tokenValue, string configServerName, bool configUseGlobalCatalog) + { + UseGlobalCatalog = configUseGlobalCatalog; + SyncServer = configServerName; + + if ((tokenValue == null) || (tokenValue.Length == 0)) + { + LastDeleteUsn = 0; + LastModifiedUsn = 0; + return; + } + + string[] tokenParts = (tokenValue).Split('|'); + if (tokenParts.Length != 4) + { + throw new ConnectorException("Unable to parse sync token"); + } + + string tokenSyncServer = tokenParts[3]; + bool tokenUseGlobalCatalog = bool.Parse(tokenParts[2]); + + // If the token server is the same as the configured server, + // use the token value (usn) to limit the query. The token is + // server specific though, so we cant use the usn if it didn't come + // from this server. + // If no server is configured, just try to use what we used last time. + if ((SyncServer != null) && (SyncServer.Equals(configServerName)) && + (UseGlobalCatalog.Equals(tokenUseGlobalCatalog))) + { + LastModifiedUsn = long.Parse(tokenParts[0]); + LastDeleteUsn = long.Parse(tokenParts[1]); + } + else + { + LastModifiedUsn = 0; + LastDeleteUsn = 0; + } + } + + public SyncToken GetSyncToken() + { + return new SyncToken(String.Format("{0}|{1}|{2}|{3}", + LastModifiedUsn, LastDeleteUsn, UseGlobalCatalog, SyncServer)); + } + } +} diff --git a/DotNetConnectors.sln/ActiveDirectoryConnector/ActiveDirectoryUtils.cs b/DotNetConnectors.sln/ActiveDirectoryConnector/ActiveDirectoryUtils.cs new file mode 100644 index 00000000..814b4366 --- /dev/null +++ b/DotNetConnectors.sln/ActiveDirectoryConnector/ActiveDirectoryUtils.cs @@ -0,0 +1,669 @@ +/* + * ==================== + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER. + * + * Copyright 2007-2008 Sun Microsystems, Inc. All rights reserved. + * + * The contents of this file are subject to the terms of the Common Development + * and Distribution License("CDDL") (the "License"). You may not use this file + * except in compliance with the License. + * + * You can obtain a copy of the License at + * http://IdentityConnectors.dev.java.net/legal/license.txt + * See the License for the specific language governing permissions and limitations + * under the License. + * + * When distributing the Covered Code, include this CDDL Header Notice in each file + * and include the License file at identityconnectors/legal/license.txt. + * If applicable, add the following below this CDDL Header, with the fields + * enclosed by brackets [] replaced by your own identifying information: + * "Portions Copyrighted [year] [name of copyright owner]" + * ==================== + */ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using Org.IdentityConnectors.Framework.Common.Objects; +using System.DirectoryServices; +using Org.IdentityConnectors.Framework.Common.Exceptions; +using System.Diagnostics; +using Org.IdentityConnectors.Framework.Common; +using Org.IdentityConnectors.Framework.Spi.Operations; +using System.Security; +using ActiveDs; +using Org.IdentityConnectors.Common.Security; +using System.DirectoryServices.ActiveDirectory; + +namespace Org.IdentityConnectors.ActiveDirectory +{ + /// + /// Collection of Active directory utilities. Some are static methods, + /// other require configuration, so they are instance methods. + /// + public class ActiveDirectoryUtils + { + ActiveDirectoryConfiguration _configuration = null; + private CustomAttributeHandlers _customHandlers = null; + + /// + /// Constructor + /// + /// + /// Configuration object for the connector. + /// + public ActiveDirectoryUtils(ActiveDirectoryConfiguration configuration) + { + _configuration = configuration; + _customHandlers = new CustomAttributeHandlers(_configuration); + } + + /// + /// Converts a guid in byte array form to a string suitable + /// for ldap search. + /// + /// + /// + internal static String ConvertUIDBytesToSearchString(Byte[] guidBytes) + { + String searchGuid = ""; + + for (int i = 0; i < guidBytes.Length; i++) + { + searchGuid += String.Format("\\{0:X2}", guidBytes[i]); + } + + return searchGuid; + } + + /// + /// Converts a guid in byte array form to a string with the format + /// >GUID = xxxxxxxxxxxxxxxxxxxxxxxxxxxxx< where the x's represent + /// uppercase hexadecimal digits + /// + /// + /// + internal static String ConvertUIDBytesToGUIDString(Byte[] guidBytes) + { + return ConvertBytesToADSpecialString("GUID", guidBytes); + } + + internal static String ConvertSIDBytesToGUIDString(Byte[] guidBytes) + { + return ConvertBytesToADSpecialString("SID", guidBytes); + } + + internal static String ConvertBytesToADSpecialString(string attribute, Byte[] bytes) + { + String guidString = "<" + attribute + "="; + + for (int i = 0; i < bytes.Length; i++) + { + guidString += String.Format("{0:X2}", bytes[i]); + } + guidString += ">"; + + return guidString; + } + + /// + /// Returns an ldap path in the form of: + /// LDAP://servernameIfSpecified/path + /// + /// Servername can be null + /// Path should not be null + /// + internal static String GetLDAPPath(string serverName, string path) + { + return GetFullPath("LDAP", serverName, path); + } + + /// + /// Returns a path string in the format: + /// GC://servernameIfSpecified/path + /// + /// Servername is optional + /// Path should be specified + /// + internal static String GetGCPath(string serverName, string path) + { + return GetFullPath("GC", serverName, path); + } + + /// + /// Returns a path string in the format: + /// provider://servernameIfSpecified/path + /// + /// provider (such as ldap or gc) + /// servername - optional + /// path to resource + /// + internal static String GetFullPath(string provider, string serverName, string path) + { + IADsPathname pathName = getADSPathname(provider, serverName, path); + return pathName.Retrieve((int)ADS_FORMAT_ENUM.ADS_FORMAT_X500); + } + + /// + /// uses iadspathname to create paths in a standard way + /// + /// + /// + /// + /// + internal static IADsPathname getADSPathname(string provider, string serverName, string path) + { + IADsPathname pathName = new PathnameClass(); + if ((provider != null) && (provider.Length != 0)) + { + pathName.Set(provider, (int)ADS_SETTYPE_ENUM.ADS_SETTYPE_PROVIDER); + } + + if ((serverName != null) && (serverName.Length != 0)) + { + pathName.Set(serverName, (int)ADS_SETTYPE_ENUM.ADS_SETTYPE_SERVER); + } + + if ((path != null) && (path.Length != 0)) + { + // must supply a path + pathName.Set(path, (int)ADS_SETTYPE_ENUM.ADS_SETTYPE_DN); + } + return pathName; + } + + /// + /// Gets the dn of the parent object of the object specified by childDn + /// + /// distinguished name of an object to retrieve the parent of + /// distinguished name of the parent of 'childDn' or null + internal static string GetParentDn(string childDn) + { + IADsPathname pathName = getADSPathname(null, null, childDn); + return pathName.Retrieve((int)ADS_FORMAT_ENUM.ADS_FORMAT_X500_PARENT); + } + + /// + /// Updates an AD object (also called by create after object is created) + /// + /// + /// + /// + /// + /// + internal void UpdateADObject(ObjectClass oclass, + DirectoryEntry directoryEntry, ICollection attributes, + UpdateType type, ActiveDirectoryConfiguration config) + { + if(oclass.Equals(ObjectClass.ACCOUNT)) + { + // translate attribute passed in + foreach (ConnectorAttribute attribute in attributes) + { + // encountered problems when processing change password at the same time + // as setting expired. It would be set to expired, but the change would + // clear that. So we must ensure that expired comes last. + if (OperationalAttributes.PASSWORD_EXPIRED_NAME.Equals(attribute.Name)) + { + continue; + } + + AddConnectorAttributeToADProperties(oclass, + directoryEntry, attribute, type); + + // Uncommenting the next line is very helpful in + // finding mysterious errors. + // directoryEntry.CommitChanges(); + } + + directoryEntry.CommitChanges(); + + // now do the password change. This is handled separately, because + // it might be a user changing his own password, or it might be an + // administrative change. + + GuardedString gsNewPassword = ConnectorAttributeUtil.GetPasswordValue(attributes); + if (gsNewPassword != null) + { + GuardedString gsCurrentPassword = ConnectorAttributeUtil.GetCurrentPasswordValue(attributes); + PasswordChangeHandler changeHandler = new PasswordChangeHandler(_configuration); + if (gsCurrentPassword == null) + { + // just a normal password change + changeHandler.changePassword(directoryEntry, gsNewPassword); + } + else + { + changeHandler.changePassword(directoryEntry, + gsCurrentPassword, gsNewPassword); + } + + + UserAccountControl.Set(directoryEntry.Properties[ActiveDirectoryConnector.ATT_USER_ACOUNT_CONTROL], + UserAccountControl.PASSWD_NOTREQD, false); + directoryEntry.CommitChanges(); + } + + // see note in loop above for explaination of this + ConnectorAttribute expirePasswordAttribute = ConnectorAttributeUtil.Find( + OperationalAttributes.PASSWORD_EXPIRED_NAME, attributes); + + if (expirePasswordAttribute != null) + { + AddConnectorAttributeToADProperties(oclass, + directoryEntry, expirePasswordAttribute, type); + directoryEntry.CommitChanges(); + } + + UserAccountControl.Set(directoryEntry.Properties[ActiveDirectoryConnector.ATT_USER_ACOUNT_CONTROL], + UserAccountControl.PASSWD_NOTREQD, false); + + directoryEntry.CommitChanges(); + + HandleNameChange(type, directoryEntry, attributes); + HandleContainerChange(type, directoryEntry, attributes, config); + } + else if (oclass.Equals(ObjectClass.GROUP)) + { + // translate attribute passed in + foreach (ConnectorAttribute attribute in attributes) + { + // Temporary + // Trace.TraceInformation(String.Format("Setting attribute {0} to {1}", + // attribute.Name, attribute.Value)); + AddConnectorAttributeToADProperties(oclass, + directoryEntry, attribute, type); + // Uncommenting the next line is very helpful in + // finding mysterious errors. + directoryEntry.CommitChanges(); + } + + directoryEntry.CommitChanges(); + HandleNameChange(type, directoryEntry, attributes); + HandleContainerChange(type, directoryEntry, attributes, config); + } + else if (oclass.Equals(ActiveDirectoryConnector.ouObjectClass)) + { + // translate attribute passed in + foreach (ConnectorAttribute attribute in attributes) + { + // Temporary + // Trace.TraceInformation(String.Format("Setting attribute {0} to {1}", + // attribute.Name, attribute.Value)); + AddConnectorAttributeToADProperties(oclass, + directoryEntry, attribute, type); + // Uncommenting the next line is very helpful in + // finding mysterious errors. + directoryEntry.CommitChanges(); + } + + directoryEntry.CommitChanges(); + HandleNameChange(type, directoryEntry, attributes); + HandleContainerChange(type, directoryEntry, attributes, config); + } + else + { + throw new ConnectorException( + _configuration.ConnectorMessages.Format("ex_InvalidObjectClass", + "Invalid object class: {0}", oclass.GetObjectClassValue())); + } + } + + internal ConnectorAttribute GetConnectorAttributeFromADEntry(ObjectClass oclass, + String attributeName, SearchResult searchResult) + { + // Boolean translated = false; + if (searchResult == null) + { + throw new ConnectorException(_configuration.ConnectorMessages.Format( + "ex_AttributeNull", + "Could not add connector attribute to search result")); + } + + return _customHandlers.GetCaFromDe(oclass, + attributeName, searchResult); + + } + + internal void AddConnectorAttributeToADProperties(ObjectClass oclass, + DirectoryEntry directoryEntry, ConnectorAttribute attribute, + UpdateType type) + { + // Boolean translated = false; + if (directoryEntry == null) + { + throw new ConnectorException(_configuration.ConnectorMessages.Format( + "ex_CouldNotAddNullAttributeToDe", + "Could not add connector attribute to directory entry")); + } + + _customHandlers.UpdateDeFromCa(oclass, type, + directoryEntry, attribute); + + } + + /* + /// + /// creates and returns a connector attribute or null. the attribute + /// has the name 'name' and the values associated with 'name' in the + /// directory entry + /// + /// + /// + /// + private static ConnectorAttribute CreateConnectorAttribute(String name, + PropertyValueCollection pvc) + { + ConnectorAttributeBuilder attributeBuilder = new ConnectorAttributeBuilder(); + + if (name == null) + { + return null; + } + + attributeBuilder.Name = name; + + if (pvc == null) + { + attributeBuilder.AddValue(null); + } + else + { + for (int i = 0; i < pvc.Count; i++) + { + Object valueObject = pvc[i]; + if ((pvc[i] == null) || + (FrameworkUtil.IsSupportedAttributeType(valueObject.GetType()))) + { + attributeBuilder.AddValue(pvc[i]); + } + else + { + Trace.TraceWarning( + "Unsupported attribute type ... calling ToString (Name: \'{0}\'({1}) Type: \'{2}\' String Value: \'{3}\'", + name, i, pvc[i].GetType(), pvc[i].ToString()); + attributeBuilder.AddValue(pvc[i].ToString()); + } + } + } + + return attributeBuilder.Build(); + } + + private static void AddConnectorAttributeToADProperties_general( + PropertyCollection properties, + ConnectorAttribute attribute, UpdateType type) + { + // null out the values if we are deleting + // or replacing attributes. + if (type.Equals(UpdateType.DELETE) || + type.Equals(UpdateType.REPLACE)) + { + properties[attribute.Name].Value = null; + } + + // if we are updating or adding, put the + // new values in. + if (type.Equals(UpdateType.ADD) || + type.Equals(UpdateType.REPLACE)) + { + foreach (Object valueObject in attribute.Value) + { + properties[attribute.Name].Add(valueObject); + } + } + } + */ + + /// + /// Gets a single value from a propertyvaluecollection + /// for a particular property name. Its an error if the + /// property contains multiple values. + /// + /// + /// + internal Object GetSingleValue(PropertyValueCollection pvc) + { + if((pvc == null) || (pvc.Count == 0)) + { + return null; + } + + if (pvc.Count > 1) + { + String msg = _configuration.ConnectorMessages.Format( + "ex_ExpectingSingleValue", + "Expecting single value, but found multiple values for attribute {0}", + pvc.PropertyName); + throw new ConnectorException(msg); + } + + return pvc[0]; + } + + /// + /// Finds a DirectoryEntry by it's uid + /// + /// + /// + /// + /// + /// + internal static DirectoryEntry GetDirectoryEntryFromUid(String serverName, + Uid uid, string adminUserName, string adminPassword) + { + DirectoryEntry foundDirectoryEntry = new DirectoryEntry( + ActiveDirectoryUtils.GetLDAPPath(serverName, uid.GetUidValue()), + adminUserName, adminPassword); + string dn = (string)foundDirectoryEntry.Properties["distinguishedName"][0]; + foundDirectoryEntry = new DirectoryEntry( + ActiveDirectoryUtils.GetLDAPPath(serverName, dn), + adminUserName, adminPassword); + return foundDirectoryEntry; + } + + /// + /// Returns the AD ObjectClass associated with a particular + /// Connector ObjectClass + /// + /// + /// + internal String GetADObjectClass(ObjectClass oclass) + { + + if (oclass.Equals(ObjectClass.ACCOUNT)) + { + return "User"; + } + else if (oclass.Equals(ObjectClass.GROUP)) + { + return "Group"; + } + else if ("ORGANIZATIONAL UNIT".Equals(oclass.GetObjectClassValue(), StringComparison.CurrentCultureIgnoreCase)) + { + return "organizationalUnit"; + } + else + { + String msg = _configuration.ConnectorMessages.Format( + "ex_ObjectClassInvalidForConnector", + "ObjectClass \'{0}\' is not valid for this connector", + oclass.GetObjectClassValue()); + throw new ConnectorException(msg); + } + } + + /// + /// Puts an ldap string into a normalilzed format + /// + /// + /// + public static String NormalizeLdapString(String ldapString) + { + StringBuilder normalPath = new StringBuilder(); + String[] parts = ldapString.Split(','); + for (int i = 0; i < parts.Length; i++) + { + normalPath.Append(parts[i].Trim().ToUpper()); + // append a comma after each part (except the last one) + if (i < (parts.Length - 1)) + { + normalPath.Append(","); + } + } + return normalPath.ToString(); + } + + public static String GetRelativeName(Name name) + { + return GetNameAsCN(name.GetNameValue()); + } + + /// + /// Returns the leaf value of a distinguished name + /// + /// + /// + internal static String GetNameAsCN(String nameValue) + { + IADsPathname pathName = getADSPathname(null, null, nameValue); + return pathName.Retrieve((int)ADS_FORMAT_ENUM.ADS_FORMAT_LEAF); + } + + /// + /// This does not work ... for now, don't handle container changes + /// + /// + /// + /// + /// + private static void HandleContainerChange(UpdateType type, + DirectoryEntry directoryEntry, ICollection attributes, + ActiveDirectoryConfiguration config) + { + // this return means that te connector attribute is ignored for + // the purpose of moving an object to a different container + return; + + // this code seems right, but doesnt work. The DirectoryEntry.Move() + // method always throws an Exception with the text 'unspecified error' + + ConnectorAttribute containerAttribute = + ConnectorAttributeUtil.Find(ActiveDirectoryConnector.ATT_CONTAINER, attributes); + if (containerAttribute != null) + { + // this only make sense for replace. you can't + // add a name or delete a name + if (type.Equals(UpdateType.REPLACE)) + { + DirectoryEntry parent = directoryEntry.Parent; + String oldContainer = null; + if (parent != null) + { + PropertyValueCollection parentDNValues = + parent.Properties[ActiveDirectoryConnector.ATT_DISTINGUISHED_NAME]; + if ((parentDNValues.Count == 1) && (parentDNValues[0] is String)) + { + oldContainer = (String)parentDNValues[0]; + } + else + { + String msg = String.Format("Unable to retrieve the distinguished name for {0}.", + parent.Path); + throw new ConnectorException(msg); + } + } + + String newContainer = ConnectorAttributeUtil.GetStringValue(containerAttribute); + + if (newContainer != null) + { + try + { + if (!NormalizeLdapString(oldContainer).Equals( + NormalizeLdapString(newContainer))) + { + DirectoryEntry newContainerDe = new DirectoryEntry(newContainer, + config.DirectoryAdminName, config.DirectoryAdminPassword); + directoryEntry.MoveTo(newContainerDe); + } + } + catch (Exception e) + { + throw e; + } + } + } + } + } + + private static void HandleNameChange(UpdateType type, + DirectoryEntry directoryEntry, + ICollection attributes) + { + Name nameAttribute = ConnectorAttributeUtil.GetNameFromAttributes(attributes); + if (nameAttribute != null) + { + // this only make sense for replace. you can't + // add a name or delete a name + if (type.Equals(UpdateType.REPLACE)) + { + String oldName = directoryEntry.Name; + String newName = GetRelativeName(nameAttribute); + if (!NormalizeLdapString(oldName).Equals(NormalizeLdapString(newName))) + { + directoryEntry.Rename(newName); + } + } + } + } + + public static SecureString GetSecureString(String stringToSecure) + { + SecureString secure = new SecureString(); + + foreach (char nextChar in stringToSecure) + { + secure.AppendChar(nextChar); + } + + return secure; + } + + internal static string GetDnFromPath(string fullPath) + { + IADsPathname pathName = new PathnameClass(); + pathName.Set(fullPath, (int)ADS_SETTYPE_ENUM.ADS_SETTYPE_FULL); + return pathName.Retrieve((int)ADS_FORMAT_ENUM.ADS_FORMAT_X500_DN); + } + + internal static DomainController GetDomainController(ActiveDirectoryConfiguration configuration) + { + String serverName = configuration.LDAPHostName; + DomainController controller = null; + + if ((serverName == null) || (serverName.Length == 0)) + { + // get the active directory schema + DirectoryContext context = new DirectoryContext( + DirectoryContextType.Domain, + configuration.DomainName, + configuration.DirectoryAdminName, + configuration.DirectoryAdminPassword); + controller = DomainController.FindOne(context); + } + else + { + DirectoryContext context = new DirectoryContext( + DirectoryContextType.DirectoryServer, + configuration.LDAPHostName, + configuration.DirectoryAdminName, + configuration.DirectoryAdminPassword); + controller = DomainController.GetDomainController(context); + } + + return controller; + } + } + +} diff --git a/DotNetConnectors.sln/ActiveDirectoryConnector/AssemblyInfo.cs b/DotNetConnectors.sln/ActiveDirectoryConnector/AssemblyInfo.cs new file mode 100644 index 00000000..65843b5d --- /dev/null +++ b/DotNetConnectors.sln/ActiveDirectoryConnector/AssemblyInfo.cs @@ -0,0 +1,54 @@ +/* + * ==================== + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER. + * + * Copyright 2007-2008 Sun Microsystems, Inc. All rights reserved. + * + * The contents of this file are subject to the terms of the Common Development + * and Distribution License("CDDL") (the "License"). You may not use this file + * except in compliance with the License. + * + * You can obtain a copy of the License at + * http://IdentityConnectors.dev.java.net/legal/license.txt + * See the License for the specific language governing permissions and limitations + * under the License. + * + * When distributing the Covered Code, include this CDDL Header Notice in each file + * and include the License file at identityconnectors/legal/license.txt. + * If applicable, add the following below this CDDL Header, with the fields + * enclosed by brackets [] replaced by your own identifying information: + * "Portions Copyrighted [year] [name of copyright owner]" + * ==================== + */ +#region Using directives + +using System; +using System.Reflection; +using System.Resources; +using System.Runtime.InteropServices; + +#endregion + +// General Information about an assembly is controlled through the following +// set of attributes. Change these attribute values to modify the information +// associated with an assembly. +[assembly: AssemblyTitle("ActiveDirectoryConnector")] +[assembly: AssemblyDescription("")] +[assembly: AssemblyConfiguration("")] +[assembly: AssemblyCompany("")] +[assembly: AssemblyProduct("ActiveDirectoryConnector")] +[assembly: AssemblyCopyright("")] +[assembly: AssemblyTrademark("")] +[assembly: AssemblyCulture("")] + +// This sets the default COM visibility of types in the assembly to invisible. +// If you need to expose a type to COM, use [ComVisible(true)] on that type. +[assembly: ComVisible(false)] + +// The assembly version has following format : +// +// Major.Minor.Build.Revision +// +// You can specify all the values or you can use the default the Revision and +// Build Numbers by using the '*' as shown below: +[assembly: AssemblyVersion("1.0.0.0")] diff --git a/DotNetConnectors.sln/ActiveDirectoryConnector/CommonUtils.cs b/DotNetConnectors.sln/ActiveDirectoryConnector/CommonUtils.cs new file mode 100644 index 00000000..d42bb8cd --- /dev/null +++ b/DotNetConnectors.sln/ActiveDirectoryConnector/CommonUtils.cs @@ -0,0 +1,73 @@ +/* + * ==================== + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER. + * + * Copyright 2007-2008 Sun Microsystems, Inc. All rights reserved. + * + * The contents of this file are subject to the terms of the Common Development + * and Distribution License("CDDL") (the "License"). You may not use this file + * except in compliance with the License. + * + * You can obtain a copy of the License at + * http://IdentityConnectors.dev.java.net/legal/license.txt + * See the License for the specific language governing permissions and limitations + * under the License. + * + * When distributing the Covered Code, include this CDDL Header Notice in each file + * and include the License file at identityconnectors/legal/license.txt. + * If applicable, add the following below this CDDL Header, with the fields + * enclosed by brackets [] replaced by your own identifying information: + * "Portions Copyrighted [year] [name of copyright owner]" + * ==================== + */ +using System; +using System.Collections.Generic; +using System.Reflection; +using System.IO; +using Org.IdentityConnectors.Common; +using Org.IdentityConnectors.Framework.Common.Objects; +using Org.IdentityConnectors.Framework.Common.Serializer; + +namespace Org.IdentityConnectors.ActiveDirectory +{ + public class CommonUtils + { + /// + /// reads the object class info definitions from xml + /// + ///Dictionary of object classes + protected internal static IDictionary GetOCInfo(string name) + { + Assembly assembly = Assembly.GetExecutingAssembly(); + Stream stream = assembly.GetManifestResourceStream(name); + + Assertions.NullCheck(stream, "stream"); + + //we just read + TextReader streamReader = new StreamReader(stream); + String xml; + try + { + xml = streamReader.ReadToEnd(); + } + finally + { + streamReader.Close(); + } + + //read from xml + var ret = (ICollection)SerializerUtil.DeserializeXmlObject(xml, true); + + Assertions.NullCheck(ret, "ret"); + + //create map of object infos + var map = new Dictionary(ret.Count); + foreach (ObjectClassInfo o in ret) + { + map.Add(new ObjectClass(o.ObjectType.ToString()), o); + } + + return map; + } + } +} diff --git a/DotNetConnectors.sln/ActiveDirectoryConnector/CustomAttributeHandlers.cs b/DotNetConnectors.sln/ActiveDirectoryConnector/CustomAttributeHandlers.cs new file mode 100644 index 00000000..50c175a8 --- /dev/null +++ b/DotNetConnectors.sln/ActiveDirectoryConnector/CustomAttributeHandlers.cs @@ -0,0 +1,1167 @@ +/* + * ==================== + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER. + * + * Copyright 2007-2008 Sun Microsystems, Inc. All rights reserved. + * + * The contents of this file are subject to the terms of the Common Development + * and Distribution License("CDDL") (the "License"). You may not use this file + * except in compliance with the License. + * + * You can obtain a copy of the License at + * http://IdentityConnectors.dev.java.net/legal/license.txt + * See the License for the specific language governing permissions and limitations + * under the License. + * + * When distributing the Covered Code, include this CDDL Header Notice in each file + * and include the License file at identityconnectors/legal/license.txt. + * If applicable, add the following below this CDDL Header, with the fields + * enclosed by brackets [] replaced by your own identifying information: + * "Portions Copyrighted [year] [name of copyright owner]" + * ==================== + */ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Security; +using Org.IdentityConnectors.Framework.Common.Objects; +using Org.IdentityConnectors.Framework.Common; +using Org.IdentityConnectors.Framework.Spi.Operations; +using System.DirectoryServices; +using Org.IdentityConnectors.Framework.Common.Exceptions; +using Org.IdentityConnectors.Common.Security; +using System.Diagnostics; +using ActiveDs; +using System.IO; +using System.Security.AccessControl; +using System.Security.Principal; +using Org.IdentityConnectors.Common; + +namespace Org.IdentityConnectors.ActiveDirectory +{ + /// + /// This class will encapsulate all changes from AD attributes + /// to Connector attributes and from Connector attributes to + /// AD attributes. If attributes are more complex and can't be + /// handled here, add them to the appropriate ignore list, and handle + /// them in the AD Connector (or elsewhere). + /// + /// If it is a connector attribute that has the same name as the + /// ad attribute, and the value requires no translation, it will + /// be handled by the generic handler. If not, add a delegate for + /// either AD->Connector or for Connector->AD (or both). + /// + internal class CustomAttributeHandlers + { + // names from active directory attributes to ignore during + // generic translation + IList IgnoreADAttributeNames_account = new List(); + IList IgnoreADAttributeNames_group = new List(); + + // names from connector attributes to ignore during + // generic translation + IList IgnoreConnectorAttributeNames_account = new List(); + IList IgnoreConnectorAttributeNames_group = new List(); + IList IgnoreConnectorAttributeNames_ou = new List(); + + // method to update a directory entry from a connector attribute + Dictionary + UpdateDeFromCaDelegates = new Dictionary(StringComparer.CurrentCultureIgnoreCase); + + // method to get a connector attribute from a directory entry + Dictionary + GetCaFromDeDelegates = new Dictionary(StringComparer.CurrentCultureIgnoreCase); + + ActiveDirectoryConfiguration _configuration = null; + + internal CustomAttributeHandlers(ActiveDirectoryConfiguration configuration) { + // save the configuration + _configuration = configuration; + + // Connector attributes names to ignore for accounts + IgnoreConnectorAttributeNames_account.Add(Name.NAME); + IgnoreConnectorAttributeNames_account.Add(ActiveDirectoryConnector.ATT_CONTAINER); + IgnoreConnectorAttributeNames_account.Add(Uid.NAME); + IgnoreConnectorAttributeNames_account.Add(OperationalAttributes.PASSWORD_NAME); + IgnoreConnectorAttributeNames_account.Add(OperationalAttributes.CURRENT_PASSWORD_NAME); + + // Connector attributes names to ignore for groups + IgnoreConnectorAttributeNames_group.Add(Name.NAME); + IgnoreConnectorAttributeNames_group.Add(ActiveDirectoryConnector.ATT_CONTAINER); + IgnoreConnectorAttributeNames_group.Add(Uid.NAME); + IgnoreConnectorAttributeNames_group.Add("authOrig"); + IgnoreConnectorAttributeNames_group.Add("unauthOrig"); + IgnoreConnectorAttributeNames_group.Add("groupTypes"); + + // Connector attributes names to ignore for ous + IgnoreConnectorAttributeNames_ou.Add(Name.NAME); + IgnoreConnectorAttributeNames_ou.Add(Uid.NAME); + + // methods to update a directory entry from a connectorattribute + UpdateDeFromCaDelegates.Add(PredefinedAttributes.ACCOUNTS_NAME, + UpdateDeFromCa_OpAtt_Accounts); + UpdateDeFromCaDelegates.Add(PredefinedAttributes.GROUPS_NAME, + UpdateDeFromCa_OpAtt_Groups); + UpdateDeFromCaDelegates.Add(ActiveDirectoryConnector.ATT_HOME_DIRECTORY, + UpdateDeFromCa_Att_HomeDirectory); + UpdateDeFromCaDelegates.Add(OperationalAttributes.ENABLE_NAME, + UpdateDeFromCa_OpAtt_Enable); + UpdateDeFromCaDelegates.Add(OperationalAttributes.PASSWORD_EXPIRED_NAME, + UpdateDeFromCa_OpAtt_PasswordExpired); + UpdateDeFromCaDelegates.Add(OperationalAttributes.PASSWORD_EXPIRATION_DATE_NAME, + UpdateDeFromCa_OpAtt_PasswordExpireDate); + UpdateDeFromCaDelegates.Add(OperationalAttributes.LOCK_OUT_NAME, + UpdateDeFromCa_OpAtt_Lockout); + // supporting class not implemented in the framework + /* + UpdateDeFromCaDelegates.Add(OperationalAttributes.ENABLE_DATE_NAME, + UpdateDeFromCa_OpAtt_EnableDate); + UpdateDeFromCaDelegates.Add(OperationalAttributes.DISABLE_DATE_NAME, + UpdateDeFromCa_OpAtt_DisableDate); + */ + UpdateDeFromCaDelegates.Add(TerminalServicesUtils.TS_ALLOW_LOGON, + UpdateDeFromCa_Att_TSAllowLogon); + UpdateDeFromCaDelegates.Add(TerminalServicesUtils.TS_INITIAL_PROGRAM, + UpdateDeFromCa_Att_TSInitialProgram); + UpdateDeFromCaDelegates.Add(TerminalServicesUtils.TS_INITIAL_PROGRAM_DIR, + UpdateDeFromCa_Att_TSInitialProgramDir); + UpdateDeFromCaDelegates.Add(TerminalServicesUtils.TS_MAX_CONNECTION_TIME, + UpdateDeFromCa_Att_TSMaxConnectionTime); + UpdateDeFromCaDelegates.Add(TerminalServicesUtils.TS_MAX_DISCONNECTION_TIME, + UpdateDeFromCa_Att_TSMaxDisconnectionTime); + UpdateDeFromCaDelegates.Add(TerminalServicesUtils.TS_MAX_IDLE_TIME, + UpdateDeFromCa_Att_TSMaxIdleTime); + UpdateDeFromCaDelegates.Add(TerminalServicesUtils.TS_CONNECT_CLIENT_DRIVES_AT_LOGON, + UpdateDeFromCa_Att_TSConnectClientDrivesAtLogon); + UpdateDeFromCaDelegates.Add(TerminalServicesUtils.TS_CONNECT_CLIENT_PRINTERS_AT_LOGON, + UpdateDeFromCa_Att_TSConnectClientPrintersAtLogon); + UpdateDeFromCaDelegates.Add(TerminalServicesUtils.TS_DEFAULT_TO_MAIN_PRINTER, + UpdateDeFromCa_Att_TSDefaultToMainPrinter); + UpdateDeFromCaDelegates.Add(TerminalServicesUtils.TS_BROKEN_CONNECTION_ACTION, + UpdateDeFromCa_Att_TSBrokenConnectionAction); + UpdateDeFromCaDelegates.Add(TerminalServicesUtils.TS_RECONNECTION_ACTION, + UpdateDeFromCa_Att_TSReconnectionAction); + UpdateDeFromCaDelegates.Add(TerminalServicesUtils.TS_ENABLE_REMOTE_CONTROL, + UpdateDeFromCa_Att_TSEnableRemoteControl); + UpdateDeFromCaDelegates.Add(TerminalServicesUtils.TS_PROFILE_PATH, + UpdateDeFromCa_Att_TSProfilePath); + UpdateDeFromCaDelegates.Add(TerminalServicesUtils.TS_HOME_DIRECTORY, + UpdateDeFromCa_Att_TSHomeDirectory); + UpdateDeFromCaDelegates.Add(TerminalServicesUtils.TS_HOME_DRIVE, + UpdateDeFromCa_Att_TSHomeDrive); + + // methods to create a connector attribute from a directory entry + GetCaFromDeDelegates.Add(Name.NAME, GetCaFromDe_OpAtt_Name); + GetCaFromDeDelegates.Add(Uid.NAME, GetCaFromDe_OpAtt_Uid); + GetCaFromDeDelegates.Add(ActiveDirectoryConnector.ATT_CONTAINER, + GetCaFromDe_Att_Container); + GetCaFromDeDelegates.Add(PredefinedAttributes.ACCOUNTS_NAME, + GetCaFromDe_OpAtt_Accounts); + GetCaFromDeDelegates.Add(PredefinedAttributes.GROUPS_NAME, + GetCaFromDe_OpAtt_Groups); + GetCaFromDeDelegates.Add(OperationalAttributes.ENABLE_NAME, + GetCaFromDe_OpAtt_Enabled); + GetCaFromDeDelegates.Add(OperationalAttributes.PASSWORD_EXPIRED_NAME, + GetCaFromDe_OpAtt_PasswordExpired); + GetCaFromDeDelegates.Add(PredefinedAttributes.DESCRIPTION, + GetCaFromDe_OpAtt_Description); + GetCaFromDeDelegates.Add(PredefinedAttributes.SHORT_NAME, + GetCaFromDe_OpAtt_ShortName); + GetCaFromDeDelegates.Add(OperationalAttributes.LOCK_OUT_NAME, + GetCaFromDe_OpAtt_Lockout); + GetCaFromDeDelegates.Add(OperationalAttributes.PASSWORD_EXPIRATION_DATE_NAME, + GetCaFromDe_OpAtt_PasswordExpireDate); + // supporting class not implemented in the framework + /* + GetCaFromDeDelegates.Add(OperationalAttributes.ENABLE_DATE_NAME, + GetCaFromDe_OpAtt_EnableDate); + GetCaFromDeDelegates.Add(OperationalAttributes.DISABLE_DATE_NAME, + GetCaFromDe_OpAtt_DisableDate); + */ + GetCaFromDeDelegates.Add(TerminalServicesUtils.TS_INITIAL_PROGRAM, + GetCaFromDe_Att_TSInitialProgram); + GetCaFromDeDelegates.Add(TerminalServicesUtils.TS_INITIAL_PROGRAM_DIR, + GetCaFromDe_Att_TSInitalProgramDir); + GetCaFromDeDelegates.Add(TerminalServicesUtils.TS_ALLOW_LOGON, + GetCaFromDe_Att_TSAllowLogon); + GetCaFromDeDelegates.Add(TerminalServicesUtils.TS_MAX_CONNECTION_TIME, + GetCaFromDe_Att_TSMaxConnectionTime); + GetCaFromDeDelegates.Add(TerminalServicesUtils.TS_MAX_DISCONNECTION_TIME, + GetCaFromDe_Att_TSMaxDisconnectionTime); + GetCaFromDeDelegates.Add(TerminalServicesUtils.TS_MAX_IDLE_TIME, + GetCaFromDe_Att_TSMaxIdleTime); + GetCaFromDeDelegates.Add(TerminalServicesUtils.TS_CONNECT_CLIENT_DRIVES_AT_LOGON, + GetCaFromDe_Att_TSConnectClientDrivesAtLogon); + GetCaFromDeDelegates.Add(TerminalServicesUtils.TS_CONNECT_CLIENT_PRINTERS_AT_LOGON, + GetCaFromDe_Att_TSConnectClientPrintersAtLogon); + GetCaFromDeDelegates.Add(TerminalServicesUtils.TS_DEFAULT_TO_MAIN_PRINTER, + GetCaFromDe_Att_TSDefaultToMainPrinter); + GetCaFromDeDelegates.Add(TerminalServicesUtils.TS_BROKEN_CONNECTION_ACTION, + GetCaFromDe_Att_TSBrokenConnectionAction); + GetCaFromDeDelegates.Add(TerminalServicesUtils.TS_RECONNECTION_ACTION, + GetCaFromDe_Att_TSReconnectionAction); + GetCaFromDeDelegates.Add(TerminalServicesUtils.TS_ENABLE_REMOTE_CONTROL, + GetCaFromDe_Att_TSEnableRemoteControl); + GetCaFromDeDelegates.Add(TerminalServicesUtils.TS_PROFILE_PATH, + GetCaFromDe_Att_TSProfilePath); + GetCaFromDeDelegates.Add(TerminalServicesUtils.TS_HOME_DIRECTORY, + GetCaFromDe_Att_TSHomeDirectory); + GetCaFromDeDelegates.Add(TerminalServicesUtils.TS_HOME_DRIVE, + GetCaFromDe_Att_TSHomeDrive); + } + + internal void UpdateDeFromCa(ObjectClass oclass, + UpdateType type, DirectoryEntry directoryEntry, + ConnectorAttribute attribute) { + + // if this gets big, replace with dictionary key by object class + IList ignoreList = null; + if(oclass.Equals(ObjectClass.ACCOUNT)) { + ignoreList = IgnoreConnectorAttributeNames_account; + } + else if (oclass.Equals(ObjectClass.GROUP)) + { + ignoreList = IgnoreConnectorAttributeNames_group; + } + else if (oclass.Equals(ActiveDirectoryConnector.ouObjectClass)) + { + ignoreList = IgnoreConnectorAttributeNames_ou; + } + + // if it's an ignored attribute, we're done + if ((ignoreList != null) && + (ignoreList.Contains(attribute.Name, + StringComparer.CurrentCultureIgnoreCase))) { + return; + } + + if (UpdateDeFromCaDelegates.ContainsKey(attribute.Name)) + { + // if it's an attribute with a special handler, + // call the handler + UpdateDeFromCa_delegate handler = + UpdateDeFromCaDelegates[attribute.Name]; + handler(oclass, type, directoryEntry, attribute); + } + else + { + // if none of the above, call the generic handler + UpdateDeFromCa_Att_Generic(oclass, type, directoryEntry, attribute); + } + } + + + internal ConnectorAttribute GetCaFromDe(ObjectClass oclass, + string attributeName, SearchResult searchResult) + { + ConnectorAttribute attribute = null; + + if (GetCaFromDeDelegates.ContainsKey(attributeName)) + { + // if it's an attribute with a special handler, + // call the handler + GetCaFromDe_delegate handler = GetCaFromDeDelegates[attributeName]; + attribute = handler(oclass, attributeName, searchResult); + } + else + { + // if none of the above, call the generic handler + attribute = GetCaFromDe_Att_Generic(oclass, attributeName, searchResult); + } + + return attribute; + } + + internal delegate void UpdateDeFromCa_delegate(ObjectClass oclass, + UpdateType type, DirectoryEntry directoryEntry, + ConnectorAttribute attribute); + + internal delegate ConnectorAttribute GetCaFromDe_delegate(ObjectClass oclass, + string attributeName, SearchResult searchResult); + + public void GetAddsAndDeletes(ICollectionvaluesToAdd, ICollectionvaluesToRemove, + PropertyValueCollection oldValues, ICollectionnewValues, UpdateType type) { + if (UpdateType.ADD.Equals(type)) + { + // add all groups + foreach (Object value in newValues) + { + valuesToAdd.Add(value); + } + } + else if (UpdateType.REPLACE.Equals(type)) + { + // look through existing values, and remove them + // if they are not in the newValues + if (oldValues != null) + { + foreach (Object value in oldValues) + { + if (!newValues.Contains(value)) + { + valuesToRemove.Add(value); + } + } + } + + // look through the values passed in and + // add them if they are not existing values + foreach (Object value in newValues) + { + if ((oldValues == null) || (!oldValues.Contains(value))) + { + valuesToAdd.Add(value); + } + } + } + else if (UpdateType.DELETE.Equals(type)) + { + foreach (Object value in newValues) + { + valuesToRemove.Add(value); + } + } + } + + #region UpdateDeFromCa handlers + + + + internal void UpdateDeFromCa_OpAtt_Groups(ObjectClass oclass, + UpdateType type, DirectoryEntry directoryEntry, + ConnectorAttribute attribute) + { + if (oclass.Equals(ObjectClass.ACCOUNT)) + { + // in this case, AD will not allow groups to be added + // to a user. To simulate this, lookup each added group + // and add this user to the group + ICollection newValues = attribute.Value; + PropertyValueCollection oldValues = null; + if(directoryEntry.Properties.Contains(ActiveDirectoryConnector.ATT_MEMBEROF)) { + oldValues = directoryEntry.Properties[ActiveDirectoryConnector.ATT_MEMBEROF]; + } + + ICollection groupsToAdd = new HashSet(); + ICollection groupsToRemove = new HashSet(); + + GetAddsAndDeletes(groupsToAdd, groupsToRemove, oldValues, newValues, type); + + foreach (Object obj in groupsToRemove) + { + // lookup the group and remove this user from group if it's a + // valid group. + String groupPath = ActiveDirectoryUtils.GetLDAPPath( + _configuration.LDAPHostName, (String)obj); + DirectoryEntry groupDe = new DirectoryEntry(groupPath, + _configuration.DirectoryAdminName, _configuration.DirectoryAdminPassword); + String distinguishedName = ActiveDirectoryUtils.GetDnFromPath(directoryEntry.Path); + groupDe.Properties[ActiveDirectoryConnector.ATT_MEMBER].Remove(distinguishedName); + groupDe.CommitChanges(); + } + + foreach (Object obj in groupsToAdd) + { + // lookup the group and add this user to group if it's a + // valid group. + String groupPath = ActiveDirectoryUtils.GetLDAPPath( + _configuration.LDAPHostName, (String)obj); + DirectoryEntry groupDe = new DirectoryEntry(groupPath, + _configuration.DirectoryAdminName, _configuration.DirectoryAdminPassword); + String distinguishedName = ActiveDirectoryUtils.GetDnFromPath(directoryEntry.Path); + groupDe.Properties[ActiveDirectoryConnector.ATT_MEMBER].Add(distinguishedName); + groupDe.CommitChanges(); + } + } + else + { + throw new ConnectorException( + String.Format("''{0}'' is an invalid attribute for object class ''{1}''", + PredefinedAttributeInfos.GROUPS, oclass.GetObjectClassValue())); + } + } + + internal void UpdateDeFromCa_OpAtt_Accounts(ObjectClass oclass, + UpdateType type, DirectoryEntry directoryEntry, + ConnectorAttribute attribute) + { + if (ObjectClass.GROUP.Equals(oclass)) + { + // create an 'attribute' with the real name, and then call the + // generic version + ConnectorAttribute newAttribute = ConnectorAttributeBuilder.Build( + ActiveDirectoryConnector.ATT_MEMBER, attribute.Value); + UpdateDeFromCa_Att_Generic(oclass, type, directoryEntry, newAttribute); + } + else + { + throw new ConnectorException( + String.Format("'{0}' is an invalid attribute for object class '{1}'", + PredefinedAttributeInfos.ACCOUNTS, oclass.GetObjectClassValue())); + } + } + + internal void UpdateDeFromCa_Att_HomeDirectory(ObjectClass oclass, + UpdateType type, DirectoryEntry directoryEntry, + ConnectorAttribute attribute) + { + String homeDir = ConnectorAttributeUtil.GetStringValue(attribute); + + if (homeDir != null) + { + if (type == UpdateType.REPLACE) + { + // first set the attribute + UpdateDeFromCa_Att_Generic(oclass, type, directoryEntry, attribute); + + // now create attribute if needed/possible + if (_configuration.CreateHomeDirectory) + { + // from old code ... should start with '\\' and have at least one + // '\' later that's not the end of the string + // i.e + // \\somemachine\someshare\somedirectory + // \\somemachine\someshare\somedirectory\someotherdirectory + // but not + // \\somemachine\someshare\ + // \\somemachine\someshare + // just ignore if it's not correct + String directoryName = ConnectorAttributeUtil.GetStringValue(attribute); + if (directoryName.StartsWith("\\\\")) + { + int secondPathSepIndex = directoryName.IndexOf('\\', 2); + if ((secondPathSepIndex > 2) && (directoryName.Length > secondPathSepIndex + 1)) + { + // name passes, so create directory + + // create security object + DirectorySecurity dirSecurity = new DirectorySecurity(); + PropertyValueCollection pvc = + directoryEntry.Properties[ActiveDirectoryConnector.ATT_OBJECT_SID]; + // there should always be exactly one sid + SecurityIdentifier sid = new SecurityIdentifier((byte[])pvc[0], 0); + // dirSecurity.SetOwner(sid); + InheritanceFlags iFlags = InheritanceFlags.ContainerInherit; + dirSecurity.AddAccessRule( + new FileSystemAccessRule(sid, FileSystemRights.FullControl, + InheritanceFlags.ContainerInherit | InheritanceFlags.ObjectInherit, + PropagationFlags.None, AccessControlType.Allow) + ); + Directory.CreateDirectory(directoryName, dirSecurity); + } + } + } + } + else + { + throw new ConnectorException("Only updatetype of replace is supported for home directory"); + } + } + } + + internal void UpdateDeFromCa_OpAtt_Enable(ObjectClass oclass, + UpdateType type, DirectoryEntry directoryEntry, + ConnectorAttribute attribute) { + + // set the proper flag in the userAccountControl bitfield + PropertyValueCollection uacPvc = + directoryEntry.Properties[UserAccountControl.UAC_ATTRIBUTE_NAME]; + + UserAccountControl.Set(uacPvc, + UserAccountControl.ACCOUNTDISABLE, + // attribute is enable, but the flag is for + // disable, so send the opposite + !ConnectorAttributeUtil.GetBooleanValue(attribute)); + } + + internal void UpdateDeFromCa_OpAtt_PasswordExpired(ObjectClass oclass, + UpdateType type, DirectoryEntry directoryEntry, + ConnectorAttribute attribute) + { + bool? passwordExpired = ConnectorAttributeUtil.GetBooleanValue(attribute); + if ((passwordExpired.HasValue) && (passwordExpired.Value == true)) + { + directoryEntry.Properties[ActiveDirectoryConnector.ATT_PWD_LAST_SET].Clear(); + directoryEntry.Properties[ActiveDirectoryConnector.ATT_PWD_LAST_SET].Value = GetLargeIntegerFromLong(0); + } + else + { + // this value can't be set (other than to zero) I'm throwing my own exception + // here, because if not, AD thows this (at least on my machine): + // System.DirectoryServices.DirectoryServicesCOMException : A device attached to the system is not functioning. (Exception from HRESULT: 0x8007001F) + throw new ConnectorException(_configuration.ConnectorMessages.Format( + "ex_PasswordMustBeReset", + "Password expiration can only be reset by reseting the password")); + } + } + + internal void UpdateDeFromCa_OpAtt_PasswordExpireDate(ObjectClass oclass, + UpdateType type, DirectoryEntry directoryEntry, + ConnectorAttribute attribute) + { + DateTime? expireDate = ConnectorAttributeUtil.GetDateTimeValue(attribute); + if(expireDate.HasValue) { + directoryEntry.Properties[ActiveDirectoryConnector.ATT_ACCOUNT_EXPIRES].Value = + GetLargeIntegerFromLong((ulong)expireDate.Value.ToFileTime()); + } + } + + internal void UpdateDeFromCa_OpAtt_Lockout(ObjectClass oclass, + UpdateType type, DirectoryEntry directoryEntry, + ConnectorAttribute attribute) + { + bool? lockout = ConnectorAttributeUtil.GetBooleanValue(attribute); + if (lockout.HasValue) + { + long lockoutTime = lockout.Value ? DateTimeUtil.GetCurrentUtcTimeMillis() : 0; + + if(lockoutTime != 0) { + throw new ConnectorException(_configuration.ConnectorMessages.Format( + "ex_LockAccountNotAllowed", "Active Directory does not support locking users. User may be unlocked only")); + } + directoryEntry.Properties[ActiveDirectoryConnector.ATT_LOCKOUT_TIME].Value = + GetLargeIntegerFromLong((ulong)lockoutTime); + } + } + + // supporting class not implemented in the framework +/* + internal void UpdateDeFromCa_OpAtt_EnableDate(ObjectClass oclass, + UpdateType type, DirectoryEntry directoryEntry, + ConnectorAttribute attribute) + { + } + + internal void UpdateDeFromCa_OpAtt_DisableDate(ObjectClass oclass, + UpdateType type, DirectoryEntry directoryEntry, + ConnectorAttribute attribute) + { + / * + long? utcMilliDate = ConnectorAttributeUtil.GetLongValue(attribute); + if(utcMilliDate == null) { + return; + } + + DateTime disableDate = DateTime.FromFileTimeUtc((long)utcMilliDate); + + LargeInteger disableTicks = new LargeIntegerClass(); + disableTicks.HighPart = (int)((disableDate.Ticks >> 32) & 0xFFFFFFFF); + disableTicks.LowPart = (int)(disableDate.Ticks & 0xFFFFFFFF); + + PropertyValueCollection pvc = directoryEntry.Properties["accountExpires"]; + if ((pvc == null) || (pvc.Count == 0)) + { + // if nothing there, add the value + pvc.Add(disableTicks); + } + else + { + // set the value + pvc[0] = disableTicks; + } + * / + } +*/ + internal void UpdateDeFromCa_Att_TSAllowLogon(ObjectClass oclass, + UpdateType type, DirectoryEntry directoryEntry, + ConnectorAttribute attribute) + { + TerminalServicesUtils.SetAllowLogon(type, directoryEntry, + ConnectorAttributeUtil.GetIntegerValue(attribute)); + } + + internal void UpdateDeFromCa_Att_TSInitialProgram(ObjectClass oclass, + UpdateType type, DirectoryEntry directoryEntry, + ConnectorAttribute attribute) + { + TerminalServicesUtils.SetInitialProgram(type, directoryEntry, + ConnectorAttributeUtil.GetStringValue(attribute)); + } + + internal void UpdateDeFromCa_Att_TSInitialProgramDir(ObjectClass oclass, + UpdateType type, DirectoryEntry directoryEntry, + ConnectorAttribute attribute) + { + TerminalServicesUtils.SetInitialProgramDir(type, directoryEntry, + ConnectorAttributeUtil.GetStringValue(attribute)); + } + + internal void UpdateDeFromCa_Att_TSMaxConnectionTime(ObjectClass oclass, + UpdateType type, DirectoryEntry directoryEntry, + ConnectorAttribute attribute) + { + TerminalServicesUtils.SetMaxConnectionTime(type, directoryEntry, + ConnectorAttributeUtil.GetIntegerValue(attribute)); + } + + internal void UpdateDeFromCa_Att_TSMaxDisconnectionTime(ObjectClass oclass, + UpdateType type, DirectoryEntry directoryEntry, + ConnectorAttribute attribute) + { + TerminalServicesUtils.SetMaxDisconnectionTime(type, directoryEntry, + ConnectorAttributeUtil.GetIntegerValue(attribute)); + } + + internal void UpdateDeFromCa_Att_TSMaxIdleTime(ObjectClass oclass, + UpdateType type, DirectoryEntry directoryEntry, + ConnectorAttribute attribute) + { + TerminalServicesUtils.SetMaxIdleTime(type, directoryEntry, + ConnectorAttributeUtil.GetIntegerValue(attribute)); + } + + internal void UpdateDeFromCa_Att_TSConnectClientDrivesAtLogon(ObjectClass oclass, + UpdateType type, DirectoryEntry directoryEntry, + ConnectorAttribute attribute) + { + TerminalServicesUtils.SetConnectClientDrivesAtLogon(type, directoryEntry, + ConnectorAttributeUtil.GetIntegerValue(attribute)); + } + + internal void UpdateDeFromCa_Att_TSConnectClientPrintersAtLogon(ObjectClass oclass, + UpdateType type, DirectoryEntry directoryEntry, + ConnectorAttribute attribute) + { + TerminalServicesUtils.SetConnectClientPrintersAtLogon(type, directoryEntry, + ConnectorAttributeUtil.GetIntegerValue(attribute)); + } + + internal void UpdateDeFromCa_Att_TSDefaultToMainPrinter(ObjectClass oclass, + UpdateType type, DirectoryEntry directoryEntry, + ConnectorAttribute attribute) + { + TerminalServicesUtils.SetDefaultToMainPrinter(type, directoryEntry, + ConnectorAttributeUtil.GetIntegerValue(attribute)); + } + + internal void UpdateDeFromCa_Att_TSBrokenConnectionAction(ObjectClass oclass, + UpdateType type, DirectoryEntry directoryEntry, + ConnectorAttribute attribute) + { + TerminalServicesUtils.SetBrokenConnectionAction(type, directoryEntry, + ConnectorAttributeUtil.GetIntegerValue(attribute)); + } + + internal void UpdateDeFromCa_Att_TSReconnectionAction(ObjectClass oclass, + UpdateType type, DirectoryEntry directoryEntry, + ConnectorAttribute attribute) + { + TerminalServicesUtils.SetReconnectionAction(type, directoryEntry, + ConnectorAttributeUtil.GetIntegerValue(attribute)); + } + + internal void UpdateDeFromCa_Att_TSEnableRemoteControl(ObjectClass oclass, + UpdateType type, DirectoryEntry directoryEntry, + ConnectorAttribute attribute) + { + TerminalServicesUtils.SetEnableRemoteControl(type, directoryEntry, + ConnectorAttributeUtil.GetIntegerValue(attribute)); + } + + internal void UpdateDeFromCa_Att_TSProfilePath(ObjectClass oclass, + UpdateType type, DirectoryEntry directoryEntry, + ConnectorAttribute attribute) + { + TerminalServicesUtils.SetProfilePath(type, directoryEntry, + ConnectorAttributeUtil.GetStringValue(attribute)); + } + + internal void UpdateDeFromCa_Att_TSHomeDirectory(ObjectClass oclass, + UpdateType type, DirectoryEntry directoryEntry, + ConnectorAttribute attribute) + { + TerminalServicesUtils.SetHomeDirectory(type, directoryEntry, + ConnectorAttributeUtil.GetStringValue(attribute)); + } + + internal void UpdateDeFromCa_Att_TSHomeDrive(ObjectClass oclass, + UpdateType type, DirectoryEntry directoryEntry, + ConnectorAttribute attribute) + { + TerminalServicesUtils.SetHomeDrive(type, directoryEntry, + ConnectorAttributeUtil.GetStringValue(attribute)); + } + + internal void UpdateDeFromCa_Att_Generic(ObjectClass oclass, + UpdateType type, DirectoryEntry directoryEntry, + ConnectorAttribute attribute) + { + + // null out the values if we are replacing attributes. + if (type.Equals(UpdateType.REPLACE)) + { + directoryEntry.Properties[attribute.Name].Value = null; + } + + if (attribute.Value == null) + { + return; + } + // if we are updating or adding, put the + // new values in. + if (type.Equals(UpdateType.ADD) || + type.Equals(UpdateType.REPLACE)) + { + foreach (Object valueObject in attribute.Value) + { + directoryEntry.Properties[attribute.Name].Add(valueObject); + } + } + else if (type.Equals(UpdateType.DELETE)) + { + // if deleting, find the values, + // and remove them if they exist + if (directoryEntry.Properties.Contains(attribute.Name)) + { + PropertyValueCollection pvc = directoryEntry.Properties[attribute.Name]; + + foreach (Object valueObject in attribute.Value) + { + if (pvc.Contains(valueObject)) + { + pvc.Remove(valueObject); + } + } + } + + } + } + + #endregion + + #region GetCaFromDe Handlers + private ConnectorAttribute GetCaFromDe_Att_Generic( + ObjectClass oclass, string attributeName, SearchResult searchResult) + { + ConnectorAttributeBuilder attributeBuilder = new ConnectorAttributeBuilder(); + + if (attributeName == null) + { + return null; + } + + attributeBuilder.Name = attributeName; + + ResultPropertyValueCollection pvc = null; + if (searchResult.Properties.Contains(attributeName)) + { + pvc = searchResult.Properties[attributeName]; + } + + if (pvc == null) + { + return null; + } + else + { + for (int i = 0; i < pvc.Count; i++) + { + Object valueObject = pvc[i]; + if ((pvc[i] == null) || + (FrameworkUtil.IsSupportedAttributeType(valueObject.GetType()))) + { + attributeBuilder.AddValue(pvc[i]); + } + else + { + Trace.TraceWarning( + "Unsupported attribute type ... calling ToString (Name: \'{0}\'({1}) Type: \'{2}\' String Value: \'{3}\'", + attributeName, i, pvc[i].GetType(), pvc[i].ToString()); + attributeBuilder.AddValue(pvc[i].ToString()); + } + } + } + + return attributeBuilder.Build(); + } + + private ConnectorAttribute GetCaFromDe_OpAtt_Name( + ObjectClass oclass, string attributeName, SearchResult searchResult) + { + String value = null; + ResultPropertyValueCollection pvc = null; + + pvc = searchResult.Properties[ActiveDirectoryConnector.ATT_DISTINGUISHED_NAME]; + if ((pvc != null) && (pvc.Count == 1) && (pvc[0] is String)) + { + value = (String)pvc[0]; + } + else + { + throw new ConnectorException("There should be exactly one value for the name attribute"); + } + + return ConnectorAttributeBuilder.Build(Name.NAME, ActiveDirectoryUtils.NormalizeLdapString(value)); + } + + private ConnectorAttribute GetCaFromDe_OpAtt_Uid( + ObjectClass oclass, string attributeName, SearchResult searchResult) + { + ICollection value = new List(); + + if (ObjectClass.ACCOUNT.Equals(oclass)) + { + // uid is objectGuid + ResultPropertyValueCollection pvc = + searchResult.Properties[ActiveDirectoryConnector.ATT_OBJECT_GUID]; + + if ((pvc.Count == 1) && (pvc[0] is Byte[])) + { + value.Add(ActiveDirectoryUtils.ConvertUIDBytesToGUIDString((Byte[])pvc[0])); + } + else if (pvc.Count > 1) + { + throw new ConnectorException("There should be only one UID, but multiple values were specified"); + } + } + else + { + ConnectorAttribute name = GetCaFromDe_OpAtt_Name(oclass, attributeName, searchResult); + if ((name.Value != null) && (name.Value.Count != 0)) + { + value.Add(ActiveDirectoryUtils.NormalizeLdapString((String)name.Value[0])); + } + } + + return ConnectorAttributeBuilder.Build(Uid.NAME, value); + } + + private ConnectorAttribute GetCaFromDe_Att_Container( + ObjectClass oclass, string attributeName, SearchResult searchResult) + { + if (searchResult == null) + { + return null; + } + + DirectoryEntry parentDe = searchResult.GetDirectoryEntry().Parent; + String container = ""; + if (parentDe != null) + { + container = ActiveDirectoryUtils.GetDnFromPath(parentDe.Path); + } + + return ConnectorAttributeBuilder.Build( + ActiveDirectoryConnector.ATT_CONTAINER, container); + } + + private ConnectorAttribute GetCaFromDe_OpAtt_Groups( + ObjectClass oclass, string attributeName, SearchResult searchResult) + { + ConnectorAttribute realAttribute = GetCaFromDe_Att_Generic( + oclass, ActiveDirectoryConnector.ATT_MEMBEROF, searchResult); + if (realAttribute == null) + { + return null; + } + else + { + return ConnectorAttributeBuilder.Build(PredefinedAttributes.GROUPS_NAME, + realAttribute.Value); + } + } + + private ConnectorAttribute GetCaFromDe_OpAtt_Accounts( + ObjectClass oclass, string attributeName, SearchResult searchResult) + { + ConnectorAttribute realAttribute = GetCaFromDe_Att_Generic( + oclass, ActiveDirectoryConnector.ATT_MEMBER, searchResult); + if (realAttribute == null) + { + return null; + } + else + { + return ConnectorAttributeBuilder.Build(PredefinedAttributes.ACCOUNTS_NAME, + realAttribute.Value); + } + } + + private ConnectorAttribute GetCaFromDe_OpAtt_Enabled( + ObjectClass oclass, string attributeName, SearchResult searchResult) + { + if (searchResult == null) + { + return null; + } + + bool disabled = UserAccountControl.IsSet( + searchResult.GetDirectoryEntry().Properties[UserAccountControl.UAC_ATTRIBUTE_NAME], + UserAccountControl.ACCOUNTDISABLE); + + return ConnectorAttributeBuilder.BuildEnabled(!disabled); + } + + private ConnectorAttribute GetCaFromDe_OpAtt_PasswordExpired( + ObjectClass oclass, string attributeName, SearchResult searchResult) + { + ConnectorAttribute realAttribute = GetCaFromDe_Att_Generic( + oclass, ActiveDirectoryConnector.ATT_PWD_LAST_SET, searchResult); + long? lastSetDate = ConnectorAttributeUtil.GetLongValue(realAttribute); + if ((lastSetDate.HasValue) && (lastSetDate.Value != 0)) + { + return ConnectorAttributeBuilder.BuildPasswordExpired(false); + } + + return ConnectorAttributeBuilder.BuildPasswordExpired(true); + } + + private ConnectorAttribute GetCaFromDe_OpAtt_Description( + ObjectClass oclass, string attributeName, SearchResult searchResult) + { + ConnectorAttribute realDescription = GetCaFromDe_Att_Generic( + oclass, ActiveDirectoryConnector.ATT_DESCRIPTION, searchResult); + + if (realDescription != null) + { + string description = ConnectorAttributeUtil.GetStringValue(realDescription); + + if (description != null) + { + return ConnectorAttributeBuilder.Build(PredefinedAttributes.DESCRIPTION, description); + } + } + return null; + } + + private ConnectorAttribute GetCaFromDe_OpAtt_ShortName( + ObjectClass oclass, string attributeName, SearchResult searchResult) + { + ConnectorAttribute realShortName = GetCaFromDe_Att_Generic( + oclass, ActiveDirectoryConnector.ATT_SHORT_NAME, searchResult); + + if (realShortName != null) + { + string shortName = ConnectorAttributeUtil.GetStringValue(realShortName); + + if (shortName != null) + { + return ConnectorAttributeBuilder.Build(PredefinedAttributes.SHORT_NAME, shortName); + } + } + return null; + } + + private ConnectorAttribute GetCaFromDe_OpAtt_PasswordExpireDate( + ObjectClass oclass, string attributeName, SearchResult searchResult) + { + // get the value from ad + ConnectorAttribute accountExpireAttribute = GetCaFromDe_Att_Generic( + oclass, ActiveDirectoryConnector.ATT_ACCOUNT_EXPIRES, searchResult); + + // now change name + if (accountExpireAttribute != null) + { + long? expireValue = ConnectorAttributeUtil.GetLongValue(accountExpireAttribute); + if (expireValue != null) + { + return ConnectorAttributeBuilder.BuildPasswordExpirationDate(expireValue.Value); + } + else + { + return null; + } + } + return null; + } + + private ConnectorAttribute GetCaFromDe_OpAtt_Lockout( + ObjectClass oclass, string attributeName, SearchResult searchResult) + { + bool locked = false; + + ConnectorAttribute realAttribute = GetCaFromDe_Att_Generic( + oclass, ActiveDirectoryConnector.ATT_LOCKOUT_TIME, searchResult); + if (realAttribute != null) + { + long? lockoutDate = ConnectorAttributeUtil.GetLongValue(realAttribute); + if ((lockoutDate.HasValue) && (lockoutDate.Value != 0)) + { + // if there is a date (non zero), then the account + // is locked + locked = true; + } + } + return ConnectorAttributeBuilder.BuildLockOut(locked); + } + + // supporting class not implemented in the framework +/* + private ConnectorAttribute GetCaFromDe_OpAtt_EnableDate( + ObjectClass oclass, string attributeName, SearchResult searchResult) + { + return null; + } + + private ConnectorAttribute GetCaFromDe_OpAtt_DisableDate( + ObjectClass oclass, string attributeName, SearchResult searchResult) + { + / * + if (searchResult == null) + { + return null; + } + + ResultPropertyValueCollection rpvc = + searchResult.Properties["accountExpires"]; + if(rpvc.Count == 0) { + return null; + } + + long ticks = (long)rpvc[0]; + if ((ticks < DateTime.MinValue.Ticks) || (ticks > DateTime.MaxValue.Ticks)) + { + return null; + } + + DateTime disableDate = new DateTime(ticks); + + return ConnectorAttributeBuilder.BuildDisableDate(disableDate); + * / + return null; + } + */ + + private ConnectorAttribute GetCaFromDe_Att_TSInitialProgram( + ObjectClass oclass, string attributeName, SearchResult searchResult) + { + return ReturnConnectorAttribute(TerminalServicesUtils.TS_INITIAL_PROGRAM, + TerminalServicesUtils.GetInitialProgram(searchResult)); + } + + private ConnectorAttribute GetCaFromDe_Att_TSInitalProgramDir( + ObjectClass oclass, string attributeName, SearchResult searchResult) + { + return ReturnConnectorAttribute(TerminalServicesUtils.TS_INITIAL_PROGRAM_DIR, + TerminalServicesUtils.GetInitialProgramDir(searchResult)); + } + + + private ConnectorAttribute GetCaFromDe_Att_TSAllowLogon( + ObjectClass oclass, string attributeName, SearchResult searchResult) + { + return ReturnConnectorAttribute(TerminalServicesUtils.TS_ALLOW_LOGON, + TerminalServicesUtils.GetAllowLogon(searchResult)); + } + + private ConnectorAttribute GetCaFromDe_Att_TSMaxConnectionTime( + ObjectClass oclass, string attributeName, SearchResult searchResult) + { + return ReturnConnectorAttribute(TerminalServicesUtils.TS_MAX_CONNECTION_TIME, + TerminalServicesUtils.GetMaxConnectionTime(searchResult)); + } + + private ConnectorAttribute GetCaFromDe_Att_TSMaxDisconnectionTime( + ObjectClass oclass, string attributeName, SearchResult searchResult) + { + return ReturnConnectorAttribute(TerminalServicesUtils.TS_MAX_DISCONNECTION_TIME, + TerminalServicesUtils.GetMaxDisconnectionTime(searchResult)); + } + + private ConnectorAttribute GetCaFromDe_Att_TSMaxIdleTime( + ObjectClass oclass, string attributeName, SearchResult searchResult) + { + return ReturnConnectorAttribute(TerminalServicesUtils.TS_MAX_IDLE_TIME, + TerminalServicesUtils.GetMaxIdleTime(searchResult)); + } + + private ConnectorAttribute GetCaFromDe_Att_TSConnectClientDrivesAtLogon( + ObjectClass oclass, string attributeName, SearchResult searchResult) + { + return ReturnConnectorAttribute(TerminalServicesUtils.TS_CONNECT_CLIENT_DRIVES_AT_LOGON, + TerminalServicesUtils.GetConnectClientDrivesAtLogon(searchResult)); + } + + private ConnectorAttribute GetCaFromDe_Att_TSConnectClientPrintersAtLogon( + ObjectClass oclass, string attributeName, SearchResult searchResult) + { + return ReturnConnectorAttribute( + TerminalServicesUtils.TS_CONNECT_CLIENT_PRINTERS_AT_LOGON, + TerminalServicesUtils.GetConnectClientPrintersAtLogon(searchResult)); + } + + private ConnectorAttribute GetCaFromDe_Att_TSDefaultToMainPrinter( + ObjectClass oclass, string attributeName, SearchResult searchResult) + { + return ReturnConnectorAttribute( + TerminalServicesUtils.TS_DEFAULT_TO_MAIN_PRINTER, + TerminalServicesUtils.GetDefaultToMainPrinter(searchResult)); + } + + private ConnectorAttribute GetCaFromDe_Att_TSBrokenConnectionAction( + ObjectClass oclass, string attributeName, SearchResult searchResult) + { + return ReturnConnectorAttribute( + TerminalServicesUtils.TS_BROKEN_CONNECTION_ACTION, + TerminalServicesUtils.GetBrokenConnectionAction(searchResult)); + } + + private ConnectorAttribute GetCaFromDe_Att_TSReconnectionAction( + ObjectClass oclass, string attributeName, SearchResult searchResult) + { + return ReturnConnectorAttribute(TerminalServicesUtils.TS_RECONNECTION_ACTION, + TerminalServicesUtils.GetReconnectionAction(searchResult)); + } + + private ConnectorAttribute GetCaFromDe_Att_TSEnableRemoteControl( + ObjectClass oclass, string attributeName, SearchResult searchResult) + { + return ReturnConnectorAttribute(TerminalServicesUtils.TS_ENABLE_REMOTE_CONTROL, + TerminalServicesUtils.GetEnableRemoteControl(searchResult)); + } + + private ConnectorAttribute GetCaFromDe_Att_TSProfilePath( + ObjectClass oclass, string attributeName, SearchResult searchResult) + { + return ReturnConnectorAttribute(TerminalServicesUtils.TS_PROFILE_PATH, + TerminalServicesUtils.GetProfilePath(searchResult)); + } + private ConnectorAttribute GetCaFromDe_Att_TSHomeDirectory( + ObjectClass oclass, string attributeName, SearchResult searchResult) + { + return ReturnConnectorAttribute(TerminalServicesUtils.TS_HOME_DIRECTORY, + TerminalServicesUtils.GetHomeDirectory(searchResult)); + } + private ConnectorAttribute GetCaFromDe_Att_TSHomeDrive( + ObjectClass oclass, string attributeName, SearchResult searchResult) + { + return ReturnConnectorAttribute(TerminalServicesUtils.TS_HOME_DRIVE, + TerminalServicesUtils.GetHomeDrive(searchResult)); + } + + #endregion + + internal ConnectorAttribute ReturnConnectorAttribute + (string name, T value) { + ConnectorAttribute newAttribute = null; + + if (value != null) + { + newAttribute = ConnectorAttributeBuilder.Build( + name, value); + } + return newAttribute; + } + + // gets a long from a LargeInteger (COM object) + ulong GetLongFromLargeInteger(LargeInteger largeInteger) + { + ulong longValue = ((ulong)largeInteger.HighPart) << 32; + longValue += (ulong)largeInteger.LowPart; + return longValue; + } + + // sets a LargeInteger (COM object) from a long + LargeInteger GetLargeIntegerFromLong(ulong longValue) + { + LargeInteger largeInteger = new LargeIntegerClass(); + largeInteger.HighPart = (int)((longValue & 0xFFFFFFFF00000000) >> 32); + largeInteger.LowPart = (int)(longValue & 0x00000000FFFFFFFF); + return largeInteger; + } + } + +} diff --git a/DotNetConnectors.sln/ActiveDirectoryConnector/Messages.en.Designer.cs b/DotNetConnectors.sln/ActiveDirectoryConnector/Messages.en.Designer.cs new file mode 100644 index 00000000..e69de29b diff --git a/DotNetConnectors.sln/ActiveDirectoryConnector/Messages.en.resx b/DotNetConnectors.sln/ActiveDirectoryConnector/Messages.en.resx new file mode 100644 index 00000000..aacdfbf3 --- /dev/null +++ b/DotNetConnectors.sln/ActiveDirectoryConnector/Messages.en.resx @@ -0,0 +1,123 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + text/microsoft-resx + + + 2.0 + + + System.Resources.ResXResourceReader, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + System.Resources.ResXResourceWriter, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + ActiveDirectory Connector + + \ No newline at end of file diff --git a/DotNetConnectors.sln/ActiveDirectoryConnector/Messages.es-ES.resx b/DotNetConnectors.sln/ActiveDirectoryConnector/Messages.es-ES.resx new file mode 100644 index 00000000..03886a03 --- /dev/null +++ b/DotNetConnectors.sln/ActiveDirectoryConnector/Messages.es-ES.resx @@ -0,0 +1,123 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + text/microsoft-resx + + + 2.0 + + + System.Resources.ResXResourceReader, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + System.Resources.ResXResourceWriter, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + Active Direcory Connector + + \ No newline at end of file diff --git a/DotNetConnectors.sln/ActiveDirectoryConnector/Messages.es.resx b/DotNetConnectors.sln/ActiveDirectoryConnector/Messages.es.resx new file mode 100644 index 00000000..aacdfbf3 --- /dev/null +++ b/DotNetConnectors.sln/ActiveDirectoryConnector/Messages.es.resx @@ -0,0 +1,123 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + text/microsoft-resx + + + 2.0 + + + System.Resources.ResXResourceReader, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + System.Resources.ResXResourceWriter, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + ActiveDirectory Connector + + \ No newline at end of file diff --git a/DotNetConnectors.sln/ActiveDirectoryConnector/Messages.resx b/DotNetConnectors.sln/ActiveDirectoryConnector/Messages.resx new file mode 100644 index 00000000..d685fb80 --- /dev/null +++ b/DotNetConnectors.sln/ActiveDirectoryConnector/Messages.resx @@ -0,0 +1,264 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + text/microsoft-resx + + + 2.0 + + + System.Resources.ResXResourceReader, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + System.Resources.ResXResourceWriter, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + Windows Active Directory Connector + + + Create Home Directory + + + Directory Adminstrator''s Account + + + Directory Administrator''s Password + + + Domain Name + + + Active Directory Domain Controller Hostname + + + Object Class for User Objects + + + Search Child Domains + + + Search Container + + + Sync Domain Controller + + + Sync Global Catalog Server + + + Sync Search Context + + + Specify whether or not the home directory for the user will be created. + + + Enter the administrator user name with which the system should authenticate. The setting can be either be a username or 'domainname'\'username'. + + + Enter the password that should be used when authenticating. + + + Name of the windows domain (e.g. windowsdomain.mycompany.com) + + + For cross-domain administration, enter the hostname, IP address, or domain name of the LDAP server. + + + Specify the Active Directory object class for user objects that will be managed on this resource. The default is User, and for most situations, this should be fine. + + + Select if you want searches of Active Directory to include child domains. In addition, the Search Container and Sync Search Context (see sync settings) attributes must be set to the top of the parent domain, e.g. DC=mydomain,DC=com. + + + Specify a container object which will be the default root of all searches. Unless a search explicitly passes in other criteria, only objects under this container will be searched. For example, if you want to retrieve users from the Users container, enter CN=Users,DC=MYDOMAIN,DC=COM. + + + Domain controller to use during active sync. Only used if not searching child domains. + + + Name of the global catalog server. This is needed only if searching child domains. + + + Distinguished name of the object under which to search for changes during sync operation. + + + Connector has not been configured + + + Delete is not supported for ObjectClass {0} + + + Invalid object class: {0} + + + Attribute {0} is not present in connector object. Cannot proceed with synchronization + + + The name operational attribute cannot be null + + + Sync operation is not available for ObjectClass {0} + + + Uid was not present + + + Invalid Object Class was specified in the connector configuration. Object Class \'{0}\' was not found in Active Directory + + + Could not add connector attribute to <null> search result + + + Could not add connector attribute to <null> directory entry + + + Expecting single value, but found multiple values for attribute {0} + + + ObjectClass \'{0}\' is not valid for this connector + + + Invalid credentials supplied for user {0} + + + Password expiration can only be reset by reseting the password + + + Active Directory does not support locking users. User may be unlocked only + + + An invalid searchscope was supplied: {0} + + + An execption occurred during validation of user {0}. The user was successfully authenticated, but the user's guid could not be determined. + + + An execption occurred during validation of user {0}. The user was successfully authenticated, but the user's sid could not be determined. + + + Directory administrator name not supplied. + + + Directory administrator password not supplied. + + + Domain name not supplied. + + + ObjectClass was not supplied. + + + Search Container was not supplied. + + + Using Identity Manger Resource Adapter style query '{0}'. This should be updated to use the new connector query syntax. + + + An invalid search container was supplied: {0} + + \ No newline at end of file diff --git a/DotNetConnectors.sln/ActiveDirectoryConnector/ObjectClasses.xml b/DotNetConnectors.sln/ActiveDirectoryConnector/ObjectClasses.xml new file mode 100644 index 00000000..7dd21635 --- /dev/null +++ b/DotNetConnectors.sln/ActiveDirectoryConnector/ObjectClasses.xml @@ -0,0 +1,262 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/DotNetConnectors.sln/ActiveDirectoryConnector/PasswordChangeHandler.cs b/DotNetConnectors.sln/ActiveDirectoryConnector/PasswordChangeHandler.cs new file mode 100644 index 00000000..98b3efbc --- /dev/null +++ b/DotNetConnectors.sln/ActiveDirectoryConnector/PasswordChangeHandler.cs @@ -0,0 +1,237 @@ +/* + * ==================== + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER. + * + * Copyright 2007-2008 Sun Microsystems, Inc. All rights reserved. + * + * The contents of this file are subject to the terms of the Common Development + * and Distribution License("CDDL") (the "License"). You may not use this file + * except in compliance with the License. + * + * You can obtain a copy of the License at + * http://IdentityConnectors.dev.java.net/legal/license.txt + * See the License for the specific language governing permissions and limitations + * under the License. + * + * When distributing the Covered Code, include this CDDL Header Notice in each file + * and include the License file at identityconnectors/legal/license.txt. + * If applicable, add the following below this CDDL Header, with the fields + * enclosed by brackets [] replaced by your own identifying information: + * "Portions Copyrighted [year] [name of copyright owner]" + * ==================== + */ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.DirectoryServices; +using Org.IdentityConnectors.Common.Security; +using ActiveDs; +using Org.IdentityConnectors.Framework.Common.Exceptions; +using System.DirectoryServices.AccountManagement; +using System.DirectoryServices.ActiveDirectory; +using System.Threading; +using Org.IdentityConnectors.Framework.Common.Objects; +using System.Security.Principal; + +namespace Org.IdentityConnectors.ActiveDirectory +{ + + /** + * This class will decrypt passwords, and handle + * authentication and password changes (both + * administrative and user) + */ + internal class PasswordChangeHandler + { + String _currentPassword; + String _newPassword; + ActiveDirectoryConfiguration _configuration = null; + static Semaphore authenticationSem = new Semaphore(1, 1, "ActiveDirectoryConnectorAuthSem"); + static readonly int ERR_PASSWORD_MUST_BE_CHANGED = -2147022989; + static readonly int ERR_PASSWORD_EXPIRED = -2147023688; + + + internal PasswordChangeHandler(ActiveDirectoryConfiguration configuration) + { + _configuration = configuration; + } + + /// + /// sets the _currentPassword variable + /// + /// + internal void setCurrentPassword(UnmanagedArray clearChars) + { + _currentPassword = ""; + + // build up the string from the unmanaged array + for (int i = 0; i < clearChars.Length; i++) + { + _currentPassword += clearChars[i]; + } + } + + /// + /// Sets the _newPassword variable + /// + /// + internal void setNewPassword(UnmanagedArray clearChars) + { + _newPassword = ""; + + // build up the string from the unmanaged array + for (int i = 0; i < clearChars.Length; i++) + { + _newPassword += clearChars[i]; + } + } + + /// + /// Does an administrative password change. The Directory + /// entry must be created with username and password of + /// a user with permission to change the password + /// + /// + /// + internal void changePassword(DirectoryEntry directoryEntry, + GuardedString gsNewPassword) + { + // decrypt and save the new password + gsNewPassword.Access(setNewPassword); + + // get the native com object as an IADsUser, and set the + // password + IADsUser user = (IADsUser)directoryEntry.NativeObject; + user.SetPassword(_newPassword); + } + + /// + /// Does a user password change. Must supply the currentpassword + /// and the new password + /// + /// + /// + /// + internal void changePassword(DirectoryEntry directoryEntry, + GuardedString gsCurrentPassword, GuardedString gsNewPassword) + { + // decrypt and save the old nad new passwords + gsNewPassword.Access(setNewPassword); + gsCurrentPassword.Access(setCurrentPassword); + + // get the native com object as an IADsUser, and change the + // password + IADsUser user = (IADsUser)directoryEntry.NativeObject; + user.ChangePassword(_currentPassword, _newPassword); + } + + /// + /// Authenticates the user + /// + /// + /// + /// + internal Uid Authenticate(/*DirectoryEntry directoryEntry,*/ string username, + Org.IdentityConnectors.Common.Security.GuardedString password) + { + password.Access(setCurrentPassword); + + // create principle context for authentication + string serverName = _configuration.LDAPHostName; + PrincipalContext context = null; + UserPrincipal userPrincipal = null; + try + { + // according to microsoft docs: + // Wait on return - true if the current instance receives a signal. If the current instance is never signaled, WaitOne never returns. + // no need to check return, since it will not return false; + authenticationSem.WaitOne(); + if ((serverName == null) || (serverName.Length == 0)) + { + // if they haven't specified an ldap host, use the domain that is + // in the connector configuration + DomainController domainController = ActiveDirectoryUtils.GetDomainController(_configuration); + context = new PrincipalContext(ContextType.Domain, + domainController.Domain.Name, _configuration.DirectoryAdminName, + _configuration.DirectoryAdminPassword); + } + else + { + // if the specified an ldap host, use it. + context = new PrincipalContext(ContextType.Machine, + _configuration.LDAPHostName, _configuration.DirectoryAdminName, + _configuration.DirectoryAdminPassword); + } + + if (context == null) + { + throw new ConnectorException("Unable to get PrincipalContext"); + } + + if (!context.ValidateCredentials(username, _currentPassword)) + { + throw new InvalidCredentialException(_configuration.ConnectorMessages.Format( + "ex_InvalidCredentials", "Invalid credentials supplied for user {0}", + username)); + } + return GetUidFromSamAccountName(context, username); + } + catch (PrincipalOperationException e) + { + if ((e.ErrorCode.Equals(ERR_PASSWORD_MUST_BE_CHANGED)) || + (e.ErrorCode.Equals(ERR_PASSWORD_EXPIRED))) + { + Uid uid = GetUidFromSamAccountName(context, username); + PasswordExpiredException exception = new PasswordExpiredException(e.Message); + exception.Uid = uid; + throw exception; + } + + throw; + } + finally + { + if (context != null) + { + context.Dispose(); + context = null; + } + authenticationSem.Release(); + } + } + + public Uid GetUidFromSamAccountName(PrincipalContext context, String sAMAccountName) + { + UserPrincipal userPrincipal = null; + + try + { + userPrincipal = UserPrincipal.FindByIdentity(context, + IdentityType.SamAccountName, sAMAccountName); + + if (userPrincipal.Sid == null) + { + throw new ConnectorException(_configuration.ConnectorMessages.Format( + "ex_SIDLookup", "An execption occurred during validation of user {0}. The user was successfully authenticated, but the user's sid could not be determined.", + sAMAccountName)); + } + + string sidString = ""; + DirectoryEntry userDe = new DirectoryEntry( + ActiveDirectoryUtils.GetLDAPPath(_configuration.LDAPHostName, sidString), + _configuration.DirectoryAdminName, _configuration.DirectoryAdminPassword); + + return new Uid(ActiveDirectoryUtils.ConvertUIDBytesToGUIDString(userDe.Guid.ToByteArray())); + } + finally + { + if (userPrincipal != null) + { + userPrincipal.Dispose(); + userPrincipal = null; + } + } + } + } +} diff --git a/DotNetConnectors.sln/ActiveDirectoryConnector/TerminalServicesUtils.cs b/DotNetConnectors.sln/ActiveDirectoryConnector/TerminalServicesUtils.cs new file mode 100644 index 00000000..599506d2 --- /dev/null +++ b/DotNetConnectors.sln/ActiveDirectoryConnector/TerminalServicesUtils.cs @@ -0,0 +1,304 @@ +/* + * ==================== + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER. + * + * Copyright 2007-2008 Sun Microsystems, Inc. All rights reserved. + * + * The contents of this file are subject to the terms of the Common Development + * and Distribution License("CDDL") (the "License"). You may not use this file + * except in compliance with the License. + * + * You can obtain a copy of the License at + * http://IdentityConnectors.dev.java.net/legal/license.txt + * See the License for the specific language governing permissions and limitations + * under the License. + * + * When distributing the Covered Code, include this CDDL Header Notice in each file + * and include the License file at identityconnectors/legal/license.txt. + * If applicable, add the following below this CDDL Header, with the fields + * enclosed by brackets [] replaced by your own identifying information: + * "Portions Copyrighted [year] [name of copyright owner]" + * ==================== + */ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.DirectoryServices; +using Org.IdentityConnectors.Framework.Spi.Operations; +using Org.IdentityConnectors.Framework.Common.Exceptions; + +namespace Org.IdentityConnectors.ActiveDirectory +{ + /** + * This class will handle setting all of the terminal services attributes + */ + public class TerminalServicesUtils + { + // used to be 'Terminal Services Initial Program' + public static string TS_INITIAL_PROGRAM = "TerminalServicesInitialProgram"; + + // used to be 'Terminal Services Initial Program Directory' + public static string TS_INITIAL_PROGRAM_DIR = "TerminalServicesWorkDirectory"; + + // used to be 'Terminal Services Inherit Initial Program' + + // used to be 'Terminal Services Allow Logon' - defaults to false, so testing true + public static string TS_ALLOW_LOGON = "AllowLogon"; + + // used to be 'Terminal Services Active Session Timeout' + public static string TS_MAX_CONNECTION_TIME = "MaxConnectionTime"; + + // used to be 'Terminal Services Disconnected Session Timeout' + public static string TS_MAX_DISCONNECTION_TIME = "MaxDisconnectionTime"; + + // used to be 'Terminal Services Idle Timeout' + public static string TS_MAX_IDLE_TIME = "MaxIdleTime"; + + // used to be 'Terminal Services Connect Client Drives At Logon' + public static string TS_CONNECT_CLIENT_DRIVES_AT_LOGON = "ConnectClientDrivesAtLogon"; + + // used to be 'Terminal Services Connect Client Printers At Logon' + public static string TS_CONNECT_CLIENT_PRINTERS_AT_LOGON = "ConnectClientPrintersAtLogon"; + + // used to be 'Terminal Services Default To Main Client Printer' + public static string TS_DEFAULT_TO_MAIN_PRINTER = "DefaultToMainPrinter"; + + // used to be 'Terminal Services End Session On Timeout Or Broken Connection' + public static string TS_BROKEN_CONNECTION_ACTION = "BrokenConnectionAction"; + + // used to be 'Terminal Services Allow Reconnect From Originating Client Only' + public static string TS_RECONNECTION_ACTION = "ReconnectionAction"; + + // used to be 'Terminal Services Callback Settings' + + // used to be 'Terminal Services Callback Phone Number' + + // used to be 'Terminal Services Remote Control Settings' + public static string TS_ENABLE_REMOTE_CONTROL = "EnableRemoteControl"; + + // used to be 'Terminal Services User Profile' + public static string TS_PROFILE_PATH = "TerminalServicesProfilePath"; + + // used to be 'Terminal Services Local Home Directory' + public static string TS_HOME_DIRECTORY = "TerminalServicesHomeDirectory"; + + // used to be 'Terminal Services Home Directory Drive' + public static string TS_HOME_DRIVE = "TerminalServicesHomeDrive"; + + private static T GetValue(SearchResult searchResult, string name, T defaultValue) + { + // get the directory entry + DirectoryEntry directoryEntry = searchResult.GetDirectoryEntry(); + + // get 'name' from the directory entry, and return it if it exists + object result = directoryEntry.InvokeGet(name); + if (result != null) + { + T value = (T)result; + return value; + } + + // if the name didn't exist, return 'defaultValue' + return defaultValue; + } + + internal static void SetValue(UpdateType type, + DirectoryEntry directoryEntry, string name, T value) + { + if (!type.Equals(UpdateType.REPLACE)) + { + // Only allow replace on single value attributes, + // and for now, all terminal services are single value + ThrowInvalidUpdateType(name); + } + + if (value == null) + { + // just ignore a null + return; + } + + // invoke set on 'name' with 'value' + directoryEntry.InvokeSet(name, value); + } + + private static void ThrowInvalidUpdateType(string attributeName) + { + // throws an exception that says invalid update type + string msg = string.Format("The update type specified is invalid for the terminal services attribute ''{0}''", + attributeName); + throw new ConnectorException(msg); + } + + internal static string GetInitialProgram(SearchResult searchResult) + { + return GetValue(searchResult, TS_INITIAL_PROGRAM, null); + } + + internal static void SetInitialProgram(UpdateType type, DirectoryEntry directoryEntry, + string initialProgram) + { + SetValue(type, directoryEntry, TS_INITIAL_PROGRAM, initialProgram); + } + + internal static string GetInitialProgramDir(SearchResult searchResult) + { + return GetValue(searchResult, TS_INITIAL_PROGRAM_DIR, null); + } + + internal static void SetInitialProgramDir(UpdateType type, + DirectoryEntry directoryEntry, string initialProgramDir) + { + SetValue(type, directoryEntry, TS_INITIAL_PROGRAM_DIR, initialProgramDir); + } + + internal static int? GetAllowLogon(SearchResult searchResult) + { + return GetValue(searchResult, TS_ALLOW_LOGON, null); + } + + internal static void SetAllowLogon(UpdateType type, DirectoryEntry directoryEntry, + int? isAllowed) + { + SetValue(type, directoryEntry, TS_ALLOW_LOGON, isAllowed); + } + + internal static int? GetMaxConnectionTime(SearchResult searchResult) + { + return GetValue(searchResult, TS_MAX_CONNECTION_TIME, null); + } + + internal static void SetMaxConnectionTime(UpdateType type, DirectoryEntry directoryEntry, + int? maxConnectionTime) + { + SetValue(type, directoryEntry, TS_MAX_CONNECTION_TIME, maxConnectionTime); + } + + internal static int? GetMaxDisconnectionTime(SearchResult searchResult) + { + return GetValue(searchResult, TS_MAX_DISCONNECTION_TIME, null); + } + + internal static void SetMaxDisconnectionTime(UpdateType type, + DirectoryEntry directoryEntry, int? maxDisconnectionTime) + { + SetValue(type, directoryEntry, TS_MAX_DISCONNECTION_TIME, maxDisconnectionTime); + } + + internal static int? GetMaxIdleTime(SearchResult searchResult) + { + return GetValue(searchResult, TS_MAX_IDLE_TIME, null); + } + + internal static void SetMaxIdleTime(UpdateType type, DirectoryEntry directoryEntry, + int? maxIdleTime) + { + SetValue(type, directoryEntry, TS_MAX_IDLE_TIME, maxIdleTime); + } + + internal static int? GetConnectClientDrivesAtLogon(SearchResult searchResult) + { + return GetValue(searchResult, TS_CONNECT_CLIENT_DRIVES_AT_LOGON, null); + } + + internal static void SetConnectClientDrivesAtLogon(UpdateType type, + DirectoryEntry directoryEntry, int? connectClientDrivesAtLogon) + { + SetValue(type, directoryEntry, TS_CONNECT_CLIENT_DRIVES_AT_LOGON, + connectClientDrivesAtLogon); + } + + internal static int? GetConnectClientPrintersAtLogon(SearchResult searchResult) + { + return GetValue(searchResult, TS_CONNECT_CLIENT_PRINTERS_AT_LOGON, null); + } + + internal static void SetConnectClientPrintersAtLogon(UpdateType type, + DirectoryEntry directoryEntry, int? connectClientPrintersAtLogon) + { + SetValue(type, directoryEntry, TS_CONNECT_CLIENT_PRINTERS_AT_LOGON, + connectClientPrintersAtLogon); + } + + internal static int? GetDefaultToMainPrinter(SearchResult searchResult) + { + return GetValue(searchResult, TS_DEFAULT_TO_MAIN_PRINTER, null); + } + + internal static void SetDefaultToMainPrinter(UpdateType type, + DirectoryEntry directoryEntry, int? defaultToMainPrinter) + { + SetValue(type, directoryEntry, TS_DEFAULT_TO_MAIN_PRINTER, + defaultToMainPrinter); + } + + internal static int? GetBrokenConnectionAction(SearchResult searchResult) + { + return GetValue(searchResult, TS_BROKEN_CONNECTION_ACTION, null); + } + + internal static void SetBrokenConnectionAction(UpdateType type, + DirectoryEntry directoryEntry, int? brokenConnectionAction) + { + SetValue(type, directoryEntry, TS_BROKEN_CONNECTION_ACTION, + brokenConnectionAction); + } + + internal static int? GetReconnectionAction(SearchResult searchResult) + { + return GetValue(searchResult, TS_RECONNECTION_ACTION, null); + } + + internal static void SetReconnectionAction(UpdateType type, + DirectoryEntry directoryEntry, int? reconnectionAction) + { + SetValue(type, directoryEntry, TS_RECONNECTION_ACTION, reconnectionAction); + } + + internal static int? GetEnableRemoteControl(SearchResult searchResult) + { + return GetValue(searchResult, TS_ENABLE_REMOTE_CONTROL, null); + } + + internal static void SetEnableRemoteControl(UpdateType type, + DirectoryEntry directoryEntry, int? enableRemoteControl) + { + SetValue(type, directoryEntry, TS_ENABLE_REMOTE_CONTROL, enableRemoteControl); + } + + internal static string GetProfilePath(SearchResult searchResult) + { + return GetValue(searchResult, TS_PROFILE_PATH, null); + } + + internal static void SetProfilePath(UpdateType type, + DirectoryEntry directoryEntry, string profilePath) + { + SetValue(type, directoryEntry, TS_PROFILE_PATH, profilePath); + } + + internal static string GetHomeDirectory(SearchResult searchResult) + { + return GetValue(searchResult, TS_HOME_DIRECTORY, null); + } + + internal static void SetHomeDirectory(UpdateType type, + DirectoryEntry directoryEntry, string homeDirectory) + { + SetValue(type, directoryEntry, TS_HOME_DIRECTORY, homeDirectory); + } + + internal static string GetHomeDrive(SearchResult searchResult) + { + return GetValue(searchResult, TS_HOME_DRIVE, null); + } + + internal static void SetHomeDrive(UpdateType type, + DirectoryEntry directoryEntry, string homeDrive) + { + SetValue(type, directoryEntry, TS_HOME_DRIVE, homeDrive); + } + + } +} diff --git a/DotNetConnectors.sln/ActiveDirectoryConnector/UserAccountControl.cs b/DotNetConnectors.sln/ActiveDirectoryConnector/UserAccountControl.cs new file mode 100644 index 00000000..074de031 --- /dev/null +++ b/DotNetConnectors.sln/ActiveDirectoryConnector/UserAccountControl.cs @@ -0,0 +1,110 @@ +/* + * ==================== + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER. + * + * Copyright 2007-2008 Sun Microsystems, Inc. All rights reserved. + * + * The contents of this file are subject to the terms of the Common Development + * and Distribution License("CDDL") (the "License"). You may not use this file + * except in compliance with the License. + * + * You can obtain a copy of the License at + * http://IdentityConnectors.dev.java.net/legal/license.txt + * See the License for the specific language governing permissions and limitations + * under the License. + * + * When distributing the Covered Code, include this CDDL Header Notice in each file + * and include the License file at identityconnectors/legal/license.txt. + * If applicable, add the following below this CDDL Header, with the fields + * enclosed by brackets [] replaced by your own identifying information: + * "Portions Copyrighted [year] [name of copyright owner]" + * ==================== + */ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.DirectoryServices; +using Org.IdentityConnectors.Framework.Common.Exceptions; + +namespace Org.IdentityConnectors.ActiveDirectory +{ + internal class UserAccountControl + { + public static string UAC_ATTRIBUTE_NAME = "userAccountControl"; + + // values taken from - http://support.microsoft.com/kb/305144 + static public int SCRIPT = 0x0001; + static public int ACCOUNTDISABLE = 0x0002; + static public int HOMEDIR_REQUIRED = 0x0008; + static public int LOCKOUT = 0x0010; + static public int PASSWD_NOTREQD = 0x0020; + + // Note You cannot assign this permission by directly modifying + // the UserAccountControl attribute. For information about how + // to set the permission programmatically, see the "Property + // flag descriptions" section. + static public int PASSWD_CANT_CHANGE = 0x0040; + + static public int ENCRYPTED_TEXT_PWD_ALLOWED = 0x0080; + static public int TEMP_DUPLICATE_ACCOUNT = 0x0100; + static public int NORMAL_ACCOUNT = 0x0200; + static public int INTERDOMAIN_TRUST_ACCOUNT = 0x0800; + static public int WORKSTATION_TRUST_ACCOUNT = 0x1000; + static public int SERVER_TRUST_ACCOUNT = 0x2000; + static public int DONT_EXPIRE_PASSWORD = 0x10000; + static public int MNS_LOGON_ACCOUNT = 0x20000; + static public int SMARTCARD_REQUIRED = 0x40000; + static public int TRUSTED_FOR_DELEGATION = 0x80000; + static public int NOT_DELEGATED = 0x100000; + static public int USE_DES_KEY_ONLY = 0x200000; + static public int DONT_REQ_PREAUTH = 0x400000; + public static int PASSWORD_EXPIRED = 0x800000; + public static int TRUSTED_TO_AUTH_FOR_DELEGATION = 0x1000000; + + // get the uac value from the property value collection + private static int GetUAC(PropertyValueCollection pvc) + { + // default schema says it's an integer, so it better be one + if ((pvc != null) && (pvc.Count == 1) && (pvc[0] is int)) + { + return (int)pvc[0]; + } + else + { + return 0; + } + } + + // sets the uac value in a propertyvaluecollection + private static void SetUAC(PropertyValueCollection pvc, int value) + { + // set the value + pvc[0] = value; + } + + // generically set a value in the uac to the value of 'isSet' + internal static void Set(PropertyValueCollection pvc, int flag, bool? isSet) + { + int uac = GetUAC(pvc); + // boolean false (null is same as false) + if ((isSet == null) || (isSet.Value.Equals(false))) + { + int clearMask = 0xFFFF ^ flag; + uac &= clearMask; + } + else + { + uac |= flag; + } + SetUAC(pvc, uac); + } + + // chec to see if a particular value of the uac is set + internal static bool IsSet(PropertyValueCollection pvc, int flag) + { + int uac = GetUAC(pvc); + return ((uac & flag) != 0); + } + } +} diff --git a/DotNetConnectors.sln/ActiveDirectoryConnector/build.xml b/DotNetConnectors.sln/ActiveDirectoryConnector/build.xml new file mode 100644 index 00000000..5972f10c --- /dev/null +++ b/DotNetConnectors.sln/ActiveDirectoryConnector/build.xml @@ -0,0 +1,28 @@ + + + + + + + diff --git a/DotNetConnectors.sln/ActiveDirectoryConnectorTests/ActiveDirectoryConnectorTest.cs b/DotNetConnectors.sln/ActiveDirectoryConnectorTests/ActiveDirectoryConnectorTest.cs new file mode 100644 index 00000000..8b9d562a --- /dev/null +++ b/DotNetConnectors.sln/ActiveDirectoryConnectorTests/ActiveDirectoryConnectorTest.cs @@ -0,0 +1,2750 @@ +/* + * ==================== + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER. + * + * Copyright 2007-2008 Sun Microsystems, Inc. All rights reserved. + * + * The contents of this file are subject to the terms of the Common Development + * and Distribution License("CDDL") (the "License"). You may not use this file + * except in compliance with the License. + * + * You can obtain a copy of the License at + * http://IdentityConnectors.dev.java.net/legal/license.txt + * See the License for the specific language governing permissions and limitations + * under the License. + * + * When distributing the Covered Code, include this CDDL Header Notice in each file + * and include the License file at identityconnectors/legal/license.txt. + * If applicable, add the following below this CDDL Header, with the fields + * enclosed by brackets [] replaced by your own identifying information: + * "Portions Copyrighted [year] [name of copyright owner]" + * ==================== + */ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using NUnit.Framework; +using Org.IdentityConnectors.Framework; +using Org.IdentityConnectors.Framework.Spi; +using Org.IdentityConnectors.Framework.Spi.Operations; +using Org.IdentityConnectors.Framework.Common.Objects; +using Org.IdentityConnectors.Framework.Common.Objects.Filters; +using Org.IdentityConnectors.Framework.Test; +using Org.IdentityConnectors.Common.Security; +using System.Diagnostics; +using System.Resources; +using System.IO; +using System.Security.AccessControl; +using System.Security.Principal; +using Org.IdentityConnectors.Framework.Common.Exceptions; +using Org.IdentityConnectors.Common; +using System.Threading; + +namespace Org.IdentityConnectors.ActiveDirectory +{ + [TestFixture] + public class ActiveDirectoryConnectorTest + { + Random _rand = new Random(); + public static readonly string CONFIG_PROPERTY_USER = "config_user"; + public static readonly string CONFIG_PROPERTY_PASSWORD = "config_password"; + public static readonly string CONFIG_PROPERTY_HOST = "config_host"; + public static readonly string CONFIG_PROPERTY_PORT = "config_port"; + public static readonly string CONFIG_PROPERTY_CONTAINER = "config_container"; + public static readonly string CONFIG_PROPERTY_SCRIPT_USER_LOCAL = "config_script_user_local"; + public static readonly string CONFIG_PROPERTY_SCRIPT_PASSWORD_LOCAL = "config_script_password_local"; + public static readonly string CONFIG_PROPERTY_SCRIPT_USER_DOMAIN = "config_script_user_domain"; + public static readonly string CONFIG_PROPERTY_SCRIPT_PASSWORD_DOMAIN = "config_script_password_domain"; + public static readonly string CONFIG_PROPERTY_LDAPHOSTNAME = "config_ldap_hostname"; + public static readonly string CONFIG_PROPERTY_SEARCH_CONTEXT = "config_search_context"; + public static readonly string CONFIG_PROPERTY_SYNC_SEARCH_CONTEXT_ROOT = "config_sync_search_context_root"; + public static readonly string CONFIG_PROPERTY_SYNC_SEARCH_CONTEXT_CHILD = "config_sync_search_context_child"; + public static readonly string CONFIG_PROPERTY_DOMAIN_NAME = "config_domain_name"; + public static readonly string CONFIG_PROPERTY_SYNC_DOMAIN_CONTROLLER = "config_sync_domain_controller"; + public static readonly string CONFIG_PROPERTY_GC_DOMAIN_CONTROLLER = "config_sync_gc_domain_controller"; + public static readonly string TEST_PARAM_SHARED_HOME_FOLDER = "test_param_shared_home_folder"; + + // having troubles with duplicate random numbers + public static List randomList = new List(); + + [Test] + public void TestConfiguration() { + ActiveDirectoryConfiguration config = new ActiveDirectoryConfiguration(); + config.ConnectorMessages = TestHelpers.CreateDummyMessages(); + String directoryAdminName = GetProperty(CONFIG_PROPERTY_USER); + config.DirectoryAdminName = directoryAdminName; + Assert.AreEqual(directoryAdminName, config.DirectoryAdminName); + } + + [Test] + public void TestTest() + { + ActiveDirectoryConnector connectorGood = new ActiveDirectoryConnector(); + ActiveDirectoryConfiguration config = (ActiveDirectoryConfiguration)GetConfiguration(); + connectorGood.Init(config); + connectorGood.Test(); + + bool threwException = false; + try + { + config.ObjectClass = "BadObjectClass"; + ActiveDirectoryConnector connectorBad = new ActiveDirectoryConnector(); + connectorBad.Init(config); + connectorBad.Test(); + } + catch (ConnectorException e) + { + threwException = true; + } + + Assert.IsTrue(threwException, "Bad configuration should have caused an exception"); + } + + [Test] + public void TestSchema() + { + ActiveDirectoryConnector connector = new ActiveDirectoryConnector(); + connector.Init(GetConfiguration()); + Schema schema = connector.Schema(); + Boolean foundOptionalAttributes = false; + Boolean foundOperationalAttributes = false; + + // just a very high level check of things. Should have 3 ObjectClassInfos, + // (group, account, and organizationalUnit) and nothing contained in them + // should be null. Group and account should have some operational + // attributes + Assert.AreEqual(3, schema.ObjectClassInfo.Count); + foreach(ObjectClassInfo ocInfo in schema.ObjectClassInfo) { + Assert.IsNotNull(ocInfo); + Assert.That((ocInfo.ObjectType == ObjectClass.ACCOUNT.GetObjectClassValue()) + || (ocInfo.ObjectType == ObjectClass.GROUP.GetObjectClassValue()) + || (ocInfo.ObjectType == ActiveDirectoryConnector.OBJECTCLASS_OU)); + Trace.WriteLine("****** " + ocInfo.ObjectType); + + // skip this for organizational unit ... it doesnt really have this + if (ocInfo.ObjectType.Equals(ActiveDirectoryConnector.ouObjectClass)) + { + continue; + } + + foreach (ConnectorAttributeInfo caInfo in ocInfo.ConnectorAttributeInfos) + { + Assert.IsNotNull(caInfo); + Trace.WriteLine(String.Format("{0} {1} {2} {3}", caInfo.Name, + caInfo.IsCreatable ? "createable" : "", + caInfo.IsUpdateable ? "updateable" : "", + caInfo.IsRequired ? "required" : "", + caInfo.IsMultiValued ? "multivalue" : "")); + if(ConnectorAttributeUtil.IsSpecial(caInfo)) { + foundOperationalAttributes = true; + } else { + if (!caInfo.IsRequired) + { + foundOptionalAttributes = true; + } + } + } + Assert.That(foundOperationalAttributes && foundOptionalAttributes); + } + } + + // test proper behavior of each supported operation + // and test proper reporting of unsuppoorted operations + [Test] + public void TestBasics_Account() + { + //Initialize Connector + ActiveDirectoryConnector connector = new ActiveDirectoryConnector(); + connector.Init(GetConfiguration()); + + // create user + ICollection createAttributes = GetNormalAttributes_Account(); + Uid createUid = CreateAndVerifyObject(connector, + ObjectClass.ACCOUNT, createAttributes); + + // update the user - replace + ICollection updateReplaceAttrs = + new List(); + Name oldName = ConnectorAttributeUtil.GetNameFromAttributes(createAttributes); + String newName = ActiveDirectoryUtils.GetRelativeName(oldName); + newName = newName.Trim() + "_new, " + GetProperty(CONFIG_PROPERTY_CONTAINER); + updateReplaceAttrs.Add(ConnectorAttributeBuilder.Build( + Name.NAME, newName)); + updateReplaceAttrs.Add(ConnectorAttributeBuilder.Build( + "sn", "newsn")); + Uid updateReplaceUid = UpdateReplaceAndVerifyObject(connector, + ObjectClass.ACCOUNT, createUid, updateReplaceAttrs); + + // update the user - add + ICollection updateAddAttrs = + new List(); + updateAddAttrs.Add(ConnectorAttributeBuilder.Build("otherHomePhone", "123.456.7890", "098.765.4321")); + Uid updateAddUid = UpdateAddAndVerifyUser(connector, + ObjectClass.ACCOUNT, createUid, updateAddAttrs, null); + + // update the user - delete + + // delete user + DeleteAndVerifyObject(connector, ObjectClass.ACCOUNT, createUid, true, true); + } + + // test proper behaviour of each supported operation + // and test proper reporting of unsuppoorted operations + [Test] + public void TestBasics_Group() + { + //Initialize Connector + ActiveDirectoryConnector connector = new ActiveDirectoryConnector(); + connector.Init(GetConfiguration()); + + Uid uidToDelete = null; + try + { + // create group + ICollection createAttributes = GetNormalAttributes_Group(); + createAttributes.Add(ConnectorAttributeBuilder.Build(PredefinedAttributes.ACCOUNTS_NAME, + CreateGroupMember(connector))); + + // create object + uidToDelete = connector.Create(ObjectClass.GROUP, createAttributes, null); + Uid createUid = uidToDelete; + Assert.IsNotNull(createUid); + + // find new object ... have to add groups to list of things to return + OperationOptionsBuilder optionsBuilder = new OperationOptionsBuilder(); + ICollection attributesToGet = GetDefaultAttributesToGet(ObjectClass.GROUP); + attributesToGet.Add(PredefinedAttributes.ACCOUNTS_NAME); + optionsBuilder.AttributesToGet = attributesToGet.ToArray(); + + ConnectorObject newObject = GetConnectorObjectFromUid(connector, + ObjectClass.GROUP, createUid, optionsBuilder.Build()); + VerifyObject(createAttributes, newObject); + // update the group - replace + ICollection updateReplaceAttrs = + new List(); + Name oldName = ConnectorAttributeUtil.GetNameFromAttributes(createAttributes); + String newName = ActiveDirectoryUtils.GetRelativeName(oldName); + newName = newName.Trim() + "_new, " + GetProperty(CONFIG_PROPERTY_CONTAINER); + + updateReplaceAttrs.Add(createUid); + updateReplaceAttrs.Add(ConnectorAttributeBuilder.Build( + Name.NAME, newName)); + updateReplaceAttrs.Add(ConnectorAttributeBuilder.Build( + "description", "New description")); + uidToDelete = UpdateReplaceAndVerifyObject(connector, + ObjectClass.GROUP, createUid, updateReplaceAttrs); + Uid updateReplaceUid = uidToDelete; + + // update the group - add + ICollection updateAddAttrs = + new List(); + updateAddAttrs.Add(ConnectorAttributeBuilder.Build(PredefinedAttributes.ACCOUNTS_NAME, + CreateGroupMember(connector), CreateGroupMember(connector))); + + uidToDelete = UpdateAddAndVerifyUser(connector, + ObjectClass.GROUP, updateReplaceUid, updateAddAttrs, optionsBuilder.Build()); + } + finally + { + if (uidToDelete != null) + { + // delete user + DeleteAndVerifyObject(connector, ObjectClass.GROUP, uidToDelete, true, true); + } + } + } + + [Test] + public void TestCreate_Account() + { + //Initialize Connector + ActiveDirectoryConnector connector = new ActiveDirectoryConnector(); + connector.Init(GetConfiguration()); + Uid createUid = null; + try + { + ICollection createAttributes = GetNormalAttributes_Account(); + createUid = CreateAndVerifyObject(connector, ObjectClass.ACCOUNT, createAttributes); + } + finally + { + if (createUid != null) + { + //remove the one we created + DeleteAndVerifyObject(connector, ObjectClass.ACCOUNT, + createUid, false, true); + } + } + } + + [Test] + public void TestCreate_Group() + { + //Initialize Connector + ActiveDirectoryConnector connector = new ActiveDirectoryConnector(); + connector.Init(GetConfiguration()); + Uid createUid = null; + try + { + ICollection createAttributes = GetNormalAttributes_Group(); + createUid = CreateAndVerifyObject(connector, ObjectClass.GROUP, createAttributes); + } + finally + { + if (createUid != null) + { + //remove the one we created + DeleteAndVerifyObject(connector, ObjectClass.GROUP, + createUid, false, true); + } + } + } + + [Test] + public void TestCreate_OrganizationalUnit() + { + //Initialize Connector + ActiveDirectoryConnector connector = new ActiveDirectoryConnector(); + connector.Init(GetConfiguration()); + Uid createUid = null; + try + { + ICollection createAttributes = GetNormalAttributes_OrganizationalUnit(); + createUid = CreateAndVerifyObject(connector, + ActiveDirectoryConnector.ouObjectClass, createAttributes); + } + finally + { + if (createUid != null) + { + //remove the one we created + DeleteAndVerifyObject(connector, + ActiveDirectoryConnector.ouObjectClass, + createUid, false, true); + } + } + } + + [Test] + public void TestCreateWithHomeDirectory_Account() + { + //Initialize Connector + ActiveDirectoryConnector connector = new ActiveDirectoryConnector(); + ActiveDirectoryConfiguration config = (ActiveDirectoryConfiguration)GetConfiguration(); + config.CreateHomeDirectory = true; + connector.Init(config); + Uid userUid = null; + try + { + // get the normal attributes + ICollection createAttributes = GetNormalAttributes_Account(); + + // read the homedir path, and append the samaccountname + StringBuilder homeDirPathBuilder = new StringBuilder(GetProperty(TEST_PARAM_SHARED_HOME_FOLDER)); + if (!homeDirPathBuilder.ToString().EndsWith("\\")) + { + homeDirPathBuilder.Append('\\'); + } + ConnectorAttribute samAccountNameAttr = ConnectorAttributeUtil.Find( + ActiveDirectoryConnector.ATT_SAMACCOUNT_NAME, createAttributes); + homeDirPathBuilder.Append(ConnectorAttributeUtil.GetStringValue(samAccountNameAttr)); + + // if it exists, delete it + String homeDir = homeDirPathBuilder.ToString(); + if (Directory.Exists(homeDir)) + { + Directory.Delete(homeDir); + } + Assert.IsFalse(Directory.Exists(homeDir)); + + // add homeDirectory to the attributes, and create user + createAttributes.Add(ConnectorAttributeBuilder.Build("homeDirectory", homeDir)); + userUid = CreateAndVerifyObject(connector, + ObjectClass.ACCOUNT, createAttributes); + + // now directory should exist + Assert.IsTrue(Directory.Exists(homeDir)); + + // get sid to check permissions + OperationOptionsBuilder optionsBuilder = new OperationOptionsBuilder(); + ICollection attributesToGet = GetDefaultAttributesToGet(ObjectClass.ACCOUNT); + attributesToGet.Add(ActiveDirectoryConnector.ATT_OBJECT_SID); + optionsBuilder.AttributesToGet = attributesToGet.ToArray(); + + ConnectorObject newUser = GetConnectorObjectFromUid(connector, + ObjectClass.ACCOUNT, userUid, optionsBuilder.Build()); + ConnectorAttribute sidAttr = + newUser.GetAttributeByName(ActiveDirectoryConnector.ATT_OBJECT_SID); + Byte[] sidBytes = (Byte[])ConnectorAttributeUtil.GetSingleValue(sidAttr); + SecurityIdentifier newUserSid = new SecurityIdentifier(sidBytes, 0); + + // check permissions + DirectoryInfo dirInfo = new DirectoryInfo(homeDir); + DirectorySecurity dirSec = dirInfo.GetAccessControl(); + AuthorizationRuleCollection rules = dirSec.GetAccessRules(true, true, typeof(SecurityIdentifier)); + bool foundCorrectRule = false; + foreach (AuthorizationRule rule in rules) + { + if (rule is FileSystemAccessRule) + { + FileSystemAccessRule fsaRule = (FileSystemAccessRule)rule; + if (fsaRule.IdentityReference.Equals(newUserSid)) + { + if ((fsaRule.AccessControlType.Equals(AccessControlType.Allow)) && + (fsaRule.FileSystemRights.Equals(FileSystemRights.FullControl)) && + (fsaRule.InheritanceFlags.Equals(InheritanceFlags.ContainerInherit | InheritanceFlags.ObjectInherit)) && + (fsaRule.IsInherited.Equals(false))) + { + foundCorrectRule = true; + } + + } + } + } + + // remove the directory (before assertion may fail) + Directory.Delete(homeDir); + + // check that we found the proper permission record + Assert.IsTrue(foundCorrectRule); + } + finally + { + if (userUid != null) + { + DeleteAndVerifyObject(connector, ObjectClass.ACCOUNT, userUid, false, false); + } + } + } + + [Test] // tests that if create home directory is set to false, no directory is created + public void TestCreateWithHomeDirectoryNoCreateConfig_Account() + { + //Initialize Connector + ActiveDirectoryConnector connector = new ActiveDirectoryConnector(); + ActiveDirectoryConfiguration config = (ActiveDirectoryConfiguration)GetConfiguration(); + config.CreateHomeDirectory = false; + connector.Init(config); + Uid userUid = null; + try + { + // get the normal attributes + ICollection createAttributes = GetNormalAttributes_Account(); + + // read the homedir path, and append the samaccountname + StringBuilder homeDirPathBuilder = new StringBuilder(GetProperty(TEST_PARAM_SHARED_HOME_FOLDER)); + if (!homeDirPathBuilder.ToString().EndsWith("\\")) + { + homeDirPathBuilder.Append('\\'); + } + ConnectorAttribute samAccountNameAttr = ConnectorAttributeUtil.Find( + ActiveDirectoryConnector.ATT_SAMACCOUNT_NAME, createAttributes); + homeDirPathBuilder.Append(ConnectorAttributeUtil.GetStringValue(samAccountNameAttr)); + + // if it exists, delete it + String homeDir = homeDirPathBuilder.ToString(); + if (Directory.Exists(homeDir)) + { + Directory.Delete(homeDir); + } + Assert.IsFalse(Directory.Exists(homeDir)); + + // add homeDirectory to the attributes, and create user + createAttributes.Add(ConnectorAttributeBuilder.Build("homeDirectory", homeDir)); + userUid = CreateAndVerifyObject(connector, + ObjectClass.ACCOUNT, createAttributes); + + // now directory should not exist + // (createhomedirectory was set to false in the configuration) + Assert.IsFalse(Directory.Exists(homeDir)); + } + finally + { + if (userUid != null) + { + DeleteAndVerifyObject(connector, ObjectClass.ACCOUNT, userUid, false, false); + } + } + } + + [Test] + public void TestSearchNoFilter_Account() + { + ActiveDirectoryConnector connector = new ActiveDirectoryConnector(); + Configuration config = GetConfiguration(); + config.ConnectorMessages = TestHelpers.CreateDummyMessages(); + connector.Init(config); + + ICollection createdUids = new HashSet(); + + try + { + int numCreated = 0; + for (numCreated = 0; numCreated < 5; numCreated++) + { + createdUids.Add(CreateAndVerifyObject(connector, + ObjectClass.ACCOUNT, GetNormalAttributes_Account())); + } + + ICollection results = TestHelpers.SearchToList(connector, + ObjectClass.ACCOUNT, null); + + // not sure how many should be found ... it should find everything + // it's hard to say how many that is, but it should at least find the + // number we created + Assert.GreaterOrEqual(results.Count, numCreated); + + // check that they are all of the proper objectclass + foreach (ConnectorObject co in results) + { + ConnectorAttribute objectClassAttr = + co.GetAttributeByName("objectClass"); + Boolean foundCorrectObjectClass = false; + foreach (Object o in objectClassAttr.Value) + { + if ((o is String) && (o != null)) + { + String stringValue = (String)o; + if (stringValue.ToUpper().Trim().Equals("USER")) + { + foundCorrectObjectClass = true; + } + } + } + Assert.IsTrue(foundCorrectObjectClass); + } + } + finally + { + foreach (Uid uid in createdUids) + { + if (uid != null) + { + DeleteAndVerifyObject(connector, ObjectClass.ACCOUNT, uid, false, true); + } + } + } + } + + [Test] + public void TestSearchNoFilter_Group() + { + ActiveDirectoryConnector connector = new ActiveDirectoryConnector(); + connector.Init(GetConfiguration()); + ICollection createdUids = new HashSet(); + + try + { + int numCreated = 0; + for (numCreated = 0; numCreated < 5; numCreated++) + { + createdUids.Add(CreateAndVerifyObject(connector, + ObjectClass.GROUP, GetNormalAttributes_Group())); + } + + ICollection results = TestHelpers.SearchToList(connector, + ObjectClass.GROUP, null); + + // not sure how many should be found ... it should find everything + // it's hard to say how many that is, but it should at least find the + // number we created + Assert.GreaterOrEqual(results.Count, numCreated); + + // check that they are all of the proper objectclass + foreach (ConnectorObject co in results) + { + ConnectorAttribute objectClassAttr = + co.GetAttributeByName("objectClass"); + Boolean foundCorrectObjectClass = false; + foreach (Object o in objectClassAttr.Value) + { + if ((o is String) && (o != null)) + { + String stringValue = (String)o; + if (stringValue.ToUpper().Trim().Equals("GROUP")) + { + foundCorrectObjectClass = true; + } + } + } + Assert.IsTrue(foundCorrectObjectClass); + } + } + finally + { + foreach (Uid uid in createdUids) + { + if (uid != null) + { + DeleteAndVerifyObject(connector, ObjectClass.GROUP, uid, false, true); + } + } + } + } + + [Test] + public void TestSearchByName_account() + { + ActiveDirectoryConnector connector = new ActiveDirectoryConnector(); + connector.Init(GetConfiguration()); + + Uid createUid = null; + + try + { + createUid = CreateAndVerifyObject(connector, ObjectClass.ACCOUNT, + GetNormalAttributes_Account()); + + // find out what the name was + ConnectorObject newObject = GetConnectorObjectFromUid(connector, + ObjectClass.ACCOUNT, createUid); + Name nameAttr = newObject.Name; + Assert.IsNotNull(nameAttr); + + //search normally + ICollection results = TestHelpers.SearchToList(connector, + ObjectClass.ACCOUNT, FilterBuilder.EqualTo(nameAttr)); + + // there really should only be one + Assert.AreEqual(results.Count, 1); + + // and it must have the value we were searching for + String createName = ActiveDirectoryUtils.NormalizeLdapString(nameAttr.GetNameValue()); + String foundName = ActiveDirectoryUtils.NormalizeLdapString(results.ElementAt(0).Name.GetNameValue()); + Assert.AreEqual(createName, foundName); + + //search in uppercase + ConnectorAttribute nameUpper = ConnectorAttributeBuilder.Build( + nameAttr.Name, nameAttr.GetNameValue().ToUpper()); + results = TestHelpers.SearchToList(connector, + ObjectClass.ACCOUNT, FilterBuilder.EqualTo(nameUpper)); + + // there really should only be one + Assert.AreEqual(results.Count, 1); + + // and it must have the value we were searching for + createName = ActiveDirectoryUtils.NormalizeLdapString(nameAttr.GetNameValue()); + foundName = ActiveDirectoryUtils.NormalizeLdapString(results.ElementAt(0).Name.GetNameValue()); + Assert.AreEqual(createName, foundName); + + //search in lowercase + ConnectorAttribute nameLower = ConnectorAttributeBuilder.Build( + nameAttr.Name, nameAttr.GetNameValue().ToLower()); + results = TestHelpers.SearchToList(connector, + ObjectClass.ACCOUNT, FilterBuilder.EqualTo(nameLower)); + + // there really should only be one + Assert.AreEqual(results.Count, 1); + + // and it must have the value we were searching for + createName = ActiveDirectoryUtils.NormalizeLdapString(nameAttr.GetNameValue()); + foundName = ActiveDirectoryUtils.NormalizeLdapString(results.ElementAt(0).Name.GetNameValue()); + Assert.AreEqual(createName, foundName); + + } + finally + { + if (createUid != null) + { + //remove the one we created + DeleteAndVerifyObject(connector, ObjectClass.ACCOUNT, + createUid, false, true); + } + } + } + + [Test] + public void TestSearchByName_group() + { + ActiveDirectoryConnector connector = new ActiveDirectoryConnector(); + connector.Init(GetConfiguration()); + + Uid createUid = null; + + try + { + createUid = CreateAndVerifyObject(connector, ObjectClass.GROUP, + GetNormalAttributes_Group()); + + // find out what the name was + ConnectorObject newObject = GetConnectorObjectFromUid(connector, + ObjectClass.GROUP, createUid); + Name nameAttr = newObject.Name; + Assert.IsNotNull(nameAttr); + + //search normally + ICollection results = TestHelpers.SearchToList(connector, + ObjectClass.GROUP, FilterBuilder.EqualTo(nameAttr)); + + // there really should only be one + Assert.AreEqual(results.Count, 1); + + // and it must have the value we were searching for + String createName = ActiveDirectoryUtils.NormalizeLdapString(nameAttr.GetNameValue()); + String foundName = ActiveDirectoryUtils.NormalizeLdapString(results.ElementAt(0).Name.GetNameValue()); + Assert.AreEqual(createName, foundName); + + //search in uppercase + ConnectorAttribute nameUpper = ConnectorAttributeBuilder.Build( + nameAttr.Name, nameAttr.GetNameValue().ToUpper()); + results = TestHelpers.SearchToList(connector, + ObjectClass.GROUP, FilterBuilder.EqualTo(nameUpper)); + + // there really should only be one + Assert.AreEqual(results.Count, 1); + + // and it must have the value we were searching for + createName = ActiveDirectoryUtils.NormalizeLdapString(nameAttr.GetNameValue()); + foundName = ActiveDirectoryUtils.NormalizeLdapString(results.ElementAt(0).Name.GetNameValue()); + Assert.AreEqual(createName, foundName); + + //search in lowercase + ConnectorAttribute nameLower = ConnectorAttributeBuilder.Build( + nameAttr.Name, nameAttr.GetNameValue().ToLower()); + results = TestHelpers.SearchToList(connector, + ObjectClass.GROUP, FilterBuilder.EqualTo(nameLower)); + + // there really should only be one + Assert.AreEqual(results.Count, 1); + + // and it must have the value we were searching for + createName = ActiveDirectoryUtils.NormalizeLdapString(nameAttr.GetNameValue()); + foundName = ActiveDirectoryUtils.NormalizeLdapString(results.ElementAt(0).Name.GetNameValue()); + Assert.AreEqual(createName, foundName); + + } + finally + { + if (createUid != null) + { + //remove the one we created + DeleteAndVerifyObject(connector, ObjectClass.GROUP, + createUid, false, true); + } + } + } + + [Test] + public void TestSearchByCNWithWildcard_account() + { + ActiveDirectoryConnector connector = new ActiveDirectoryConnector(); + connector.Init(GetConfiguration()); + + Uid uid1 = null; + Uid uid2 = null; + + try + { + // create a couple things to find + uid1 = CreateAndVerifyObject(connector, ObjectClass.ACCOUNT, + GetNormalAttributes_Account()); + uid2 = CreateAndVerifyObject(connector, ObjectClass.ACCOUNT, + GetNormalAttributes_Account()); + + ICollection results = TestHelpers.SearchToList(connector, + ObjectClass.ACCOUNT, FilterBuilder.EqualTo( + ConnectorAttributeBuilder.Build("CN", "nunit*"))); + + // there should be at least the two we just created + Assert.GreaterOrEqual(results.Count, 2); + foreach (ConnectorObject co in results) + { + // and it must have the value we were searching for + String foundName = ActiveDirectoryUtils.NormalizeLdapString( + co.Name.GetNameValue()); + Assert.That(foundName.ToUpper().StartsWith("CN=NUNIT")); + } + } + finally + { + if (uid1 != null) + { + //remove the one we created + DeleteAndVerifyObject(connector, ObjectClass.ACCOUNT, + uid1, false, true); + } + if (uid2 != null) + { + //remove the one we created + DeleteAndVerifyObject(connector, ObjectClass.ACCOUNT, + uid2, false, true); + } + } + } + + [Test] + public void TestSearchByRegularAttribute_account() + { + ActiveDirectoryConnector connector = new ActiveDirectoryConnector(); + connector.Init(GetConfiguration()); + + Uid createUid = null; + + try + { + ConnectorAttribute createSnAttr = + ConnectorAttributeBuilder.Build("sn", "nunitSearch"); + ICollection attributes = GetNormalAttributes_Account(); + attributes.Remove(ConnectorAttributeUtil.Find("sn", attributes)); + attributes.Add(createSnAttr); + createUid = CreateAndVerifyObject(connector, ObjectClass.ACCOUNT, + attributes); + + ICollection results = TestHelpers.SearchToList(connector, + ObjectClass.ACCOUNT, FilterBuilder.EqualTo(createSnAttr)); + + // there should be at least the newly created one + Assert.GreaterOrEqual(results.Count, 1); + + // and it must have the value we were searching for + Boolean foundCreated = false; + foreach (ConnectorObject resultObject in results) + { + ConnectorAttribute foundSnAttr = + resultObject.GetAttributeByName("sn"); + Assert.AreEqual(createSnAttr, foundSnAttr); + + // keep track of if we've found the one we created + if (createUid.Equals(resultObject.Uid)) + { + // cant have it twice + Assert.IsFalse(foundCreated); + foundCreated = true; + } + } + // be certain we saw the one we created + Assert.IsTrue(foundCreated); + } + finally + { + if (createUid != null) + { + //remove the one we created + DeleteAndVerifyObject(connector, ObjectClass.ACCOUNT, + createUid, false, true); + } + } + } + + [Test] + public void TestSearchByRegularAttributeWithWildcard_account() + { + ActiveDirectoryConnector connector = new ActiveDirectoryConnector(); + connector.Init(GetConfiguration()); + + ICollection uids = new HashSet(); + + try + { + int randomNumber = GetRandomNumber(); + + String snPrefix = "nunitWCTest"; + + for (int i = 0; i < 10; i++) + { + ICollection attributes = + GetNormalAttributes_Account(); + attributes.Remove(ConnectorAttributeUtil.Find("sn", attributes)); + attributes.Add(ConnectorAttributeBuilder.Build("sn", + snPrefix + GetRandomNumber())); + Uid tempUid = CreateAndVerifyObject(connector, ObjectClass.ACCOUNT, + attributes); + Assert.IsNotNull(tempUid); + uids.Add(tempUid); + } + + ICollection results = TestHelpers.SearchToList(connector, + ObjectClass.ACCOUNT, FilterBuilder.StartsWith( + ConnectorAttributeBuilder.Build("sn", snPrefix))); + + // there should be at least the newly created one + Assert.GreaterOrEqual(results.Count, 1); + + // make a duplicate list + ICollection uidsToValidate = new HashSet(uids); + + // and it must have the value we were searching for + foreach (ConnectorObject resultObject in results) + { + ConnectorAttribute foundSnAttr = + resultObject.GetAttributeByName("sn"); + String snValue = ConnectorAttributeUtil.GetStringValue(foundSnAttr); + Assert.That(snValue.StartsWith(snPrefix)); + uidsToValidate.Remove(resultObject.Uid); + if (uidsToValidate.Count == 0) + { + break; + } + } + Assert.AreEqual(0, uidsToValidate.Count); + } + finally + { + foreach (Uid createdUid in uids) + { + //remove the one we created + DeleteAndVerifyObject(connector, ObjectClass.ACCOUNT, + createdUid, false, true); + } + } + } + + // test proper behavior of create with ALL attributes specified + [Test] + public void TestCreateWithAllAttributes_Account() + { + //Initialize Connector + ActiveDirectoryConnector connector = new ActiveDirectoryConnector(); + connector.Init(GetConfiguration()); + Uid createUid = null; + + try + { + // create user + ICollection createAttributes = GetAllAttributes_Account(); + createUid = CreateAndVerifyObject(connector, + ObjectClass.ACCOUNT, createAttributes); + } + finally + { + if (createUid != null) + { + //remove the one we created + DeleteAndVerifyObject(connector, ObjectClass.ACCOUNT, + createUid, false, true); + } + } + } + + // test proper behavior of create with ALL attributes specified + [Test] + public void Test_OpAtt_Enabled_Account() + { + //Initialize Connector + ActiveDirectoryConnector connector = new ActiveDirectoryConnector(); + connector.Init(GetConfiguration()); + Uid createUid = null; + + try + { + // create user + ICollection createAttributes = GetNormalAttributes_Account(); + createAttributes.Add(ConnectorAttributeBuilder.BuildEnabled(true)); + createUid = CreateAndVerifyObject(connector, + ObjectClass.ACCOUNT, createAttributes); + + ICollection updateReplaceAttributes = + new HashSet(); + updateReplaceAttributes.Add(ConnectorAttributeBuilder.BuildEnabled(false)); + UpdateReplaceAndVerifyObject(connector, ObjectClass.ACCOUNT, + createUid, updateReplaceAttributes); + } + finally + { + if (createUid != null) + { + //remove the one we created + DeleteAndVerifyObject(connector, ObjectClass.ACCOUNT, + createUid, false, true); + } + } + } + + // Test scripting + [Test] + public void TestScriptOnResource() + { + //Initialize Connector + ActiveDirectoryConnector connector = new ActiveDirectoryConnector(); + connector.Init(GetConfiguration()); + + try + { + RunScript(connector, "", ""); + RunScript(connector, GetProperty(CONFIG_PROPERTY_SCRIPT_USER_LOCAL), + GetProperty(CONFIG_PROPERTY_SCRIPT_PASSWORD_LOCAL)); + RunScript(connector, GetProperty(CONFIG_PROPERTY_SCRIPT_USER_DOMAIN), + GetProperty(CONFIG_PROPERTY_SCRIPT_PASSWORD_DOMAIN)); + + + // try with invalid credentials + bool scriptFailed = false; + try + { + RunScript(connector, GetProperty(CONFIG_PROPERTY_USER), "bogus"); + } + catch (Exception e) + { + scriptFailed = true; + } + Assert.That(scriptFailed); + } + finally + { + } + } + + [Test] + public void TestAddGroup_Account() + { + //Initialize Connector + ActiveDirectoryConnector connector = new ActiveDirectoryConnector(); + connector.Init(GetConfiguration()); + Uid groupUid = null; + Uid userUid = null; + try + { + userUid = CreateAndVerifyObject(connector, + ObjectClass.ACCOUNT, GetNormalAttributes_Account()); + Filter userUidFilter = FilterBuilder.EqualTo(userUid); + IList foundUserObjects = + TestHelpers.SearchToList(connector, ObjectClass.ACCOUNT, userUidFilter); + Assert.AreEqual(1, foundUserObjects.Count); + + groupUid = CreateAndVerifyObject(connector, + ObjectClass.GROUP, GetNormalAttributes_Group()); + Filter groupUidFilter = FilterBuilder.EqualTo(groupUid); + IList foundGroupObjects = + TestHelpers.SearchToList(connector, ObjectClass.GROUP, groupUidFilter); + Assert.AreEqual(1, foundGroupObjects.Count); + String groupName = foundGroupObjects[0].Name.GetNameValue(); + + ICollection modifiedAttrs = new HashSet(); + modifiedAttrs.Add(ConnectorAttributeBuilder.Build(PredefinedAttributes.GROUPS_NAME, groupName)); + OperationOptionsBuilder optionsBuilder = new OperationOptionsBuilder(); + ICollection attributesToGet = GetDefaultAttributesToGet(ObjectClass.ACCOUNT); + attributesToGet.Add(PredefinedAttributes.GROUPS_NAME); + optionsBuilder.AttributesToGet = attributesToGet.ToArray(); + UpdateAddAndVerifyUser(connector, ObjectClass.ACCOUNT, + userUid, modifiedAttrs, optionsBuilder.Build()); + + } finally { + DeleteAndVerifyObject(connector, ObjectClass.ACCOUNT, userUid, false, false); + DeleteAndVerifyObject(connector, ObjectClass.GROUP, groupUid, false, false); + } + } + + [Test] + public void TestRemoveAttributeValue() + { + ActiveDirectoryConnector connector = new ActiveDirectoryConnector(); + connector.Init(GetConfiguration()); + + Uid createUid = null; + + try + { + int randomNumber = GetRandomNumber(); + ICollection attributes = new HashSet(); + + attributes.Add(ConnectorAttributeBuilder.Build( + "ad_container", GetProperty(CONFIG_PROPERTY_CONTAINER))); + attributes.Add(ConnectorAttributeBuilder.Build( + "userPassword", "secret")); + attributes.Add(ConnectorAttributeBuilder.Build( + "sAMAccountName", "nunit" + randomNumber)); + attributes.Add(ConnectorAttributeBuilder.Build( + "givenName", "nunit")); + attributes.Add(ConnectorAttributeBuilder.Build( + "sn", "TestUser" + randomNumber)); + attributes.Add(ConnectorAttributeBuilder.Build( + "displayName", "nunit test user " + randomNumber)); + attributes.Add(ConnectorAttributeBuilder.Build( + Name.NAME, "cn=nunit" + randomNumber + "," + + GetProperty(CONFIG_PROPERTY_CONTAINER))); + attributes.Add(ConnectorAttributeBuilder.Build( + "mail", "nunitUser" + randomNumber + "@some.com")); + attributes.Add(ConnectorAttributeBuilder.Build( + "otherHomePhone", "512.555.1212", "512.123.4567")); + + createUid = CreateAndVerifyObject(connector, ObjectClass.ACCOUNT, + attributes); + + ICollection modifyAttributes = new HashSet(); + modifyAttributes.Add(createUid); + modifyAttributes.Add(ConnectorAttributeBuilder.Build("otherHomePhone", "512.555.1212")); + + connector.Update(UpdateType.DELETE, ObjectClass.ACCOUNT, modifyAttributes, null); + + Filter uidFilter = FilterBuilder.EqualTo(createUid); + IList objects = TestHelpers.SearchToList(connector, ObjectClass.ACCOUNT, uidFilter); + Assert.AreEqual(1, objects.Count); + + ConnectorAttribute otherHomePhoneAttr = ConnectorAttributeUtil.Find( + "otherHomePhone", objects[0].GetAttributes()); + + Assert.AreEqual(1, otherHomePhoneAttr.Value.Count); + Assert.AreEqual("512.123.4567", ConnectorAttributeUtil.GetSingleValue(otherHomePhoneAttr)); + } + finally + { + if (createUid != null) + { + //remove the one we created + DeleteAndVerifyObject(connector, ObjectClass.ACCOUNT, + createUid, false, true); + } + } + } + + + [Ignore] + [Test] + public void TestContainerChange_account() + { + ActiveDirectoryConnector connector = new ActiveDirectoryConnector(); + connector.Init(GetConfiguration()); + + Uid createGroupUid = null; + Uid createUserUid = null; + + try { + // create container for this test + ICollection groupAttributes = GetNormalAttributes_Group(); + createGroupUid = CreateAndVerifyObject(connector, + ObjectClass.GROUP, groupAttributes); + ICollection groupResults = TestHelpers.SearchToList( + connector, ObjectClass.GROUP, FilterBuilder.EqualTo(createGroupUid)); + Assert.AreEqual(1, groupResults.Count); + Assert.AreEqual(createGroupUid, groupResults.ElementAt(0).Uid); + ConnectorAttribute groupDnAttr = + groupResults.ElementAt(0).GetAttributeByName("distinguishedName"); + Assert.IsNotNull(groupDnAttr); + String groupPath = ConnectorAttributeUtil.GetStringValue(groupDnAttr); + + // create user + createUserUid = CreateAndVerifyObject(connector, + ObjectClass.ACCOUNT, GetNormalAttributes_Account()); + + //now change user container to the newly created one + ICollection updateAttrs = new HashSet(); + updateAttrs.Add(ConnectorAttributeBuilder.Build("ad_container", groupPath)); + updateAttrs.Add(createUserUid); + + connector.Update(UpdateType.REPLACE, ObjectClass.ACCOUNT, updateAttrs, null); + + ICollection results = TestHelpers.SearchToList( + connector, ObjectClass.ACCOUNT, FilterBuilder.EqualTo(createUserUid)); + Assert.AreEqual(1, results.Count); + Assert.AreEqual(createUserUid, results.ElementAt(0).Uid); + ConnectorAttribute foundContainerAttr = results.ElementAt(0).GetAttributeByName("ad_container"); + Assert.IsNotNull(foundContainerAttr); + + String lhs = ActiveDirectoryUtils.NormalizeLdapString(groupPath); + String rhs = ActiveDirectoryUtils.NormalizeLdapString(ConnectorAttributeUtil.GetStringValue(foundContainerAttr)); + Assert.AreEqual(lhs, rhs); + } + finally + { + if (createUserUid != null) + { + //remove the one we created + DeleteAndVerifyObject(connector, ObjectClass.ACCOUNT, + createUserUid, false, true); + } + + if (createGroupUid != null) + { + //remove the one we created + DeleteAndVerifyObject(connector, ObjectClass.GROUP, + createGroupUid, false, true); + } + } + } + + [Ignore] + [Test] + public void TestContainerChange_group() + { + ActiveDirectoryConnector connector = new ActiveDirectoryConnector(); + connector.Init(GetConfiguration()); + + // create user + Uid createUid = CreateAndVerifyObject(connector, + ObjectClass.ACCOUNT, GetNormalAttributes_Account()); + + // create container for this test + + //now change user container to the newly created one + + throw new NotImplementedException(); + } + + [Ignore] + [Test] + public void TestEnableDate() + { + //Initialize Connector + ActiveDirectoryConnector connector = new ActiveDirectoryConnector(); + connector.Init(GetConfiguration()); + Uid createUid = null; + + try + { + // create user + ICollection createAttributes = GetNormalAttributes_Account(); + createAttributes.Add(ConnectorAttributeBuilder.BuildEnabled(true)); + createAttributes.Add(ConnectorAttributeBuilder.BuildEnableDate(new DateTime(2000, 01, 01))); + createUid = CreateAndVerifyObject(connector, + ObjectClass.ACCOUNT, createAttributes); + + ICollection updateReplaceAttributes = + new HashSet(); + updateReplaceAttributes.Add(ConnectorAttributeBuilder.BuildEnabled(false)); + UpdateReplaceAndVerifyObject(connector, ObjectClass.ACCOUNT, + createUid, updateReplaceAttributes); + } + finally + { + if (createUid != null) + { + //remove the one we created + DeleteAndVerifyObject(connector, ObjectClass.ACCOUNT, + createUid, false, true); + } + } + } + + [Ignore] + [Test] + public void TestDisableDate() + { + //Initialize Connector + ActiveDirectoryConnector connector = new ActiveDirectoryConnector(); + connector.Init(GetConfiguration()); + Uid createUid = null; + + try + { + // create user + ICollection createAttributes = GetNormalAttributes_Account(); + createAttributes.Add(ConnectorAttributeBuilder.BuildEnabled(true)); + + // disable tommorrow + DateTime disableDate = DateTime.Now.AddHours(24); + + createAttributes.Add(ConnectorAttributeBuilder.BuildDisableDate(disableDate)); + createUid = CreateAndVerifyObject(connector, + ObjectClass.ACCOUNT, createAttributes); + + ICollection updateReplaceAttributes = + new HashSet(); + updateReplaceAttributes.Add(ConnectorAttributeBuilder.BuildEnabled(false)); + UpdateReplaceAndVerifyObject(connector, ObjectClass.ACCOUNT, + createUid, updateReplaceAttributes); + } + finally + { + if (createUid != null) + { + //remove the one we created + DeleteAndVerifyObject(connector, ObjectClass.ACCOUNT, + createUid, false, true); + } + } + } + + // test sync + [Test] + public void TestSyncGC() + { + // test with searchChildDomain (uses GC) + TestSync(true, GetProperty(CONFIG_PROPERTY_SYNC_SEARCH_CONTEXT_ROOT)); + TestSync(true, GetProperty(CONFIG_PROPERTY_SYNC_SEARCH_CONTEXT_CHILD)); + } + + // test sync + [Test] + public void TestSyncDC() + { + // test withouth searchChildDomains (uses DC) + TestSync(false, GetProperty(CONFIG_PROPERTY_SYNC_SEARCH_CONTEXT_ROOT)); + TestSync(false, GetProperty(CONFIG_PROPERTY_SYNC_SEARCH_CONTEXT_CHILD)); + } + + [Test] + public void TestUserPasswordChange() + { + //Initialize Connector + ActiveDirectoryConnector connector = new ActiveDirectoryConnector(); + connector.Init(GetConfiguration()); + Uid createUid = null; + + try + { + // create user + ICollection createAttributes = GetNormalAttributes_Account(); + // remove password, and set to something memorable + createAttributes.Remove(ConnectorAttributeUtil.Find(OperationalAttributes.PASSWORD_NAME, createAttributes)); + GuardedString gsCurrentPassword = GetGuardedString("1Password"); + createAttributes.Add(ConnectorAttributeBuilder.BuildPassword(gsCurrentPassword)); + createAttributes.Add(ConnectorAttributeBuilder.BuildEnabled(true)); + createUid = CreateAndVerifyObject(connector, + ObjectClass.ACCOUNT, createAttributes); + + // make sure authenticate works here + connector.Authenticate(ObjectClass.ACCOUNT, + ConnectorAttributeUtil.GetAsStringValue(ConnectorAttributeUtil.Find("sAMAccountName", createAttributes)), + gsCurrentPassword, null); + + ICollection updateReplaceAttributes = + new HashSet(); + GuardedString gsNewPassword = GetGuardedString("LongPassword2MeetTheRequirements!"); + updateReplaceAttributes.Add(ConnectorAttributeBuilder.BuildCurrentPassword(gsCurrentPassword)); + updateReplaceAttributes.Add(ConnectorAttributeBuilder.BuildPassword(gsNewPassword)); + UpdateReplaceAndVerifyObject(connector, ObjectClass.ACCOUNT, + createUid, updateReplaceAttributes); + + // make sure authenticate works here + connector.Authenticate(ObjectClass.ACCOUNT, + ConnectorAttributeUtil.GetAsStringValue(ConnectorAttributeUtil.Find("sAMAccountName", createAttributes)), + gsNewPassword, null); + + bool caughtAuthenticateFailedException = false; + try + { + // make sure authenticate doesnt work with original password + connector.Authenticate(ObjectClass.ACCOUNT, + ConnectorAttributeUtil.GetAsStringValue(ConnectorAttributeUtil.Find("sAMAccountName", createAttributes)), + gsCurrentPassword, null); + } + catch (Exception e) + { + caughtAuthenticateFailedException = true; + } + + Assert.IsTrue(caughtAuthenticateFailedException, "Negative test case should throw an exception"); + + + // now a negative test case + GuardedString gsBogusPassword = GetGuardedString("BogusPassword"); + ICollection updateErrorReplaceAttributes = + new HashSet(); + updateErrorReplaceAttributes.Add(ConnectorAttributeBuilder.BuildCurrentPassword(gsBogusPassword)); + updateErrorReplaceAttributes.Add(ConnectorAttributeBuilder.BuildPassword(gsNewPassword)); + bool caughtWrongCurrentPasswordException = false; + try + { + // update should fail due to wrong current password + UpdateReplaceAndVerifyObject(connector, ObjectClass.ACCOUNT, + createUid, updateErrorReplaceAttributes); + } + catch (Exception e) + { + caughtWrongCurrentPasswordException = true; + } + + Assert.IsTrue(caughtWrongCurrentPasswordException, "Negative test case should throw an exception"); + + // make sure authenticate works here + + } + finally + { + if (createUid != null) + { + //remove the one we created + DeleteAndVerifyObject(connector, ObjectClass.ACCOUNT, + createUid, false, true); + } + } + } + + [Test] + public void TestAuthenticateUser() + { + //Initialize Connector + ActiveDirectoryConnector connector = new ActiveDirectoryConnector(); + connector.Init(GetConfiguration()); + Uid createUid = null; + + try + { + // create user + ICollection createAttributes = GetNormalAttributes_Account(); + // remove password, and set to something memorable + createAttributes.Remove(ConnectorAttributeUtil.Find(OperationalAttributes.PASSWORD_NAME, createAttributes)); + GuardedString gsCurrentPassword = GetGuardedString("1Password"); + createAttributes.Add(ConnectorAttributeBuilder.BuildPassword(gsCurrentPassword)); + createAttributes.Add(ConnectorAttributeBuilder.BuildEnabled(true)); + createUid = CreateAndVerifyObject(connector, + ObjectClass.ACCOUNT, createAttributes); + + // make sure authenticate works here + Uid authUid = connector.Authenticate(ObjectClass.ACCOUNT, + ConnectorAttributeUtil.GetAsStringValue(ConnectorAttributeUtil.Find("sAMAccountName", createAttributes)), + gsCurrentPassword, null); + + Assert.AreEqual(createUid, authUid); + + // make sure authenticate fails - wrong password + bool caughtException = false; + try + { + connector.Authenticate(ObjectClass.ACCOUNT, + ConnectorAttributeUtil.GetAsStringValue(ConnectorAttributeUtil.Find("sAMAccountName", createAttributes)), + GetGuardedString("boguspassword"), null); + + } + catch (InvalidCredentialException e) + { + caughtException = true; + } + Assert.IsTrue(caughtException, "Negative test case should throw InvalidCredentialsException"); + + // change password + ICollection updateReplaceAttributes = + new HashSet(); + GuardedString gsNewPassword = GetGuardedString("LongPassword2MeetTheRequirements!"); + updateReplaceAttributes.Add(ConnectorAttributeBuilder.BuildCurrentPassword(gsCurrentPassword)); + updateReplaceAttributes.Add(ConnectorAttributeBuilder.BuildPassword(gsNewPassword)); + UpdateReplaceAndVerifyObject(connector, ObjectClass.ACCOUNT, + createUid, updateReplaceAttributes); + + // make sure authenticate works here - new password + connector.Authenticate(ObjectClass.ACCOUNT, + ConnectorAttributeUtil.GetAsStringValue(ConnectorAttributeUtil.Find("sAMAccountName", createAttributes)), + gsNewPassword, null); + + // make sure it fails with the wrong password + caughtException = false; + try { + connector.Authenticate(ObjectClass.ACCOUNT, + ConnectorAttributeUtil.GetAsStringValue(ConnectorAttributeUtil.Find("sAMAccountName", createAttributes)), + GetGuardedString("bogusPassword"), null); + } + catch (Exception e) + { + caughtException = true; + } + Assert.IsTrue(caughtException, "Negative test case should throw an exception"); + + // now set user must change password attribute + ICollection expirePasswordAttrs = + new HashSet(); + expirePasswordAttrs.Add(ConnectorAttributeBuilder.BuildPasswordExpired(true)); + UpdateReplaceAndVerifyObject(connector, ObjectClass.ACCOUNT, + createUid, expirePasswordAttrs); + + // make sure authenticate fails - correct password, but expired + caughtException = false; + try + { + // make sure authenticate fails with correct password + connector.Authenticate(ObjectClass.ACCOUNT, + ConnectorAttributeUtil.GetAsStringValue(ConnectorAttributeUtil.Find("sAMAccountName", createAttributes)), + gsNewPassword, null); + } + catch (PasswordExpiredException e) + { + caughtException = true; + Assert.AreEqual(createUid, e.Uid); + } + Assert.IsTrue(caughtException, "Negative test case should throw an exception"); + + // make sure authenticate fails - incorrect password, and expired + caughtException = false; + try + { + // make sure authenticate fails with wrong password (invalid credentials exception) + connector.Authenticate(ObjectClass.ACCOUNT, + ConnectorAttributeUtil.GetAsStringValue(ConnectorAttributeUtil.Find("sAMAccountName", createAttributes)), + GetGuardedString("bogusPassword"), null); + } + catch (InvalidCredentialException e) + { + caughtException = true; + } + Assert.IsTrue(caughtException, "Negative test case should throw an exception"); + } + finally + { + if (createUid != null) + { + //remove the one we created + DeleteAndVerifyObject(connector, ObjectClass.ACCOUNT, + createUid, false, true); + } + } + } + + [Test] + public void TestShortnameAndDescription() + { + //Initialize Connector + ActiveDirectoryConnector connector = new ActiveDirectoryConnector(); + connector.Init(GetConfiguration()); + Uid uidAccount = null; + Uid uidGroup = null; + Uid uidOu = null; + string accountDescription = "nunit test account description"; + string groupDescription = "nunit test group description"; + string ouDescription = "nunit test ou description"; + try + { + ICollection accountAttributes = GetNormalAttributes_Account(); + RemoveAttributeByName(accountAttributes, "description"); + accountAttributes.Add(ConnectorAttributeBuilder.Build( + "description", accountDescription)); + ICollection groupAttributes = GetNormalAttributes_Group(); + RemoveAttributeByName(groupAttributes, "description"); + groupAttributes.Add(ConnectorAttributeBuilder.Build( + "description", groupDescription)); + ICollection ouAttributes = GetNormalAttributes_OrganizationalUnit(); + RemoveAttributeByName(ouAttributes, "description"); + ouAttributes.Add(ConnectorAttributeBuilder.Build( + "description", ouDescription)); + + uidAccount = CreateObject(connector, ObjectClass.ACCOUNT, accountAttributes); + + OperationOptionsBuilder accountOptionsBuilder = new OperationOptionsBuilder(); + ICollection accountAttributesToGet = GetDefaultAttributesToGet(ObjectClass.ACCOUNT); + accountAttributesToGet.Add(PredefinedAttributes.DESCRIPTION); + accountAttributesToGet.Add(PredefinedAttributes.SHORT_NAME); + accountAttributesToGet.Add("name"); + accountAttributesToGet.Add("description"); + accountOptionsBuilder.AttributesToGet = accountAttributesToGet.ToArray(); + + ConnectorObject accountObject = GetConnectorObjectFromUid(connector, + ObjectClass.ACCOUNT, uidAccount, accountOptionsBuilder.Build()); + + // compare description + string foundAccountDescription = ConnectorAttributeUtil.GetStringValue( + ConnectorAttributeUtil.Find(PredefinedAttributes.DESCRIPTION, accountObject.GetAttributes())); + Assert.AreEqual(accountDescription, foundAccountDescription); + + // compare shortname + string accountShortName = ConnectorAttributeUtil.GetStringValue( + ConnectorAttributeUtil.Find( + PredefinedAttributes.SHORT_NAME, accountObject.GetAttributes())); + string accountDisplayName = ConnectorAttributeUtil.GetStringValue( + ConnectorAttributeUtil.Find( + "name", accountObject.GetAttributes())); + Assert.AreEqual(accountShortName, accountDisplayName); + + uidGroup = CreateObject(connector, ObjectClass.GROUP, groupAttributes); + + OperationOptionsBuilder groupOptionsBuilder = new OperationOptionsBuilder(); + ICollection groupAttributesToGet = GetDefaultAttributesToGet(ObjectClass.GROUP); + groupAttributesToGet.Add(PredefinedAttributes.DESCRIPTION); + groupAttributesToGet.Add(PredefinedAttributes.SHORT_NAME); + groupAttributesToGet.Add("name"); + groupAttributesToGet.Add("description"); + groupOptionsBuilder.AttributesToGet = groupAttributesToGet.ToArray(); + + ConnectorObject groupObject = GetConnectorObjectFromUid(connector, + ObjectClass.GROUP, uidGroup, groupOptionsBuilder.Build()); + + // compare description + string foundGroupDescription = ConnectorAttributeUtil.GetStringValue( + ConnectorAttributeUtil.Find(PredefinedAttributes.DESCRIPTION, groupObject.GetAttributes())); + Assert.AreEqual(groupDescription, foundGroupDescription); + + // compare shortnameB + string groupShortName = ConnectorAttributeUtil.GetStringValue( + ConnectorAttributeUtil.Find( + PredefinedAttributes.SHORT_NAME, groupObject.GetAttributes())); + string groupDisplayName = ConnectorAttributeUtil.GetStringValue( + ConnectorAttributeUtil.Find( + "name", groupObject.GetAttributes())); + Assert.AreEqual(groupShortName, groupDisplayName); + + uidOu = CreateObject(connector, ActiveDirectoryConnector.ouObjectClass, ouAttributes); + OperationOptionsBuilder ouOptionsBuilder = new OperationOptionsBuilder(); + ICollection ouAttributesToGet = GetDefaultAttributesToGet(ActiveDirectoryConnector.ouObjectClass); + ouAttributesToGet.Add(PredefinedAttributes.DESCRIPTION); + ouAttributesToGet.Add(PredefinedAttributes.SHORT_NAME); + ouAttributesToGet.Add("name"); + ouAttributesToGet.Add("description"); + ouOptionsBuilder.AttributesToGet = ouAttributesToGet.ToArray(); + + ConnectorObject ouObject = GetConnectorObjectFromUid(connector, + ActiveDirectoryConnector.ouObjectClass, uidOu, ouOptionsBuilder.Build()); + + // compare description + string foundOuDescription = ConnectorAttributeUtil.GetStringValue( + ConnectorAttributeUtil.Find(PredefinedAttributes.DESCRIPTION, ouObject.GetAttributes())); + Assert.AreEqual(ouDescription, foundOuDescription); + + // compare shortname + string ouShortName = ConnectorAttributeUtil.GetStringValue( + ConnectorAttributeUtil.Find( + PredefinedAttributes.SHORT_NAME, ouObject.GetAttributes())); + string ouDisplayName = ConnectorAttributeUtil.GetStringValue( + ConnectorAttributeUtil.Find( + "name", ouObject.GetAttributes())); + Assert.AreEqual(ouShortName, ouDisplayName); + } + finally + { + if (uidAccount != null) + { + //remove the one we created + DeleteAndVerifyObject(connector, ObjectClass.ACCOUNT, + uidAccount, false, true); + } + if (uidGroup != null) + { + //remove the one we created + DeleteAndVerifyObject(connector, ObjectClass.GROUP, + uidGroup, false, true); + } + if (uidOu != null) + { + //remove the one we created + DeleteAndVerifyObject(connector, ActiveDirectoryConnector.ouObjectClass, + uidOu, false, true); + } + } + } + + [Test] + public void TestPasswordExpiration() + { + //Initialize Connector + ActiveDirectoryConnector connector = new ActiveDirectoryConnector(); + connector.Init(GetConfiguration()); + Uid createUid = null; + + try + { + // create user + ICollection createAttributes = GetNormalAttributes_Account(); + // remove password, and set to something memorable + createAttributes.Remove(ConnectorAttributeUtil.Find(OperationalAttributes.PASSWORD_NAME, createAttributes)); + GuardedString gsCurrentPassword = GetGuardedString("1Password"); + createAttributes.Add(ConnectorAttributeBuilder.BuildPassword(gsCurrentPassword)); + createAttributes.Add(ConnectorAttributeBuilder.BuildEnabled(true)); + createUid = CreateAndVerifyObject(connector, + ObjectClass.ACCOUNT, createAttributes); + + // make sure authenticate works here + connector.Authenticate(ObjectClass.ACCOUNT, + ConnectorAttributeUtil.GetAsStringValue(ConnectorAttributeUtil.Find("sAMAccountName", createAttributes)), + gsCurrentPassword, null); + + // now set expiration to right now + ICollection expirePasswordNowAttrs = + new HashSet(); + + expirePasswordNowAttrs.Add( + ConnectorAttributeBuilder.BuildPasswordExpirationDate(DateTime.UtcNow)); + UpdateReplaceAndVerifyObject(connector, ObjectClass.ACCOUNT, + createUid, expirePasswordNowAttrs); + + // sometimes expiring now, really means in a few milliseconds + // there is some rounding or something that happens. + Thread.Sleep(30000); + + // make sure authenticate fails - correct password, but expired + bool caughtException = false; + try + { + // make sure authenticate fails with correct password + connector.Authenticate(ObjectClass.ACCOUNT, + ConnectorAttributeUtil.GetAsStringValue(ConnectorAttributeUtil.Find("sAMAccountName", createAttributes)), + gsCurrentPassword, null); + } + catch (PasswordExpiredException e) + { + caughtException = true; + Assert.AreEqual(createUid, e.Uid); + } + Assert.IsTrue(caughtException, "Negative test case should throw an exception"); + + // set expiration to tommorrow + ICollection expirePasswordTomorrowAttrs = + new HashSet(); + expirePasswordTomorrowAttrs.Add( + ConnectorAttributeBuilder.BuildPasswordExpirationDate(DateTime.UtcNow.AddDays(1))); + UpdateReplaceAndVerifyObject(connector, ObjectClass.ACCOUNT, + createUid, expirePasswordTomorrowAttrs); + + // make sure succeeds + connector.Authenticate(ObjectClass.ACCOUNT, + ConnectorAttributeUtil.GetAsStringValue(ConnectorAttributeUtil.Find("sAMAccountName", createAttributes)), + gsCurrentPassword, null); + + } + finally + { + if (createUid != null) + { + //remove the one we created + DeleteAndVerifyObject(connector, ObjectClass.ACCOUNT, + createUid, false, true); + } + } + } + + [Test] + public void TestAccountLocked() + { + //Initialize Connector + ActiveDirectoryConnector connector = new ActiveDirectoryConnector(); + connector.Init(GetConfiguration()); + Uid createUid = null; + + try + { + // create user + ICollection createAttributes = GetNormalAttributes_Account(); + // remove password, and set to something memorable + createAttributes.Remove(ConnectorAttributeUtil.Find(OperationalAttributes.PASSWORD_NAME, createAttributes)); + GuardedString gsCurrentPassword = GetGuardedString("1Password"); + createAttributes.Add(ConnectorAttributeBuilder.BuildPassword(gsCurrentPassword)); + createAttributes.Add(ConnectorAttributeBuilder.BuildEnabled(true)); + createUid = CreateAndVerifyObject(connector, + ObjectClass.ACCOUNT, createAttributes); + + // make sure authenticate works here + connector.Authenticate(ObjectClass.ACCOUNT, + ConnectorAttributeUtil.GetAsStringValue(ConnectorAttributeUtil.Find("sAMAccountName", createAttributes)), + gsCurrentPassword, null); + + // not allowed to lock ... only unlock, so test unlock + // setting on machine must lockout user after 3 unsuccessful + // attempst for this to work. + // lock out by having unsucessful attempts. + try + { + connector.Authenticate(ObjectClass.ACCOUNT, + ConnectorAttributeUtil.GetAsStringValue(ConnectorAttributeUtil.Find("sAMAccountName", createAttributes)), + GetGuardedString("bogusPassword"), null); + } + catch(Exception e) + { + } + + try + { + connector.Authenticate(ObjectClass.ACCOUNT, + ConnectorAttributeUtil.GetAsStringValue(ConnectorAttributeUtil.Find("sAMAccountName", createAttributes)), + GetGuardedString("bogusPassword"), null); + } + catch (Exception e) + { + } + + try + { + connector.Authenticate(ObjectClass.ACCOUNT, + ConnectorAttributeUtil.GetAsStringValue(ConnectorAttributeUtil.Find("sAMAccountName", createAttributes)), + GetGuardedString("bogusPassword"), null); + } + catch (Exception e) + { + } + + bool exceptionCaught = false; + try + { + connector.Authenticate(ObjectClass.ACCOUNT, + ConnectorAttributeUtil.GetAsStringValue(ConnectorAttributeUtil.Find("sAMAccountName", createAttributes)), + gsCurrentPassword, null); + } + catch (Exception e) + { + exceptionCaught = true; + } + Assert.IsTrue(exceptionCaught, "Account not locked. Make sure that the server is setup for account lockout after 3 attempts"); + + ICollection unlockAttrs = + new HashSet(); + unlockAttrs.Add( + ConnectorAttributeBuilder.BuildLockOut(false)); + UpdateReplaceAndVerifyObject(connector, ObjectClass.ACCOUNT, + createUid, unlockAttrs); + + // make sure succeeds + connector.Authenticate(ObjectClass.ACCOUNT, + ConnectorAttributeUtil.GetAsStringValue(ConnectorAttributeUtil.Find("sAMAccountName", createAttributes)), + gsCurrentPassword, null); + + + // now try to write lockout. Should get connector exception + bool connectorExceptionCaught = false; + try + { + ICollection lockAttrs = + new HashSet(); + lockAttrs.Add( + ConnectorAttributeBuilder.BuildLockOut(true)); + UpdateReplaceAndVerifyObject(connector, ObjectClass.ACCOUNT, + createUid, lockAttrs); + + } + catch (ConnectorException e) + { + connectorExceptionCaught = true; + } + Assert.IsTrue(connectorExceptionCaught); + } + finally + { + if (createUid != null) + { + //remove the one we created + DeleteAndVerifyObject(connector, ObjectClass.ACCOUNT, + createUid, false, true); + } + } + } + + public void TestSync(bool searchChildDomains, String syncSearchContext) + { + //Initialize Connector + ActiveDirectoryConnector connector = new ActiveDirectoryConnector(); + ActiveDirectoryConfiguration configuration = + (ActiveDirectoryConfiguration)GetConfiguration(); + configuration.SyncSearchContext = syncSearchContext; + configuration.SearchChildDomains = searchChildDomains; + connector.Init(configuration); + + Uid createUid = null; + + ICollection createdUids = new List(); + try + { + SyncTestHelper syncHelper = new SyncTestHelper(); + + // do the first sync + //connector.Sync(ObjectClass.ACCOUNT, syncHelper.Token, syncHelper.SyncHandler_Initial, null); + + syncHelper.Init(connector.GetLatestSyncToken(ObjectClass.ACCOUNT)); + ICollection attributes = null; + + // create some users + for (int i = 0; i < 10; i++) + { + attributes = GetNormalAttributes_Account(); + createUid = CreateAndVerifyObject(connector, ObjectClass.ACCOUNT, + attributes); + syncHelper.AddModUid(createUid, attributes); + createdUids.Add(createUid); + } + + // sync, and verify + connector.Sync(ObjectClass.ACCOUNT, syncHelper._token, syncHelper.SyncHandler_ModifiedAccounts, null); + syncHelper.CheckAllSyncsProcessed(); + + // reset everything + syncHelper.Init(connector.GetLatestSyncToken(ObjectClass.ACCOUNT)); + + // modify a user, then add some users, then modify one of the added users + attributes = new List(); + attributes.Add(createdUids.First()); + attributes.Add(ConnectorAttributeBuilder.Build("sn", "replaced")); + connector.Update(UpdateType.REPLACE, ObjectClass.ACCOUNT, attributes, null); + syncHelper.AddModUid(createdUids.First(), attributes); + + for(int i = 0; i < 10; i++) + { + attributes = GetNormalAttributes_Account(); + createUid = CreateAndVerifyObject(connector, ObjectClass.ACCOUNT, + attributes); + syncHelper.AddModUid(createUid, attributes); + createdUids.Add(createUid); + } + + attributes = new List(); + attributes.Add(createdUids.Last()); + attributes.Add(ConnectorAttributeBuilder.Build("sn", "replaced")); + connector.Update(UpdateType.REPLACE, ObjectClass.ACCOUNT, attributes, null); + syncHelper.AddModUid(createdUids.Last(), attributes); + + // sync, and verify + connector.Sync(ObjectClass.ACCOUNT, syncHelper._token, syncHelper.SyncHandler_ModifiedAccounts, null); + syncHelper.CheckAllSyncsProcessed(); + + syncHelper.Init(connector.GetLatestSyncToken(ObjectClass.ACCOUNT)); + // delete the user + foreach (Uid uid in createdUids) + { + DeleteAndVerifyObject(connector, ObjectClass.ACCOUNT, uid, + false, true); + syncHelper.AddDelUid(uid); + } + // sync and verify + connector.Sync(ObjectClass.ACCOUNT, syncHelper._token, syncHelper.SyncHandler_DeletedAccounts, null); + syncHelper.CheckAllSyncsProcessed(); + + createUid = CreateAndVerifyObject(connector, ObjectClass.ACCOUNT, + GetNormalAttributes_Account()); + syncHelper.AddModUid(createUid, attributes); + createdUids.Add(createUid); + + // now get the latest sync token, and it + // should be greater or equal to the last one we saw + SyncToken latestToken = connector.GetLatestSyncToken(ObjectClass.ACCOUNT); + Assert.Greater(GetUpdateUsnFromToken(latestToken), GetUpdateUsnFromToken(syncHelper._token)); + Assert.GreaterOrEqual(GetDeleteUsnFromToken(latestToken), GetDeleteUsnFromToken(syncHelper._token)); + } + finally + { + foreach (Uid uid in createdUids) + { + DeleteAndVerifyObject(connector, ObjectClass.ACCOUNT, uid, + false, false); + } + } + } + + [Test] + public void TestGetLastSyncToken() + { + //Initialize Connector + ActiveDirectoryConnector connector = new ActiveDirectoryConnector(); + ActiveDirectoryConfiguration configuration = + (ActiveDirectoryConfiguration)GetConfiguration(); + configuration.SearchChildDomains = false; + connector.Init(configuration); + SyncToken noGCToken = connector.GetLatestSyncToken(ObjectClass.ACCOUNT); + + configuration.SearchChildDomains = true; + connector.Init(configuration); + SyncToken GCToken = connector.GetLatestSyncToken(ObjectClass.ACCOUNT); + } + + public long GetUpdateUsnFromToken(SyncToken token) + { + string[] tokenParts = ((string)token.Value).Split('|'); + return long.Parse(tokenParts[0]); + } + + public long GetDeleteUsnFromToken(SyncToken token) + { + string[] tokenParts = ((string)token.Value).Split('|'); + return long.Parse(tokenParts[1]); + } + + class SyncTestHelper + { + IDictionary> _mods = null; + IList _dels = null; + + public SyncToken _token { get; set; } + + public void Init(SyncToken token) + { + _mods = new Dictionary>(); + _dels = new List(); + _token = token; + } + + public void AddModUid(Uid uid, ICollection attributes) + { + _mods[uid]=attributes; + } + + public void AddDelUid(Uid uid) + { + _dels.Add(uid); + } + + public bool SyncHandler_Initial(SyncDelta delta) + { + // do nothing .. just establishing the baseline + _token = delta.Token; + return true; + } + + public bool SyncHandler_ModifiedAccounts(SyncDelta delta) + { + _token = delta.Token; + if(delta.DeltaType.Equals(SyncDeltaType.CREATE_OR_UPDATE)) { + // just ignore extra ones. they might have come in by other means + if (_mods.ContainsKey(delta.Uid)) + { + ICollection requestedAttrs = _mods[delta.Uid]; + + ActiveDirectoryConnectorTest.VerifyObject(requestedAttrs, + delta.Object); + _mods.Remove(delta.Uid); + } + } + return true; + } + + public bool SyncHandler_DeletedAccounts(SyncDelta delta) + { + _token = delta.Token; + + _dels.Remove(delta.Uid); + return true; + } + + public bool SyncHandler_Mixed(SyncDelta delta) + { + return true; + } + + public void CheckAllSyncsProcessed() + { + // since the handlers remove things from + // the list as found, this method is called + // at then end of a sync, and all arrays should + // be empty ... meaning everything is accounted + // for. + Assert.AreEqual(0, _dels.Count); + Assert.AreEqual(0, _mods.Count); + } + } + + public void RunScript(ActiveDirectoryConnector connector, String user, + string password) + { + string tempFileName = Path.GetTempFileName(); + string scriptText = String.Format( + "echo %ARG0%:%ARG1%:%USERNAME%:%PASSWORD% > \"{0}\"", tempFileName); + + IDictionary arguments = new Dictionary(); + string arg0 = "argument_zero"; + string arg1 = "argument one"; + arguments.Add("ARG0", arg0); + arguments.Add("ARG1", arg1); + + OperationOptionsBuilder builder = new OperationOptionsBuilder(); + if (user.Length > 0) + { + builder.RunAsUser = user; + } + if (password.Length > 0) + { + builder.RunWithPassword = GetGuardedString(password); + } + + ScriptContext context = new ScriptContext("Shell", scriptText, arguments); + object resultObject = connector.RunScriptOnResource(context, builder.Build()); + Assert.IsNotNull(resultObject); + Assert.That(resultObject is int); + Assert.AreEqual(0, resultObject); + FileStream outputFs = new FileStream(tempFileName, FileMode.Open, FileAccess.Read); + StreamReader outputReader = new StreamReader(outputFs); + // read the first line + string output = outputReader.ReadLine(); + string[] returnedArray = output.Split(':'); + Assert.AreEqual(4, returnedArray.Length); + Assert.AreEqual(arg0, returnedArray[0]); + Assert.AreEqual(arg1, returnedArray[1]); + } +/* + [Test] + public void testBooScript() + { + //Initialize Connector + ActiveDirectoryConnector connector = new ActiveDirectoryConnector(); + connector.Init(GetConfiguration()); + + try + { + string tempFileName = Path.GetTempFileName(); + StringBuilder scriptText = new StringBuilder(); + scriptText.Append("print(\"this is, \");"); + scriptText.Append("print(\"a test.\");"); + + IDictionary arguments = new Dictionary(); + string arg0 = "argument_zero"; + string arg1 = "argument one"; + arguments.Add("ARG0", arg0); + arguments.Add("ARG1", arg1); + + OperationOptionsBuilder builder = new OperationOptionsBuilder(); + + ScriptContext context = new ScriptContext("Boo", scriptText.ToString(), arguments); + object resultObject = connector.RunScriptOnResource(context, builder.Build()); + } + finally + { + } + } +*/ + + // does a create and verify, then looks up and returns + // the new user's dn (used for adding to a group) + public String CreateGroupMember(ActiveDirectoryConnector connector) + { + Uid uidMember = CreateAndVerifyObject(connector, ObjectClass.ACCOUNT, + GetNormalAttributes_Account()); + + Filter uidFilter = FilterBuilder.EqualTo(uidMember); + ICollection foundObjects = TestHelpers.SearchToList( + connector, ObjectClass.ACCOUNT, uidFilter); + Assert.IsTrue(foundObjects.Count == 1); + String dnMember = ConnectorAttributeUtil.GetAsStringValue( + foundObjects.ElementAt(0).GetAttributeByName("distinguishedName")); + return dnMember; + } + + public Uid UpdateReplaceAndVerifyObject(ActiveDirectoryConnector connector, + ObjectClass oclass, Uid uid, ICollection attributes) + { + attributes.Add(uid); + Filter uidFilter = FilterBuilder.EqualTo(uid); + + // find the object ... can't update if it doesn't exist + ICollection currentConnectorObjects = TestHelpers.SearchToList( + connector, oclass, uidFilter); + Assert.AreEqual(1, currentConnectorObjects.Count); + + Uid updatedUid = connector.Update(UpdateType.REPLACE, oclass, + attributes, null); + + Assert.IsNotNull(updatedUid); + + uidFilter = FilterBuilder.EqualTo(updatedUid); + ICollection updatedConnectorObjects = TestHelpers.SearchToList( + connector, oclass, uidFilter); + Assert.IsTrue(updatedConnectorObjects.Count == 1); + VerifyObject(attributes, updatedConnectorObjects.ElementAt(0)); + return updatedUid; + } + + public Uid UpdateAddAndVerifyUser(ActiveDirectoryConnector connector, + ObjectClass oclass, Uid uid, ICollection attributes, + OperationOptions searchOptions) + { + // find the existing one, and save off all attributes + Filter uidFilter = FilterBuilder.EqualTo(uid); + ICollection currentObjects = TestHelpers.SearchToList( + connector, oclass, uidFilter, searchOptions); + Assert.IsTrue(currentObjects.Count == 1); + ICollection currentAttributes = + currentObjects.ElementAt(0).GetAttributes(); + + // build a list that has the 'added' values added to the existing values + ICollection comparisonAttributes = new List(); + foreach (ConnectorAttribute updateAttribute in attributes) + { + ConnectorAttribute existingAttribute = ConnectorAttributeUtil.Find( + updateAttribute.Name, currentAttributes); + comparisonAttributes.Add(AttConcat(updateAttribute, existingAttribute)); + } + + // make sure the uid is present in the attributes + attributes.Add(uid); + // now update with ADD to add additional home phones + Uid updatedUid = connector.Update(UpdateType.ADD, oclass, + attributes, null); + + // find it back + ICollection updatedObjects = TestHelpers.SearchToList( + connector, oclass, uidFilter, searchOptions); + Assert.IsTrue(updatedObjects.Count == 1); + + VerifyObject(comparisonAttributes, updatedObjects.ElementAt(0)); + + return updatedUid; + } + + /// + /// Concatenates two attributes' values + /// + /// Must be non null + /// May be null + /// new attribute with name of ca1 and value of ca1 + ca2 + public ConnectorAttribute AttConcat(ConnectorAttribute ca1, ConnectorAttribute ca2) + { + ConnectorAttributeBuilder builder = new ConnectorAttributeBuilder(); + Assert.IsNotNull(ca1); + if (ca2 == null) + { + // if the second is null, just build up a dummy one + ca2 = ConnectorAttributeBuilder.Build(ca1.Name); + } + + Assert.AreEqual(ca1.Name, ca2.Name); + builder.Name = ca1.Name; + builder.AddValue(ca1.Value); + builder.AddValue(ca2.Value); + + return builder.Build(); + } + + public Uid UpdateDeleteAndVerifyUser(ActiveDirectoryConnector connector, + ICollection attributes) + { + throw new NotImplementedException(); + } + + public void DeleteAndVerifyObject(ActiveDirectoryConnector connector, + ObjectClass oclass, Uid uid, bool verifyExists, bool verifyDeleted) + { + Filter uidFilter = FilterBuilder.EqualTo(uid); + + if (verifyExists) + { + // verify that object currently exists + ICollection foundObjects = TestHelpers.SearchToList( + connector, oclass, uidFilter); + + // verify that it was deleted + Assert.AreEqual(1, foundObjects.Count); + } + + // delete + try + { + connector.Delete(oclass, uid, null); + } + catch + { + if (verifyDeleted) + { + throw; + } + } + + if (verifyDeleted) + { + // verify that object was deleted + ICollection deletedObjects = TestHelpers.SearchToList( + connector, oclass, uidFilter); + + // verify that it was deleted + Assert.AreEqual(0, deletedObjects.Count); + } + } + + [Test] + // note that you must create at least one ou for this test to work + public void TestOuSearch() + { + ActiveDirectoryConnector connector = new ActiveDirectoryConnector(); + ActiveDirectoryConfiguration config = (ActiveDirectoryConfiguration)GetConfiguration(); + connector.Init(config); + ObjectClass OUObjectClass = ActiveDirectoryConnector.ouObjectClass; + + ICollection foundObjects = TestHelpers.SearchToList( + connector, OUObjectClass, null); + Assert.Greater(foundObjects.Count, 0); + } + + [Test] + public void TestUnmatchedCaseGUIDSearch() + { + //Initialize Connector + ActiveDirectoryConnector connector = new ActiveDirectoryConnector(); + connector.Init(GetConfiguration()); + Uid userUid = null; + try + { + // test normal case + userUid = CreateAndVerifyObject(connector, + ObjectClass.ACCOUNT, GetNormalAttributes_Account()); + Filter userUidFilter = FilterBuilder.EqualTo(userUid); + IList foundUserObjects = + TestHelpers.SearchToList(connector, ObjectClass.ACCOUNT, userUidFilter); + Assert.AreEqual(1, foundUserObjects.Count); + + // now test for searching with uppercase guid + userUidFilter = FilterBuilder.EqualTo(new Uid(userUid.GetUidValue().ToUpper())); + foundUserObjects = TestHelpers.SearchToList( + connector, ObjectClass.ACCOUNT, userUidFilter); + Assert.AreEqual(1, foundUserObjects.Count); + + // now test for searching with lowercase guid + userUidFilter = FilterBuilder.EqualTo(new Uid(userUid.GetUidValue().ToLower())); + foundUserObjects = TestHelpers.SearchToList( + connector, ObjectClass.ACCOUNT, userUidFilter); + Assert.AreEqual(1, foundUserObjects.Count); + } + finally + { + if (userUid != null) + { + DeleteAndVerifyObject(connector, ObjectClass.ACCOUNT, userUid, false, false); + } + } + } + + public Uid CreateAndVerifyObject(ActiveDirectoryConnector connector, + ObjectClass oclass, ICollection attributes) + { + // create object + Uid uid = CreateObject(connector, oclass, attributes); + VerifyObject(connector, uid, oclass, attributes); + return uid; + } + + public Uid CreateObject(ActiveDirectoryConnector connector, + ObjectClass oclass, ICollection attributes) + { + // if it exists, remove and recreate + Filter nameFilter = FilterBuilder.EqualTo(ConnectorAttributeBuilder.Build( + Name.NAME, ConnectorAttributeUtil.GetNameFromAttributes(attributes).Value)); + ICollection foundObjects = TestHelpers.SearchToList(connector, oclass, nameFilter); + if ((foundObjects != null) && (foundObjects.Count > 0)) + { + Assert.AreEqual(1, foundObjects.Count); + DeleteAndVerifyObject(connector, oclass, foundObjects.ElementAt(0).Uid, false, true); + } + + // create object + Uid uid = connector.Create(oclass, attributes, null); + Assert.IsNotNull(uid); + + return uid; + } + + public void VerifyObject(ActiveDirectoryConnector connector, Uid uid, + ObjectClass oclass, ICollection attributes) + { + // verify the object + VerifyObject(attributes, GetConnectorObjectFromUid(connector, oclass, uid)); + } + + + public Configuration GetConfiguration() + { + ActiveDirectoryConfiguration config = new ActiveDirectoryConfiguration(); + config.DomainName = GetProperty(CONFIG_PROPERTY_DOMAIN_NAME); + config.LDAPHostName = GetProperty(CONFIG_PROPERTY_LDAPHOSTNAME); + config.DirectoryAdminName = GetProperty(CONFIG_PROPERTY_USER); + config.DirectoryAdminPassword = GetProperty(CONFIG_PROPERTY_PASSWORD); + config.SearchContainer = GetProperty(CONFIG_PROPERTY_SEARCH_CONTEXT); + config.SyncDomainController = GetProperty(CONFIG_PROPERTY_SYNC_DOMAIN_CONTROLLER); + config.SyncGlobalCatalogServer = GetProperty(CONFIG_PROPERTY_GC_DOMAIN_CONTROLLER); + config.ConnectorMessages = TestHelpers.CreateDummyMessages(); + return config; + } + + /** + * NOTES: + * - cn and @@NAME@@ should be the same. Test if they are not + * test for proper behavior if @@name@@ is not supplied + * - test bogus attributes to like attribut named BogusAttr = hello + * or something + * - test writing to a read only attribute + * - test attributes with special values such as *, (, ), \ + */ + + public ICollection GetNormalAttributes_Account() + { + ICollection attributes = new HashSet(); + int randomNumber = GetRandomNumber(); + + // the container ... is a fabricated attribute + attributes.Add(ConnectorAttributeBuilder.Build( + "ad_container", GetProperty(CONFIG_PROPERTY_CONTAINER))); + attributes.Add(ConnectorAttributeBuilder.Build( + "userPassword", "secret")); + attributes.Add(ConnectorAttributeBuilder.Build( + "sAMAccountName", "nunitUser" + randomNumber)); + attributes.Add(ConnectorAttributeBuilder.Build( + "givenName", "nunit")); + attributes.Add(ConnectorAttributeBuilder.Build( + "sn", "TestUser" + randomNumber)); + attributes.Add(ConnectorAttributeBuilder.Build( + "displayName", "nunit test user " + randomNumber)); + attributes.Add(ConnectorAttributeBuilder.Build( + Name.NAME, "cn=nunit" + randomNumber + "," + + GetProperty(CONFIG_PROPERTY_CONTAINER))); + attributes.Add(ConnectorAttributeBuilder.Build( + "mail", "nunitUser" + randomNumber + "@some.com")); + attributes.Add(ConnectorAttributeBuilder.Build( + "otherHomePhone", "512.555.1212", "512.123.4567")); + return attributes; + } + + public ICollection GetAllAttributes_Account() + { + ICollection attributes = new HashSet(); + int randomNumber = GetRandomNumber(); + + // the container ... is a fabricated attribute + attributes.Add(ConnectorAttributeBuilder.Build( + "ad_container", GetProperty(CONFIG_PROPERTY_CONTAINER))); + GuardedString password = new GuardedString(); + foreach (char c in "secret") + { + password.AppendChar(c); + } + attributes.Add(ConnectorAttributeBuilder.BuildPassword( + password)); + attributes.Add(ConnectorAttributeBuilder.Build( + "sAMAccountName", "nunit" + randomNumber)); + attributes.Add(ConnectorAttributeBuilder.Build( + "givenName", "nunit")); + attributes.Add(ConnectorAttributeBuilder.Build( + "sn", "TestUser" + randomNumber)); + attributes.Add(ConnectorAttributeBuilder.Build( + "displayName", "nunit test user " + randomNumber)); + attributes.Add(ConnectorAttributeBuilder.Build( + "mail", "nunitUser" + randomNumber + "@some.com")); + attributes.Add(ConnectorAttributeBuilder.Build( + "telephoneNumber", "333-547-8453")); + // attributes.Add(ConnectorAttributeBuilder.Build( + // "msExchHomeServerName", "")); + attributes.Add(ConnectorAttributeBuilder.Build( + "employeeID", "1234567")); + attributes.Add(ConnectorAttributeBuilder.Build( + "division", "Identity Services")); + attributes.Add(ConnectorAttributeBuilder.Build( + "mobile", "554-210-8631")); + // attributes.Add(ConnectorAttributeBuilder.Build( + // "mDBOverQuotaLimit", "")); + attributes.Add(ConnectorAttributeBuilder.Build( + "middleName", "testCase")); + attributes.Add(ConnectorAttributeBuilder.Build( + "description", "This user was created as a test case for the AD Connector")); + // attributes.Add(ConnectorAttributeBuilder.Build( + // "mDBOverHardQuotaLimit", "")); + // attributes.Add(ConnectorAttributeBuilder.Build( + // "mDBUseDefaults", "")); + attributes.Add(ConnectorAttributeBuilder.Build( + "department", "Connector Affairs")); + // for manager, it looks like the manager has to exist + // attributes.Add(ConnectorAttributeBuilder.Build( + // "manager", "Some Guy")); + // attributes.Add(ConnectorAttributeBuilder.Build( + // "mDBStorageQuota", "")); + // attributes.Add(ConnectorAttributeBuilder.Build( + // "mailNickName", "")); + attributes.Add(ConnectorAttributeBuilder.Build( + "title", "Manager")); + attributes.Add(ConnectorAttributeBuilder.Build( + "initials", "XYZ")); + // attributes.Add(ConnectorAttributeBuilder.Build( + // "homeMTA", "")); + attributes.Add(ConnectorAttributeBuilder.Build( + "co", "United States")); + // attributes.Add(ConnectorAttributeBuilder.Build( + // "homeMDB", "")); + attributes.Add(ConnectorAttributeBuilder.Build( + "company", "NUnit Test Company")); + attributes.Add(ConnectorAttributeBuilder.Build( + "facsimileTelephoneNumber", "111-222-3333")); + attributes.Add(ConnectorAttributeBuilder.Build( + "homePhone", "222-333-4444")); + // attributes.Add(ConnectorAttributeBuilder.Build( + // "directoryEntryWS_PasswordExpired", "")); + attributes.Add(ConnectorAttributeBuilder.Build( + "streetAddress", "12345 Some Street")); + attributes.Add(ConnectorAttributeBuilder.Build( + "l", "Austin")); + attributes.Add(ConnectorAttributeBuilder.Build( + "st", "Texas")); + attributes.Add(ConnectorAttributeBuilder.Build( + "postalCode", "78717")); + // attributes.Add(ConnectorAttributeBuilder.Build( + // "AccountLocked", "")); + + // used to be 'Terminal Services Initial Program' + attributes.Add(ConnectorAttributeBuilder.Build( + TerminalServicesUtils.TS_INITIAL_PROGRAM, "myprog.exe")); + + // used to be 'Terminal Services Initial Program Directory' + attributes.Add(ConnectorAttributeBuilder.Build( + TerminalServicesUtils.TS_INITIAL_PROGRAM_DIR, "c:\\nunittest\\dir")); + + // unknown ... + // attributes.Add(ConnectorAttributeBuilder.Build( + // "Terminal Services Inherit Initial Program", true)); + + // used to be 'Terminal Services Allow Logon' - defaults to false, so testing true + attributes.Add(ConnectorAttributeBuilder.Build( + TerminalServicesUtils.TS_ALLOW_LOGON, 1)); + + // used to be 'Terminal Services Active Session Timeout' + attributes.Add(ConnectorAttributeBuilder.Build( + TerminalServicesUtils.TS_MAX_CONNECTION_TIME, 10000)); + + // used to be 'Terminal Services Disconnected Session Timeout' + attributes.Add(ConnectorAttributeBuilder.Build( + TerminalServicesUtils.TS_MAX_DISCONNECTION_TIME, 20000)); + + // used to be 'Terminal Services Idle Timeout' + attributes.Add(ConnectorAttributeBuilder.Build( + TerminalServicesUtils.TS_MAX_IDLE_TIME, 30000)); + + // used to be 'Terminal Services Connect Client Drives At Logon' + attributes.Add(ConnectorAttributeBuilder.Build( + TerminalServicesUtils.TS_CONNECT_CLIENT_DRIVES_AT_LOGON, 1)); + + // used to be 'Terminal Services Connect Client Printers At Logon' + attributes.Add(ConnectorAttributeBuilder.Build( + TerminalServicesUtils.TS_CONNECT_CLIENT_PRINTERS_AT_LOGON, 1)); + + // used to be 'Terminal Services Default To Main Client Printer' + attributes.Add(ConnectorAttributeBuilder.Build( + TerminalServicesUtils.TS_DEFAULT_TO_MAIN_PRINTER, 1)); + + // used to be 'Terminal Services End Session On Timeout Or Broken Connection' + attributes.Add(ConnectorAttributeBuilder.Build( + TerminalServicesUtils.TS_BROKEN_CONNECTION_ACTION, 1)); + + // used to be 'Terminal Services Allow Reconnect From Originating Client Only' + attributes.Add(ConnectorAttributeBuilder.Build( + TerminalServicesUtils.TS_RECONNECTION_ACTION, 1)); + + // attributes.Add(ConnectorAttributeBuilder.Build( + // "Terminal Services Callback Settings", "")); + + // attributes.Add(ConnectorAttributeBuilder.Build( + // "Terminal Services Callback Phone Number", "")); + + // used to be 'Terminal Services Remote Control Settings' + attributes.Add(ConnectorAttributeBuilder.Build( + TerminalServicesUtils.TS_ENABLE_REMOTE_CONTROL, 1)); + + // used to be 'Terminal Services User Profile + attributes.Add(ConnectorAttributeBuilder.Build( + TerminalServicesUtils.TS_PROFILE_PATH, "\\My Profile")); + + // used to be 'Terminal Services Local Home Directory + attributes.Add(ConnectorAttributeBuilder.Build( + TerminalServicesUtils.TS_HOME_DIRECTORY, "\\My Home Dir")); + + // used to be 'Terminal Services Home Directory Drive + attributes.Add(ConnectorAttributeBuilder.Build( + TerminalServicesUtils.TS_HOME_DRIVE, "C:")); + + // uSNChanged should be read only + // attributes.Add(ConnectorAttributeBuilder.Build( + // "uSNChanged", "")); + // objectGUID should be read only + // attributes.Add(ConnectorAttributeBuilder.Build( + // "objectGUID", "")); + + + // now set name operational attribute + attributes.Add(ConnectorAttributeBuilder.Build( + Name.NAME, "cn=nunit" + randomNumber + "," + + GetProperty(CONFIG_PROPERTY_CONTAINER))); + + + /* + // a few attributes not used in IDM + + // country code is not returned by default + attributes.Add(ConnectorAttributeBuilder.Build( + "countryCode", 23)); + + */ + return attributes; + } + + public ICollection GetNormalAttributes_Group() + { + ICollection attributes = new List(); + int randomNumber = GetRandomNumber(); + + attributes.Add(ConnectorAttributeBuilder.Build( + "mail", "groupmail@example.com")); + attributes.Add(ConnectorAttributeBuilder.Build( + "description", "Original Description" + randomNumber)); + attributes.Add(ConnectorAttributeBuilder.Build( + Name.NAME, "cn=nunitGroup" + randomNumber + "," + + GetProperty(CONFIG_PROPERTY_CONTAINER))); + attributes.Add(ConnectorAttributeBuilder.Build( + "groupType", 4)); + return attributes; + } + + public ICollection GetNormalAttributes_OrganizationalUnit() + { + ICollection attributes = new List(); + int randomNumber = GetRandomNumber(); + + attributes.Add(ConnectorAttributeBuilder.Build( + Name.NAME, "ou=nunitOU" + randomNumber + "," + + GetProperty(CONFIG_PROPERTY_CONTAINER))); + return attributes; + } + + private static void VerifyObject(ICollection requestedAttributes, + ConnectorObject returnedObject) + { + + // for now, skipping values that are very difficult to + // determine equality ... or they are not returned like + // 'userPassword'. + ICollection skipAttributeNames = new List(); + skipAttributeNames.Add("USERPASSWORD"); + skipAttributeNames.Add(OperationalAttributes.PASSWORD_NAME); + skipAttributeNames.Add(OperationalAttributes.CURRENT_PASSWORD_NAME); + skipAttributeNames.Add(Uid.NAME); + // have to ignore the password expire attribute. It will not come + // back EXACTLY the same as it was set. It seems like may ad rounds + // off to the nearest second, or minute, or something. + skipAttributeNames.Add(OperationalAttributes.PASSWORD_EXPIRATION_DATE_NAME); + + ICollection ldapStringAttributes = new List(); + ldapStringAttributes.Add("AD_CONTAINER"); + ldapStringAttributes.Add(Name.NAME); + ldapStringAttributes.Add(PredefinedAttributes.GROUPS_NAME); + ldapStringAttributes.Add(PredefinedAttributes.ACCOUNTS_NAME); + + // for each attribute in the connector object ... + foreach (ConnectorAttribute attribute in requestedAttributes) + { + ConnectorAttribute returnedAttribute = returnedObject.GetAttributeByName( + attribute.Name); + + if (skipAttributeNames.Contains(attribute.Name.ToUpper())) + { + Trace.TraceWarning("Skipping comparison of attribute {0}", + attribute.Name); + Trace.TraceWarning("requested values were:"); + foreach (Object requestedValueObject in attribute.Value) + { + Trace.TraceWarning(requestedValueObject.ToString()); + } + if (returnedAttribute == null) + { + Trace.TraceWarning(" no {0} attribute was returned", + attribute.Name); + } + else + { + Trace.TraceWarning("returned values were:"); + foreach (Object returnedValueObject in returnedAttribute.Value) + { + Trace.TraceWarning(returnedValueObject.ToString()); + } + } + continue; + } + + Assert.IsNotNull(returnedAttribute); + + // for each value in the attribute ... + foreach (Object requestedValueObject in attribute.Value) + { + // order of multivalue attributes is not gauranted, so check + // all values of the returned object against the value + // also attributes like the ldap 'objectclass' might return + // more values than I set ... (set User, return user, top, inetorgperson) + Boolean foundValue = false; + foreach (Object returnedValueObject in returnedAttribute.Value) + { + Object lhs = requestedValueObject; + Object rhs = returnedValueObject; + + // if its an ldap string, put it in a standard form + if (ldapStringAttributes.Contains(attribute.Name.ToUpper())) + { + Assert.That(requestedValueObject is String); + Assert.That(returnedValueObject is String); + lhs = ActiveDirectoryUtils.NormalizeLdapString((String)requestedValueObject); + rhs = ActiveDirectoryUtils.NormalizeLdapString((String)returnedValueObject); + /* + // if either of them start with a server name, take it off + // it's not important to the comparison + string []lhsParts = ((string)lhs).Split('/'); + Assert.LessOrEqual(lhsParts.Length, 2); + lhs = (lhsParts.Length) == 1 ? lhsParts[0] : lhsParts[1]; + string[] rhsParts = ((string)rhs).Split('/'); + Assert.LessOrEqual(rhsParts.Length, 2); + lhs = (rhsParts.Length) == 1 ? rhsParts[0] : rhsParts[1]; + */ + } + + if (lhs.Equals(rhs)) + { + foundValue = true; + break; + } + } + Assert.IsTrue(foundValue, + String.Format("Could not find value {0} for attribute named {1}", + requestedValueObject, attribute.Name)); + } + } + } + + // this needs to be replaced by the real one. + public string GetProperty(string propertyName) + { + string propertyValue = TestHelpers.GetProperty(propertyName, null); + //Trace.WriteLine(String.Format("GetProperty: {0} = {1}", propertyName, propertyValue)); + return propertyValue; + } + + public ConnectorObject GetConnectorObjectFromUid( + ActiveDirectoryConnector connector, ObjectClass oclass, Uid uid) + { + return GetConnectorObjectFromUid(connector, oclass, uid, null); + } + + public ConnectorObject GetConnectorObjectFromUid( + ActiveDirectoryConnector connector, ObjectClass oclass, Uid uid, + OperationOptions options) + { + // get sid to check permissions + Filter uidFilter = FilterBuilder.EqualTo(uid); + ICollection objects = TestHelpers.SearchToList(connector, + oclass, uidFilter, options); + Assert.AreEqual(1, objects.Count); + return objects.ElementAt(0); + } + + public ICollection GetDefaultAttributesToGet(ObjectClass oclass) + { + ICollection attributesToGet = new HashSet(); + + ActiveDirectoryConnector connector = new ActiveDirectoryConnector(); + connector.Init(GetConfiguration()); + Schema schema = connector.Schema(); + ObjectClassInfo ocInfo = schema.FindObjectClassInfo(oclass.GetObjectClassValue()); + Assert.IsNotNull(ocInfo); + + foreach (ConnectorAttributeInfo caInfo in ocInfo.ConnectorAttributeInfos) + { + if (caInfo.IsReturnedByDefault) + { + attributesToGet.Add(caInfo.Name); + } + } + + return attributesToGet; + } + + public GuardedString GetGuardedString(string regularString) + { + GuardedString guardedString = new GuardedString(); + foreach (char c in regularString) + { + guardedString.AppendChar(c); + } + return guardedString; + } + + int GetRandomNumber() + { + const int randomRange = 10000000; + + int number = 0; + + // having trouble with duplicate random numbers, so try a few hundred + // times to get a unique one before giving up. + for(int i=0;i<500;i++) { + number = _rand.Next(randomRange); +#if DEBUG + // make sure the debug numbers are in a different + // range than release ones to eliminate conflicts during + // the build where both configurations are run concurrently + number += randomRange; +#endif + if(!(randomList.Contains(number))) { + randomList.Add(number); + break; + } + } + + return number; + } + + public void RemoveAttributeByName(ICollection accountAttributes, string name) + { + ConnectorAttribute ca = ConnectorAttributeUtil.Find(name, accountAttributes); + if (ca != null) + { + accountAttributes.Remove(ca); + } + } + } + +} diff --git a/DotNetConnectors.sln/ActiveDirectoryConnectorTests/ActiveDirectoryConnectorTests.csproj b/DotNetConnectors.sln/ActiveDirectoryConnectorTests/ActiveDirectoryConnectorTests.csproj new file mode 100644 index 00000000..29bfa34e --- /dev/null +++ b/DotNetConnectors.sln/ActiveDirectoryConnectorTests/ActiveDirectoryConnectorTests.csproj @@ -0,0 +1,140 @@ + + + + Debug + AnyCPU + 9.0.21022 + 2.0 + {DB8F7DA9-11A2-4BEB-8C14-25D8229EBE7E} + Library + Properties + Sun.OpenConnectors.ActiveDirectory + ActiveDirectoryConnectorTests + v3.5 + 512 + + + + + true + full + false + bin\Debug\ + DEBUG;TRACE + prompt + 4 + + + pdbonly + true + bin\Release\ + TRACE + prompt + 4 + + + Project + + + + + + + + + + + + + + + + + + + + + + + + + 3.5 + + + + 3.5 + + + 3.5 + + + + + + + + + + + {BDF495CA-0FCD-4E51-A871-D467CDE3B43E} + ActiveDirectoryConnector + + + {F140E8DA-52B4-4159-992A-9DA10EA8EEFB} + Common + + + {8B24461B-456A-4032-89A1-CD418F7B5B62} + Framework + + + {5B011775-B121-4EEE-A410-BA2D2F5BFB8B} + FrameworkInternal + + + {4700690A-2D29-40A0-86AC-E5A9F71A479A} + ShellScriptExecutorFactory + + + + + Always + + + + + + + + + + + + + + + + + + diff --git a/DotNetConnectors.sln/ActiveDirectoryConnectorTests/Properties/AssemblyInfo.cs b/DotNetConnectors.sln/ActiveDirectoryConnectorTests/Properties/AssemblyInfo.cs new file mode 100644 index 00000000..5b458efe --- /dev/null +++ b/DotNetConnectors.sln/ActiveDirectoryConnectorTests/Properties/AssemblyInfo.cs @@ -0,0 +1,58 @@ +/* + * ==================== + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER. + * + * Copyright 2007-2008 Sun Microsystems, Inc. All rights reserved. + * + * The contents of this file are subject to the terms of the Common Development + * and Distribution License("CDDL") (the "License"). You may not use this file + * except in compliance with the License. + * + * You can obtain a copy of the License at + * http://IdentityConnectors.dev.java.net/legal/license.txt + * See the License for the specific language governing permissions and limitations + * under the License. + * + * When distributing the Covered Code, include this CDDL Header Notice in each file + * and include the License file at identityconnectors/legal/license.txt. + * If applicable, add the following below this CDDL Header, with the fields + * enclosed by brackets [] replaced by your own identifying information: + * "Portions Copyrighted [year] [name of copyright owner]" + * ==================== + */ +using System.Reflection; +using System.Runtime.CompilerServices; +using System.Runtime.InteropServices; + +// General Information about an assembly is controlled through the following +// set of attributes. Change these attribute values to modify the information +// associated with an assembly. +[assembly: AssemblyTitle("ActiveDirectoryConnectorTests")] +[assembly: AssemblyDescription("")] +[assembly: AssemblyConfiguration("")] +[assembly: AssemblyCompany("Sun Microsystems")] +[assembly: AssemblyProduct("ActiveDirectoryConnectorTests")] +[assembly: AssemblyCopyright("Copyright Sun Microsystems 2008")] +[assembly: AssemblyTrademark("")] +[assembly: AssemblyCulture("")] + +// Setting ComVisible to false makes the types in this assembly not visible +// to COM components. If you need to access a type in this assembly from +// COM, set the ComVisible attribute to true on that type. +[assembly: ComVisible(false)] + +// The following GUID is for the ID of the typelib if this project is exposed to COM +[assembly: Guid("bdb18bb6-ec8d-4a9b-a56b-7c600534f7f5")] + +// Version information for an assembly consists of the following four values: +// +// Major Version +// Minor Version +// Build Number +// Revision +// +// You can specify all the values or you can default the Build and Revision Numbers +// by using the '*' as shown below: +// [assembly: AssemblyVersion("1.0.*")] +[assembly: AssemblyVersion("1.0.0.0")] +[assembly: AssemblyFileVersion("1.0.0.0")] diff --git a/DotNetConnectors.sln/ActiveDirectoryConnectorTests/TestParams.resx b/DotNetConnectors.sln/ActiveDirectoryConnectorTests/TestParams.resx new file mode 100644 index 00000000..735169b0 --- /dev/null +++ b/DotNetConnectors.sln/ActiveDirectoryConnectorTests/TestParams.resx @@ -0,0 +1,147 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + text/microsoft-resx + + + 2.0 + + + System.Resources.ResXResourceReader, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + System.Resources.ResXResourceWriter, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + cn=users,dc=dv207518domain2,dc=central,dc=sun,dc=com + + + localhost + + + etegrity + + + 389 + + + etegrity + + + etegrity + + + dv207518domain2\administrator + + + administrator + + + dv207518domain2\administrator + + \ No newline at end of file diff --git a/DotNetConnectors.sln/ActiveDirectoryConnectorTests/project.xml b/DotNetConnectors.sln/ActiveDirectoryConnectorTests/project.xml new file mode 100644 index 00000000..748bfeaf --- /dev/null +++ b/DotNetConnectors.sln/ActiveDirectoryConnectorTests/project.xml @@ -0,0 +1,14 @@ + + + + + + + + + + + + + + diff --git a/DotNetConnectors.sln/BooScriptExecutorFactory/AssemblyInfo.cs b/DotNetConnectors.sln/BooScriptExecutorFactory/AssemblyInfo.cs new file mode 100644 index 00000000..95eb3457 --- /dev/null +++ b/DotNetConnectors.sln/BooScriptExecutorFactory/AssemblyInfo.cs @@ -0,0 +1,53 @@ +/* + * ==================== + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER. + * + * Copyright 2007-2008 Sun Microsystems, Inc. All rights reserved. + * + * The contents of this file are subject to the terms of the Common Development + * and Distribution License("CDDL") (the "License"). You may not use this file + * except in compliance with the License. + * + * You can obtain a copy of the License at + * http://IdentityConnectors.dev.java.net/legal/license.txt + * See the License for the specific language governing permissions and limitations + * under the License. + * + * When distributing the Covered Code, include this CDDL Header Notice in each file + * and include the License file at identityconnectors/legal/license.txt. + * If applicable, add the following below this CDDL Header, with the fields + * enclosed by brackets [] replaced by your own identifying information: + * "Portions Copyrighted [year] [name of copyright owner]" + * ==================== + */ +#region Using directives + +using System; +using System.Reflection; +using System.Runtime.InteropServices; + +#endregion + +// General Information about an assembly is controlled through the following +// set of attributes. Change these attribute values to modify the information +// associated with an assembly. +[assembly: AssemblyTitle("BooScriptExecutorFactory")] +[assembly: AssemblyDescription("")] +[assembly: AssemblyConfiguration("")] +[assembly: AssemblyCompany("")] +[assembly: AssemblyProduct("BooScriptExecutorFactory")] +[assembly: AssemblyCopyright("")] +[assembly: AssemblyTrademark("")] +[assembly: AssemblyCulture("")] + +// This sets the default COM visibility of types in the assembly to invisible. +// If you need to expose a type to COM, use [ComVisible(true)] on that type. +[assembly: ComVisible(false)] + +// The assembly version has following format : +// +// Major.Minor.Build.Revision +// +// You can specify all the values or you can use the default the Revision and +// Build Numbers by using the '*' as shown below: +[assembly: AssemblyVersion("1.0.*")] diff --git a/DotNetConnectors.sln/BooScriptExecutorFactory/BooScriptExecutorFactory.cs b/DotNetConnectors.sln/BooScriptExecutorFactory/BooScriptExecutorFactory.cs new file mode 100644 index 00000000..ff822ba7 --- /dev/null +++ b/DotNetConnectors.sln/BooScriptExecutorFactory/BooScriptExecutorFactory.cs @@ -0,0 +1,81 @@ +/* + * ==================== + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER. + * + * Copyright 2007-2008 Sun Microsystems, Inc. All rights reserved. + * + * The contents of this file are subject to the terms of the Common Development + * and Distribution License("CDDL") (the "License"). You may not use this file + * except in compliance with the License. + * + * You can obtain a copy of the License at + * http://IdentityConnectors.dev.java.net/legal/license.txt + * See the License for the specific language governing permissions and limitations + * under the License. + * + * When distributing the Covered Code, include this CDDL Header Notice in each file + * and include the License file at identityconnectors/legal/license.txt. + * If applicable, add the following below this CDDL Header, with the fields + * enclosed by brackets [] replaced by your own identifying information: + * "Portions Copyrighted [year] [name of copyright owner]" + * ==================== + */ +using System; +using System.Reflection; +using System.Collections; +using System.Collections.Generic; +using Boo.Lang.Interpreter; +using Boo.Lang.Compiler; + +namespace Org.IdentityConnectors.Common.Script.Boo +{ + [ScriptExecutorFactoryClass("Boo")] + public class BooScriptExecutorFactory : ScriptExecutorFactory + { + /// + /// Attempt to trigger an exception if the runtime is not present. + /// + public BooScriptExecutorFactory() { + new BooScriptExecutor(new Assembly[0], "1").Execute(null); + } + + /// + /// Creates a script executor give the Boo script. + /// + override + public ScriptExecutor NewScriptExecutor(Assembly [] referencedAssemblies, string script, bool compile) { + return new BooScriptExecutor(referencedAssemblies,script); + } + + /// + /// Processes the script. + /// + class BooScriptExecutor : ScriptExecutor { + private readonly Assembly [] _referencedAssemblies; + private readonly string _script; + private readonly InteractiveInterpreter _engine; + + public BooScriptExecutor(Assembly [] referencedAssemblies, string script) { + _referencedAssemblies = referencedAssemblies; + _script = script; + _engine = new InteractiveInterpreter(); + _engine.RememberLastValue = true; + foreach (Assembly assembly in referencedAssemblies) { + _engine.References.Add(assembly); + } + } + public object Execute(IDictionary arguments) { + // add all the globals + IDictionary args = CollectionUtil.NullAsEmpty(arguments); + foreach (KeyValuePair entry in args) { + _engine.SetValue(entry.Key, entry.Value); + } + CompilerContext context = _engine.Eval(_script); + if ( context.Errors.Count > 0 ) { + throw context.Errors[0]; + } + return _engine.LastValue; + } + } + } +} diff --git a/DotNetConnectors.sln/BooScriptExecutorFactory/BooScriptExecutorFactory.csproj b/DotNetConnectors.sln/BooScriptExecutorFactory/BooScriptExecutorFactory.csproj new file mode 100644 index 00000000..469a1f3b --- /dev/null +++ b/DotNetConnectors.sln/BooScriptExecutorFactory/BooScriptExecutorFactory.csproj @@ -0,0 +1,95 @@ + + + + {0747C440-70E4-4E63-9F9D-03B3A010C991} + Debug + AnyCPU + Library + Org.IdentityConnectors.Common.Script.Boo + Boo.ScriptExecutorFactory + v3.5 + + + prompt + 4 + AnyCPU + bin\Debug\ + True + Full + False + True + DEBUG;TRACE + + + pdbonly + bin\Release\ + TRACE + prompt + 4 + AnyCPU + False + True + False + + + + + lib\Boo.Lang.dll + + + lib\Boo.Lang.Compiler.dll + + + lib\Boo.Lang.Interpreter.dll + + + lib\Boo.Lang.Parser.dll + + + lib\Boo.Lang.Useful.dll + + + + 3.5 + + + + 3.5 + + + + + + + + + {F140E8DA-52B4-4159-992A-9DA10EA8EEFB} + Common + + + + + + + diff --git a/DotNetConnectors.sln/BooScriptExecutorFactory/lib/Boo.Lang.Compiler.dll b/DotNetConnectors.sln/BooScriptExecutorFactory/lib/Boo.Lang.Compiler.dll new file mode 100644 index 0000000000000000000000000000000000000000..7a4bc09ba3dce42a0b0abf3030063f29df749338 GIT binary patch literal 729088 zcmeEv37j2Om42n(>(||{v(V{~Kmw%05{m8+b_ih$JA@r%a|2{RL7|{KElT?kb;Jc^ z5z!F`1sz4l4Mk*J7-#XN^kaqL%@ zJnKalj1ONjmcMZ9oQsCfJ?G+!^EJaSIB$4t;^N^8E*?JYgj0qu%KzfL<+Elrw|A&d zJUmH`ol#33Te$mCp4wx{>?H#;`jX_w%_JGtyI1rK=VHeL{J~lpfE^Y~L?7P5F2JydD5Xkbe^Innzkh~_~ zS>QG+!5tD?ZThuI0-GeTNdlWBut@@&B(O;Wn|%iVZ6aX3k?jKz+>Ui#ec+%oWrM^@i7aKKfsylKVX-23r+4jK9O z&7b}GtndHD!#&F`fAH-eduz|ZmwfD&r;dNgfByFIC6`YmkImcf!&}{Y_5(8?JZku{ z!DV+G_MD&0`uo>^`RWt%^ClV>-2CA;ZhqBgHK&>>Loo7yt1cvXxCb@4BX8~lEEasX1gR=v~2S^Nlkt| zJ)h-#Ns=^1`}Nkmr)M-rlg0?!;l69PtUs+O*qqw3fm*&}nl$D=J!5pn0s#ixqFvME z{rH;-9biHAtp1(n)|U0vG7ujH?93If14J!U_NP)ab9B6x&rOnMGfCy|PhA5G0M^0Ww{GrW9r#hzhr64jW%3<|LKy;<(fWd! zqgX*0nSr%~G&v;Mua$s&k_@2_K;-%?tO#PXlx}_`z2Y&fthCUby88(}>S;EgbP`jW zKz?a*pu=SsX~MymnBBf|3x z3@oH7a?giRw+2k5eq4rjogr%=K#cL9WHYH+3alQ9hrx0WV6sK?l7_&H047_AQZgd4 zSqeH>Kq4Oep`0O^f!>|#^O~b`^jpBi8x zdUzN1yC&<0u$#f8w!_oCOVDooNp!lIHtHE<+!z>5dc_ZMoe^FcaP{#sX9z^Tm}DCH zvr!Z{(Pv6taL-5}bM3by==-N*k?nxyp?|UCi%it9Mwnk_ul@+%Y<_&6Y`<^K&-O|pRU_DR~v zPlmkKtb%_S=xt=%qi3jSCbi!DM}PzxP<*AMNZeI4g{;Wpjc72((S5bj9jXu3cYYZt zzmwxc%5n#z<7i{N18u1i>hA(Hkk#^A@nhz^zSyi!Sw=WcXZ809%f4BEew#J})35PN zSM#iLdNxSFtw6tP`D=rmRC`VtKJchPlW0_CWq@QFcBP$63K7$AYBmOqXkq9xkI z>4-K_9q~>6RhyvSHz=#sGFAQfHjpC`5FT$-9;s_9{|Wa?LcNo6fY%lY-^GF-zPnj) z5`Hn9RFxTEizRf=>vM1Z5yY@{9LWy4p)$8L-@frQZ{ARvf4%WEZ`n|q@7j2pw@!s7{T-5M z(dTz7O?WQxchNu8@=sR=33}Z2An}`WOu6(?b8fN?+^=zPHVuYWW`C3y3wW zFJA2FJ;7ey6ZmG%7eMVRY-8`>4c<2u{ujd-Foh8Q-|zVE9qe7%#Ql;{e1z8KBFG*1 ztyT~4onvQqFftutmAJK!sm0dvkK>0Lx@+b-qs?6K3(fBI%|3x2v*z_9X0mRlB<lKlNJBf`|BDOE7GS>3@@I%GsnpKG{H2bn|c0Yco z*jzIwHkGy$T4?qa-|VaSF;t&7SclA5OPSS;zXzbnRgkeMi`a+Atp;cyujO_8n0j34(Prqv9j*&q{rXId zJEnrd|HsX^|FSghz_|VdrA;FWJqu%0m88`2;~@Jg^(cf-hoe6~K8(}}_?3YWL+~m1 z-C@whP$~!gSpn6ZDzR>MV&!$$|8;qNa>L|>Gdi3g%pNnWD@X?aI15+=kizE`b#;FfFZ+U36v6w& zLSHWFt?COClJ<1!Qa}O?Soxzv~hGWv=q*>$|B=bq-*0?pM{LK{GH zTgWM!<+S1f&p@|6qn}*Yl0HAE^u;g1r7`A)j<*Fg+jXguP#uQp_0amN7`BrgrVzv4 zpf*y=--sU_!d3$tXmk^*m*l;G`a&%6M%C=YUl|;r|gcUccF>@%jKPyK20ubB@0ae&B=nmCe?i zrlPTFCdAjDe?;-r@{h8q%j>BXZ0hoQ*VJY8L1pgHrjYzMRchVFoH~wG zQIXld7@0X8e-sL&=_t|1*$%6jl7eA_GMokT_C@}n(Z}xBaVZ~(farU)Z z$G@ko(cN!5CF$1Re(1=s>+hqkznD#}{8_t>aEcZCThMVB^`Iqs-_m{O@`pasaf86g|e}%0Z^9H#v>sz~(*&r=l7?x^n{Z(xZX{~e8>J;lW zHc~&9IC(u*){o!Q2YwGKI(7Yc!rQ|4X$#f!wb0|zeC^3#4v$YkKb{5s*w*#;-&}wD z2K!bXg!=`bkDHzJgH={PAnZdR_6+OmoPZpL-`aVHqe@vEj{f{`9k5z{1b+C=v$l(} zuv&-vNZwsy+mTX#^F*!pPSdNo`3&KvB7s`=KvI#CXS@70NNY<-NjM(@9;g70RR zM;f_*-^hLUjgWgS$5z}1h#JT8;h-?eDSm9QsB6A;ovk-i)IGj+gRM7I)Va1^`y5li#GHk#%Sr$m!E_IA#ef21@wHpl)!hy0}w z*jx_OpZ`b5Uw-Tv?q&m)-=9nmze7+A^))VMtMWC0-+4iee!8Lj{(O4){n`e}_kkea zUrdEx=C@uSeiixEbvUE^q9h$+e?@-#69gFV+kb}NQNyyJe z%o!KKM^ByKt%67YZFhd-+UJ#!IrgR{zkEh{#(p063TFbDq?Wqz=YkOp$tRY6<8PLd zt>v>lx7x($@<4BKuW(M_6e|Y6DQ6iPXUhgC-}e9!Q-h=u<}}l zQ4)*cshQCrHC_G@JkUjWu@z!mwwXo(i3>0Z+-;IGBsYl8PuxbD0cVFSAwP= zqdukS#^}c=O>K;x5=vn6C-@;6wcauMsWw5-LQ=lIG5P{{#A?3i(Ez}?MhM+siS=|w z?3I`V2%~MbYlljzWU%)06OR4yqm%l3m5R*{@XZdy58ZB{`N31T?FOahwi|+{I7pkJ zpI8{~+|Ot;RtK=>;`EU^aMYCb@#vtBm=%*K#MCYJaIMAb-P}NPHE6!V+i4*uu!8kA zfFo46%{8zq@GI=04|xWv>7(D>|AFr`u`(GB{81)Lr)(EH2D@0|S?VeiOLMyd&F4>< z=2?N}_EVyHB?RbgajDXDwz!9y(;)kDj zu-44+XmHgFAcuwC@4Wu8L%%f!!b*41zWZ}azk{C&feXw3ps42|JH<_l3-dJkDUa*3 zbO1i??XiKl_A27K48_3L-c!IuUzWM?U7@((qU5YZ#8t~*jjD1$#X@CJOz+rUBSh)g z_I2dD`i*aZJ{HI%R_&+sIdUtpPeT2Aws)w!Y1R-p!antpDv4Fk2~PF7l&M=ocs_n> z;Bq*OeT2ZpD#3A<;;7|kS>k4tLGMh;FkE6;b@+S`EV!ZuDZ%O1#$z?Rjk4Iyh`R2 zH+v4e6RwCk(RaT?xq$z~k18S4e+J(deD^Kd4AMGkL-}3Z#_zQq_RXx!BEQ%9eY5NF zW9t0gpv}PVF;n4}IfUOQJx=+>urY7>f|oAJ-S=w07({fuC)r_4k&4c1!Clu!@T;c3 zMv_{pN31cKRw z_vNPuGgwAW=FI}S{*f8jEaU0xo%TxVaI~rX-Zzvy*GI2ae}Ysq_od%5o)Ckk90@0&l=SaA(%<~M6O zW)jT-JUw!8(%bhQED0={mG;Y1G_t1oUfvn1>z%>6K3=0(RwR9mGr1k(?|SruNn^no zO`Z>03fz$IEFKnu2Zd8a5f23{IAdmC@3j{^T^Dw)S}@cYO$Hky2f7h+Po9jtAkEGY z!D1x(Fd}Ka)sH`VK3vcB?;?Gl1X_Qtzm^;d5?CsQ9Z#}VE&q3{)}j)nw$%`-3Ygq)G~z7W;TM0j(Yx%{4fGHA5cp2#*-}Cq7gkk*m1=j^9Q%qR&mGd_KV4 zzi|8)2x3uBbu65Wp6gYZ&q9EfmmPlM>4Tdw85>{4xf{`rq?{U|i+DT+QpXSZt@AVT zfn!JL12tPuABaq-x4z^L+H8!`S4YxtEL$QJS$4 z3ez<<+UXYis^fHse=-MFA`*b`&J&eY;hpb(2f6HR54 zT%-qofN`0pe902o4p43pM-F$~oPbWIttg{L4krwUqaz7E>Dx!~SN)XQpBvYJ6$#^P z=svOJ@N;O+e%6@JC;N#!r7k?LHZx7fe~yWZ(A9Rqc3}hFClVim9kB1fz{sK{ImeMq z9m@%g^kSoDdApwNFTAMz!JBr_vty#3jYU19pF4oyQNvl%k!HtQQS^;`8;+{iaO(FF=--CvcOd6f zU#MYBf!FzaXlrti>OL#0^qcpMezR!tQongGbqtGszuf5e8cKuM?ozY5rN9tj2+Zx|1Zeme zQjXN9Ics#dKSzGhZ>*c!56hO{z5U@4YHyGF z%=B=w>xuE1=?lTf;mXH$RP5fxd!2cB4L4C@a>j=!lHSE9FHc8Tq-!qbSiM=gwRgG( zI+|uTN-0a%9M94XQd*dM*MPuOJ$KdM^4j>_N%!SQX1fp4#*Ru>cp$F zHqV#ZXY>-%M)=ZmwBT(p*-|kv?HHkf<3hgA1>BLWuT9Xw$fO@igY<&AG~&T*m~KO^ zd{EUn`vo(CvqwCbBOttH7$FC8dIa%SNt-+i^x$34N8~22hg?j~ca&07e~u(w{5iBL z&E5q-X6#FqSP|kVLfqycY)*eKXrU7RUV_3-5Vz*^($i_OYq&a!D0*y`bS%hvE6UG% zY_y=PVhfhE1-sIU;`A+C7Q^wvSQmVJAn!c5P*)Eg=(s8&7q5(HY;) zqNPaR&U?|fd%Q=>7)wX1@RApUmqp7i@$z28i=5~t4lx4LA^P%zTJ|^Y#_`al1v*8_ z-skQe;ehJl&c>yj5z`x++xKqiUn+yw<5*XzUPM9D4&4S$jG5Ch9b@e0|XWwD7`) zaadThh4dYpNn_z|t%by=nc7N?OYoI4h8}VrbOC07VHy~N(~3{jOZ)mba%_)XY!(hC zS*`LV&03D+wE14B_aYJXX%L>=uV2G^InqVl-{ZSA)}rk(Xq!&1!tH@x@voS^ifB{( z9j%W(ceH24E%=#k-Xaiwq?3rbO~INUf?useaY+|Q_2{0cW;us$DjO|ClP2!+PYWKs znQkGV3!3Yml zFMd&94@y0RnTwp#N%A1FSvU?8YhoRUvN#p>#|Bw68w<`noDv`!CyDxKI=a`o-Pg+< z-%en;`wrD8(HOTlNtbY1&9R@s^*HV)LjL4Q21poNsmg%)!7AL>`4xo2cRCEtABQ#J zeFABVqoB@!xR^Waf_gXJIdsCmf zCOsucO<;9Sg*83TgJKg--bCeFvy2)Dg3UEMUAbmUz#7ah}i$;#vPwYBugbHV2+Q3hDPWRLv&DN7xtp-ws2NJkN0uBBX}#Q*hyF@Ie#}e5;T?g(@%yWq%`~EV}#^xn-ECUP848NOeGOANUnhjW);H5uX zVd;~;NLZMOW!8KcQgPaVIyxQMc6p%#0y;r87I(ZZ}aG zu@mO`XGq_rtD;qu31};QcnpRKD}sU1MMXB!NrJffS77Gb-I!5cHcxgs(DRZe7l7uO zMhEDVld%sN9l!|DnrF?&DD2%oSbq z8uM%AHyyhN!m4G@Wqa<%;_txdU|_zHJx^-F5Ixa_*ZIlfy_8qx)vo{zumu~Dm*hCc zgb+;gIJ`F{dp>Xrzgp?vGqWt2r`_^5u)`9@2Bw9#=$s{7VO_n-S}{_$(RcKYKxS4z zZtR=@&jyaAW<=r8P!2k-aV&cQ?gkzIIQoPhaBd>j?&kVBrJO%&#f_*}BH}n2d{1}N zO*!lHXN|tJk^cqmpbr`U4)u)g4^K};fm2W~z^y=lt~+LKs-qwRa@unq8+nMG60{Z1 z!8>=a!_)3`7%Z6hu|RjDye%J{9QRgv6&U(y-#;~WU(^HSy|hlr|L{V!f~{&Euo(T6 z*x+83g+y^~GWL+5I3G>NUx>@)3d^_(B|^+xi(Hp8&FWB4J_=uu$K z#!=FUffeY1b=$^(^zgdP`XEI3d-!MLrS`bJ!X9k7j-uE^DBgizFH;Pjiy} zkMV03nuDE9p4?s)+q%#%y4?r)?8ExSn*7L)C=x2a?*9hV!xX6F(zLjqg^34}3(UWH z%@7Bj`^3^e9>4eNt(c`F&H##6vpcX|1i>sFx(g6q`JOp9OFu4x_*uH@$j87Jj1m2c z`(_dy`;!Gjbu~SqM+}R=SCJ1|BH$iGKpGS~Dv{240n!Rx|9xx#q z-obm*$b4Ua{&JOrL4!<}AqOFZK9~8_L--4KfVqDdO`ZFTl&5L#4>TN@7B4Vk_A<`u zSu%SQA_5&Jx6qFk>`-jMQhqr)H?bG`+hS1%tie}%!2-Ll11Nbn*%#`&NUK1{RS6ly z3m}aC7ZSu+OLs(lWe=bIfqC7{Kday1S<}1?-XkZuAXvIe}K3V`~g$PYJaeY+!lX;Jiz;d*8u(bY+sv@ zrbT~nVLAyn39aB&6bGIF?;JCp0Je>KUb6+k%&~^+LEs5+A0WIMkS8LD_XHdR_*-^a z{Xp9uE!OKuM`vAry$-->_7p{?>vbek0r67@Vb|-v3t9+euvvE9tiy{Wrq5I4bx+_$GFGA=qF-Xi?4NhMZCy7s`lGg`=z~}?3ZGi$;g{wkGdTXd;Ae^Vr@^9Fr8h` zMAw|JL!!cYOt4S5xDNJ-myz&GzI~jTlYhvb?+k2-~bnmEj@; zd@GzNHjjAhAZ)}kF5uac1j#1soFX)KE)x9%*g4dUor{*r5?^uIoH!L6w4~?Sf4;V| zDaOV=#}+K9ZKF1AvDnaobkl0k;BdtjENKf$q7_H9%^PUGf*i1eKTyt{(F@~O7Fl#v z!eMX~%8RQKR)VVt@f?{x{Qz+mV(ayok&8^UuF96##@Mo;@IrPzg7;D+k-q9mW=(o8!3jHk2eSvNsfFl!o`KczkPU!syxTaN ziN7$$(HU+D7h|XEjgD6lkmO>fFN*^n8QW!>>B!`-63b2lGM}$tWj6RjXo>NMDaR|x zSznuws-nMxr&$%jDo$W*jxmei)nT8l;Pd! zm4NV?!9<;k|2l&BU`!oH^h_p%U(B25sP!Ud%GtZ~t&aOZYIIyq=aEbd)uPQNRI`jw zEgbcVpiHQS9u>|z-3wk`N?z8o>&8Yz68~#90)-|2(W+R={}O{fftgdeABQ}I6J=I(i*~bTpf7EVEfT#Y zNn7N-)OAAQ#C)2Dn@n!uev40^@9|m`y*Qyr6k-dh%)!x`{y~WOIXLG~*ihuX{B`Kx z{HN80d)rt0kuGpWomN)Mkr-kP`{YpO-5je?_NKT72Z>!}ZCTg5Zxm4MAG*7_-taNy zWx}W0ET$hOrr5Kv2S=O5#KqezWbaHF-v>{hfBBaJ!*F%Est&#qt z?F^VZ3k#o9I{!ThnwOL2T9P!jA{5D9%m(Ckgmh{su))P|XC38BK z>`760Y&|rJ3}kQ7ELo+{!=soLxqHh|?GKdtu|nTN{D&TP7^^If)d_K|R)N1)=vYb9 z;#hUc4^A%)S1CVmzSED@QtLaXm>;xcJjs?BPvLT!#uK*6@DV*3OMjUB0CV9EzC+iX zFqsKsh|JrJVVc0_C79OGo}{WcMwqj11GXD-_4b5Uzxf5D(87huM;v(CaxF{&RAl&?k_fps8!n*5)X5IBzdNFveC*78F1htZ^JCe->liskn^w^x$w;hFy?Gb{z!S9K}&tmO{TU zr=k6TMt(Z)HEJbC;m4#)<4K@R??3<9c?Q zy+Hq*!#{!zaHPd`FohrHVW;eT0CcJcT(|lwfvQwLJK$eDY%56{y6MHyn#mg7@93yTzb>__yIv0H6TlufF<-Xi^? zP7On$C!k|SZ_SzAt)+=9K`z&jD>B*!1A4WK5KU7_x z6SL_w+ME4_3R)4#5bsXz$(NU_bsN=a8tK3`5Y)Q@y;v-#8xI! z6fU+JOrIknV4{>fCOon`aUgE`*E4ura<4~W2kSVWaDzd|4c5jea(@-7uB-?leDJBh zriqFSC!`eYSQV=$$j#iM5V#BLgdp+{VOKCJ66iV}bd@JdMNcOn6a;6)UaF~yy`r^$ zbg>uFn!^{PHA`ldn8k*GG~y~ThbgvTDStBtVd4PvxW!^awLZTQcV%6fx*c1vq+Wmn z(u(TuSCqgN1s+?lWO4`@4Yi4?9D=rqVSxUJHK<`hSI!|q|6P@I|4PrccGOi#Ki}f3 zv`4N=I)r5k#Oc7gs}jzyl~%}CG|#w5c=L7xQ$TLd`M0b=lLH+T=Q|7YZ`PVTgGJi~ z&c6kLS%X%ZNWd7U*$F6or1>K5Df6z@RncUvp(!X7I;>9%0yo@oU2m1rq;#tA^1i{# zBKpEV0WS-7U9XU*QM84=NS?;KsyS@D;N?5adn+pnsdhLS$aAMEca8c@2!t)y!*vDHwAznG;JKWgLdugm}3S zmLeU(ea2Jx9X(^SQ0_{+95wOsT-0C3_O%IV8snu~1Ka_UXiV~ILKj}}=OYS&&Qby4 z)x@Qt-T>l*4rKwS9{1ziGcJC1a(*gwFjlzJWiw7#Ds9GP=((_haf99hMcj;&kBHf= zHu1stf*1Gz_+WP3_+XL5H2GktD*25*iY4PYr85FzJSSxtTd-vOU=+9agFzoEpMg>G z&d&(-E4_D8I<88{sCSBMfX)Z8S;2jVDz7*3SJ!z@1EL}n1|2eb2n{q#Xc_F?_vlcE8=Obv)R zGVKRJjrJo=OZKxPSZPf1Y7Uf~9*qO`1I+~puf~2}8$qo7oCSIf;TJhu`OFFFsQ&wP zAOPbDN4g_Db3!sj2+4Nj^(kg2iG6zrv=EwMAMBd359#SN*$2>sbw3OvI}kOWCDnX( zqs5}i(rZ3iutTv0OU71MRMGkt##W9%|7M6R+s~OO7tSt(i4e7woU0OQ-eknza$d7c za7tII^q2UI{<0W4?(Bm1qW)4(iFcLaCDAIpyl?Qb7{uf7@?OLXyYPFnvx^IxLYn|atLnZgt~((43HF4!Ob2_~H`>$L(Vh-PKW}3D+JrPM+S5on zxf8n3kZWN5GhV{Q|IT>9p57(W*3;m(y5FlYGWr|u4C1ii@rZb9Y{$OjlKM3h za77suzv&?S*!-m#8?2)kTS}8(LAP&K*-KTFJ=Z$EgWA&aH5Mm!0zJ3Z@hJh}>Sk#L z8-UM?fVzq?EP%Y4#f8@eD6V3R2netIys%rvxF>>O6$9BP$nlziYX|bf2!enJ2MQ2g z&05Hke$TgkpUVkw;O`I_nKg|5!Tz1OhS9fTH&lm}+9BRAu3_*_=QWIo=Le7!F|lXJ zg94^4a8uVXJSN`38U_Keh5?r%YZwvJyBsD8z+>VXMicG)8ivOu_ppXRSXsk>r3Q3d zSLho(gi2mGFFvuTt(@l`yAE5kP=3)p;?1Imai?LQek~?(6fe)>Y*gtTj1?pzvaMIffu` z0_GfFX}(iwlGy^yuaGA1>QGeCWUZkoDEz=Vnu5R$QKWf|(xem$G-WM=chwrIXtLJO z6ch>_)*J*uSj&L#?=b(j`%3as5J#^r&kVvHU@S8CJ`x7)u zD7lq%$KL~(rr9CFsk(WH?aSPzkgQfL)`x{gXDq;!dKb530ePad4PT514eGuzgogc&^51Roi zc1lLwvynb-L;4{H2CpV=+cO>m(j!@v**)e|jO!Q~=D^Z)YH|HFAwI6?9@jq=0lUQY zCjwN*xW08WMC49!{e%e8DXzaZg1E$8*31xlLR`NCV~;#gqVA@+u3whg$hZz$#axH# zW#YPM+orhAx95p7iMtgn{&B{21fEa9Jm50q6zZqlZ~DD6tNq>%-tR4*-w+SK2e$2> zk9O+fG1V&gzXzw}|Moc8KZ~03d>Y^NI*9j>SdTkvbVa{-T>nan#jt6bHJ=mS3g_-( zslRs(!bduD-R}DMyLorkXDc5}9lIZ1M4$E5ciO36-Yhxl4F5c8%4cykX7K&GAdKW; zVC)e#T!1)mIp$D>&iAR2Ym9q)_39vOTc%lE&Q`0@5ZOa`^_R=JcrlFmbgB@28BT6 zTkFSbW%=!ZS7gaot#L9dviy#s>LAP5|L2(ge>dPehGWCA&bD*-auz((m$Cr;5K7}H zgdR-THjkmuc|0QzR#yLpHmV=`&0O>;Ab;EzzNg5$jlru;-@YI4j+OdGh0U=1F5RI% z2Prc73?1cZp1{jRC}glpX_MQUOIhQ^_9PHc1Rcgf?c;dE=MT_8`;Q~IZDTM+KL2Aw zY5v#7)BNFv()`iJ)BN#<()`KB)BNdFXj*;#d7vr!T(GH6TZi=v#oSGw4>o%8tAKa6 zlAi2g?PM=o__b%ht3JNS(D_ZEb4H2IN2uxEC){Ce;N!ON30wH2E&O)A*|b zKiz9(d7mxZZwp_wg=d^Js%0Mila+gl_@eeHa>slpiXcTHIEcC})X+-YKv%h2f7KJ{ zl>OE3p)Ljx{;h&~jy+Atos33!Fa0^Y?N z$m5cLtIQ)Gwc*Q1M2_38pGWJbwhws4PZfCN*WE+k-)IXvIPN(vopW$YXU8sdmQi25 zQY^QcoK1<2_5CA(j`;pUfB5zD&>#7vIo3c$X#A#msNq-2Lt)f$Fx7tU9O#QYAGK2B zm&5~Nejhv(mQSglHvhvCvCaPq{rr;ES$>y0(BWscLt!^hbgF&ZBhW1C%J;0$_-*Sz zW=~J1tFG8wV(&mla*0CUL;5}l)@E+9#@7FB9N3S2R^=rojyk(n^kLh|{<=1or^T#R? zF8cZ9siOH~wS1w=zGuh?^g?4e0op+E6&98*Yu!RyL;wyds-Zt(fJ@ zvfk&*mt}n-aGDZXk8G@soDt}Vjqu~Lf#owDwyDbbIRTf*`NwuS=O?!CTt(asua(8~ z174BEO6&dk<<`J3zsDL1XF2XW=}8<!n3p{NQQ8b=eefS$VuX z;1YT8L#6>2KT#SAmn-6Gd6aGC6#=j4M`8ZY?~Vq%{F-Pev>f-H^h4>`yy2CBj;tGX zo;UETpMeg)X666tKvTw2zRl@P|1}QTRB^qwh)cfB={?6)8;I-mMO^sI zTTDaWFa=yzFRl)_L@x@s_;Jo4y=xS4xBlAkx;EgI@p`tkKlzBJk1za+W+1|EV}`{bWBh_;DDbUIDxhv0B z{%;F(ME?BXWWXk0l3eaO@K=hu8~+yX%|*QYjAX#ek3)vSJ2njOEk(Ti++#p>y)C?R z{dn!z-5T)9*vYp=-i>?Q>$pu(b(AT;r-&`}@lUWP=Hxu!DQS~ z{QhkZ*oe00ZbaMbHlpokHlpq0<+csJgEM)*jFEiLC#{Y3%*08MJ`BeA^N^r5p;ofV zxeNRh#t^Eu8|;CZF<_7Ou=sr@emmHCY&}R{|F_&m<$Jcb$1UAQswZccXx{Ge1e)9o z%?5B$n$R3i^Vc30oC#EKCu&1@mvWib=Cj)S_XY-%N?I3qSkMxvncZmNlvkT9$9?$2+dL23RTE&qW*x)sf?&U90$g*tZY-Zp=8=?!N!sC~0@=YEVx+_p01XRHT)N&pTG^`qnwpJ!kB`b3q$p~#_A9P!!J$5&KJJ{0b z)NYW!<6I}RKVCf6v=*pR>)6jm^6|92pYQeULqGr2ZAs2@U_w&1$M5>S zA~yU8ziYX`mb9>z%Tbj|k9+t~7_Bk&xB^>&Lr%C%(H;f10tXMqRFAvWIe{vTdo7of zG<#J^;!M+v3-H^)R&c-rIMp279dYCWRl$Lr*s1gg_r})FHwtWt1FyZz`5VbcO0nRF z9?vHI4LR)}i~g=}?Dn14?qfIko(KR2Jr zvzpZz#tVI4kr@kADZ}s)hPeS=Q_+^gztXq=v&jv3{w*fynk#6Noae>Sp~Gq_WnlM8HxPvOzsYKcGN<%RJQsFK8MInVl5x1KdW z%t92ZqBRc(SGTshB2X2rd78Mo^&&%Cph~Tay7Dh2yWGoT__NWKdpr8Di+o=(&H`1E zRxRhD>S|d}nAQSS(VC~Qt6RUuv=*p});zXd-P&r2K$TkCajsg=zRuGR^Id@|#U`A7VC#=m z#YMZ%EKnaU$HiOOx^g$9yi+W&rO51~WA*uwyGpXUq~na==Gy{Ql9i=ZK0mVMmhJxN zWgVlwr6o`$TD6>y%&mW11gbPHo>R@0|GhLfcwUKD`6_zl*$>OBKvnSiU7mHpalG;l z<1L;NGOUl`H+FOhTN=k&E>H5UsKU{^Fw70ccEE23Tf*UcOSUo-uc}ke!3RKtesp9oXk0J2N2bij zYk?}Y_EM!TRq=Y9$1qL2Haq$wtBC?t(nQN^)j6cMO*60Z)a1TZI{Sd91igO(ziaX# z!E7#!x^ULUb4=Nm&s^-gRl85wI6dIgnAKWzou>qy6{wOP)N($)S>4*MmI_pF%J&tL7O1YWDzD-H&bOZ?S@DV1 z>QOy(>*!eos>Eq6mq%b%R$0I~vGv$%@!P>x$cj(SR&(%V#E}bB1qXc8wz~C;E*Z1*+7#D5tVNpRss){W+hQuIAOsNuWx+)^a{#UESJhgg}*A`<`;``L9cM zbKErdQ=Zt~ugc5Z=qVx2ei*-NxxiL<89wM;-P@kw=qCiK)Z1FlC&Q~-TUiNIMQc7v zUfp_up)F7qt@-SEb?Y5XYk?}Yj^k8M&M1x3e!l%Q*)5-DujbV1qd=87t>t|Dy}GrP zu|QR{=JWE^t*wj&s-iU?u&-`yWh_vo)>g*lGrF&xRvCAv^}rvDR(U?Pc**qoGCuKN zJpwQGl;E`ms^bWBnxnkRW0)pA=UW8U&#ORfFt7JdGp}FkDA&!mjebv{y7F4J$GDTn zFimomw;fE;&kJmYpXW;w);~4^)wRD>bKQwjfBB+=YHFYJu+Un8Dp9NDd^19I>-$Y> zfhx83o}kkj&$m2=FgAR7LN&Ghwu_n|P$g=$T;8@Y#aWTSR*a3jq+yC{JOW$7;d@=Z z7IC!iN9gmz)P_%1<;*vESd5QA-CfR^`5K67iQR5m3sgyBo>TQn8ZXJW z50z+L3r~8@HpM3(&0~eOa|F#cgPm3`l!6xh44zGEJO8uCj_rE(6!h$xmraQ#LCX(a z)^dCZxN!~Mt8E|LG;H7CX<|$Wn$7COb~VrM^6kTz+zJHOT;7qOl@md;Iqd{%`MZgt zGta;AG{G}LyYsv)$sHR&)Y!wd%D7xb#1{;MSgIq7dF&I09J+s z?JNMNYiE~qEici0w4;x(_X-^2 z@dUk;m*iAtZtwB1Sb@>qIf30^njiIe0!?`VPGvtWP2E)#*!9pX$EM!VcHP-{nzfwo z>Zy)zc3)ATy2OjBeUfuZ{PJZ!)wJxaS)fX^YWZ{7dW!Qcfh|e6mh+`S)g0J$HG!(& zfNvS9Zhe>UEBur|m0Ekx!!^jV4u9Ua5Bijm*8Wwih*qC=T*g&Ifi3mcQ&eA9&grj1 z+v58;__Ctv{@v?g(LaGI^{42({*=y(M(GJIN2itLEs^^6_cr1ZmzJzK0{0dYBzkF*` zb!%(k0#$15C967A&g~yB@yZuFRa3LF6{r%mTFy5?Rkyah3RJ0ek=Jq@99`b7?|fO* z`l$(2M{54PTD{CW+!vg9Zzkff-pe^@k{1y1{X=+tS+b?T*7A^rH`7VHuB)DZ5NFEp z4h+18sxkg*{Jva24CmY7r8oLsCVfvv-}u#ndA@&PmBaEOv<&p-f*xq%T}kpDo_h8b zEC;$5CV4)!KKrV#A-y?hhb4Hn`>=2o`exsuq@PT4=;XalGXuTx+c21c4|9J-hx@;P z%d3KaFA~41`!`_t-2Ga@Tu%Cd@6qywBx5hfaM!YrfcNohfOz~SPZh7RLQeiXz(d>^ zGkgG~)Fg@d*38!{zctg450p`c zo8dJ@_`J;Shxu>J)RQf6U-DGY9=n}tNj9@>$psSj5x$pId%O*q_b%0vBV&KYp5km@dg7#q2ij<6l%R4K?(_AL+1BBHe3=u!?KKi{=6oNd zAXsxEy4+0hy&b+4GTRbu;~B)ig{pELt}co`#$}7~RUAKjW)y&Cf=1l!H2C_*HX3{h zWE%~ULe3XzX2Zbgq(EFqH=Vs?+o1*)iVp>R-N|`p>VYe$K8H__qdq7&~mDUbACZcLNu{4_K!UE6%m*CYPWRqn>^Q z@2bQg5obORztmyiWncCA&C$7fPhNtzc)5Fz8x&8RAs(QkhAN&JK;Z3LT_tQoo!s&1 z_?`IOq61B{4`8ja&<2)8+QXoY1#$18$(z2%9Vt7%K}C+`6MH+7T@WSjt3VSjf#2AZ zrtuqlb-V)*->SpA%kaf7Ub6#{5WsKGPRGuHX!YB<_8YW($2Po*`kO@{0u90Y@bDq6 znWKj(CjAWJ9-wwI86`D9Y@5{Ausq<%_2!>IcTHQ0($4<049r20+ufDR_Z519(K zyQN+DHYk(57H#Q(^A@)?{pHor8vi-O&zNVznI-aHNcli zp6`FA*>V_TE&n@Zi(k?PfWU|@Q9q%FLD%qN#}40f+%qa0l!D5}ak~obF|gD%Xs1rbQeg(}KehTd-tW z#1Zo?3bu3(#>m!^?Xe@v=8nczNI8Wf6G!M}j!{Wg+5)e>e}; z0Ljbe6wi0RgCBs)3w{8vH0|IAHWywb=wP1j{lJ|VOt?P!0cjfj0KeD%Hq=&quly;VTWsQckhB$z>5MGVg#UgHGJ(8q244= zdk7S-Sqd3y0KPOpjlxSI5C9Rc8M0;52v89R54FeT(aeklm_xslT`+Y+J}Uu@U8h#ggx1=q7a4X&95#|yoLOu1JnXTaflS5U}<)|1QUF; z{-vr7p4wE5UH8O z_yy1n;6nVOX`A>ZmOg#)3!$jq;sq$_0$RMlr48dxj593d*Pv??cqek&VzD70jU2{> zu|f-KO>DtZeg#{c$`&j(1O(sq_ARIyu?0)!%l9m*gYaLz2MuaUY{8O7KWa;hMRgWF z`k@6?A+})2^h5MS-AC*PbtAT5Noi0&S}Zn1eGCmcjM##u{L}3DF>Jx2`jkfg8R#MUe+J*r2G#fCZ+7Xn>GXCC-Cba{sOkB}Sdu(^;dqjs&AHitlh6w_uF z@+*Cn4uoqZN-mcE$_T(V;V_X;E=PG;T-fZh&R*^$N=j=(x& zISU>YbB|h*p*;o{K3n2M;DPgtxtw3HOIlZzKd{#14=jQv|AhQO5I9wd`3_WvG^sO! zcPUwBN&JQ8L=YrlD7h-yyl-f;7}O6!V1Icp_7}O!W%R%X_H&4fX>g#jy_a5>%FJ(B#oE3w2V6u2ni!!%gK5y|{Qh>JpNaLI@~YoOkdArPY6RF#c@?wAO?ef35c8^kplz?EX`8%C zEPYd6g*9-=vea{K%By-bN)F>sNS;{8L$32DoL8NeW~Xxgk)&h0?Uh_2xqmY9Y{d1` z%C{NAHMahviLF=z@33aEljI<>W^rPSt*eqG`F|Q)rD=?4siU!~F9$L;yPqH`&;U~?%ma<};|U*r1o7Vfv?VVeWb7ueVF zi!sd2Mw%ll!xx6)x_d2I>S5AJq*BfCTp)9#Mfe&^_7C`zUyRFAZ3Jpi3U~`$<-!2V zZM)M7-_aOYw=C&n!q-H3 z?Yf11x;=>n1ti|M)i+r8o0IdvebPIkdy})1Jr7A1f^5*)e-b@WSqs6iR&4XZg;8e@ zjyj96N$zF)+JrPM>g;jpB$7$!EU#t-4NF6>ifW4#W`#iU>KC&B^4)93JIg^o#VJ4`;KWr+$oHx@R9iL=mZ*1nz3y_ujL-z@
GU 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

T_?v+9kPqlvw@YaP6tl3J0exTmi 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�FL@bc7u8lj-pJ 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 W7|1Y?VIadmhJg$N83z7uG4OBPtaM!f literal 0 HcmV?d00001 diff --git a/DotNetConnectors.sln/BooScriptExecutorFactory/lib/Boo.Lang.Interpreter.dll b/DotNetConnectors.sln/BooScriptExecutorFactory/lib/Boo.Lang.Interpreter.dll new file mode 100644 index 0000000000000000000000000000000000000000..407d01147637f06a2a95f0ad3445cabc552c6683 GIT binary patch literal 86016 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 V1`afEpn(Go9BAM`1OFlo{C^)m7oGqB literal 0 HcmV?d00001 diff --git a/DotNetConnectors.sln/BooScriptExecutorFactory/lib/Boo.Lang.Parser.dll b/DotNetConnectors.sln/BooScriptExecutorFactory/lib/Boo.Lang.Parser.dll new file mode 100644 index 0000000000000000000000000000000000000000..8bf0313d832a327a3568d7b39af0325641090f04 GIT binary patch literal 421888 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

|?;D`Z! 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 T4zxJX;y{Z7Ee`yD$$|e13;By` literal 0 HcmV?d00001 diff --git a/DotNetConnectors.sln/BooScriptExecutorFactory/lib/Boo.Lang.Useful.dll b/DotNetConnectors.sln/BooScriptExecutorFactory/lib/Boo.Lang.Useful.dll new file mode 100644 index 0000000000000000000000000000000000000000..73b494556eb679b91547b5e8e616973ae92ae8e1 GIT binary patch literal 81920 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 literal 0 HcmV?d00001 diff --git a/DotNetConnectors.sln/BooScriptExecutorFactory/lib/Boo.Lang.dll b/DotNetConnectors.sln/BooScriptExecutorFactory/lib/Boo.Lang.dll new file mode 100644 index 0000000000000000000000000000000000000000..83dc5fbc52b26ef9a7916040df05d3b79b5f07ce GIT binary patch literal 110592 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 + /// Description of Assertions. + /// + public static class Assertions + { + /** + * Throws {@link NullPointerException} if the parameter o + * is null. + * + * @param o + * check if the object is null. + * @param param + * name of the parameter to check for null. + * @throws NullPointerException + * if o is null and constructs a + * message with the name of the parameter. + */ + public static void NullCheck(Object o, String param) { + String FORMAT = "Parameter '{0}' must not be null."; + if (o == null) { + throw new ArgumentNullException(String.Format(FORMAT, param)); + } + } + } +} diff --git a/DotNetConnectors.sln/Common/CollectionUtil.cs b/DotNetConnectors.sln/Common/CollectionUtil.cs new file mode 100644 index 00000000..5e57742e --- /dev/null +++ b/DotNetConnectors.sln/Common/CollectionUtil.cs @@ -0,0 +1,861 @@ +/* + * ==================== + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER. + * + * Copyright 2007-2008 Sun Microsystems, Inc. All rights reserved. + * + * The contents of this file are subject to the terms of the Common Development + * and Distribution License("CDDL") (the "License"). You may not use this file + * except in compliance with the License. + * + * You can obtain a copy of the License at + * http://IdentityConnectors.dev.java.net/legal/license.txt + * See the License for the specific language governing permissions and limitations + * under the License. + * + * When distributing the Covered Code, include this CDDL Header Notice in each file + * and include the License file at identityconnectors/legal/license.txt. + * If applicable, add the following below this CDDL Header, with the fields + * enclosed by brackets [] replaced by your own identifying information: + * "Portions Copyrighted [year] [name of copyright owner]" + * ==================== + */ +using System; +using System.Runtime.CompilerServices; +using System.Reflection; +using System.Collections.Generic; + +namespace Org.IdentityConnectors.Common +{ + internal class IdentityEqualityComparer : IEqualityComparer where T : class { + public bool Equals(T x, T y) { + return Object.ReferenceEquals(x,y); + } + public int GetHashCode(T o) { + return RuntimeHelpers.GetHashCode(o); + } + } + + internal class ReadOnlyCollection : ICollection { + private readonly ICollection _target; + public ReadOnlyCollection(ICollection target) { + _target = target; + } + public void Add(T item) { + throw new NotSupportedException(); + } + public void Clear() { + throw new NotSupportedException(); + } + public bool Contains(T item) { + return _target.Contains(item); + } + System.Collections.IEnumerator System.Collections.IEnumerable.GetEnumerator() { + return _target.GetEnumerator(); + } + public IEnumerator GetEnumerator() { + return _target.GetEnumerator(); + } + public bool IsReadOnly { + get { + return true; + } + } + public int Count { + get { + return _target.Count; + } + } + public bool Remove(T item) { + throw new NotSupportedException(); + } + public void CopyTo(T[] array, + int arrayIndex) { + _target.CopyTo(array,arrayIndex); + } + + public ICollection GetTarget() { + return _target; + } + + } + + + internal class ReadOnlyList : ReadOnlyCollection, IList { + + public ReadOnlyList(IList target) : base(target) { + + } + public int IndexOf(T item) { + return GetTarget().IndexOf(item); + } + public void Insert(int index, T item) { + throw new NotSupportedException(); + } + public void RemoveAt(int index) { + throw new NotSupportedException(); + } + + public T this[int index] { + get { + return GetTarget()[index]; + } + set { + throw new NotSupportedException(); + } + } + + protected new IList GetTarget() { + return (IList)base.GetTarget(); + } + } + + internal class ReadOnlyDictionary : + ReadOnlyCollection>, + IDictionary { + public ReadOnlyDictionary(IDictionary target) : base(target) { + + } + public void Add(TKey key, TValue val) { + throw new NotSupportedException(); + } + protected new IDictionary GetTarget() { + return (IDictionary)base.GetTarget(); + } + public ICollection Keys { + get { + return new ReadOnlyCollection(GetTarget().Keys); + } + } + public ICollection Values { + get { + return new ReadOnlyCollection(GetTarget().Values); + } + } + public bool Remove(TKey key) { + throw new NotSupportedException(); + } + public bool ContainsKey(TKey key) { + return GetTarget().ContainsKey(key); + } + public bool TryGetValue(TKey key, out TValue value) { + return GetTarget().TryGetValue(key,out value); + } + public TValue this[TKey key] { + get { + return GetTarget()[key]; + } + set { + throw new NotSupportedException(); + } + } + } + + + + + /// + /// Delegate that returns the Key, given a value + /// + public delegate TKey KeyFunction(TValue value); + + + /// + /// Description of CollectionUtil. + /// + public static class CollectionUtil + { + /// + /// Creates a case-insensitive set + /// + /// An empty case-insensitive set + public static ICollection NewCaseInsensitiveSet() { + HashSet rv = new HashSet(StringComparer.OrdinalIgnoreCase); + return rv; + } + + /// + /// Returns true iff the collection is a case-insensitive set + /// + /// The collection. May be null. + /// true iff the collection is a case-insensitive set + public static bool IsCaseInsensitiveSet(ICollection collection) { + if ( collection is ReadOnlyCollection ) { + ReadOnlyCollection roc = + (ReadOnlyCollection)collection; + return IsCaseInsensitiveSet(roc.GetTarget()); + } + else if ( collection is HashSet ) { + HashSet set = (HashSet)collection; + return StringComparer.OrdinalIgnoreCase.Equals(set.Comparer); + } + else { + return false; + } + } + /// + /// Creates a case-insensitive map + /// + /// An empty case-insensitive map + public static IDictionary NewCaseInsensitiveDictionary() { + Dictionary rv = new Dictionary(StringComparer.OrdinalIgnoreCase); + return rv; + } + + /// + /// Returns true iff the collection is a case-insensitive map + /// + /// The map. May be null. + /// true iff the collection is a case-insensitive map + public static bool IsCaseInsensitiveDictionary(IDictionary map) { + if ( map is ReadOnlyDictionary ) { + ReadOnlyDictionary roc = + (ReadOnlyDictionary)map; + return IsCaseInsensitiveDictionary((IDictionary)roc.GetTarget()); + } + else if ( map is Dictionary ) { + Dictionary d = (Dictionary)map; + return StringComparer.OrdinalIgnoreCase.Equals(d.Comparer); + } + else { + return false; + } + } + + /// + /// Creates a dictionary where the keys are looked up using + /// reference equality + /// + /// + public static IDictionary NewIdentityDictionary() + where K : class + { + IdentityEqualityComparer comp = new IdentityEqualityComparer(); + return new Dictionary(comp); + } + + /// + /// Computes a hashCode over the enumeration. The hashCode is computed + /// such that it doesn't matter what order the elements are listed in. Thus, it + /// is suitable for arrays, lists, sets, and dictionaries + /// + /// The enumerable + /// The hashcode + public static int GetEnumerableHashCode(IEnumerable enum1) { + if ( enum1 == null ) { + return 0; + } + int rv = 0; + foreach (T val1 in enum1) { + unchecked { + rv+=CollectionUtil.GetHashCode(val1); + } + } + return rv; + } + + /// + /// Computes a hashCode for a key value pair. + /// + /// The pair + /// The hashcode + public static int GetKeyValuePairHashCode(KeyValuePair pair) { + int rv = 0; + unchecked { + rv+=CollectionUtil.GetHashCode(pair.Key); + rv+=CollectionUtil.GetHashCode(pair.Value); + } + return rv; + } + + /// + /// Returns true iff the two sets contain the same elements. This is only for + /// sets and dictionaries. Does not work for Lists or Arrays. + /// + /// The first collection + /// The second collection + /// + public static bool SetsEqual(ICollection collection1, + ICollection collection2) { + if ( collection1 == null || collection1 == null ) { + return collection1 == null && collection1 == null; + } + if ( collection1.Count != collection2.Count ) { + return false; + } + foreach (T val1 in collection1) { + if (!collection2.Contains(val1)) { + return false; + } + } + return true; + } + + /// + /// Gets the given value from the map or default if not exists. Always + /// use this rather than map[] since map[] throws an exception if not + /// exists. + /// + /// The map + /// The key + /// The default value + /// + public static TValue GetValue(IDictionary map, + TKey key, + TValue def) { + TValue rv; + bool present = map.TryGetValue(key,out rv); + if (present) { + return rv; + } + else { + return def; + } + } + + /// + /// Adds all the elements from the given enumerable to the given collection. + /// + /// The collection to add to + /// The enumerable to get from + public static void AddAll(ICollection collection, + IEnumerable enumerable) + where U : T { + if ( enumerable != null ) { + foreach(U obj in enumerable) { + collection.Add(obj); + } + } + } + /// + /// Adds all the elements from the given enumerable to the given collection. + /// + /// The collection to add to + /// The enumerable to get from + public static void RemoveAll(ICollection collection, + IEnumerable enumerable) + where U : T { + if ( enumerable != null ) { + foreach(U obj in enumerable) { + collection.Remove(obj); + } + } + } + + /// + /// Adds all the elements from the given enumerable to the given collection. + /// + /// The collection to add to + /// The enumerable to get from + public static void RemovalAll(ICollection collection, + IEnumerable enumerable) + where U : T { + if ( enumerable != null ) { + foreach(U obj in enumerable) { + collection.Remove(obj); + } + } + } + + + /// + /// Returns c or an empty collection iff c is null. + /// + /// The collection + /// c or an empty collection iff c is null. + public static ICollection NullAsEmpty(ICollection c) { + return c ?? new HashSet(); + } + + /// + /// Returns c or an empty collection iff c is null. + /// + /// The collection + /// c or an empty collection iff c is null. + public static IDictionary NullAsEmpty(IDictionary c) { + return c ?? new Dictionary(); + } + + /// + /// Returns c or an empty collection iff c is null. + /// + /// The collection + /// c or an empty collection iff c is null. + public static IList NullAsEmpty(IList c) { + return c ?? new List(); + } + + /// + /// Returns c or an empty array iff c is null. + /// + /// The array + /// c or an empty collection iff c is null. + public static T[] NullAsEmpty(T [] c) { + return c ?? new T[0]; + } + + /// + /// Given a collection of values a key function, builds a dictionary + /// + /// List of values + /// Key function, mapping from key to value + /// The dictionay + public static IDictionary NewDictionary( + TKey k1, + TValue v1) { + IDictionary rv = new Dictionary(); + rv[k1] = v1; + return rv; + } + + /// + /// Given a collection of values a key function, builds a dictionary + /// + /// List of values + /// Key function, mapping from key to value + /// The dictionay + public static IDictionary NewDictionary( + IEnumerable values, + KeyFunction keyFunction) { + IDictionary rv = new Dictionary(); + if ( values != null ) { + foreach (TValue value in values) { + TKey key = keyFunction(value); + //DONT use Add - it throws exceptions if already there + rv[key] = value; + } + } + return rv; + } + + /// + /// Given a collection of values a key function, builds a dictionary + /// + /// List of values + /// Key function, mapping from key to value + /// The dictionay + public static IDictionary NewReadOnlyDictionary( + IEnumerable values, + KeyFunction keyFunction) { + IDictionary rv = + NewDictionary(values,keyFunction); + return new ReadOnlyDictionary(rv); + } + + /// + /// Given a collection of values a key function, builds a dictionary + /// + /// List of values + /// Key function, mapping from key to value + /// The dictionay + public static IDictionary NewDictionary( + IDictionary original) { + return NewDictionary(original); + } + + /// + /// Given a collection of values a key function, builds a dictionary + /// + /// List of values + /// Key function, mapping from key to value + /// The dictionay + public static IDictionary NewDictionary( + IDictionary original) { + IDictionary rv = new Dictionary(); + if ( original != null ) { + foreach (KeyValuePair entry in original) { + //DONT use Add - it throws exceptions if already there + rv[(TKey2)(object)entry.Key] = (TValue2)(object)entry.Value; + } + } + return rv; + } + + /// + /// Given a collection of values a key function, builds a dictionary + /// + /// List of values + /// Key function, mapping from key to value + /// The dictionay + public static IDictionary NewReadOnlyDictionary( + IDictionary original) { + return NewReadOnlyDictionary(original); + } + + /// + /// Given a collection of values a key function, builds a dictionary + /// + /// List of values + /// Key function, mapping from key to value + /// The dictionay + public static IDictionary NewReadOnlyDictionary( + IDictionary original) { + IDictionary rv = NewDictionary(original); + return new ReadOnlyDictionary(rv); + } + + + /// + /// Returns a modifiable list, after first copying the collection. + /// + /// A collection. May be null. + /// a modifiable list, after first copying the collection. + public static IList NewList(IEnumerable collection) { + return NewList(collection); + } + + /// + /// Returns a modifiable list, after first copying the collection. + /// + /// A collection. May be null. + /// a modifiable list, after first copying the collection. + public static IList NewList(IEnumerable collection) { + IList rv = new List(); + if ( collection != null ) { + foreach (T element in collection) { + rv.Add((U)(object)element); + } + } + return rv; + } + + /// + /// Returns a modifiable set, after first copying the collection. + /// + /// A collection. May be null. + /// a modifiable set, after first copying the collection. + public static ICollection NewSet(IEnumerable collection) { + return NewSet(collection); + } + + /// + /// Returns a modifiable set, after first copying the array. + /// + /// An array maybe null. + /// a modifiable set, after first copying the array. + public static ICollection NewSet(params T[] items) { + return NewSet((IEnumerable)items); + } + + /// + /// Returns a modifiable set, after first copying the collection. + /// + /// A collection. May be null. + /// a modifiable set, after first copying the collection. + public static ICollection NewSet(IEnumerable collection) { + ICollection rv = new HashSet(); + if ( collection != null ) { + foreach (T element in collection) { + rv.Add((U)(object)element); + } + } + return rv; + } + + + /// + /// Returns an unmodifiable list, after first copying the collection. + /// + /// A collection. May be null. + /// an unmodifiable list, after first copying the collection. + public static IList NewReadOnlyList(ICollection collection) { + return NewReadOnlyList(collection); + } + + /// + /// Returns an unmodifiable list, after first copying the collection. + /// + /// A collection. May be null. + /// an unmodifiable list, after first copying the collection. + public static IList NewReadOnlyList(ICollection collection) { + IList list = NewList(collection); + return new ReadOnlyList(list); + } + + /// + /// Returns an unmodifiable list, after first copying the collection. + /// + /// A collection. May be null. + /// an unmodifiable list, after first copying the collection. + public static ICollection NewReadOnlySet(ICollection collection) { + return NewReadOnlySet(collection); + } + + /// + /// Returns an unmodifiable list, after first copying the collection. + /// + /// A collection. May be null. + /// an unmodifiable list, after first copying the collection. + private static ICollection NewReadOnlySet(ICollection collection) { + ICollection list = NewSet(collection); + return new ReadOnlyCollection(list); + } + + /// + /// Returns an unmodifiable set, backed by the original + /// + /// A collection. May be null. + /// an unmodifiable list, after first copying the collection. + public static ICollection AsReadOnlySet(ICollection collection) { + ICollection list = NullAsEmpty(collection); + return new ReadOnlyCollection(list); + } + + /// + /// Returns an unmodifiable list, backed by the original + /// + /// A collection. May be null. + /// an unmodifiable list, after first copying the collection. + public static IList AsReadOnlyList(IList collection) { + IList list = NullAsEmpty(collection); + return new ReadOnlyList(list); + } + + /// + /// Returns an unmodifiable list, backed by the original + /// + /// A collection. May be null. + /// an unmodifiable list, after first copying the collection. + public static IDictionary AsReadOnlyDictionary(IDictionary d) { + d = NullAsEmpty(d); + return new ReadOnlyDictionary(d); + } + + /// + /// Creates a new read-only list from an array. + /// + /// + /// + public static IList NewReadOnlyList(params T [] args) { + return NewReadOnlyList(args); + } + + /// + /// Creates a new read-only list from an array. + /// + /// + /// + private static IList NewReadOnlyList(params T [] args) { + IList list = CollectionUtil.NewList(args); + return new ReadOnlyList(list); + } + + /// + /// Creates a new read-only set from an array. + /// + /// + /// + public static ICollection NewReadOnlySet(params T [] args) { + return NewReadOnlySet(args); + } + /// + /// Creates a new read-only set from an array. + /// + /// + /// + private static ICollection NewReadOnlySet(params T [] args) { + ICollection list = CollectionUtil.NewSet(args); + return new ReadOnlyCollection(list); + } + + public static bool DictionariesEqual(IDictionary m1, + IDictionary m2) + { + if ( m1.Count != m2.Count ) { + return false; + } + foreach (KeyValuePair entry1 in m1) { + K key1 = entry1.Key; + V val1 = entry1.Value; + if (!m2.ContainsKey(key1)) { + return false; + } + Object val2 = m2[key1]; + if (!CollectionUtil.Equals(val1,val2)) { + return false; + } + } + return true; + } + + public static bool ListsEqual(IList v1, + IList v2) + { + if ( v1.Count != v2.Count ) { + return false; + } + for ( int i = 0; i < v1.Count; i++ ) { + if (!CollectionUtil.Equals(v1[i],v2[i])) { + return false; + } + } + return true; + } + + /** + * hashCode function that properly handles arrays, + * collections, maps, collections of arrays, and maps of arrays. + * @param o The object. May be null. + * @return the hashCode + */ + public static int GetHashCode(Object o) { + if ( o == null ) { + return 0; + } + else if ( o is Array ) { + Array array = (Array)o; + int length = array.Length; + int rv = 0; + for ( int i = 0; i < length; i++ ) { + Object el = array.GetValue(i); + unchecked { + rv += CollectionUtil.GetHashCode(el); + } + } + return rv; + } + else if ( ReflectionUtil.IsParentTypeOf(typeof(KeyValuePair<,>),o.GetType()) ) { + Type parent = ReflectionUtil.FindInHierarchyOf(typeof(KeyValuePair<,>),o.GetType()); + Type [] genericArguments = + parent.GetGenericArguments(); + + Type collectionUtil = typeof(CollectionUtil); + MethodInfo info = collectionUtil.GetMethod("GetKeyValuePairHashCode"); + + info = info.MakeGenericMethod(genericArguments); + + Object rv = info.Invoke(null, new object[]{o}); + return (int)rv; + } + else if ( ReflectionUtil.IsParentTypeOf(typeof(ICollection<>),o.GetType()) ) { + Type parent = ReflectionUtil.FindInHierarchyOf(typeof(ICollection<>),o.GetType()); + + Type [] genericArguments = + parent.GetGenericArguments(); + + + Type collectionUtil = typeof(CollectionUtil); + MethodInfo info = collectionUtil.GetMethod("GetEnumerableHashCode"); + + info = info.MakeGenericMethod(genericArguments); + + Object rv = info.Invoke(null, new object[]{o}); + return (int)rv; + } + else { + return o.GetHashCode(); + } + } + + /** + * Equality function that properly handles arrays, + * lists, maps, lists of arrays, and maps of arrays. + *

+ * NOTE: For Sets, this relies on the equals method + * of the Set to do the right thing. This is a reasonable + * assumption since, in order for Sets to behave + * properly as Sets, their values must already have + * a proper implementation of equals. (Or they must + * be specialized Sets that define a custom comparator that + * knows how to do the right thing). The same holds true for Map keys. + * Map values, on the other hand, are compared (so Map values + * can be arrays). + * @param o1 The first object. May be null. + * @param o2 The second object. May be null. + * @return true iff the two objects are equal. + */ + public new static bool Equals(Object o1, Object o2) { + if ( o1 == o2 ) { //same object or both null + return true; + } + else if ( o1 == null ) { + return false; + } + else if ( o2 == null ) { + return false; + } + else if ( o1 is Array ) { + Type clazz1 = o1.GetType(); + Type clazz2 = o2.GetType(); + if ( !clazz1.Equals(clazz2)) { + return false; + } + Array array1 = (Array)o1; + Array array2 = (Array)o2; + int length1 = array1.Length; + int length2 = array2.Length; + if ( length1 != length2 ) { + return false; + } + for ( int i = 0; i < length1; i++ ) { + Object el1 = array1.GetValue(i); + Object el2 = array2.GetValue(i); + if (!CollectionUtil.Equals(el1,el2)) { + return false; + } + } + return true; + } + else if ( ReflectionUtil.IsParentTypeOf(typeof(IList<>),o1.GetType()) ) { + Type parent1 = ReflectionUtil.FindInHierarchyOf(typeof(IList<>),o1.GetType()); + Type parent2 = ReflectionUtil.FindInHierarchyOf(typeof(IList<>),o2.GetType()); + if (!parent1.Equals(parent2)) { + return false; + } + Type [] genericArguments = + parent1.GetGenericArguments(); + + + Type collectionUtil = typeof(CollectionUtil); + MethodInfo info = collectionUtil.GetMethod("ListsEqual"); + + info = info.MakeGenericMethod(genericArguments); + + Object rv = info.Invoke(null, new object[]{o1,o2}); + return (bool)rv; + } + else if ( ReflectionUtil.IsParentTypeOf(typeof(IDictionary<,>),o1.GetType()) ) { + Type parent1 = ReflectionUtil.FindInHierarchyOf(typeof(IDictionary<,>),o1.GetType()); + Type parent2 = ReflectionUtil.FindInHierarchyOf(typeof(IDictionary<,>),o2.GetType()); + if (!parent1.Equals(parent2)) { + return false; + } + Type [] genericArguments = + parent1.GetGenericArguments(); + + + Type collectionUtil = typeof(CollectionUtil); + MethodInfo info = collectionUtil.GetMethod("DictionariesEqual"); + + info = info.MakeGenericMethod(genericArguments); + + Object rv = info.Invoke(null, new object[]{o1,o2}); + return (bool)rv; + } + else if ( ReflectionUtil.IsParentTypeOf(typeof(ICollection<>),o1.GetType()) ) { + Type parent1 = ReflectionUtil.FindInHierarchyOf(typeof(ICollection<>),o1.GetType()); + Type parent2 = ReflectionUtil.FindInHierarchyOf(typeof(ICollection<>),o2.GetType()); + if (!parent1.Equals(parent2)) { + return false; + } + Type [] genericArguments = + parent1.GetGenericArguments(); + + + Type collectionUtil = typeof(CollectionUtil); + MethodInfo info = collectionUtil.GetMethod("SetsEqual"); + + info = info.MakeGenericMethod(genericArguments); + + Object rv = info.Invoke(null, new object[]{o1,o2}); + return (bool)rv; + } + else { + return o1.Equals(o2); + } + } + + } + +} diff --git a/DotNetConnectors.sln/Common/Common.csproj b/DotNetConnectors.sln/Common/Common.csproj new file mode 100644 index 00000000..abf5a07e --- /dev/null +++ b/DotNetConnectors.sln/Common/Common.csproj @@ -0,0 +1,94 @@ + + + + {F140E8DA-52B4-4159-992A-9DA10EA8EEFB} + Debug + AnyCPU + Library + Org.IdentityConnectors.Common + Common + v3.5 + True + False + 4 + false + + + bin\Debug\ + true + Full + False + True + DEBUG;TRACE + + + bin\Release\ + False + None + True + False + TRACE + + + False + Auto + 4194304 + AnyCPU + 4096 + + + + + + 3.5 + + + + 3.5 + + + + 3.5 + + + + + + + + + + + + + + + + + + + + + + diff --git a/DotNetConnectors.sln/Common/DateTimeUtil.cs b/DotNetConnectors.sln/Common/DateTimeUtil.cs new file mode 100644 index 00000000..55c086b7 --- /dev/null +++ b/DotNetConnectors.sln/Common/DateTimeUtil.cs @@ -0,0 +1,44 @@ +/* + * ==================== + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER. + * + * Copyright 2007-2008 Sun Microsystems, Inc. All rights reserved. + * + * The contents of this file are subject to the terms of the Common Development + * and Distribution License("CDDL") (the "License"). You may not use this file + * except in compliance with the License. + * + * You can obtain a copy of the License at + * http://IdentityConnectors.dev.java.net/legal/license.txt + * See the License for the specific language governing permissions and limitations + * under the License. + * + * When distributing the Covered Code, include this CDDL Header Notice in each file + * and include the License file at identityconnectors/legal/license.txt. + * If applicable, add the following below this CDDL Header, with the fields + * enclosed by brackets [] replaced by your own identifying information: + * "Portions Copyrighted [year] [name of copyright owner]" + * ==================== + */ +using System; + +namespace Org.IdentityConnectors.Common +{ + ///

+ /// Description of DateTimeUtil. + /// + public static class DateTimeUtil + { + public static DateTime GetDateTimeFromUtcMillis(long dateTime) { + return DateTime.FromFileTimeUtc(dateTime*10000); + } + + public static long GetUtcTimeMillis(DateTime dateTime) { + return dateTime.ToFileTimeUtc()/10000; + } + + public static long GetCurrentUtcTimeMillis() { + return GetUtcTimeMillis(DateTime.Now); + } + } +} diff --git a/DotNetConnectors.sln/Common/FrameworkInternalBridge.cs b/DotNetConnectors.sln/Common/FrameworkInternalBridge.cs new file mode 100644 index 00000000..a72c0e71 --- /dev/null +++ b/DotNetConnectors.sln/Common/FrameworkInternalBridge.cs @@ -0,0 +1,54 @@ +/* + * ==================== + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER. + * + * Copyright 2007-2008 Sun Microsystems, Inc. All rights reserved. + * + * The contents of this file are subject to the terms of the Common Development + * and Distribution License("CDDL") (the "License"). You may not use this file + * except in compliance with the License. + * + * You can obtain a copy of the License at + * http://IdentityConnectors.dev.java.net/legal/license.txt + * See the License for the specific language governing permissions and limitations + * under the License. + * + * When distributing the Covered Code, include this CDDL Header Notice in each file + * and include the License file at identityconnectors/legal/license.txt. + * If applicable, add the following below this CDDL Header, with the fields + * enclosed by brackets [] replaced by your own identifying information: + * "Portions Copyrighted [year] [name of copyright owner]" + * ==================== + */ +using System; +using System.Reflection; +namespace Org.IdentityConnectors.Common +{ + /// + /// Description of FrameworkInternalBridge. + /// + internal static class FrameworkInternalBridge { + private static readonly Object LOCK = new Object(); + private static Assembly _assembly = null; + /// + /// Loads a class from the FrameworkInternal module + /// + /// + /// + public static Type LoadType(String typeName) { + + Assembly assembly; + lock(LOCK) { + if (_assembly == null) { + AssemblyName assemName = new AssemblyName(); + assemName.Name = "FrameworkInternal"; + _assembly = Assembly.Load(assemName); + } + assembly = _assembly; + } + + return assembly.GetType(typeName,true); + + } + } +} diff --git a/DotNetConnectors.sln/Common/IOUtil.cs b/DotNetConnectors.sln/Common/IOUtil.cs new file mode 100644 index 00000000..41483294 --- /dev/null +++ b/DotNetConnectors.sln/Common/IOUtil.cs @@ -0,0 +1,47 @@ +/* + * ==================== + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER. + * + * Copyright 2007-2008 Sun Microsystems, Inc. All rights reserved. + * + * The contents of this file are subject to the terms of the Common Development + * and Distribution License("CDDL") (the "License"). You may not use this file + * except in compliance with the License. + * + * You can obtain a copy of the License at + * http://IdentityConnectors.dev.java.net/legal/license.txt + * See the License for the specific language governing permissions and limitations + * under the License. + * + * When distributing the Covered Code, include this CDDL Header Notice in each file + * and include the License file at identityconnectors/legal/license.txt. + * If applicable, add the following below this CDDL Header, with the fields + * enclosed by brackets [] replaced by your own identifying information: + * "Portions Copyrighted [year] [name of copyright owner]" + * ==================== + */ +using System; +using System.Net; +namespace Org.IdentityConnectors.Common +{ + /// + /// Description of IOUtil. + /// + public static class IOUtil + { + /// + /// Given an ip address or host, returns the + /// IPAddress + /// + /// + /// + public static IPAddress GetIPAddress(String hostOrIp) + { + if ( hostOrIp.Equals("0.0.0.0") || + hostOrIp.Equals("::0") ) { + return IPAddress.Parse(hostOrIp); + } + return Dns.GetHostAddresses(hostOrIp)[0]; + } + } +} diff --git a/DotNetConnectors.sln/Common/Locale.cs b/DotNetConnectors.sln/Common/Locale.cs new file mode 100644 index 00000000..b1620142 --- /dev/null +++ b/DotNetConnectors.sln/Common/Locale.cs @@ -0,0 +1,210 @@ +/* + * ==================== + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER. + * + * Copyright 2007-2008 Sun Microsystems, Inc. All rights reserved. + * + * The contents of this file are subject to the terms of the Common Development + * and Distribution License("CDDL") (the "License"). You may not use this file + * except in compliance with the License. + * + * You can obtain a copy of the License at + * http://IdentityConnectors.dev.java.net/legal/license.txt + * See the License for the specific language governing permissions and limitations + * under the License. + * + * When distributing the Covered Code, include this CDDL Header Notice in each file + * and include the License file at identityconnectors/legal/license.txt. + * If applicable, add the following below this CDDL Header, with the fields + * enclosed by brackets [] replaced by your own identifying information: + * "Portions Copyrighted [year] [name of copyright owner]" + * ==================== + */ +using System; +using System.Collections.Generic; +using System.Globalization; +namespace Org.IdentityConnectors.Common +{ + /// + /// Represents a Java Locale and has facilities for converting to/from + /// C# CultureInfo + /// + public sealed class Locale + { + private static readonly IDictionary + Locale2CultureInfoName = new Dictionary(); + private static readonly IDictionary + CultureInfoName2Locale = new Dictionary(); + + private static void AddMapping(Locale locale, String name) { + Locale2CultureInfoName[locale] = name; + CultureInfoName2Locale[name] = locale; + } + + /// + /// Special cases + /// + static Locale() + { + AddMapping(new Locale(),""); + AddMapping(new Locale("iw"),"he"); + AddMapping(new Locale("zh"),"zh-CHS"); + AddMapping(new Locale("iw","IL"),"he-IL"); + AddMapping(new Locale("no","NO"),"nb-NO"); + AddMapping(new Locale("no","NO","NY"),"nn-NO"); + } + + private String _language; + private String _country; + private String _variant; + + public Locale() + : this("") + { + + } + + public Locale(String language) + : this(language,"") + { + } + public Locale(String language, String country) + : this(language,country,"") + { + } + public Locale(String language, String country, String variant) + { + _language = language ?? ""; + _country = country ?? ""; + _variant = variant ?? ""; + } + + public string Language { + get { + return _language; + } + } + + public string Country { + get { + return _country; + } + } + + public string Variant { + get { + return _variant; + } + } + + public override bool Equals(Object o) { + Locale other = o as Locale; + if ( other != null ) { + if (!Object.Equals(Language,other.Language)) { + return false; + } + if (!Object.Equals(Country,other.Country)) { + return false; + } + if (!Object.Equals(Variant,other.Variant)) { + return false; + } + return true; + } + return false; + } + + public override int GetHashCode() { + unchecked { + return _language.GetHashCode()+_country.GetHashCode(); + } + } + + public override string ToString() { + return Language+"_"+Country+"_"+Variant; + } + + /// + /// Creates the closest CultureInfo that maps to this locale + /// + /// The culture info + public CultureInfo ToCultureInfo() + { + CultureInfo rv = null; + String code = CollectionUtil.GetValue(Locale2CultureInfoName,this,null); + if (code != null ) { + rv = TryCultureCode(code); + } + if ( rv == null ) { + if (Country.Length > 0) { + rv = TryCultureCode(Language+"-"+Country); + } + } + if ( rv == null ) { + rv = TryCultureCode(Language); + } + if ( rv == null ) { + rv = CultureInfo.InvariantCulture; + } + return rv; + } + + public static Locale FindLocale(CultureInfo info) + { + String code = info.Name; + Locale rv = CollectionUtil.GetValue(CultureInfoName2Locale,code,null); + if (rv == null) { + String [] parts = code.Split(new string[]{"-"}, + StringSplitOptions.RemoveEmptyEntries); + String language = ""; + String country = ""; + if ( parts.Length > 0 ) { + String l = parts[0]; + if (LooksLikeValidLanguageCode(l)) { + language = l; + if ( parts.Length > 1 ) { + String c = parts[1]; + if (LooksLikeValidCountryCode(c)) { + country = c; + } + } + } + } + rv = new Locale(language,country); + } + return rv; + } + + /// + /// Weeds out some junk + /// + /// + /// + private static bool LooksLikeValidLanguageCode(String l) { + char [] chars = l.ToCharArray(); + return (chars.Length == 2 && + Char.IsLower(chars[0]) && + Char.IsLower(chars[1])); + } + /// + /// Weeds out some junk + /// + /// + /// + private static bool LooksLikeValidCountryCode(String l) { + char [] chars = l.ToCharArray(); + return (chars.Length == 2 && + Char.IsUpper(chars[0]) && + Char.IsUpper(chars[1])); + } + + private static CultureInfo TryCultureCode(String code) { + try { + return new CultureInfo(code); + } + catch (Exception) { + return null; + } + } + } +} diff --git a/DotNetConnectors.sln/Common/Pair.cs b/DotNetConnectors.sln/Common/Pair.cs new file mode 100644 index 00000000..cad448f7 --- /dev/null +++ b/DotNetConnectors.sln/Common/Pair.cs @@ -0,0 +1,73 @@ +/* + * ==================== + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER. + * + * Copyright 2007-2008 Sun Microsystems, Inc. All rights reserved. + * + * The contents of this file are subject to the terms of the Common Development + * and Distribution License("CDDL") (the "License"). You may not use this file + * except in compliance with the License. + * + * You can obtain a copy of the License at + * http://IdentityConnectors.dev.java.net/legal/license.txt + * See the License for the specific language governing permissions and limitations + * under the License. + * + * When distributing the Covered Code, include this CDDL Header Notice in each file + * and include the License file at identityconnectors/legal/license.txt. + * If applicable, add the following below this CDDL Header, with the fields + * enclosed by brackets [] replaced by your own identifying information: + * "Portions Copyrighted [year] [name of copyright owner]" + * ==================== + */ +using System; + +namespace Org.IdentityConnectors.Common +{ + /// + /// Represents a Pair of objects + /// + public class Pair + { + public Pair() + { + } + + public Pair(T1 first, T2 second) + { + First = first; + Second = second; + } + + public T1 First { get; set; } + public T2 Second { get; set; } + + public override bool Equals(object obj) + { + Pair other = obj as Pair; + if ( other != null ) + { + return Object.Equals(First,other.First) && + Object.Equals(Second,other.Second); + } + return false; + } + + public override int GetHashCode() + { + int rv = 0; + if ( First != null ) { + rv ^= First.GetHashCode(); + } + if ( Second != null ) { + rv ^= Second.GetHashCode(); + } + return rv; + } + + public override string ToString() + { + return "( "+First+", "+Second+" )"; + } + } +} diff --git a/DotNetConnectors.sln/Common/Pooling.cs b/DotNetConnectors.sln/Common/Pooling.cs new file mode 100644 index 00000000..063c8610 --- /dev/null +++ b/DotNetConnectors.sln/Common/Pooling.cs @@ -0,0 +1,189 @@ +/* + * ==================== + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER. + * + * Copyright 2007-2008 Sun Microsystems, Inc. All rights reserved. + * + * The contents of this file are subject to the terms of the Common Development + * and Distribution License("CDDL") (the "License"). You may not use this file + * except in compliance with the License. + * + * You can obtain a copy of the License at + * http://IdentityConnectors.dev.java.net/legal/license.txt + * See the License for the specific language governing permissions and limitations + * under the License. + * + * When distributing the Covered Code, include this CDDL Header Notice in each file + * and include the License file at identityconnectors/legal/license.txt. + * If applicable, add the following below this CDDL Header, with the fields + * enclosed by brackets [] replaced by your own identifying information: + * "Portions Copyrighted [year] [name of copyright owner]" + * ==================== + */ +using System; + +using System.Collections.Generic; + +namespace Org.IdentityConnectors.Common.Pooling +{ + /** + * Configuration for pooling objects + */ + public sealed class ObjectPoolConfiguration { + + /** + * Max objects (idle+active). + */ + private int _maxObjects = 10; + + /** + * Max idle objects. + */ + private int _maxIdle = 10; + + /** + * Max time to wait if the pool is waiting for a free object to become + * available before failing. Zero means don't wait + */ + private long _maxWait = 150 * 1000; + + /** + * Minimum time to wait before evicting an idle object. + * Zero means don't wait + */ + private long _minEvictableIdleTimeMillis = 120 * 1000; + + /** + * Minimum number of idle objects. + */ + private int _minIdle = 1; + + + /** + * Get the set number of maximum objects (idle+active) + */ + public int MaxObjects { + get { + return _maxObjects; + } + set { + _maxObjects = value; + } + } + + /** + * Get the maximum number of idle objects. + */ + public int MaxIdle { + get { + return _maxIdle; + } + set { + _maxIdle = value; + } + } + + /** + * Max time to wait if the pool is waiting for a free object to become + * available before failing. Zero means don't wait + */ + public long MaxWait { + get { + return _maxWait; + } + set { + _maxWait = value; + } + } + + /** + * Minimum time to wait before evicting an idle object. + * Zero means don't wait + */ + public long MinEvictableIdleTimeMillis { + get { + return _minEvictableIdleTimeMillis; + } + set { + _minEvictableIdleTimeMillis = value; + } + } + + /** + * Minimum number of idle objects. + */ + public int MinIdle { + get { + return _minIdle; + } + set { + _minIdle = value; + } + } + + public void Validate() { + if (_minIdle < 0) { + throw new InvalidOperationException("Min idle is less than zero."); + } + if (_maxObjects < 0) { + throw new InvalidOperationException("Max active is less than zero."); + } + if (_maxIdle < 0) { + throw new InvalidOperationException("Max idle is less than zero."); + } + if (_maxWait < 0) { + throw new InvalidOperationException("Max wait is less than zero."); + } + if (_minEvictableIdleTimeMillis < 0) { + throw new InvalidOperationException("Min evictable idle time millis less than zero."); + } + if ( _minIdle > _maxIdle ) { + throw new InvalidOperationException("Min idle is greater than max idle."); + } + if ( _maxIdle > _maxObjects ) { + throw new InvalidOperationException("Max idle is greater than max objects."); + } + } + + public override int GetHashCode() { + unchecked { + return (int)(MaxObjects+MaxIdle+MaxWait+MinEvictableIdleTimeMillis+MinIdle); + } + } + + public override bool Equals(Object obj) { + if ( obj is ObjectPoolConfiguration) { + ObjectPoolConfiguration other = (ObjectPoolConfiguration)obj; + + if (MaxObjects != other.MaxObjects) { + return false; + } + if (MaxIdle != other.MaxIdle) { + return false; + } + if (MaxWait != other.MaxWait) { + return false; + } + if (MinEvictableIdleTimeMillis != other.MinEvictableIdleTimeMillis) { + return false; + } + if (MinIdle != other.MinIdle) { + return false; + } + return true; + } + return false; + } + + public override String ToString() { + // poor man's toString() + IDictionary bld = new Dictionary(); + bld["MaxObjects"] = MaxObjects; + bld["MaxIdle"] = MaxIdle; + bld["MaxWait"] = MaxWait; + bld["MinEvictableIdleTimeMillis"] = MinEvictableIdleTimeMillis; + bld["MinIdle"] = MinIdle; + return bld.ToString(); + } + } +} diff --git a/DotNetConnectors.sln/Common/Proxy.cs b/DotNetConnectors.sln/Common/Proxy.cs new file mode 100644 index 00000000..2534fdd0 --- /dev/null +++ b/DotNetConnectors.sln/Common/Proxy.cs @@ -0,0 +1,267 @@ +/* + * ==================== + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER. + * + * Copyright 2007-2008 Sun Microsystems, Inc. All rights reserved. + * + * The contents of this file are subject to the terms of the Common Development + * and Distribution License("CDDL") (the "License"). You may not use this file + * except in compliance with the License. + * + * You can obtain a copy of the License at + * http://IdentityConnectors.dev.java.net/legal/license.txt + * See the License for the specific language governing permissions and limitations + * under the License. + * + * When distributing the Covered Code, include this CDDL Header Notice in each file + * and include the License file at identityconnectors/legal/license.txt. + * If applicable, add the following below this CDDL Header, with the fields + * enclosed by brackets [] replaced by your own identifying information: + * "Portions Copyrighted [year] [name of copyright owner]" + * ==================== + */ +using System; +using System.Reflection; +using System.Reflection.Emit; +using System.Collections.Generic; +namespace Org.IdentityConnectors.Common.Proxy +{ + /// + /// Similar to java.lang.reflect.InvocationHandler + /// + public interface InvocationHandler + { + Object + Invoke(Object proxy, MethodInfo method, Object [] args); + } + + + /// + /// Similar to java.lang.reflect.Proxy + /// + public static class Proxy + { + private static readonly MethodInfo INVOCATION_HANDLER_METHOD = + typeof(InvocationHandler).GetMethod("Invoke", + new Type[]{typeof(object), + typeof(MethodInfo), + typeof(object[])}); + private static readonly MethodInfo GET_METHOD_FROM_HANDLE_METHOD = + typeof(MethodBase).GetMethod("GetMethodFromHandle", + new Type[]{typeof(RuntimeMethodHandle)}); + + private static readonly object LOCK = new Object(); + + private static IDictionary + _implementationMap = new Dictionary(); + + private static int _count = 0; + + public static Object NewProxyInstance(Type interfaze, + InvocationHandler handler) + { + ConstructorInfo constructor = GetOrCreateConstructorInfo(interfaze); + return constructor.Invoke(new object[]{handler}); + } + + private static ConstructorInfo GetOrCreateConstructorInfo(Type type) + { + lock(LOCK) + { + ConstructorInfo rv = + CollectionUtil.GetValue(_implementationMap,type,null); + if ( rv == null ) + { + Type impl = GenerateImplementation(type); + rv = + impl.GetConstructor(new Type[]{typeof(InvocationHandler)}); + + _implementationMap[type] = rv; + } + return rv; + } + } + + private static String NextName() + { + int count; + lock(LOCK) { + count = _count; + count++; + } + return "___proxy"+count; + } + + + + private static Type GenerateImplementation(Type interfaze) + { + if (!interfaze.IsInterface) + { + throw new ArgumentException("Type is not an interface: "+interfaze); + } + if ( interfaze.IsGenericType ) + { + throw new ArgumentException("Type is a generic type: "+interfaze); + } + + String uniqueName = NextName(); + + AssemblyName assemblyName = new AssemblyName(uniqueName); + AssemblyBuilder assemblyBuilder = + AppDomain.CurrentDomain.DefineDynamicAssembly( + assemblyName, + AssemblyBuilderAccess.RunAndSave); + // For a single-module assembly, the module name is usually + // the assembly name plus an extension. + ModuleBuilder moduleBuilder = + assemblyBuilder.DefineDynamicModule(assemblyName.Name, assemblyName.Name + ".dll"); + + TypeBuilder typeBuilder = moduleBuilder.DefineType( + uniqueName, + TypeAttributes.Public); + typeBuilder.AddInterfaceImplementation(interfaze); + + MethodInfo [] methods = interfaze.GetMethods(); + ConstructorBuilder classInitializer = + typeBuilder.DefineTypeInitializer(); + ILGenerator classInitializerCode = + classInitializer.GetILGenerator(); + //generate constructor and _handler field + FieldBuilder handlerField = + typeBuilder.DefineField("_handler", + typeof(InvocationHandler), + FieldAttributes.Private|FieldAttributes.InitOnly); + int count = 0; + foreach (MethodInfo method in methods) + { + GenerateMethod(typeBuilder,method,classInitializerCode,count, + handlerField); + count++; + } + classInitializerCode.Emit(OpCodes.Ret); + + + ConstructorBuilder constructorBuilder = typeBuilder.DefineConstructor( + MethodAttributes.Public, + CallingConventions.Standard, + new Type[]{typeof(InvocationHandler)}); + ILGenerator constructorCode = constructorBuilder.GetILGenerator(); + // For a constructor, argument zero is a reference to the new + // instance. Push it on the stack before calling the base + // class constructor. Specify the default constructor of the + // base class (System.Object) by passing an empty array of + // types (Type.EmptyTypes) to GetConstructor. + constructorCode.Emit(OpCodes.Ldarg_0); + constructorCode.Emit(OpCodes.Call, + typeof(object).GetConstructor(Type.EmptyTypes)); + constructorCode.Emit(OpCodes.Ldarg_0); + constructorCode.Emit(OpCodes.Ldarg_1); + constructorCode.Emit(OpCodes.Stfld, handlerField); + constructorCode.Emit(OpCodes.Ret); + + + Type t = typeBuilder.CreateType(); + + //assemblyBuilder.Save(assemblyName.Name+".dll"); + + return t; + } + + private static void ValidateMethod(MethodInfo info) + { + if ( info.GetGenericArguments().Length != 0 ) { + throw new ArgumentException( + "Method not supported since it has generic arguments: "+info); + + } + foreach (ParameterInfo parameter in info.GetParameters()) { + if ( parameter.IsOut ) { + throw new ArgumentException( + "Method not supported since it has output paramaters: "+info); + } + if ( parameter.IsRetval ) { + throw new ArgumentException( + "Method not supported since it has retval paramaters: "+info); + } + } + } + + private static void GenerateMethod(TypeBuilder typeBuilder, + MethodInfo method, + ILGenerator classInitializerCode, + int methodCount, + FieldBuilder handlerField) + { + ValidateMethod(method); + + FieldBuilder methodField = + typeBuilder.DefineField("METHOD"+methodCount, + typeof(MethodInfo), + FieldAttributes.Private|FieldAttributes.InitOnly|FieldAttributes.Static); + + + + classInitializerCode.Emit(OpCodes.Ldtoken, + method); + classInitializerCode.Emit(OpCodes.Call, + GET_METHOD_FROM_HANDLE_METHOD); + classInitializerCode.Emit(OpCodes.Castclass,typeof(MethodInfo)); + classInitializerCode.Emit(OpCodes.Stsfld,methodField); + + + Type [] parameterTypes = ReflectionUtil.GetParameterTypes(method); + MethodBuilder methodBuilder = typeBuilder.DefineMethod( + method.Name, + MethodAttributes.Public| + MethodAttributes.HideBySig| + MethodAttributes.NewSlot| + MethodAttributes.Virtual| + MethodAttributes.Final, + method.CallingConvention, + method.ReturnType, + parameterTypes); + + ILGenerator methodCode = methodBuilder.GetILGenerator(); + methodCode.Emit(OpCodes.Ldarg_0); + methodCode.Emit(OpCodes.Ldfld,handlerField); + methodCode.Emit(OpCodes.Ldarg_0); + methodCode.Emit(OpCodes.Ldsfld,methodField); + methodCode.Emit(OpCodes.Ldc_I4,parameterTypes.Length); + methodCode.Emit(OpCodes.Newarr,typeof(object)); + + for (int index = 0; index < parameterTypes.Length; index++) + { + Type parameterType = parameterTypes[index]; + methodCode.Emit(OpCodes.Dup); + methodCode.Emit(OpCodes.Ldc_I4,index); + methodCode.Emit(OpCodes.Ldarg,index+1); + if (parameterType.IsValueType) + { + methodCode.Emit(OpCodes.Box,parameterType); + } + methodCode.Emit(OpCodes.Stelem_Ref); + } + + methodCode.Emit(OpCodes.Callvirt,INVOCATION_HANDLER_METHOD); + Type returnType = method.ReturnType; + + if (typeof(void).Equals(returnType)) + { + methodCode.Emit(OpCodes.Pop); + } + else if (returnType.IsValueType) + { + methodCode.Emit(OpCodes.Unbox_Any,returnType); + } + else + { + methodCode.Emit(OpCodes.Castclass,returnType); + } + + methodCode.Emit(OpCodes.Ret); + + typeBuilder.DefineMethodOverride(methodBuilder,method); + } + } +} diff --git a/DotNetConnectors.sln/Common/ReflectionUtil.cs b/DotNetConnectors.sln/Common/ReflectionUtil.cs new file mode 100644 index 00000000..38a455a3 --- /dev/null +++ b/DotNetConnectors.sln/Common/ReflectionUtil.cs @@ -0,0 +1,149 @@ +/* + * ==================== + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER. + * + * Copyright 2007-2008 Sun Microsystems, Inc. All rights reserved. + * + * The contents of this file are subject to the terms of the Common Development + * and Distribution License("CDDL") (the "License"). You may not use this file + * except in compliance with the License. + * + * You can obtain a copy of the License at + * http://IdentityConnectors.dev.java.net/legal/license.txt + * See the License for the specific language governing permissions and limitations + * under the License. + * + * When distributing the Covered Code, include this CDDL Header Notice in each file + * and include the License file at identityconnectors/legal/license.txt. + * If applicable, add the following below this CDDL Header, with the fields + * enclosed by brackets [] replaced by your own identifying information: + * "Portions Copyrighted [year] [name of copyright owner]" + * ==================== + */ +using System; +using System.Reflection; +using System.Collections.Generic; +using System.Linq; +namespace Org.IdentityConnectors.Common +{ + /// + /// Miscellaenous reflection utilities. + /// + public static class ReflectionUtil + { + /// + /// Gets all the interfaces in the hierarchy + /// + /// + /// + public static Type [] GetAllInterfaces(Type type) { + HashSet temp = new HashSet(); + GetAllInterfaces2(type,temp); + return temp.ToArray(); + } + + private static void GetAllInterfaces2(Type type, HashSet rv) { + if ( type != null ) { + if ( type.IsInterface ) { + rv.Add(type); + } + GetAllInterfaces2(type.BaseType,rv); + foreach (Type inter in type.GetInterfaces()) { + GetAllInterfaces2(inter,rv); + } + } + } + + /// + /// Returns the generic type definition of the given type + /// + /// + /// + public static Type [] GetTypeErasure(Type [] types) { + Type [] rv = new Type[types.Length]; + for ( int i = 0; i < types.Length; i++ ) { + rv[i] = GetTypeErasure(types[i]); + } + return rv; + } + /// + /// Returns the generic type definition of the given type + /// + /// + /// + public static Type GetTypeErasure(Type type) { + if ( type.IsGenericType ) { + type = type.GetGenericTypeDefinition(); + } + return type; + } + + /// + /// Returns true iff t1 is a parent type of t2. Unlike + /// Type.isAssignableFrom, this handles generic types as + /// well. + /// + /// + /// + /// + public static bool IsParentTypeOf(Type t1, + Type t2) { + if (t2 == null) { + return t1 == null; + } + Type found = FindInHierarchyOf(t1,t2); + return found != null; + } + + /// + /// Finds t1 in the hierarchy of t2 or null if not found. The + /// returned value will have generic parameters applied to it. + /// + /// + /// + /// + public static Type FindInHierarchyOf(Type t1, Type t2) { + if (t2 == null) { + return null; + } + if ( EqualsIgnoreGeneric(t1,t2) ) { + return t2; + } + Type found1 = FindInHierarchyOf(t1,t2.BaseType); + if (found1 != null) { + return found1; + } + foreach (Type inter in t2.GetInterfaces()) { + Type found2 = FindInHierarchyOf(t1,inter); + if (found2 != null) { + return found2; + } + } + return null; + } + + private static bool EqualsIgnoreGeneric(Type t1, + Type t2) { + if ( t1 == null || t2 == null ) { + return t1 == null && t2 == null; + } + if ( t1.IsGenericType ) { + t1 = t1.GetGenericTypeDefinition(); + } + if ( t2.IsGenericType ) { + t2 = t2.GetGenericTypeDefinition(); + } + return t1.Equals(t2); + } + + public static Type [] GetParameterTypes(MethodInfo method) + { + ParameterInfo [] parameters = method.GetParameters(); + Type [] rv = new Type[parameters.Length]; + for (int i = 0; i < parameters.Length; i++) { + rv[i] = parameters[i].ParameterType; + } + return rv; + } + } +} diff --git a/DotNetConnectors.sln/Common/SafeType.cs b/DotNetConnectors.sln/Common/SafeType.cs new file mode 100644 index 00000000..31c4621e --- /dev/null +++ b/DotNetConnectors.sln/Common/SafeType.cs @@ -0,0 +1,142 @@ +/* + * ==================== + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER. + * + * Copyright 2007-2008 Sun Microsystems, Inc. All rights reserved. + * + * The contents of this file are subject to the terms of the Common Development + * and Distribution License("CDDL") (the "License"). You may not use this file + * except in compliance with the License. + * + * You can obtain a copy of the License at + * http://IdentityConnectors.dev.java.net/legal/license.txt + * See the License for the specific language governing permissions and limitations + * under the License. + * + * When distributing the Covered Code, include this CDDL Header Notice in each file + * and include the License file at identityconnectors/legal/license.txt. + * If applicable, add the following below this CDDL Header, with the fields + * enclosed by brackets [] replaced by your own identifying information: + * "Portions Copyrighted [year] [name of copyright owner]" + * ==================== + */ +using System; + +using System.Reflection; + +namespace Org.IdentityConnectors.Common +{ + /// + /// The equivalent of java's Class<? extends...%gt; syntax. + /// Allows you to restrict a Type to a certain class hierarchy. + /// + public sealed class SafeType + where T : class + { + private readonly Type _rawType; + + /// + /// Make private so no one can create directly + /// + private SafeType(Type rawType) + { + if (!ReflectionUtil.IsParentTypeOf(typeof(T),rawType)) { + throw new ArgumentException("Type: "+rawType+" is not a subclass of"+typeof(T)); + } + _rawType = rawType; + } + + /// + /// Returns the SafeType for a given raw type. + /// NOTE: If possible use Get() instead since it is statically + /// checked at compile time. + /// + /// + /// + public static SafeType ForRawType(Type type) + { + return new SafeType(type); + } + + /// + /// Gets an instance of the safe type in a type-safe fashion. + /// + /// The instance of the safe type + public static SafeType Get() + where U : T + { + return new SafeType(typeof(U)); + } + + /// + /// Gets an instance of the safe type in a type-safe fashion from an object. + /// + /// The instance of the safe type + public static SafeType Get(T obj) + { + return new SafeType(obj.GetType()); + } + + /// + /// Returns the generic type definition corresponding to this type. + /// Will return the same type if this is already a generic type. + /// + /// + public SafeType GetTypeErasure() + { + return SafeType.ForRawType(ReflectionUtil.GetTypeErasure(RawType)); + } + + /// + /// Returns the underlying C# type + /// + public Type RawType { + get { + return _rawType; + } + } + + /// + /// Creates a new instance of the given type + /// + /// The new instance + public T CreateInstance() + { + return (T)Activator.CreateInstance(RawType); + } + + /// + /// Returns true iff these represent the same underlying type + /// and the SafeType has the same parent type. + /// + /// The other + /// true iff these represent the same underylying type + /// and the TypeGroup has the same parent type + public override bool Equals(object o) + { + if ( o is SafeType ) { + SafeType other = (SafeType)o; + return RawType.Equals(other.RawType); + } + return false; + } + /// + /// Returns a hash of the type + /// + /// a hash of the type + public override int GetHashCode() + { + return RawType.GetHashCode(); + } + /// + /// Returns a string representation of the member type + /// + /// a string representation of the member type + public override string ToString() + { + return RawType.ToString(); + } + } + + +} diff --git a/DotNetConnectors.sln/Common/Script.cs b/DotNetConnectors.sln/Common/Script.cs new file mode 100644 index 00000000..4d1bbc36 --- /dev/null +++ b/DotNetConnectors.sln/Common/Script.cs @@ -0,0 +1,167 @@ +/* + * ==================== + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER. + * + * Copyright 2007-2008 Sun Microsystems, Inc. All rights reserved. + * + * The contents of this file are subject to the terms of the Common Development + * and Distribution License("CDDL") (the "License"). You may not use this file + * except in compliance with the License. + * + * You can obtain a copy of the License at + * http://IdentityConnectors.dev.java.net/legal/license.txt + * See the License for the specific language governing permissions and limitations + * under the License. + * + * When distributing the Covered Code, include this CDDL Header Notice in each file + * and include the License file at identityconnectors/legal/license.txt. + * If applicable, add the following below this CDDL Header, with the fields + * enclosed by brackets [] replaced by your own identifying information: + * "Portions Copyrighted [year] [name of copyright owner]" + * ==================== + */ +using System; +using System.IO; +using System.Reflection; +using System.Collections.Generic; +using System.Diagnostics; + +namespace Org.IdentityConnectors.Common.Script +{ + public interface ScriptExecutor { + /// + /// Executes the script with the given arguments. + /// + /// key/value set of variables to + /// pass to the script. + /// + object Execute(IDictionary arguments); + } + public abstract class ScriptExecutorFactory { + + private static readonly object LOCK = new object(); + + /// + /// Loaded w/ all supported languages. + /// + private static IDictionary _supportedLanguages = null; + + /// + /// Load all script executor factory assemblies in the same directory + /// the 'Common' assembly. + /// + private static IDictionary LoadSupportedLanguages() { + // attempt to process all assemblies.. + IDictionary ret = new Dictionary(); + Assembly assembly = Assembly.GetExecutingAssembly(); + FileInfo thisAssemblyFile = new FileInfo(assembly.Location); + DirectoryInfo directory = thisAssemblyFile.Directory; + // get all *ScriptExecutorFactory assmebly from the current directory + FileInfo [] files = directory.GetFiles("*.ScriptExecutorFactory.dll"); + Type t = typeof(ScriptExecutorFactoryClassAttribute); + foreach (FileInfo file in files) { + try { + Assembly lib = Assembly.LoadFrom(file.ToString()); + foreach (Type type in lib.GetTypes()) { + Object [] attributes = type.GetCustomAttributes(t, false); + if ( attributes.Length > 0 ) { + ScriptExecutorFactoryClassAttribute attribute = + (ScriptExecutorFactoryClassAttribute)attributes[0]; + // attempt to test assembly.. + Activator.CreateInstance(type); + // if we made it this far its okay + ret[attribute.Language.ToUpper()] = type; + } + } + } + catch (Exception e) { + TraceUtil.TraceException("Unable to load assembly: "+ + assembly.FullName+". This is a fatal exception: ",e); + throw; + } + } + return ret; + } + + private static IDictionary GetSupportedLanguages() + { + lock (LOCK) + { + if (_supportedLanguages == null) + { + _supportedLanguages = LoadSupportedLanguages(); + } + return _supportedLanguages; + } + } + + /** + * Returns the set of supported languages. + * @return The set of supported languages. + */ + public static ICollection SupportedLanguages + { + get + { + IDictionary map = + GetSupportedLanguages(); + return CollectionUtil.AsReadOnlySet(map.Keys); + } + } + + /** + * Creates a ScriptExecutorFactory for the given language + * @param language The name of the language + * @return The script executor factory + * @throws IllegalArgumentException If the given language is not + * supported. + */ + public static ScriptExecutorFactory NewInstance(String language) { + if ( language == null ) { + throw new ArgumentException("Language must be specified"); + } + Type type = CollectionUtil.GetValue(GetSupportedLanguages(),language.ToUpper(),null); + if ( type == null ) { + throw new ArgumentException("Language not supported: "+language); + } + return (ScriptExecutorFactory) Activator.CreateInstance(type); + } + + + /** + * Creates a script executor for the given script. + * @param loader The classloader that contains the java classes + * that the script should have access to. + * @param script The script text. + * @param compile A hint to tell the script executor whether or + * not to compile the given script. This need not be implemented + * by all script executors. If true, the caller is saying that + * they intend to call the script multiple times with different + * arguments, so compile if possible. + * @return A script executor. + */ + public abstract ScriptExecutor NewScriptExecutor( + Assembly [] referencedAssemblies, + String script, + bool compile); + } + + [AttributeUsage(AttributeTargets.Class, AllowMultiple=false)] + public class ScriptExecutorFactoryClassAttribute : System.Attribute { + private readonly string _lang; + + /// + /// Determine the language supported by the factory. + /// + /// + public ScriptExecutorFactoryClassAttribute(string lang) { + _lang = lang; + } + + public string Language { + get { + return _lang; + } + } + } +} diff --git a/DotNetConnectors.sln/Common/Security.cs b/DotNetConnectors.sln/Common/Security.cs new file mode 100644 index 00000000..4d7473bf --- /dev/null +++ b/DotNetConnectors.sln/Common/Security.cs @@ -0,0 +1,542 @@ +/* + * ==================== + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER. + * + * Copyright 2007-2008 Sun Microsystems, Inc. All rights reserved. + * + * The contents of this file are subject to the terms of the Common Development + * and Distribution License("CDDL") (the "License"). You may not use this file + * except in compliance with the License. + * + * You can obtain a copy of the License at + * http://IdentityConnectors.dev.java.net/legal/license.txt + * See the License for the specific language governing permissions and limitations + * under the License. + * + * When distributing the Covered Code, include this CDDL Header Notice in each file + * and include the License file at identityconnectors/legal/license.txt. + * If applicable, add the following below this CDDL Header, with the fields + * enclosed by brackets [] replaced by your own identifying information: + * "Portions Copyrighted [year] [name of copyright owner]" + * ==================== + */ +using System; +using System.Security; +using System.Security.Cryptography; +using System.Runtime.InteropServices; +using System.Text; +namespace Org.IdentityConnectors.Common.Security +{ + #region UnmanagedArray + /// + /// Places an array facade on an unmanaged memory data structure that + /// holds senstive data. (In C# placing senstive data in managed + /// memory is considered unsecure since the memory model allows + /// data to be copied around). + /// + public interface UnmanagedArray : IDisposable + { + int Length {get;} + T this[int index] {get;set;} + } + #endregion + + /** + * Secure string implementation that solves the problems associated with + * keeping passwords as java.lang.String. That is, anything + * represented as a String is kept in memory as a clear + * text password and stays in memory at least until it is garbage collected. + *

+ * The GuardedString class alleviates this problem by storing the characters in + * memory in an encrypted form. The encryption key will be a randomly-generated + * key. + *

+ * In their serialized form, GuardedString will be encrypted using a known + * default key. This is to provide a minimum level of protection regardless + * of the transport. For communications with the Remote Connector Framework + * it is recommended that deployments enable SSL for true encryption. + *

+ * Applications may also wish to persist GuardedStrings. In the case of + * Identity Manager, it should convert GuardedStrings to EncryptedData so + * that they can be stored and managed using the Manage Encryption features + * of Identity Manager. Other applications may wish to serialize APIConfiguration + * as a whole. These applications are responsible for encrypting the APIConfiguration + * blob for an additional layer of security (beyond the basic default key encryption + * provided by GuardedString). + */ + public sealed class GuardedString : IDisposable { + /** + * This method will be called with the clear text of the string. + * After the call the clearChars array will be automatically zeroed + * out, thus keeping the window of potential exposure to a bare-minimum. + * @param clearChars + */ + public delegate void Accessor(UnmanagedArray clearChars); + + + private SecureString _target; + private String _base64SHA1Hash; + + /** + * Creates an empty secure string + */ + public GuardedString() { + _target = new SecureString(); + ComputeHash(); + } + + public GuardedString(SecureString str) { + _target = str.Copy(); + ComputeHash(); + } + + + /** + * Provides access to the clear-text value of the string in a controlled fashion. + * The clear-text characters will only be available for the duration of the call + * and automatically zeroed out following the call. + * + *

+ * NOTE: Callers are encouraged to use {@link #verifyBase64SHA1Hash(String)} + * where possible if the intended use is merely to verify the contents of + * the string match an expected hash value. + * @param accessor Accessor callback. + * @throws IllegalStateException If the string has been disposed + */ + public void Access(Accessor accessor) { + using (SecureStringAdapter adapter = new SecureStringAdapter(_target)) { + accessor(adapter); + } + } + + /** + * Appends a single clear-text character to the secure string. + * The in-memory data will be decrypted, the character will be + * appended, and then it will be re-encrypted. + * @param c The character to append. + * @throws IllegalStateException If the string is read-only + * @throws IllegalStateException If the string has been disposed + */ + public void AppendChar(char c) { + _target.AppendChar(c); + ComputeHash(); + } + + /** + * Clears the in-memory representation of the string. + */ + public void Dispose() { + _target.Dispose(); + } + + /** + * Returns true iff this string has been marked read-only + * @return true iff this string has been marked read-only + * @throws IllegalStateException If the string has been disposed + */ + public bool IsReadOnly() { + return _target.IsReadOnly(); + } + + /** + * Mark this string as read-only. + * @throws IllegalStateException If the string has been disposed + */ + public void MakeReadOnly() { + _target.MakeReadOnly(); + } + + /** + * Create a copy of the string. If this instance is read-only, + * the copy will not be read-only. + * @return A copy of the string. + * @throws IllegalStateException If the string has been disposed + */ + public GuardedString Copy() { + SecureString t2 = _target.Copy(); + GuardedString rv = new GuardedString(t2); + return rv; + } + + /** + * Verifies that this base-64 encoded SHA1 hash of this string + * matches the given value. + * @param hash The hash to verify against. + * @return True if the hash matches the given parameter. + * @throws IllegalStateException If the string has been disposed + */ + public bool VerifyBase64SHA1Hash(String hash) { + CheckNotDisposed(); + return _base64SHA1Hash.Equals(hash); + } + + public string GetBase64SHA1Hash() { + CheckNotDisposed(); + return _base64SHA1Hash; + } + + + + private void CheckNotDisposed() + { + //this throws if disposed + _target.IsReadOnly(); + } + + + public override bool Equals(Object o) { + if ( o is GuardedString ) { + GuardedString other = (GuardedString)o; + //not the true contract of equals. however, + //due to the high mathematical improbability of + //two unequal strings having the same secure hash, + //this approach feels good. the alternative, + //decrypting for comparison, is simply too + //performance intensive to be used for equals + return _base64SHA1Hash.Equals(other._base64SHA1Hash); + } + return false; + } + + public override int GetHashCode() { + return _base64SHA1Hash.GetHashCode(); + } + + public SecureString ToSecureString() { + return _target.Copy(); + } + + private void ComputeHash() + { + Access(array=> { + _base64SHA1Hash = SecurityUtil.ComputeBase64SHA1Hash(array); + }); + } + + } + + + #region AbstractUnmanagedArray + public abstract class AbstractUnmanagedArray : UnmanagedArray + { + private readonly int _length; + private bool _disposed; + public AbstractUnmanagedArray(int length) { + if (length < 0) { + throw new ArgumentException("Invalid length: "+length); + } + _length = length; + } + public int Length { + get { + if (_disposed) { + throw new ObjectDisposedException("UnmanagedArray"); + } + return _length; + } + } + public T this[int index] { + get { + if (_disposed) { + throw new ObjectDisposedException("UnmanagedArray"); + } + if ( index < 0 || index >= Length ) { + throw new IndexOutOfRangeException(); + } + return GetValue(index); + } + set { + if (_disposed) { + throw new ObjectDisposedException("SecureStringAdapter"); + } + if ( index < 0 || index >= Length ) { + throw new IndexOutOfRangeException(); + } + SetValue(index,value); + } + } + public void Dispose() { + if (!_disposed) { + for ( int i = 0; i < Length; i++ ) { + this[i] = default(T); + } + _disposed = true; + FreeMemory(); + } + } + + abstract protected T GetValue(int index); + abstract protected void SetValue(int index, T val); + abstract protected void FreeMemory(); + } + #endregion + + #region SecureStringAdapter + internal class SecureStringAdapter : AbstractUnmanagedArray + { + private IntPtr _bstrPtr; + public SecureStringAdapter(SecureString secureString) : base(secureString.Length) { + Assertions.NullCheck(secureString,"secureString"); + _bstrPtr = Marshal.SecureStringToBSTR(secureString); + } + protected override char GetValue(int index) + { + unsafe { + char * charPtr = (char*)_bstrPtr; + return *(charPtr+index); + } + } + protected override void SetValue(int index, char c) + { + unsafe { + char * charPtr = (char*)_bstrPtr; + *(charPtr+index) = c; + } + } + protected override void FreeMemory() + { + Marshal.ZeroFreeBSTR( _bstrPtr ); + } + } + #endregion + + #region UnmanagedCharArray + public class UnmanagedCharArray : AbstractUnmanagedArray + { + private IntPtr _ptr; + public UnmanagedCharArray(int length) : base(length) { + unsafe { + _ptr = Marshal.AllocHGlobal(length*sizeof(char)); + } + } + protected override char GetValue(int index) + { + unsafe { + char * charPtr = (char*)_ptr; + return *(charPtr+index); + } + } + protected override void SetValue(int index, char c) + { + unsafe { + char * charPtr = (char*)_ptr; + *(charPtr+index) = c; + } + } + protected override void FreeMemory() + { + Marshal.FreeHGlobal( _ptr ); + } + } + #endregion + + #region UnmanagedByteArray + public class UnmanagedByteArray : AbstractUnmanagedArray + { + private IntPtr _ptr; + public UnmanagedByteArray(int length) : base(length) { + unsafe { + _ptr = Marshal.AllocHGlobal(length*sizeof(byte)); + } + } + protected override byte GetValue(int index) + { + unsafe { + byte * charPtr = (byte*)_ptr; + return *(charPtr+index); + } + } + protected override void SetValue(int index, byte c) + { + unsafe { + byte * charPtr = (byte*)_ptr; + *(charPtr+index) = c; + } + } + protected override void FreeMemory() + { + Marshal.FreeHGlobal( _ptr ); + } + } + #endregion + + #region SecurityUtil + ///

+ /// Description of SecurityUtil. + /// + public static class SecurityUtil + { + + /** + * Converts chars to bytes without using any external functions + * that might allocate additional buffers for the potentially + * sensitive data. This guarantees the caller that they only + * need to cleanup the input and result. + * @param chars The chars + * @return The bytes + */ + public static UnmanagedArray CharsToBytes(UnmanagedArray chars) + { + UnmanagedByteArray bytes = new UnmanagedByteArray(chars.Length*2); + + for ( int i = 0; i < chars.Length; i++ ) { + char v = chars[i]; + bytes[i*2] = (byte)(0xff & (v >> 8)); + bytes[i*2+1] = (byte)(0xff & (v)); + } + return bytes; + } + + /** + * Converts bytes to chars without using any external functions + * that might allocate additional buffers for the potentially + * sensitive data. This guarantees the caller that they only + * need to cleanup the input and result. + * @param chars The chars + * @return The bytes + */ + public static UnmanagedArray BytesToChars(UnmanagedArray bytes) + { + UnmanagedCharArray chars = new UnmanagedCharArray(bytes.Length/2); + for ( int i = 0; i < chars.Length; i++ ) { + char v = (char)((bytes[i*2]<<8) | bytes[i*2+1]); + chars[i] = v; + } + return chars; + } + + + public unsafe static string ComputeBase64SHA1Hash(UnmanagedArray input) + { + using (UnmanagedArray bytes = SecurityUtil.CharsToBytes(input)) { + byte [] managedBytes = new byte[bytes.Length]; + fixed (byte*dummy=managedBytes) { //pin it + try { + //populate it in pinned block + SecurityUtil.UnmanagedBytesToManagedBytes(bytes,managedBytes); + SHA1 hasher = SHA1.Create(); + byte[] data = hasher.ComputeHash(managedBytes); + return Convert.ToBase64String(data); + } + finally { + //clear it before we leave pinned block + SecurityUtil.Clear(managedBytes); + } + } + } + } + + /** + * Copies an unmanaged byte array into a managed byte array. + * NOTE: it is imperative for security reasons that this only + * be done in a context where the byte array in question is pinned. + * moreover, the byte array must be cleared prior to leaving the + * pinned block + */ + public static void UnmanagedBytesToManagedBytes(UnmanagedArray array, + byte [] bytes) + { + for ( int i = 0 ; i < array.Length; i++ ) + { + bytes[i] = array[i]; + } + } + + /** + * Clears an array of potentially sensitive bytes + * @param bytes The bytes. May be null. + * NOTE: because this is C#, this alone is not enough. The + * array must be pinned during the interval it is in-use or + * it could be copied out from under you. + */ + public static void Clear(byte [] bytes) + { + if ( bytes != null ) + { + for ( int i = 0; i < bytes.Length; i++ ) + { + bytes[i] = 0; + } + } + } + + /** + * Clears an array of potentially sensitive chars + * @param chars The characters. May be null. + * NOTE: because this is C#, this alone is not enough. The + * array must be pinned during the interval it is in-use or + * it could be copied out from under you. + */ + public static void Clear(char [] chars) + { + if ( chars != null ) + { + for ( int i = 0; i < chars.Length; i++) + { + chars[i] = (char)0; + } + } + } + + public static bool VerifyBase64SHA1Hash(UnmanagedArray input, string hash) + { + string inputHash = ComputeBase64SHA1Hash(input); + return inputHash.Equals(hash); + } + } + #endregion + + #region Encryptor + /** + * Responsible for encrypting/decrypting bytes. Implementations + * are intended to be thread-safe. + */ + public interface Encryptor { + /** + * Decrypts the given byte array + * @param bytes The encrypted bytes + * @return The decrypted bytes + */ + UnmanagedArray Decrypt(byte [] bytes); + + /** + * Encrypts the given byte array + * @param bytes The clear bytes + * @return The ecnrypted bytes + */ + byte [] Encrypt(UnmanagedArray bytes); + } + #endregion + + #region EncryptorFactory + public abstract class EncryptorFactory { + private static readonly object LOCK = new object(); + + // At some point we might make this pluggable, but for now, hard-code + private const String IMPL_NAME = "Org.IdentityConnectors.Common.Security.Impl.EncryptorFactoryImpl"; + + private static EncryptorFactory _instance; + + /** + * Get the singleton instance of the {@link EncryptorFactory}. + */ + public static EncryptorFactory GetInstance() { + lock(LOCK) { + if (_instance == null) { + Type type = FrameworkInternalBridge.LoadType(IMPL_NAME); + _instance = (EncryptorFactory)Activator.CreateInstance(type); + } + return _instance; + } + } + + /** + * Default encryptor that encrypts/descrypts using a default key + */ + public abstract Encryptor GetDefaultEncryptor(); + + + } + #endregion + +} diff --git a/DotNetConnectors.sln/Common/StringUtil.cs b/DotNetConnectors.sln/Common/StringUtil.cs new file mode 100644 index 00000000..2fef0f7b --- /dev/null +++ b/DotNetConnectors.sln/Common/StringUtil.cs @@ -0,0 +1,92 @@ +/* + * ==================== + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER. + * + * Copyright 2007-2008 Sun Microsystems, Inc. All rights reserved. + * + * The contents of this file are subject to the terms of the Common Development + * and Distribution License("CDDL") (the "License"). You may not use this file + * except in compliance with the License. + * + * You can obtain a copy of the License at + * http://IdentityConnectors.dev.java.net/legal/license.txt + * See the License for the specific language governing permissions and limitations + * under the License. + * + * When distributing the Covered Code, include this CDDL Header Notice in each file + * and include the License file at identityconnectors/legal/license.txt. + * If applicable, add the following below this CDDL Header, with the fields + * enclosed by brackets [] replaced by your own identifying information: + * "Portions Copyrighted [year] [name of copyright owner]" + * ==================== + */ +using System; +using Org.IdentityConnectors.Common.Security; +namespace Org.IdentityConnectors.Common +{ + public static class StringUtil + { + /** + * Determines if a string is empty. Empty is defined as null or empty + * string. + * + *
+         *  StringUtil.isEmpty(null)               = true
+         *  StringUtil.isEmpty("")       = true
+         *  StringUtil.isEmpty(" ")      = false
+         *  StringUtil.isEmpty("bob")    = false
+         *  StringUtil.isEmpty(" bob ")  = false
+         * 
+ * + * @param val + * string to evaluate as empty. + * @return true if the string is empty else false. + */ + public static bool IsEmpty(String val) { + return (val == null) ? true : val.Length == 0; + } + + /** + *
+         *      StringUtil.isBlank(null)                = true
+         *      StringUtil.isBlank("")        = true
+         *      StringUtil.isBlank(" ")       = true
+         *      StringUtil.isBlank("bob")     = false
+         *      StringUtil.isBlank("  bob  ") = false
+         * 
+ */ + public static bool IsBlank(String val) { + return (val == null) ? true : IsEmpty(val.Trim()); + } + + /// + /// Constructs a secure string from a char []. The char[] will + /// be cleared out when finished. + /// + /// The characters to use. Will be cleared + /// out. + /// A secure string representation + public static GuardedString NewGuardedString(char [] val) + { + GuardedString rv = new GuardedString(); + for( int i = 0; i < val.Length; i++ ) + { + rv.AppendChar(val[i]); + val[i] = (char)0; + } + return rv; + } + + + public static bool IsTrue(string val) { + if (!IsBlank(val)) { + // clean up the value.. + val = val.Trim().ToLower(); + if (val.Equals("1") || val.Equals("on") || val.Equals("true")) { + return true; + } + } + return false; + } + } +} diff --git a/DotNetConnectors.sln/Common/TraceUtil.cs b/DotNetConnectors.sln/Common/TraceUtil.cs new file mode 100644 index 00000000..d12f5e70 --- /dev/null +++ b/DotNetConnectors.sln/Common/TraceUtil.cs @@ -0,0 +1,51 @@ +/* + * ==================== + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER. + * + * Copyright 2007-2008 Sun Microsystems, Inc. All rights reserved. + * + * The contents of this file are subject to the terms of the Common Development + * and Distribution License("CDDL") (the "License"). You may not use this file + * except in compliance with the License. + * + * You can obtain a copy of the License at + * http://IdentityConnectors.dev.java.net/legal/license.txt + * See the License for the specific language governing permissions and limitations + * under the License. + * + * When distributing the Covered Code, include this CDDL Header Notice in each file + * and include the License file at identityconnectors/legal/license.txt. + * If applicable, add the following below this CDDL Header, with the fields + * enclosed by brackets [] replaced by your own identifying information: + * "Portions Copyrighted [year] [name of copyright owner]" + * ==================== + */ +using System; +using System.Diagnostics; +using System.Text; +namespace Org.IdentityConnectors.Common +{ + /// + /// Description of TraceUtil. + /// + public static class TraceUtil + { + /// + /// Traces an exception with its stack trace + /// + /// An optional error message to display in addition to the exception + /// The exception + public static void TraceException(String msg, Exception e) { + StringBuilder builder = new StringBuilder(); + if ( msg != null ) + { + builder.AppendLine(msg); + } + if ( e != null ) + { + builder.AppendLine(e.ToString()); + } + Trace.TraceError(builder.ToString()); + } + } +} diff --git a/DotNetConnectors.sln/Common/XmlUtil.cs b/DotNetConnectors.sln/Common/XmlUtil.cs new file mode 100644 index 00000000..30ea8040 --- /dev/null +++ b/DotNetConnectors.sln/Common/XmlUtil.cs @@ -0,0 +1,192 @@ +/* + * ==================== + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER. + * + * Copyright 2007-2008 Sun Microsystems, Inc. All rights reserved. + * + * The contents of this file are subject to the terms of the Common Development + * and Distribution License("CDDL") (the "License"). You may not use this file + * except in compliance with the License. + * + * You can obtain a copy of the License at + * http://IdentityConnectors.dev.java.net/legal/license.txt + * See the License for the specific language governing permissions and limitations + * under the License. + * + * When distributing the Covered Code, include this CDDL Header Notice in each file + * and include the License file at identityconnectors/legal/license.txt. + * If applicable, add the following below this CDDL Header, with the fields + * enclosed by brackets [] replaced by your own identifying information: + * "Portions Copyrighted [year] [name of copyright owner]" + * ==================== + */ +using System; +using System.Xml; +using System.Text; +namespace Org.IdentityConnectors.Common +{ + /// + /// Description of XmlUtil. + /// + public static class XmlUtil + { + ///////////////////////////////////////////////////////////// + // + // DOM Navigation utilities + // + //////////////////////////////////////////////////////////// + + /** + * Return the value of an attribute on an element.

The DOM getAttribute + * method returns an empty string if the attribute doesn't exist. Here, we + * detect this and return null. + */ + public static String GetAttribute(XmlElement e, String name) { + String value = e.GetAttribute(name); + if (value != null && value.Length == 0) + value = null; + return value; + } + + /** + * Find an immediate child of the given name + */ + public static XmlElement FindImmediateChildElement(XmlNode node, String name) { + + XmlElement found = null; + + if (node != null) { + + for (XmlNode child = node.FirstChild; child != null + && found == null; child = child.NextSibling) { + + if (child.NodeType == XmlNodeType.Element) { + XmlElement tmp = (XmlElement) child; + if ( tmp.LocalName.Equals(name) ) { + return tmp; + } + } + } + } + + return found; + } + + /** + * Returns the First child element or null if none found + * @param node The node. May be null. + * @return the First child element or null if none found + */ + public static XmlElement GetFirstChildElement(XmlNode node) { + if ( node == null ) { + return null; + } + XmlNode child = node.FirstChild; + if ( child != null && child.NodeType == XmlNodeType.Element ) { + return (XmlElement)child; + } + else { + return GetNextElement(child); + } + } + + /** + * Get the next right sibling that is an element. + */ + public static XmlElement GetNextElement(XmlNode node) { + + XmlElement found = null; + + if (node != null) { + + for (XmlNode next = node.NextSibling; next != null + && found == null; next = next.NextSibling) { + + if (next.NodeType == XmlNodeType.Element) + found = (XmlElement) next; + } + } + + return found; + } + + /** + * Locate the first text node at any level below the given node. If the + * ignoreEmpty flag is true, we will ignore text nodes that contain only + * whitespace characteres.

Note that if you're trying to extract + * element content, you probably don't want this since parser's can break up + * pcdata into multiple adjacent text nodes. See getContent() for a more + * useful method. + */ + private static XmlText FindText(XmlNode node, bool ignoreEmpty) { + + XmlText found = null; + + if (node != null) { + + if (node.NodeType == XmlNodeType.Text + || node.NodeType == XmlNodeType.CDATA) { + + XmlText t = (XmlText) node; + if (!ignoreEmpty) + found = t; + else { + String s = t.Data.Trim(); + if (s.Length > 0) + found = t; + } + } + + if (found == null) { + + for (XmlNode child = node.FirstChild; child != null + && found == null; child = child.NextSibling) { + + found = FindText(child, ignoreEmpty); + } + } + } + + return found; + } + + + /** + * Return the content of the given element.

We will descend to an + * arbitrary depth looking for the first text node.

Note that + * the parser may break what was originally a single string of pcdata into + * multiple adjacent text nodes. Xerces appears to do this when it + * encounters a '$' in the text, not sure if there is specified behavior, or + * if its parser specific.

Here, we will congeal adjacent text nodes. + *

We will NOT ignore text nodes that have only whitespace. + */ + public static String GetContent(XmlElement e) { + + String content = null; + + if (e != null) { + + // find the first inner text node, + XmlText t = FindText(e, false); + if (t != null) { + // we have at least some text + StringBuilder b = new StringBuilder(); + while (t != null) { + b.Append(t.Data); + XmlNode n = t.NextSibling; + + t = null; + if (n != null + && ((n.NodeType == XmlNodeType.Text) || + (n.NodeType == XmlNodeType.CDATA))) { + t = (XmlText) n; + } + } + content = b.ToString(); + } + } + + return content; + } + } +} diff --git a/DotNetConnectors.sln/Console/AssemblyInfo.cs b/DotNetConnectors.sln/Console/AssemblyInfo.cs new file mode 100644 index 00000000..f8507a15 --- /dev/null +++ b/DotNetConnectors.sln/Console/AssemblyInfo.cs @@ -0,0 +1,53 @@ +/* + * ==================== + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER. + * + * Copyright 2007-2008 Sun Microsystems, Inc. All rights reserved. + * + * The contents of this file are subject to the terms of the Common Development + * and Distribution License("CDDL") (the "License"). You may not use this file + * except in compliance with the License. + * + * You can obtain a copy of the License at + * http://IdentityConnectors.dev.java.net/legal/license.txt + * See the License for the specific language governing permissions and limitations + * under the License. + * + * When distributing the Covered Code, include this CDDL Header Notice in each file + * and include the License file at identityconnectors/legal/license.txt. + * If applicable, add the following below this CDDL Header, with the fields + * enclosed by brackets [] replaced by your own identifying information: + * "Portions Copyrighted [year] [name of copyright owner]" + * ==================== + */ +#region Using directives + +using System.Reflection; +using System.Runtime.CompilerServices; +using System.Runtime.InteropServices; + +#endregion + +// General Information about an assembly is controlled through the following +// set of attributes. Change these attribute values to modify the information +// associated with an assembly. +[assembly: AssemblyTitle("Console")] +[assembly: AssemblyDescription("")] +[assembly: AssemblyConfiguration("")] +[assembly: AssemblyCompany("")] +[assembly: AssemblyProduct("Console")] +[assembly: AssemblyCopyright("")] +[assembly: AssemblyTrademark("")] +[assembly: AssemblyCulture("")] + +// This sets the default COM visibility of types in the assembly to invisible. +// If you need to expose a type to COM, use [ComVisible(true)] on that type. +[assembly: ComVisible(false)] + +// The assembly version has following format : +// +// Major.Minor.Build.Revision +// +// You can specify all the values or you can use the default the Revision and +// Build Numbers by using the '*' as shown below: +[assembly: AssemblyVersion("1.0.0.0")] diff --git a/DotNetConnectors.sln/Console/Console.csproj b/DotNetConnectors.sln/Console/Console.csproj new file mode 100644 index 00000000..1192908e --- /dev/null +++ b/DotNetConnectors.sln/Console/Console.csproj @@ -0,0 +1,90 @@ + + + + {D09E6A1F-55F6-4661-8E5A-C485E4B08BB3} + Debug + AnyCPU + WinExe + Console + Console + v3.5 + + + prompt + AnyCPU + ./bin/Debug/ + True + Full + False + True + DEBUG;TRACE + WinExe + Console + False + 4 + + + pdbonly + AnyCPU + ./bin/Release/ + False + True + False + TRACE + WinExe + Console + False + 4 + + + + + ..\..\..\..\..\Program Files\SharpDevelop\3.0\AddIns\AddIns\BackendBindings\BooBinding\Boo.Lang.dll + + + + 3.5 + + + + + + + + + + Form + + + MainForm.cs + + + + + + {8B24461B-456A-4032-89A1-CD418F7B5B62} + Framework + + + diff --git a/DotNetConnectors.sln/Console/MainForm.Designer.cs b/DotNetConnectors.sln/Console/MainForm.Designer.cs new file mode 100644 index 00000000..1782f06d --- /dev/null +++ b/DotNetConnectors.sln/Console/MainForm.Designer.cs @@ -0,0 +1,47 @@ +/* + * Created by SharpDevelop. + * User: Administrator + * Date: 3/18/2008 + * Time: 4:30 PM + * + * To change this template use Tools | Options | Coding | Edit Standard Headers. + */ +namespace Console +{ + partial class MainForm + { + ///

+ /// Designer variable used to keep track of non-visual components. + /// + private System.ComponentModel.IContainer components = null; + + /// + /// Disposes resources used by the form. + /// + /// true if managed resources should be disposed; otherwise, false. + protected override void Dispose(bool disposing) + { + if (disposing) { + if (components != null) { + components.Dispose(); + } + } + base.Dispose(disposing); + } + + /// + /// This method is required for Windows Forms designer support. + /// Do not change the method contents inside the source code editor. The Forms designer might + /// not be able to load this method if it was changed manually. + /// + private void InitializeComponent() + { + // + // MainForm + // + this.AutoScaleMode = System.Windows.Forms.AutoScaleMode.Font; + this.Text = "Console"; + this.Name = "MainForm"; + } + } +} diff --git a/DotNetConnectors.sln/Console/MainForm.cs b/DotNetConnectors.sln/Console/MainForm.cs new file mode 100644 index 00000000..ca92a32a --- /dev/null +++ b/DotNetConnectors.sln/Console/MainForm.cs @@ -0,0 +1,47 @@ +/* + * ==================== + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER. + * + * Copyright 2007-2008 Sun Microsystems, Inc. All rights reserved. + * + * The contents of this file are subject to the terms of the Common Development + * and Distribution License("CDDL") (the "License"). You may not use this file + * except in compliance with the License. + * + * You can obtain a copy of the License at + * http://IdentityConnectors.dev.java.net/legal/license.txt + * See the License for the specific language governing permissions and limitations + * under the License. + * + * When distributing the Covered Code, include this CDDL Header Notice in each file + * and include the License file at identityconnectors/legal/license.txt. + * If applicable, add the following below this CDDL Header, with the fields + * enclosed by brackets [] replaced by your own identifying information: + * "Portions Copyrighted [year] [name of copyright owner]" + * ==================== + */ +using System; +using System.Collections.Generic; +using System.Drawing; +using System.Windows.Forms; + +namespace Console +{ + /// + /// Description of MainForm. + /// + public partial class MainForm : Form + { + public MainForm() + { + // + // The InitializeComponent() call is required for Windows Forms designer support. + // + InitializeComponent(); + + // + // TODO: Add constructor code after the InitializeComponent() call. + // + } + } +} diff --git a/DotNetConnectors.sln/Console/Program.cs b/DotNetConnectors.sln/Console/Program.cs new file mode 100644 index 00000000..23030249 --- /dev/null +++ b/DotNetConnectors.sln/Console/Program.cs @@ -0,0 +1,45 @@ +/* + * ==================== + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER. + * + * Copyright 2007-2008 Sun Microsystems, Inc. All rights reserved. + * + * The contents of this file are subject to the terms of the Common Development + * and Distribution License("CDDL") (the "License"). You may not use this file + * except in compliance with the License. + * + * You can obtain a copy of the License at + * http://IdentityConnectors.dev.java.net/legal/license.txt + * See the License for the specific language governing permissions and limitations + * under the License. + * + * When distributing the Covered Code, include this CDDL Header Notice in each file + * and include the License file at identityconnectors/legal/license.txt. + * If applicable, add the following below this CDDL Header, with the fields + * enclosed by brackets [] replaced by your own identifying information: + * "Portions Copyrighted [year] [name of copyright owner]" + * ==================== + */ +using System; +using System.Windows.Forms; + +namespace Console +{ + /// + /// Class with program entry point. + /// + internal sealed class Program + { + /// + /// Program entry point. + /// + [STAThread] + private static void Main(string[] args) + { + Application.EnableVisualStyles(); + Application.SetCompatibleTextRenderingDefault(false); + Application.Run(new MainForm()); + } + + } +} diff --git a/DotNetConnectors.sln/DotNetConnectors.sln b/DotNetConnectors.sln/DotNetConnectors.sln new file mode 100644 index 00000000..c59e1e95 --- /dev/null +++ b/DotNetConnectors.sln/DotNetConnectors.sln @@ -0,0 +1,90 @@ + +Microsoft Visual Studio Solution File, Format Version 10.00 +# Visual Studio 2008 +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Service", "Service\Service.csproj", "{A9D6374A-D51F-4FA3-8C02-5B1D23FAA82E}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Framework", "Framework\Framework.csproj", "{8B24461B-456A-4032-89A1-CD418F7B5B62}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Common", "Common\Common.csproj", "{F140E8DA-52B4-4159-992A-9DA10EA8EEFB}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "FrameworkTests", "FrameworkTests\FrameworkTests.csproj", "{32804F5A-812C-4FA6-835C-BDAE5B24D355}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "TestBundleV1", "TestBundleV1\TestBundleV1.csproj", "{0BC2A013-56FE-46DD-90FC-2D2D57748FB6}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "TestBundleV2", "TestBundleV2\TestBundleV2.csproj", "{3E737796-3A83-4924-9FF1-DC542F21F59E}" +EndProject +Project("{930C7802-8A8C-48F9-8165-68863BCCD9DD}") = "ServiceInstall", "ServiceInstall\ServiceInstall.wixproj", "{1CBA8F74-050C-432B-8437-08BD13BDC684}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Console", "Console\Console.csproj", "{D09E6A1F-55F6-4661-8E5A-C485E4B08BB3}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "FrameworkInternal", "FrameworkInternal\FrameworkInternal.csproj", "{5B011775-B121-4EEE-A410-BA2D2F5BFB8B}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "BooScriptExecutorFactory", "BooScriptExecutorFactory\BooScriptExecutorFactory.csproj", "{0747C440-70E4-4E63-9F9D-03B3A010C991}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "ShellScriptExecutorFactory", "ShellScriptExecutorFactory\ShellScriptExecutorFactory.csproj", "{4700690A-2D29-40A0-86AC-E5A9F71A479A}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "ActiveDirectoryConnector", "ActiveDirectoryConnector\ActiveDirectoryConnector.csproj", "{BDF495CA-0FCD-4E51-A871-D467CDE3B43E}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "ActiveDirectoryConnectorTests", "ActiveDirectoryConnectorTests\ActiveDirectoryConnectorTests.csproj", "{DB8F7DA9-11A2-4BEB-8C14-25D8229EBE7E}" +EndProject +Global + GlobalSection(SolutionConfigurationPlatforms) = preSolution + Debug|Any CPU = Debug|Any CPU + Release|Any CPU = Release|Any CPU + EndGlobalSection + GlobalSection(ProjectConfigurationPlatforms) = postSolution + {A9D6374A-D51F-4FA3-8C02-5B1D23FAA82E}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {A9D6374A-D51F-4FA3-8C02-5B1D23FAA82E}.Debug|Any CPU.Build.0 = Debug|Any CPU + {A9D6374A-D51F-4FA3-8C02-5B1D23FAA82E}.Release|Any CPU.ActiveCfg = Release|Any CPU + {A9D6374A-D51F-4FA3-8C02-5B1D23FAA82E}.Release|Any CPU.Build.0 = Release|Any CPU + {8B24461B-456A-4032-89A1-CD418F7B5B62}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {8B24461B-456A-4032-89A1-CD418F7B5B62}.Debug|Any CPU.Build.0 = Debug|Any CPU + {8B24461B-456A-4032-89A1-CD418F7B5B62}.Release|Any CPU.ActiveCfg = Release|Any CPU + {8B24461B-456A-4032-89A1-CD418F7B5B62}.Release|Any CPU.Build.0 = Release|Any CPU + {F140E8DA-52B4-4159-992A-9DA10EA8EEFB}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {F140E8DA-52B4-4159-992A-9DA10EA8EEFB}.Debug|Any CPU.Build.0 = Debug|Any CPU + {F140E8DA-52B4-4159-992A-9DA10EA8EEFB}.Release|Any CPU.ActiveCfg = Release|Any CPU + {F140E8DA-52B4-4159-992A-9DA10EA8EEFB}.Release|Any CPU.Build.0 = Release|Any CPU + {32804F5A-812C-4FA6-835C-BDAE5B24D355}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {32804F5A-812C-4FA6-835C-BDAE5B24D355}.Debug|Any CPU.Build.0 = Debug|Any CPU + {32804F5A-812C-4FA6-835C-BDAE5B24D355}.Release|Any CPU.ActiveCfg = Release|Any CPU + {32804F5A-812C-4FA6-835C-BDAE5B24D355}.Release|Any CPU.Build.0 = Release|Any CPU + {0BC2A013-56FE-46DD-90FC-2D2D57748FB6}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {0BC2A013-56FE-46DD-90FC-2D2D57748FB6}.Debug|Any CPU.Build.0 = Debug|Any CPU + {0BC2A013-56FE-46DD-90FC-2D2D57748FB6}.Release|Any CPU.ActiveCfg = Release|Any CPU + {0BC2A013-56FE-46DD-90FC-2D2D57748FB6}.Release|Any CPU.Build.0 = Release|Any CPU + {3E737796-3A83-4924-9FF1-DC542F21F59E}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {3E737796-3A83-4924-9FF1-DC542F21F59E}.Debug|Any CPU.Build.0 = Debug|Any CPU + {3E737796-3A83-4924-9FF1-DC542F21F59E}.Release|Any CPU.ActiveCfg = Release|Any CPU + {3E737796-3A83-4924-9FF1-DC542F21F59E}.Release|Any CPU.Build.0 = Release|Any CPU + {1CBA8F74-050C-432B-8437-08BD13BDC684}.Debug|Any CPU.ActiveCfg = Debug|x86 + {1CBA8F74-050C-432B-8437-08BD13BDC684}.Release|Any CPU.ActiveCfg = Release|x86 + {D09E6A1F-55F6-4661-8E5A-C485E4B08BB3}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {D09E6A1F-55F6-4661-8E5A-C485E4B08BB3}.Debug|Any CPU.Build.0 = Debug|Any CPU + {D09E6A1F-55F6-4661-8E5A-C485E4B08BB3}.Release|Any CPU.ActiveCfg = Release|Any CPU + {D09E6A1F-55F6-4661-8E5A-C485E4B08BB3}.Release|Any CPU.Build.0 = Release|Any CPU + {5B011775-B121-4EEE-A410-BA2D2F5BFB8B}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {5B011775-B121-4EEE-A410-BA2D2F5BFB8B}.Debug|Any CPU.Build.0 = Debug|Any CPU + {5B011775-B121-4EEE-A410-BA2D2F5BFB8B}.Release|Any CPU.ActiveCfg = Release|Any CPU + {5B011775-B121-4EEE-A410-BA2D2F5BFB8B}.Release|Any CPU.Build.0 = Release|Any CPU + {0747C440-70E4-4E63-9F9D-03B3A010C991}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {0747C440-70E4-4E63-9F9D-03B3A010C991}.Debug|Any CPU.Build.0 = Debug|Any CPU + {0747C440-70E4-4E63-9F9D-03B3A010C991}.Release|Any CPU.ActiveCfg = Release|Any CPU + {0747C440-70E4-4E63-9F9D-03B3A010C991}.Release|Any CPU.Build.0 = Release|Any CPU + {4700690A-2D29-40A0-86AC-E5A9F71A479A}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {4700690A-2D29-40A0-86AC-E5A9F71A479A}.Debug|Any CPU.Build.0 = Debug|Any CPU + {4700690A-2D29-40A0-86AC-E5A9F71A479A}.Release|Any CPU.ActiveCfg = Release|Any CPU + {4700690A-2D29-40A0-86AC-E5A9F71A479A}.Release|Any CPU.Build.0 = Release|Any CPU + {BDF495CA-0FCD-4E51-A871-D467CDE3B43E}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {BDF495CA-0FCD-4E51-A871-D467CDE3B43E}.Debug|Any CPU.Build.0 = Debug|Any CPU + {BDF495CA-0FCD-4E51-A871-D467CDE3B43E}.Release|Any CPU.ActiveCfg = Release|Any CPU + {BDF495CA-0FCD-4E51-A871-D467CDE3B43E}.Release|Any CPU.Build.0 = Release|Any CPU + {DB8F7DA9-11A2-4BEB-8C14-25D8229EBE7E}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {DB8F7DA9-11A2-4BEB-8C14-25D8229EBE7E}.Debug|Any CPU.Build.0 = Debug|Any CPU + {DB8F7DA9-11A2-4BEB-8C14-25D8229EBE7E}.Release|Any CPU.ActiveCfg = Release|Any CPU + {DB8F7DA9-11A2-4BEB-8C14-25D8229EBE7E}.Release|Any CPU.Build.0 = Release|Any CPU + EndGlobalSection + GlobalSection(SolutionProperties) = preSolution + HideSolutionNode = FALSE + EndGlobalSection +EndGlobal diff --git a/DotNetConnectors.sln/ExchangeConnector/CommandInfos.xml b/DotNetConnectors.sln/ExchangeConnector/CommandInfos.xml new file mode 100644 index 00000000..2690e95f --- /dev/null +++ b/DotNetConnectors.sln/ExchangeConnector/CommandInfos.xml @@ -0,0 +1,257 @@ + + + + + + New-MailUser + Name + + ExternalEmailAddress + OrganizationalUnit + Password + UserPrincipalName + Alias + DisplayName + DomainController + FirstName + Initials + LastName + MacAttachmentFormat + MessageBodyFormat + MessageFormat + ResetPasswordOnNextLogon + SamAccountName + TemplateInstance + UsePreferMessageFormat + + + + Set-MailUser + Identity + + AcceptMessagesOnlyFrom + AcceptMessagesOnlyFromDLMembers + Alias + CreateDTMFMap + CustomAttribute1 + CustomAttribute2 + CustomAttribute3 + CustomAttribute4 + CustomAttribute5 + CustomAttribute6 + CustomAttribute7 + CustomAttribute8 + CustomAttribute9 + CustomAttribute10 + CustomAttribute11 + CustomAttribute12 + CustomAttribute13 + CustomAttribute14 + CustomAttribute15 + DisplayName + DomainController + EmailAddresses + EmailAddressPolicyEnabled + Extensions + ExternalEmailAddress + GrantSendOnBehalfTo + HiddenFromAddressListsEnabled + Instance + MacAttachmentFormat + MessageBodyFormat + MessageFormat + Name + PrimarySmtpAddress + RecipientLimits + RejectMessagesFrom + RejectMessagesFromDLMembers + RequireSenderAuthenticationEnabled + SamAccountName + SecondaryAddress + SecondaryDialPlan + SimpleDisplayName + UMDtmfMap + UseMapiRichTextFormat + UsePreferMessageFormat + UserPrincipalName + WindowsEmailAddress + MaxReceiveSize + MaxSendSize + + + + Get-MailUser + + Identity + OrganizationalUnit + ReadFromDomainController + + + Filter + + AcceptMessagesOnlyFrom + AcceptMessagesOnlyFromDLMembers + Alias + CustomAttribute1 + CustomAttribute2 + CustomAttribute3 + CustomAttribute4 + CustomAttribute5 + CustomAttribute6 + CustomAttribute7 + CustomAttribute8 + CustomAttribute9 + CustomAttribute10 + CustomAttribute11 + CustomAttribute12 + CustomAttribute13 + CustomAttribute14 + CustomAttribute15 + DisplayName + DistinguishedName + EmailAddresses + EmailAddressPolicyEnabled + ExchangeVersion + ExternalEmailAddress + GrantSendOnBehalfTo + Guid + HiddenFromAddressListsEnabled + MaxReceiveSize + MaxSendSize + Name + PrimarySmtpAddress + RecipientLimits + RecipientType + RecipientTypeDetails + RejectMessagesFrom + RejectMessagesFromDLMembers + SamAccountName + SimpleDisplayName + UMDtmfMap + UserPrincipalName + WhenChanged + WhenCreated + WindowsEmailAddress + + + + Enable-MailUser + Identity + + ExternalEmailAddress + Alias + DomainController + MacAttachmentFormat + MessageBodyFormat + MessageFormat + UsePreferMessageFormat + + + + + + Get-User + Identity + + + Set-User + Identity + + AssistantName + City + Company + CountryOrRegion + CreateDTMFMap + Department + DisplayName + DomainController + Fax + FirstName + HomePhone + Initials + Instance + LastName + Manager + MobilePhone + Name + Notes + Office + OtherFax + OtherHomePhone + OtherTelephone + Pager + Phone + PhoneticDisplayName + PostalCode + PostOfficeBox + ResetPasswordOnNextLogon + SamAccountName + SimpleDisplayName + StateOrProvince + StreetAddress + Title + UMDialPlan + UMDtmfMap + UserPrincipalName + WebPage + WindowsEmailAddress + + + + + + Enable-Mailbox + Identity + + Database + Equipment + Alias + LinkedDomainController + LinkedMasterAccount + Room + Shared + ActiveSyncMailboxPolicy + DomainController + LinkedCredential + ManagedFolderMailboxPolicy + ManagedFolderMailboxPolicyAllowed + + + + \ No newline at end of file diff --git a/DotNetConnectors.sln/ExchangeConnector/ExchangeConfiguration.cs b/DotNetConnectors.sln/ExchangeConnector/ExchangeConfiguration.cs new file mode 100644 index 00000000..9d5e5477 --- /dev/null +++ b/DotNetConnectors.sln/ExchangeConnector/ExchangeConfiguration.cs @@ -0,0 +1,37 @@ +/* + * ==================== + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER. + * + * Copyright 2007-2008 Sun Microsystems, Inc. All rights reserved. + * + * The contents of this file are subject to the terms of the Common Development + * and Distribution License("CDDL") (the "License"). You may not use this file + * except in compliance with the License. + * + * You can obtain a copy of the License at + * http://IdentityConnectors.dev.java.net/legal/license.txt + * See the License for the specific language governing permissions and limitations + * under the License. + * + * When distributing the Covered Code, include this CDDL Header Notice in each file + * and include the License file at identityconnectors/legal/license.txt. + * If applicable, add the following below this CDDL Header, with the fields + * enclosed by brackets [] replaced by your own identifying information: + * "Portions Copyrighted [year] [name of copyright owner]" + * ==================== + */ +using System; +using Org.IdentityConnectors.ActiveDirectory; +using Org.IdentityConnectors.Framework.Spi; + +namespace Org.IdentityConnectors.Exchange +{ + /// + /// MS Exchange specific configuration + /// + public class ExchangeConfiguration : ActiveDirectoryConfiguration + { + + } + +} diff --git a/DotNetConnectors.sln/ExchangeConnector/ExchangeConnector.cs b/DotNetConnectors.sln/ExchangeConnector/ExchangeConnector.cs new file mode 100644 index 00000000..9ca43a69 --- /dev/null +++ b/DotNetConnectors.sln/ExchangeConnector/ExchangeConnector.cs @@ -0,0 +1,302 @@ +/* + * ==================== + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER. + * + * Copyright 2007-2008 Sun Microsystems, Inc. All rights reserved. + * + * The contents of this file are subject to the terms of the Common Development + * and Distribution License("CDDL") (the "License"). You may not use this file + * except in compliance with the License. + * + * You can obtain a copy of the License at + * http://IdentityConnectors.dev.java.net/legal/license.txt + * See the License for the specific language governing permissions and limitations + * under the License. + * + * When distributing the Covered Code, include this CDDL Header Notice in each file + * and include the License file at identityconnectors/legal/license.txt. + * If applicable, add the following below this CDDL Header, with the fields + * enclosed by brackets [] replaced by your own identifying information: + * "Portions Copyrighted [year] [name of copyright owner]" + * ==================== + */ +using System; +using System.Collections.Generic; +using System.Diagnostics; +using System.Management.Automation.Runspaces; +using Org.IdentityConnectors.ActiveDirectory; +using Org.IdentityConnectors.Common; +using Org.IdentityConnectors.Framework.Common.Objects; +using Org.IdentityConnectors.Framework.Spi; + +namespace Org.IdentityConnectors.Exchange +{ + /// + /// MS Exchange extension of Active Directory connector. + /// Full featured connector, see LegacyExchangeConnector for limited functionality connector. + /// LegacyExchangeConnector will be extension of this class, once ready. + /// + public class ExchangeConnector : ActiveDirectoryConnector + { + private static readonly string CLASS = typeof(ExchangeConnector).ToString(); + + //object class names + public const string MAILBOX_NAME = "mailbox"; + public const string MAILUSER_NAME = "mailuser"; + + //object classes + public static readonly ObjectClass MAILBOX = new ObjectClass(MAILBOX_NAME); + public static readonly ObjectClass MAILUSER = new ObjectClass(MAILUSER_NAME); + + //local vars + private ExchangeConfiguration _configuration = null; + private RunSpaceInstance _runspace = null; + private bool _disposed = false; + private IDictionary _mapOcInfo = null; + + + /// + /// Implementation of CreateOp.Create + /// + /// (oc + /// + /// + /// + public override Uid Create(ObjectClass oclass, + ICollection attributes, OperationOptions options) + { + const string METHOD = "Create"; + + Debug.WriteLine(METHOD + ":entry", CLASS); + + + //first create the object in AD + Uid uid = base.Create(oclass, attributes, options); + + try + { + if (oclass.Equals(MAILBOX)) + { + //enable mailbox for person + Command cmd = ExchangeUtils.GetCommand(CommandInfo.ENABLE_MAILBOX, attributes); + _runspace.InvokePipeline(cmd); + } else if (oclass.Equals(MAILUSER)) + { + //enable mailuser + Command cmd = ExchangeUtils.GetCommand(CommandInfo.ENABLE_MAILUSER, attributes); + _runspace.InvokePipeline(cmd); + + } + + Debug.WriteLine(METHOD + ":exit", CLASS); + + } + catch (Exception) + { + //do the rollback - delete the uid + base.Delete(oclass, uid, options); + throw; + } + + return uid; + } + + /// + /// Implementation of UpdateOp.Update + /// + /// + /// + /// + /// + /// + public override Uid Update(UpdateType type, ObjectClass oclass, + ICollection attributes, OperationOptions options) + { + //TODO: Implement Update + return base.Update(type, oclass, attributes, options); + } + + /// + /// Tests if the connector is properly configured and ready + /// + public override void Test() + { + //validate the configuration first, this will check AD configuration too + _configuration.Validate(); + //AD validation (includes configuration validation too) + base.Test(); + //runspace check + _runspace.Test(); + } + + /// + /// Implementation of SynOp.Sync + /// + /// + /// + /// + /// + public override void Sync(ObjectClass objClass, SyncToken token, + SyncResultsHandler handler, OperationOptions options) + { + //TODO: implement Sync + base.Sync(objClass, token, handler, options); + } + + /// + /// Implementation of SynOp.GetLatestSyncToken + /// + /// + public override SyncToken GetLatestSyncToken(ObjectClass oclass) + { + //TODO: Implement GetLatestSyncToken + return base.GetLatestSyncToken(oclass); + } + + /// + /// Implementation of SearchOp.ExecuteQuery + /// + /// + /// + /// + /// + public override void ExecuteQuery(ObjectClass oclass, string query, + ResultsHandler handler, OperationOptions options) + { + //TODO: Implement ExecuteQuery + base.ExecuteQuery(oclass, query, handler, options); + } + + + /// + /// Implementation of SearchOp.CreateFilterTranslator + /// + /// + /// + /// + public override Org.IdentityConnectors.Framework.Common.Objects.Filters.FilterTranslator CreateFilterTranslator(ObjectClass oclass, OperationOptions options) + { + //TODO: Implement CreateFilterTranslator + return base.CreateFilterTranslator(oclass, options); + } + + /// + /// Inits the connector with configuration injected + /// + /// + public override void Init(Configuration configuration) + { + base.Init(configuration); + _configuration = (ExchangeConfiguration)configuration; + _runspace = new RunSpaceInstance(RunSpaceInstance.SnapIn.Exchange); + _mapOcInfo = ExchangeUtils.GetOCInfo(); + } + + + /// + /// Dispose resources + /// + public override void Dispose() + { + //lock is probably not necessary + lock (this) + { + if (_disposed) + { + return; + } + if (_runspace != null) + { + _runspace.Dispose(); + } + base.Dispose(); + _disposed = true; + } + + } + + /// + /// Defines the supported object classes by the connector, used for schema building + /// + /// List of supported object classes + protected override ICollection GetSupportedObjectClasses() + { + ICollection ocList = base.GetSupportedObjectClasses(); + Assertions.NullCheck(ocList, "ocList"); + ocList.Add(MAILBOX); + ocList.Add(MAILUSER); + return ocList; + } + + /// + /// Gets the object class info for specified object class, used for schema building + /// + /// ObjectClass to get info for + /// ObjectClass' ObjectClassInfo + protected override ObjectClassInfo GetObjectClassInfo(ObjectClass oc) + { + ObjectClassInfo ret = CollectionUtil.GetValue(_mapOcInfo, oc, null) ?? base.GetObjectClassInfo(oc); + Assertions.NullCheck(ret, "ret"); + return ret; + } + + } + + + /// + /// Command definition object + /// + internal sealed class CommandInfo + { + private static IDictionary cinfos = null; + + internal static readonly CommandInfo ENABLE_MAILBOX = new CommandInfo("Enable-Mailbox"); + internal static readonly CommandInfo ENABLE_MAILUSER = new CommandInfo("Enable-MailUser"); + internal static readonly CommandInfo SET_MAILUSER = new CommandInfo("Set-MailUser"); + + + private CommandInfo(string name) + { + Name = name; + if (cinfos == null) + { + cinfos = ExchangeUtils.GetCommandInfo(); + } + + } + + /// + /// Comamnd Name + /// + internal string Name { get; private set; } + + /// + /// Comand Parameters + /// + internal string[] Parameters + { + get + { + var ret = CollectionUtil.GetValue(cinfos, Name, null); + Assertions.NullCheck(ret, "ret"); + + return ret.Parameter; + } + } + + /// + /// Name parameter + /// + internal string NameParameter + { + get + { + var ret = CollectionUtil.GetValue(cinfos, Name, null); + Assertions.NullCheck(ret, "ret"); + + return ret.NameParameter; + } + } + } +} + diff --git a/DotNetConnectors.sln/ExchangeConnector/ExchangeConnector.csproj b/DotNetConnectors.sln/ExchangeConnector/ExchangeConnector.csproj new file mode 100644 index 00000000..7ba60d80 --- /dev/null +++ b/DotNetConnectors.sln/ExchangeConnector/ExchangeConnector.csproj @@ -0,0 +1,108 @@ + + + + Debug + AnyCPU + 9.0.21022 + 2.0 + {F1CB12B6-0DD7-4DAB-9B21-630449B8610D} + Library + Properties + Org.IdentityConnectors.Exchange + Exchange.Connector + v3.5 + 512 + C:\Documents and Settings\Administrator\Application Data\ICSharpCode/SharpDevelop3.0\Settings.SourceAnalysis + + + true + full + false + bin\Debug\ + DEBUG;TRACE + prompt + 4 + bin\Debug\Exchange.Connector.xml + + + pdbonly + true + bin\Release\ + TRACE + prompt + 4 + + + + + 3.5 + + + False + ..\..\..\..\Program Files\Reference Assemblies\Microsoft\WindowsPowerShell\v1.0\System.Management.Automation.dll + + + + + + + + + + + + + + {BDF495CA-0FCD-4E51-A871-D467CDE3B43E} + ActiveDirectoryConnector + False + + + {F140E8DA-52B4-4159-992A-9DA10EA8EEFB} + Common + False + + + {8B24461B-456A-4032-89A1-CD418F7B5B62} + Framework + + + + + + + + + Designer + + + + + diff --git a/DotNetConnectors.sln/ExchangeConnector/ExchangeUtils.cs b/DotNetConnectors.sln/ExchangeConnector/ExchangeUtils.cs new file mode 100644 index 00000000..6f4bcaf8 --- /dev/null +++ b/DotNetConnectors.sln/ExchangeConnector/ExchangeUtils.cs @@ -0,0 +1,375 @@ +/* + * ==================== + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER. + * + * Copyright 2007-2008 Sun Microsystems, Inc. All rights reserved. + * + * The contents of this file are subject to the terms of the Common Development + * and Distribution License("CDDL") (the "License"). You may not use this file + * except in compliance with the License. + * + * You can obtain a copy of the License at + * http://IdentityConnectors.dev.java.net/legal/license.txt + * See the License for the specific language governing permissions and limitations + * under the License. + * + * When distributing the Covered Code, include this CDDL Header Notice in each file + * and include the License file at identityconnectors/legal/license.txt. + * If applicable, add the following below this CDDL Header, with the fields + * enclosed by brackets [] replaced by your own identifying information: + * "Portions Copyrighted [year] [name of copyright owner]" + * ==================== + */ +using System; +using System.Collections; +using System.Collections.Generic; +using System.Diagnostics; +using System.IO; +using System.Management.Automation.Runspaces; +using System.Reflection; + +using Microsoft.Win32; +using System.Xml.Serialization; +using Org.IdentityConnectors.ActiveDirectory; +using Org.IdentityConnectors.Common; +using Org.IdentityConnectors.Framework.Common.Objects; + +namespace Org.IdentityConnectors.Exchange +{ + /// + /// Description of ExchangeUtils. + /// + public class ExchangeUtils : CommonUtils + { + private static readonly string CLASS = typeof(ExchangeUtils).ToString(); + + private const string OC_DEF_FILE = "Org.IdentityConnectors.Exchange.ObjectClasses.xml"; + private const string EXCHANGE_REG_KEY = "Software\\Microsoft\\Exchange\\v8.0\\Setup\\"; + private const string EXCHANGE_REG_VALUE = "MsiInstallPath"; + + /// use reflection to load the Exchange assembly + internal static Assembly AssemblyResolver(object p, ResolveEventArgs args) + { + //Add path for the Exchange 2007 DLLs + if (args.Name.Contains("Microsoft.Exchange")) + { + String installPath = GetRegistryStringValue(EXCHANGE_REG_KEY, EXCHANGE_REG_VALUE); + installPath += "\\bin\\" + args.Name.Split(',')[0] + ".dll"; + return Assembly.LoadFrom(installPath); + + } + + return null; + } + + /// + /// Get registry value, which is expected to be a string + /// + /// Registry Key Name + /// Registry Value Name + /// + internal static String GetRegistryStringValue(string keyName, string valName) + { + const string METHOD = "GetRegistryStringValue"; + Debug.WriteLine(METHOD + ":entry", CLASS); + //argument check + if (keyName == null) + { + keyName = ""; + } + if (valName == null) + { + throw new ArgumentNullException("valName"); + } + + RegistryKey regKey = Registry.LocalMachine.OpenSubKey(keyName, false); + try + { + Object val = regKey.GetValue(valName); + if (val != null) + { + RegistryValueKind regType = regKey.GetValueKind(valName); + if (!regType.Equals(RegistryValueKind.String)) + { + throw new InvalidDataException(String.Format("Invalid Registry data type, key name: {0} value name: {1} should be String", keyName, valName)); + } + return Convert.ToString(val); + } + else + { + throw new InvalidDataException(String.Format("Missing value for key name: {0} value name: {1}", keyName, valName)); + } + } + finally + { + if (regKey != null) + { + regKey.Close(); + } + Debug.WriteLine(METHOD + ":exit", CLASS); + } + } + + + + /// + /// reads the object class info definitions from xml + /// + ///Dictionary of object classes + internal static IDictionary GetOCInfo() + { + return GetOCInfo(OC_DEF_FILE); + } + + /// + /// + /// + internal static IDictionary GetCommandInfo () + { + Assembly assembly = Assembly.GetExecutingAssembly(); + Stream stream = assembly.GetManifestResourceStream("Org.IdentityConnectors.Exchange.CommandInfos.xml"); + + Assertions.NullCheck(stream, "stream"); + + //we just read + TextReader streamReader = new StreamReader(stream); + + XmlSerializer ser = new XmlSerializer(typeof(XCommandInfos)); + XCommandInfos cInfos = (XCommandInfos)ser.Deserialize(streamReader); + streamReader.Close(); + + Assertions.NullCheck(cInfos, "cInfos"); + + //create map of command infos + var map = new Dictionary(cInfos.XCommandInfo.Length); + foreach (XCommandInfo o in cInfos.XCommandInfo) + { + map.Add(o.Name, o); + } + + return map; + + } + + /// + /// creates command based on the commanf info, reading the calues from attributes + /// + /// Command defition + /// Attribute values + /// Ready to execute Command + internal static Command GetCommand(CommandInfo cmdInfo, ICollection attributes) + { + Assertions.NullCheck(cmdInfo, "cmdInfo"); + Assertions.NullCheck(attributes, "attributes"); + + //create command + Command cmd = new Command(cmdInfo.Name); + + //map name attribute, if mapping specified + if (!string.IsNullOrEmpty(cmdInfo.NameParameter)) + { + object val = GetAttValue(Name.NAME, attributes); + if (val != null) + { + cmd.Parameters.Add(cmdInfo.NameParameter, val); + } + } + + foreach (string attName in cmdInfo.Parameters) + { + object val = GetAttValue(attName, attributes); + if (val != null) + { + cmd.Parameters.Add(attName, val); + } + } + return cmd; + } + + /// + /// Helper method: Gets attribute value from the attribute collection + /// + /// attribute name + /// collection of attribute + /// attribute value as object, null if not found + internal static object GetAttValue(String attName, ICollection attributes) + { + Assertions.NullCheck(attName, "attName"); + Assertions.NullCheck(attributes, "attributes"); + + object value = null; + ConnectorAttribute attribute = ConnectorAttributeUtil.Find(attName, attributes); + + if (attribute != null) + { + value = ConnectorAttributeUtil.GetSingleValue(attribute); + } + + return value; + } + + /// + /// Helper method for filtering the specified attributes from collection of attributes + /// + /// Collection of attributes + /// Attribute names to be filtered out + /// Filtered collection of attributes + internal static ICollection FilterOut(ICollection attributes, params string[] attName) + { + Assertions.NullCheck(attributes, "attributes"); + if (attName == null || attName.Length == 0) + { + return attributes; + } + + IList names = new ArrayList(attName); + ICollection filtered = new List(); + foreach (ConnectorAttribute attribute in attributes) + { + if (!names.Contains(attribute.Name)) + { + filtered.Add(attribute); + } + } + return filtered; + } + + /// + /// Helper method - Replaces specified collection Items + /// + /// + /// + /// + internal static ArrayList FilterReplace(ArrayList col, string[,] replace) + { + Assertions.NullCheck(col, "col"); + Assertions.NullCheck(replace, "replace"); + + ArrayList newcol = (ArrayList) col.Clone(); + for (int i = 0; i < replace.GetLength(0); i++) + { + if (newcol.Contains(replace[i,0])) + { + newcol.Remove(replace[i, 0]); + newcol.Add(replace[i,1]); + } + } + return newcol; + } + + /// + /// finds the attributes in connector object and rename it according to input array of names, but only + /// if the aatribute name is in attributes to get + /// + /// + /// + /// + /// + internal static ConnectorObject ReplaceAttributes(ConnectorObject cobject, IList attsToGet, string[,] replace) + { + Assertions.NullCheck(cobject, "cobject"); + Assertions.NullCheck(attsToGet, "attsToGet"); + Assertions.NullCheck(replace, "replace"); + + var attributes = cobject.GetAttributes(); + var builder = new ConnectorObjectBuilder(); + foreach (ConnectorAttribute attribute in attributes) + { + for (int i = 0; i < replace.GetLength(0); i++) + { + string oldName = replace[i, 1]; + string newName = replace[i, 0]; + if (attsToGet.Contains(newName) && attribute.Name == oldName) + { + var newAttribute = RenameAttribute(attribute, replace[i, 0]); + builder.AddAttribute(newAttribute); + break; + } + } + + builder.AddAttribute(attribute); + } + builder.AddAttributes(attributes); + builder.ObjectClass = cobject.ObjectClass; + builder.SetName(cobject.Name); + builder.SetUid(cobject.Uid); + return builder.Build(); + } + + /// + /// Renames the connector attribute to new name + /// + /// + /// + /// + internal static ConnectorAttribute RenameAttribute(ConnectorAttribute cattribute, string newName) + { + Assertions.NullCheck(cattribute, "cattribute"); + Assertions.NullCheck(newName, "newName"); + + var caBuilder = new ConnectorAttributeBuilder(); + caBuilder.AddValue(cattribute.Value); + caBuilder.Name = newName; + return caBuilder.Build(); + } + + + } + + /// + /// DAO class for getting serialized data from xml + /// + [XmlRoot("CommandInfos")] + public class XCommandInfos + { + private readonly ArrayList lstCommandInfos = new ArrayList(); + + /// + /// Command info array + /// + [XmlElement("CommandInfo")] + public XCommandInfo[] XCommandInfo + { + get + { + var items = new XCommandInfo[lstCommandInfos.Count]; + lstCommandInfos.CopyTo(items); + return items; + + } + set + { + if (value == null) return; + var items = (XCommandInfo[])value; + lstCommandInfos.Clear(); + foreach (XCommandInfo item in items) + lstCommandInfos.Add(item); + + } + } + } + + /// + /// DO class for getting serialized data from XML + /// + public class XCommandInfo + { + + /// + /// Command name + /// + public string Name { get; set; } + + /// + /// Special parameter name used as id for this command + /// + public string NameParameter { get; set; } + + /// + /// Command parameters + /// + public string[] Parameter { get; set; } + } + + +} diff --git a/DotNetConnectors.sln/ExchangeConnector/LegacyExchangeConnector.cs b/DotNetConnectors.sln/ExchangeConnector/LegacyExchangeConnector.cs new file mode 100644 index 00000000..8b0aab56 --- /dev/null +++ b/DotNetConnectors.sln/ExchangeConnector/LegacyExchangeConnector.cs @@ -0,0 +1,462 @@ +/* + * ==================== + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER. + * + * Copyright 2007-2008 Sun Microsystems, Inc. All rights reserved. + * + * The contents of this file are subject to the terms of the Common Development + * and Distribution License("CDDL") (the "License"). You may not use this file + * except in compliance with the License. + * + * You can obtain a copy of the License at + * http://IdentityConnectors.dev.java.net/legal/license.txt + * See the License for the specific language governing permissions and limitations + * under the License. + * + * When distributing the Covered Code, include this CDDL Header Notice in each file + * and include the License file at identityconnectors/legal/license.txt. + * If applicable, add the following below this CDDL Header, with the fields + * enclosed by brackets [] replaced by your own identifying information: + * "Portions Copyrighted [year] [name of copyright owner]" + * ==================== + */ +using System.Collections.Generic; +using System.Diagnostics; +using System.Management.Automation.Runspaces; +using Org.IdentityConnectors.ActiveDirectory; +using Org.IdentityConnectors.Common; +using Org.IdentityConnectors.Framework.Common.Exceptions; +using Org.IdentityConnectors.Framework.Common.Objects; +using Org.IdentityConnectors.Framework.Common.Objects.Filters; +using Org.IdentityConnectors.Framework.Spi; +using System.Collections; + +namespace Org.IdentityConnectors.Exchange +{ + /// + /// MS Exchange connector - build to have the same functionality as Exchange resource adapter + /// + /// + [ConnectorClass("connector_displayName", + typeof(ExchangeConfiguration), + MessageCatalogPaths = new[] { "Org.IdentityConnectors.Exchange.Messages", + "Org.IdentityConnectors.ActiveDirectory.Messages" } + )] + public class LegacyExchangeConnector : ActiveDirectoryConnector + { + private static readonly string CLASS = typeof(LegacyExchangeConnector).ToString(); + + //hardcoded stuff + internal const string ATT_RECIPIENT_TYPE = "RecipientType"; + internal const string ATT_EXTERNAL_MAIL = "ExternalEmailAddress"; + internal const string ATT_DATABASE = "Database"; + + internal const string ATT_EXTERNAL_MAIL_AD_NAME = "targetAddress"; + internal const string ATT_DATABASE_AD_NAME = "homeMDB"; + + internal static readonly string[,] ATT_MAPPING = new[,] + { + {ATT_DATABASE, ATT_DATABASE_AD_NAME}, + {ATT_EXTERNAL_MAIL, ATT_EXTERNAL_MAIL_AD_NAME} + }; + + private static readonly ConnectorAttributeInfo ATTINFO_RECIPIENT_TYPE = + ConnectorAttributeInfoBuilder.Build(ATT_RECIPIENT_TYPE, typeof(string), ConnectorAttributeInfo.Flags.REQUIRED | + ConnectorAttributeInfo.Flags.NOT_UPDATEABLE | ConnectorAttributeInfo.Flags.NOT_RETURNED_BY_DEFAULT); + + + + private static readonly ConnectorAttributeInfo ATTINFO_EXTERNAL_MAIL = + ConnectorAttributeInfoBuilder.Build(ATT_EXTERNAL_MAIL, typeof(string), ConnectorAttributeInfo.Flags.NOT_RETURNED_BY_DEFAULT | + ConnectorAttributeInfo.Flags.MULTIVALUED); + + private static readonly ConnectorAttributeInfo ATTINFO_DATABASE = + ConnectorAttributeInfoBuilder.Build(ATT_DATABASE, typeof(string), ConnectorAttributeInfo.Flags.NOT_RETURNED_BY_DEFAULT); + + private const string RCPT_TYPE_MAIL_BOX = "mailbox"; + private const string RCPT_TYPE_MAIL_USER = "mailuser"; + + + //local vars + private ExchangeConfiguration _configuration = null; + private RunSpaceInstance _runspace = null; + private bool _disposed = false; + + + #region CreateOp.Create implementation + /// + /// Implementation of CreateOp.Create + /// + /// (oc + /// + /// + /// + public override Uid Create(ObjectClass oclass, + ICollection attributes, OperationOptions options) + { + const string METHOD = "Create"; + Debug.WriteLine(METHOD + ":entry", CLASS); + + //get recipient type + string rcptType = ExchangeUtils.GetAttValue(ATT_RECIPIENT_TYPE, attributes) as string; + + if (rcptType != RCPT_TYPE_MAIL_BOX && rcptType != RCPT_TYPE_MAIL_USER) + { + //AD account only, we do nothing + return base.Create(oclass, attributes, options); + } + + //first create the object in AD + Uid uid = base.Create(oclass, FilterOut(attributes), options); + + //prepare the command + CommandInfo cmdInfo = rcptType == RCPT_TYPE_MAIL_BOX ? CommandInfo.ENABLE_MAILBOX : CommandInfo.ENABLE_MAILUSER; + Command cmd = ExchangeUtils.GetCommand(cmdInfo, attributes); + + try + { + //execute the command + _runspace.InvokePipeline(cmd); + } + catch + { + Trace.TraceWarning("Rolling back AD create for UID: " + uid.GetUidValue()); + //rollback AD create + try + { + base.Delete(oclass, uid, options); + } + catch + { + //ignore delete error + Trace.TraceWarning("Not able to rollback AD create for UID: " + uid.GetUidValue()); + } + //rethrow original exception + throw; + } + + Debug.WriteLine(METHOD + ":exit", CLASS); + return uid; + } + #endregion + + /// + /// Implementation of UpdateOp.Update + /// + /// + /// + /// + /// + /// + public override Uid Update(UpdateType type, ObjectClass oclass, + ICollection attributes, OperationOptions options) + { + const string METHOD = "Update"; + Debug.WriteLine(METHOD + ":entry", CLASS); + + Assertions.NullCheck(type, "updatetype"); + Assertions.NullCheck(oclass, "oclass"); + Assertions.NullCheck(attributes, "attributes"); + + //update in AD first + Uid uid = base.Update(type, oclass, FilterOut(attributes), options); + //get recipient type + string rcptType = ExchangeUtils.GetAttValue(ATT_RECIPIENT_TYPE, attributes) as string; + + //update is possible for mailuser's external email only + if (rcptType == RCPT_TYPE_MAIL_USER) + { + if (type == UpdateType.REPLACE) + { + //get name attribute + string name = ExchangeUtils.GetAttValue(Name.NAME, attributes) as string; + if (name == null) + { + //we don't know name, but we need it - NOTE: searching for all the default attributes, we need only Name here, it can be improved + ConnectorObject co = ADSearchByUid(uid, oclass, null); + Assertions.NullCheck(co, "co"); + //add to attributes + attributes.Add(co.Name); + } + + Command cmd = ExchangeUtils.GetCommand(CommandInfo.SET_MAILUSER, attributes); + _runspace.InvokePipeline(cmd); + } + else + { + throw new ConnectorException(string.Format("Update type [{0}] not supported", type)); + } + } + + Debug.WriteLine(METHOD + ":exit", CLASS); + return uid; + } + + /// + /// Tests if the connector is properly configured and ready + /// + public override void Test() + { + //validate the configuration first, this will check AD configuration too + _configuration.Validate(); + //AD validation (includes configuration validation too) + base.Test(); + //runspace check + _runspace.Test(); + } + + /// + /// Implementation of SynOp.Sync + /// + /// + /// + /// + /// + public override void Sync(ObjectClass objClass, SyncToken token, + SyncResultsHandler handler, OperationOptions options) + { + //TODO: implement Sync + base.Sync(objClass, token, handler, options); + } + + /// + /// Implementation of SynOp.GetLatestSyncToken + /// + /// + public override SyncToken GetLatestSyncToken(ObjectClass oclass) + { + //TODO: Implement GetLatestSyncToken + return base.GetLatestSyncToken(oclass); + } + + /// + /// Implementation of SearchOp.ExecuteQuery + /// + /// + /// + /// + /// + public override void ExecuteQuery(ObjectClass oclass, string query, + ResultsHandler handler, OperationOptions options) + { + ArrayList attsToGet = null; + if (options != null && options.AttributesToGet != null) + { + attsToGet = new ArrayList(options.AttributesToGet); + } + //delegate to get the exchange attributes if requested + ResultsHandler filter = delegate(ConnectorObject cobject) + { + ConnectorObject filtered = ExchangeUtils.ReplaceAttributes(cobject, attsToGet, ATT_MAPPING); + return handler(filtered); + }; + + ResultsHandler handler2use = handler; + OperationOptions options2use = options; + if (options != null && options.AttributesToGet != null) + { + if (attsToGet.Contains(ATT_DATABASE) || attsToGet.Contains(ATT_EXTERNAL_MAIL) || + attsToGet.Contains(ATT_RECIPIENT_TYPE)) + { + //replace Exchange attributes with AD names + var newAttsToGet = ExchangeUtils.FilterReplace(attsToGet, ATT_MAPPING); + //we have to remove recipient type, as it is unknown to AD + newAttsToGet.Remove(ATT_RECIPIENT_TYPE); + //build new op options + var builder = new OperationOptionsBuilder(options); + builder.AttributesToGet = (string[]) newAttsToGet.ToArray(typeof(string)); + options2use = builder.Build(); + handler2use = filter; + } + } + base.ExecuteQuery(oclass, query, handler2use, options2use); + } + + /// + /// Implementation of SearchOp.CreateFilterTranslator + /// + /// + /// + /// + public override FilterTranslator CreateFilterTranslator(ObjectClass oclass, OperationOptions options) + { + return new LegacyExchangeConnectorFilterTranslator(); + } + + /// + /// Inits the connector with configuration injected + /// + /// + public override void Init(Configuration configuration) + { + base.Init(configuration); + _configuration = (ExchangeConfiguration)configuration; + _runspace = new RunSpaceInstance(RunSpaceInstance.SnapIn.Exchange); + } + + + /// + /// Dispose resources + /// + public override void Dispose() + { + //lock is probably not necessary + lock (this) + { + if (_disposed) + { + return; + } + if (_runspace != null) + { + _runspace.Dispose(); + } + base.Dispose(); + _disposed = true; + } + + } + + /// + /// Gets the object class info for specified object class, used for schema building + /// + /// ObjectClass to get info for + /// ObjectClass' ObjectClassInfo + protected override ObjectClassInfo GetObjectClassInfo(ObjectClass oc) + { + //get the object class from base + ObjectClassInfo oinfo = base.GetObjectClassInfo(oc); + + //add additional attributes for ACCOUNT + if (oc == ObjectClass.ACCOUNT) + { + + var oiBuilder = new ObjectClassInfoBuilder + { + IsContainer = oinfo.IsContainer, + ObjectType = oinfo.ObjectType + }; + oiBuilder.AddAllAttributeInfo(oinfo.ConnectorAttributeInfos); + oiBuilder.AddAttributeInfo(ATTINFO_DATABASE); + oiBuilder.AddAttributeInfo(ATTINFO_RECIPIENT_TYPE); + oiBuilder.AddAttributeInfo(ATTINFO_EXTERNAL_MAIL); + } + + //return + return oinfo; + } + + /// + /// Attribute normalizer + /// + /// Object class + /// Attribute to be normalized + /// normalized attribute + public override ConnectorAttribute NormalizeAttribute(ObjectClass oclass, ConnectorAttribute attribute) + { + //normalize the attribute using AD connector first + attribute = base.NormalizeAttribute(oclass, attribute); + //normalize external mail value + if (attribute.Name == ATT_EXTERNAL_MAIL && attribute.Value != null) + { + IList normAttributes = new List(); + bool normalized = false; + foreach (object val in attribute.Value) + { + string strVal = val as string; + if (strVal != null) + { + string[] split = strVal.Split(':'); + if (split.Length == 2) + { + //it contains delimiter, use the second part + normAttributes.Add(split[1]); + normalized = true; + } else + { + //put the original value + normAttributes.Add(val); + } + } + } + if (normalized) + { + //build the attribute again + return ConnectorAttributeBuilder.Build(attribute.Name, normAttributes); + } + + } + //return the original attribute + return attribute; + } + + + /// + /// helper method to filter out all attributes used in LegacyExchangeConnector only + /// + /// + /// + private static ICollection FilterOut(ICollection attributes) + { + return ExchangeUtils.FilterOut(attributes, ATT_RECIPIENT_TYPE, ATT_DATABASE, ATT_EXTERNAL_MAIL); + } + + /// + /// helper method for searching object in AD by UID + /// + /// + /// + /// + /// + private ConnectorObject ADSearchByUid(Uid uid, ObjectClass oclass, OperationOptions options) + { + Assertions.NullCheck(uid, "uid"); + Assertions.NullCheck(oclass, "oclass"); + if (options == null) + { + options = new OperationOptionsBuilder().Build(); + } + + ConnectorObject ret = null; + Filter filter = FilterBuilder.EqualTo(uid); + var translator = + base.CreateFilterTranslator(oclass, options); + IList queries = translator.Translate(filter); + + if (queries.Count == 1) + { + ResultsHandler handler = delegate(ConnectorObject cobject) + { + ret = cobject; + return false; + }; + base.ExecuteQuery(oclass, queries[0], handler, options); + } + + + return ret; + } + + } + + /// + /// Filter translator which does MS Exchange specific things + /// + public class LegacyExchangeConnectorFilterTranslator : ActiveDirectoryFilterTranslator + { + protected override string[] GetLdapNamesForAttribute(ConnectorAttribute attr) + { + + if (attr.Is(LegacyExchangeConnector.ATT_DATABASE)) + { + return new string[] { LegacyExchangeConnector.ATT_DATABASE_AD_NAME }; + } + if (attr.Is(LegacyExchangeConnector.ATT_EXTERNAL_MAIL)) + { + return new string[] { LegacyExchangeConnector.ATT_EXTERNAL_MAIL_AD_NAME }; + } + return base.GetLdapNamesForAttribute(attr); + } + } + +} diff --git a/DotNetConnectors.sln/ExchangeConnector/Messages.resx b/DotNetConnectors.sln/ExchangeConnector/Messages.resx new file mode 100644 index 00000000..3960efa6 --- /dev/null +++ b/DotNetConnectors.sln/ExchangeConnector/Messages.resx @@ -0,0 +1,123 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + text/microsoft-resx + + + 2.0 + + + System.Resources.ResXResourceReader, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + System.Resources.ResXResourceWriter, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + Exchange Connector + + \ No newline at end of file diff --git a/DotNetConnectors.sln/ExchangeConnector/ObjectClasses.xml b/DotNetConnectors.sln/ExchangeConnector/ObjectClasses.xml new file mode 100644 index 00000000..f16c47ad --- /dev/null +++ b/DotNetConnectors.sln/ExchangeConnector/ObjectClasses.xml @@ -0,0 +1,114 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/DotNetConnectors.sln/ExchangeConnector/Properties/AssemblyInfo.cs b/DotNetConnectors.sln/ExchangeConnector/Properties/AssemblyInfo.cs new file mode 100644 index 00000000..d1492654 --- /dev/null +++ b/DotNetConnectors.sln/ExchangeConnector/Properties/AssemblyInfo.cs @@ -0,0 +1,58 @@ +/* + * ==================== + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER. + * + * Copyright 2007-2008 Sun Microsystems, Inc. All rights reserved. + * + * The contents of this file are subject to the terms of the Common Development + * and Distribution License("CDDL") (the "License"). You may not use this file + * except in compliance with the License. + * + * You can obtain a copy of the License at + * http://IdentityConnectors.dev.java.net/legal/license.txt + * See the License for the specific language governing permissions and limitations + * under the License. + * + * When distributing the Covered Code, include this CDDL Header Notice in each file + * and include the License file at identityconnectors/legal/license.txt. + * If applicable, add the following below this CDDL Header, with the fields + * enclosed by brackets [] replaced by your own identifying information: + * "Portions Copyrighted [year] [name of copyright owner]" + * ==================== + */ +using System.Reflection; +using System.Runtime.CompilerServices; +using System.Runtime.InteropServices; + +// General Information about an assembly is controlled through the following +// set of attributes. Change these attribute values to modify the information +// associated with an assembly. +[assembly: AssemblyTitle("ExchangeConnector")] +[assembly: AssemblyDescription("")] +[assembly: AssemblyConfiguration("")] +[assembly: AssemblyCompany("IDM")] +[assembly: AssemblyProduct("ExchangeConnector")] +[assembly: AssemblyCopyright("Copyright IDM 2008")] +[assembly: AssemblyTrademark("")] +[assembly: AssemblyCulture("")] + +// Setting ComVisible to false makes the types in this assembly not visible +// to COM components. If you need to access a type in this assembly from +// COM, set the ComVisible attribute to true on that type. +[assembly: ComVisible(false)] + +// The following GUID is for the ID of the typelib if this project is exposed to COM +[assembly: Guid("d23ec8a7-e491-4f91-bda5-8900423a410e")] + +// Version information for an assembly consists of the following four values: +// +// Major Version +// Minor Version +// Build Number +// Revision +// +// You can specify all the values or you can default the Build and Revision Numbers +// by using the '*' as shown below: +// [assembly: AssemblyVersion("1.0.*")] +[assembly: AssemblyVersion("1.0.0.0")] +[assembly: AssemblyFileVersion("1.0.0.0")] diff --git a/DotNetConnectors.sln/ExchangeConnector/RunSpaceInstance.cs b/DotNetConnectors.sln/ExchangeConnector/RunSpaceInstance.cs new file mode 100644 index 00000000..21a0ff76 --- /dev/null +++ b/DotNetConnectors.sln/ExchangeConnector/RunSpaceInstance.cs @@ -0,0 +1,335 @@ +/* + * ==================== + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER. + * + * Copyright 2007-2008 Sun Microsystems, Inc. All rights reserved. + * + * The contents of this file are subject to the terms of the Common Development + * and Distribution License("CDDL") (the "License"). You may not use this file + * except in compliance with the License. + * + * You can obtain a copy of the License at + * http://IdentityConnectors.dev.java.net/legal/license.txt + * See the License for the specific language governing permissions and limitations + * under the License. + * + * When distributing the Covered Code, include this CDDL Header Notice in each file + * and include the License file at identityconnectors/legal/license.txt. + * If applicable, add the following below this CDDL Header, with the fields + * enclosed by brackets [] replaced by your own identifying information: + * "Portions Copyrighted [year] [name of copyright owner]" + * ==================== + */ +using System; +using System.Collections; +using System.Collections.Generic; +using System.Collections.ObjectModel; +using System.Diagnostics; +using System.Management.Automation; +using System.Management.Automation.Runspaces; +using System.Text; + +using Org.IdentityConnectors.Framework.Common.Exceptions; + +namespace Org.IdentityConnectors.Exchange +{ + + /// + /// The implementation of the run space. This wraps the real run space object + /// from powershell for use in the pool + /// First written for the exchange adapter, the snapin is not needed if you do + /// not use it for exchange + /// + /// Two possible ways of executing a command using different access point to + /// the Runspace: + /// - RunspaceInvoke: simple commands in string form, the command string can + /// contain multiple commands and is basically the same form + /// as what you use when typing a command in the exchange + /// shell + /// - PipelineInvoke: complex (multi) command structured pipelines which also + /// allow complex parameters, like objects, to be passed in. + /// + public sealed class RunSpaceInstance : IDisposable + { + private static readonly string CLASS = typeof(RunSpaceInstance).ToString(); + + // the exchange snap in which needs to be loaded + private const string EXCHANGE_SNAPIN = "Microsoft.Exchange.Management.PowerShell.Admin"; + + private RunspaceConfiguration _runSpaceConfig = null; + private Runspace _runSpace = null; + private RunspaceInvoke _runSpaceInvoke = null; + + //SnapIn type enum + /// + /// Snapin type to load + /// + public enum SnapIn + { + /// + /// None + /// + None, + /// + /// MS Exchange snapin + /// + Exchange + }; + + /// + /// RunSpaceInstance Constructor + /// + /// Type of snapin to be loaded + public RunSpaceInstance(SnapIn snapin) + { + // initialize this + InitRunSpace(snapin); + } + + /// + /// Implementation of IDisposable + /// + public void Dispose() + { + Dispose(true); + GC.SuppressFinalize(this); + } + + /// + /// Dispose/Finalize pattern + /// + /// + private void Dispose(bool disposing) + { + if (disposing) + { + // Free other state (managed objects). + // anything we should do? + } + // clean up the runspace with attached things: + // the API docs show that the RunspaceInvoke will call Dispose on + // the Runspace which in turn calls Close on the Runspace. + if (_runSpaceInvoke != null) + { + _runSpaceInvoke.Dispose(); + } + else + { + if (_runSpace != null) + { + _runSpace.Dispose(); + } + } + } + + /// + /// Finalize + /// + ~RunSpaceInstance() + { + // Simply call Dispose(false). + Dispose(false); + } + + + + /// + /// main initialisation routine for the Runspace + /// + private void InitRunSpace(SnapIn snapin) + { + string METHOD = "InitRunSpace with snapin " + snapin; + Debug.WriteLine(METHOD + ":entry", CLASS); + + + // create a new config from scratch + PSSnapInException snapOutput = null; + _runSpaceConfig = RunspaceConfiguration.Create(); + + switch (snapin) + { + case SnapIn.Exchange: + // used for force load of the exchange dll's + AppDomain.CurrentDomain.AssemblyResolve += + new ResolveEventHandler(ExchangeUtils.AssemblyResolver); + + PSSnapInInfo info = _runSpaceConfig.AddPSSnapIn(EXCHANGE_SNAPIN, + out snapOutput); + break; + } + + //check snapOutput + if (snapOutput != null) + { + throw snapOutput; + } + + // create the real Runspace and open it for processing + _runSpace = RunspaceFactory.CreateRunspace(_runSpaceConfig); + _runSpace.Open(); + _runSpaceInvoke = new RunspaceInvoke(_runSpace); + + Debug.WriteLine(METHOD + ":exit", CLASS); + } + + + /// + /// Test the state of this RunspaceInstance, throws InvalidRunspaceStateException if in incorrect state + /// + public void Test() + { + const string METHOD = "Test"; + Debug.WriteLine(METHOD + ":entry", CLASS); + + // compare the state against the passed in state + if (_runSpace != null + && _runSpace.RunspaceStateInfo.State == RunspaceState.Opened) + { + Debug.WriteLine(METHOD + ":exit", CLASS); + return ; + } + + throw new InvalidRunspaceStateException("Runspace is not in Opened state"); + } + + /// invoke the command + /// command string to execute + /// collection of objects with the result + /// if no command is passed in return null + /// if no output/errors from the invoke return an empty collection + public ICollection InvokeCommand(String commandString) + { + return InvokeCommand(commandString, null); + } + + /// + /// invoke the command + /// The input is passed in to the environment as the $input variable and + /// can be used in the script as follows: + /// invokeCommand("$input | Set-Mailbox", inputEnum) + /// inputEnum in the example could be the output of an earlier + /// invokeCommand call (and thus a complex set of objects) + /// + /// command string to execute + /// input passed in as $input in the execution + /// environment + /// collection of objects with the result + /// if no command is passed in return null + /// if no output from the invoke return an empty collection + public ICollection InvokeCommand(String commandString, + IEnumerable input) + { + const string METHOD = "InvokeCommand"; + Debug.WriteLine(METHOD + ":entry", CLASS); + + IList errors = null; + // trim the spaces and check the length + if (commandString == null || commandString.Trim().Length == 0) + { + Trace.TraceError("CommandString argument can't be null or empty"); + throw new ArgumentException("CommandString argument can't be null or empty"); + } + + // run the command + Collection returns = + _runSpaceInvoke.Invoke(commandString, input, out errors); + //check for errors + checkErrors(errors); + + // an empty collection instead of null when we have executed + if (returns == null) + { + Debug.WriteLine(METHOD + ":exit", CLASS); + returns = new Collection(); + } //if returns + Trace.WriteLine(String.Format("{0} results returned", returns.Count), CLASS); + Debug.WriteLine(METHOD + ":exit", CLASS); + return returns; + + } + + /// + /// invoke the pipeline + /// + /// a collection of commands to execute + /// collection of objects with the result + /// if no command is passed in return null + /// if no output from the invoke return an empty collection + public ICollection InvokePipeline(Collection commands) + { + const string METHOD = "InvokePipeline"; + Debug.WriteLine(METHOD + ":entry", CLASS); + + IList errors = null; + + if (commands == null || commands.Count == 0) + { + Trace.TraceInformation("Commands argument is null or empty", CLASS); + throw new ArgumentException("Commands argument is null or empty"); + } + + // make sure the output is set + errors = null; + Collection results; + + // create the pipeline + Pipeline pipe = _runSpace.CreatePipeline(); + // add the commands to the pipeline + foreach (Command item in commands) + { + pipe.Commands.Add(item); + } // foreach item + // run the pipeline if we have something to execute + results = pipe.Invoke(); + PipelineReader reader = pipe.Error; + errors = (IList)reader.ReadToEnd(); + //check for errors + checkErrors(errors); + // an empty collection instead of null when we have executed + if (results == null) + { + Trace.TraceInformation("NO result returned"); + results = new Collection(); + } //if results + Debug.WriteLine(METHOD + ":exit", CLASS); + return results; + + } + + /// + /// invoke the pipeline + /// + /// a command to execute + /// collection of objects with the result + /// if no command is passed in return null + /// if no output from the invoke return an empty collection + public ICollection InvokePipeline(Command item) + { + // create a new collection and add the command + // specifically not a CommandCollection: that will not work here + Collection commands = new Collection(); + commands.Add(item); + return InvokePipeline(commands); + } + + /// + /// Checks whether errors List contains some error, if so the errors are concatenated and exception is thrown + /// + /// List of error messages + private void checkErrors(IList errors) + { + StringBuilder builder = new StringBuilder(); + foreach (Object error in errors) + { + builder.Append(error.ToString()); + builder.Append("\n"); + } + + if (builder.Length > 0) + { + throw new ConnectorException("Exception when executing PowerShell: " + builder.ToString()); + } + } + + } // class RunSpaceInstance +} diff --git a/DotNetConnectors.sln/ExchangeConnector/build.xml b/DotNetConnectors.sln/ExchangeConnector/build.xml new file mode 100644 index 00000000..f51519a7 --- /dev/null +++ b/DotNetConnectors.sln/ExchangeConnector/build.xml @@ -0,0 +1,28 @@ + + + + + + + diff --git a/DotNetConnectors.sln/Framework/Api.cs b/DotNetConnectors.sln/Framework/Api.cs new file mode 100644 index 00000000..d8e0cf23 --- /dev/null +++ b/DotNetConnectors.sln/Framework/Api.cs @@ -0,0 +1,535 @@ +/* + * ==================== + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER. + * + * Copyright 2007-2008 Sun Microsystems, Inc. All rights reserved. + * + * The contents of this file are subject to the terms of the Common Development + * and Distribution License("CDDL") (the "License"). You may not use this file + * except in compliance with the License. + * + * You can obtain a copy of the License at + * http://IdentityConnectors.dev.java.net/legal/license.txt + * See the License for the specific language governing permissions and limitations + * under the License. + * + * When distributing the Covered Code, include this CDDL Header Notice in each file + * and include the License file at identityconnectors/legal/license.txt. + * If applicable, add the following below this CDDL Header, with the fields + * enclosed by brackets [] replaced by your own identifying information: + * "Portions Copyrighted [year] [name of copyright owner]" + * ==================== + */ +using System; +using System.Reflection; +using System.Globalization; +using System.Collections.Generic; +using System.Net.Security; +using System.Security; +using System.Security.Cryptography.X509Certificates; +using System.Text; +using Org.IdentityConnectors.Common; +using Org.IdentityConnectors.Common.Pooling; +using Org.IdentityConnectors.Common.Security; +using Org.IdentityConnectors.Framework.Api.Operations; +using Org.IdentityConnectors.Framework.Common; +using Org.IdentityConnectors.Framework.Common.Objects; + +namespace Org.IdentityConnectors.Framework.Api +{ + public static class APIConstants { + public const int NO_TIMEOUT = -1; + } + + public interface APIConfiguration { + ConfigurationProperties ConfigurationProperties { get; } + bool IsConnectorPoolingSupported { get; } + ObjectPoolConfiguration ConnectorPoolConfiguration { get; } + ICollection> SupportedOperations { get; } + + int GetTimeout(SafeType operation); + void SetTimeout(SafeType operation, int timeout); + + int ProducerBufferSize { get; set; } + } + + /** + * Configuration properties encapsulates the {@link Configuration} and uses + * {@link Reflection} to determine the properties available for manipulation. + */ + public interface ConfigurationProperties { + + /** + * Get the list of properties names for this {@link Configuration}. + * + * @return get the list of properties names. + */ + IList PropertyNames { get; } + + /** + * Get a particular {@link ConfigurationProperty} by name. + * + * @param name + * the unique name of the property. + * @return a {@link ConfigurationProperty} if it exists otherwise null. + */ + ConfigurationProperty GetProperty(string name); + + /** + * Set the value of the {@link Configuration} property by name. + * + * @param name + * Name of the property to set the value against. + * @param value + * Value to set on the configuration property. + * @throws IllegalArgumentException + * iff the property name does not exist. + */ + void SetPropertyValue(string name, Object value); + + } + + /** + * Translation from {@link Configuration} at the SPI layer to the API. + */ + public interface ConfigurationProperty { + + int Order { get; } + + /** + * Get the unique name of the configuration property. + */ + string Name { get; } + + /** + * Get the help message from the message catalog. + */ + string GetHelpMessage(string def); + + /** + * Get the display name for the is configuration + */ + string GetDisplayName(string def); + + /** + * Get the value from the property. This should be the default value. + */ + object Value { get; set; } + + /** + * Get the type of the property. + */ + Type ValueType { get; } + + /** + * Is this a confidential property whose value should be encrypted by + * the application when persisted? + */ + bool IsConfidential { get; } + + /** + * Is this a required property + * @return True if the property is required + */ + bool IsRequired { get; } + + /** + * Set of operations for which this property must be specified. + * This is used for the case where a connector may or may not + * implement certain operations depending in the configuration. + * The default value of "empty array" is special in that + * it means that this property is applicable to all operations. + */ + ICollection> Operations { get; } + } + + /** + * Main interface for which consumers call the Connector API logic. + */ + public interface ConnectorFacade : CreateApiOp, DeleteApiOp, + SearchApiOp, UpdateApiOp, SchemaApiOp, AuthenticationApiOp, GetApiOp, + ValidateApiOp, TestApiOp, ScriptOnConnectorApiOp, ScriptOnResourceApiOp, + SyncApiOp { + + /** + * Get the set of operations that this {@link ConnectorFacade} will support. + */ + ICollection> SupportedOperations { get; } + + /** + * Get an instance of an operation that this facade supports. + */ + APIOperation GetOperation(SafeType type); + + } + + /** + * Manages a pool of connectors for use by a provisioner. + * + */ + public abstract class ConnectorFacadeFactory { + + // At some point we might make this pluggable, but for now, hard-code + private const string IMPL_NAME = + "Org.IdentityConnectors.Framework.Impl.Api.ConnectorFacadeFactoryImpl"; + private static ConnectorFacadeFactory _instance; + private static object LOCK = new Object(); + + /** + * Get the singleton instance of the {@link ConnectorFacadeFactory}. + */ + public static ConnectorFacadeFactory GetInstance() { + lock(LOCK) { + if (_instance == null) { + SafeType t = FrameworkInternalBridge.LoadType(IMPL_NAME); + _instance = t.CreateInstance(); + } + } + return _instance; + } + + /** + * Get a new instance of {@link ConnectorFacade}. + * + * @param config + * all the configuration that the framework, connector, and + * pooling needs. + * @return {@link ConnectorFacade} to call API operations against. + * @throws ClassNotFoundException + */ + public abstract ConnectorFacade NewInstance(APIConfiguration config); + + + /** + * Dispose of all connection pools, resources, etc. + */ + public abstract void Dispose(); + } + + /** + * The connector meta-data for a given connector. + */ + public interface ConnectorInfo { + /** + * Returns a friendly name suitable for display in the UI. + * + * @return The friendly name + */ + string GetConnectorDisplayName(); + + ConnectorMessages Messages { get; } + + ConnectorKey ConnectorKey { get; } + + /** + * Loads the {@link Connector} and {@link Configuration} class in order to + * determine the proper default configuration parameters. + */ + APIConfiguration CreateDefaultAPIConfiguration(); + } + /** + * Class responsible for maintaing a list of ConnectorInfo + * associated with a set of connector bundles. + */ + public interface ConnectorInfoManager { + /** + * Returns the list of ConnectorInfo + * @return the list of ConnectorInfo + */ + IList ConnectorInfos { get; } + + /** + * Given a connectorName and connectorVersion, returns the + * associated ConnectorInfo. + * @param key The connector key. + * @return The ConnectorInfo or null if it couldn't + * be found. + */ + ConnectorInfo FindConnectorInfo(ConnectorKey key); + } + /** + * The main entry point into connectors. This allows you + * to load the connector classes from a set of bundles. + */ + public abstract class ConnectorInfoManagerFactory { + //At some point we might make this pluggable, but for now, hard-code + private const string IMPL_NAME = + "Org.IdentityConnectors.Framework.Impl.Api.ConnectorInfoManagerFactoryImpl"; + private static ConnectorInfoManagerFactory _instance; + private static object LOCK = new Object(); + /// + /// Singleton pattern for getting an instance of the + /// ConnectorInfoManagerFactory. + /// + /// + public static ConnectorInfoManagerFactory GetInstance() { + lock(LOCK) { + if (_instance == null) { + SafeType t = + FrameworkInternalBridge.LoadType(IMPL_NAME); + _instance = t.CreateInstance(); + } + } + return _instance; + } + public abstract ConnectorInfoManager GetLocalManager(); + public abstract ConnectorInfoManager GetRemoteManager(RemoteFrameworkConnectionInfo info); + + /** + * Clears the bundle manager cache. Generally intended for unit testing + */ + public abstract void ClearRemoteCache(); + } + + /** + * Uniquely identifies a connector within an installation. + * Consists of the triple (bundleName, bundleVersion, connectorName) + */ + public sealed class ConnectorKey { + private readonly string _bundleName; + private readonly string _bundleVersion; + private readonly string _connectorName; + + public ConnectorKey(String bundleName, + String bundleVersion, + String connectorName) { + if (bundleName == null) { + throw new ArgumentException("bundleName may not be null"); + } + if (bundleVersion == null) { + throw new ArgumentException("bundleVersion may not be null"); + } + if (connectorName == null) { + throw new ArgumentException("connectorName may not be null"); + } + _bundleName = bundleName; + _bundleVersion = bundleVersion; + _connectorName = connectorName; + } + + public string BundleName { + get { + return _bundleName; + } + } + + public string BundleVersion { + get { + return _bundleVersion; + } + } + + public string ConnectorName { + get { + return _connectorName; + } + } + + public override bool Equals(object o) { + if ( o is ConnectorKey ) { + ConnectorKey other = (ConnectorKey)o; + if (!_bundleName.Equals(other._bundleName)) { + return false; + } + if (!_bundleVersion.Equals(other._bundleVersion)) { + return false; + } + if (!_connectorName.Equals(other._connectorName)) { + return false; + } + return true; + } + return false; + } + + public override int GetHashCode() { + int rv = 0; + rv ^= _connectorName.GetHashCode(); + return rv; + } + + public override string ToString() { + StringBuilder builder = new StringBuilder(); + builder.Append("ConnectorKey("); + builder.Append(" bundleName=").Append(_bundleName); + builder.Append(" bundleVersion=").Append(_bundleVersion); + builder.Append(" connectorName=").Append(_connectorName); + builder.Append(" )"); + return builder.ToString(); + } + } + + + + public sealed class RemoteFrameworkConnectionInfo { + private readonly String _host; + private readonly int _port; + private readonly GuardedString _key; + private readonly bool _useSSL; + private readonly RemoteCertificateValidationCallback _certificateValidationCallback; + private readonly int _timeout; + + /** + * Creates a new instance of RemoteFrameworkConnectionInfo, using + * a clear (non-ssl) connection and a 60-second timeout. + * @param host The host to connect to + * @param port The port to connect to + */ + public RemoteFrameworkConnectionInfo(String host, + int port, + GuardedString key) + :this(host,port,key,false,null,60*1000) { + } + + /** + * Creates a new instance of RemoteFrameworkConnectionInfo. + * @param host The host to connect to + * @param port The port to connect to + * @param useSSL Set to true if we are to connect via SSL. + * @param certificateValidationCallback to use + * for establising the SSL connection. May be null or empty, + * in which case the default installed providers for the JVM will + * be used. Ignored if 'useSSL' is false. + * @param timeout The timeout to use (in milliseconds). A value of 0 + * means infinite timeout; + */ + public RemoteFrameworkConnectionInfo(String host, + int port, + GuardedString key, + bool useSSL, + RemoteCertificateValidationCallback certificateValidationCallback, + int timeout) { + + if ( host == null ) { + throw new ArgumentException("Parameter 'host' is null."); + } + if ( key == null ) { + throw new ArgumentException("Parameter 'key' is null."); + } + + _host = host; + _port = port; + _key = key; + _useSSL = useSSL; + _certificateValidationCallback = certificateValidationCallback; + _timeout = timeout; + } + + /** + * Returns the host to connect to. + * @return The host to connect to. + */ + public String Host { + get { + return _host; + } + } + + /** + * Returns the port to connect to + * @return The port to connect to + */ + public int Port { + get { + return _port; + } + } + + public GuardedString Key { + get { + return _key; + } + } + + /** + * Returns true iff we are to use SSL to connect. + * @return true iff we are to use SSL to connect. + */ + public bool UseSSL { + get { + return _useSSL; + } + } + + /** + * Returns the list of {@link TrustManager}'s. to use when establishing + * the connection. + * @return The list of {@link TrustManager}'s. + */ + public RemoteCertificateValidationCallback CertificateValidationCallback { + get { + return _certificateValidationCallback; + } + } + + /** + * Returns the timeout (in milliseconds) to use for the connection. + * A value of zero means infinite timeout. + * @return the timeout (in milliseconds) to use for the connection. + */ + public int Timeout { + get { + return _timeout; + } + } + + /** + * {@inheritDoc} + */ + public override bool Equals(Object o) { + if ( o is RemoteFrameworkConnectionInfo ) { + RemoteFrameworkConnectionInfo other = + (RemoteFrameworkConnectionInfo)o; + if (!Object.Equals(Host,other.Host)) { + return false; + } + if (Port != other.Port) { + return false; + } + if (UseSSL != other.UseSSL) { + return false; + } + if (CertificateValidationCallback == null || + other.CertificateValidationCallback == null) { + if (CertificateValidationCallback != null || + other.CertificateValidationCallback != null) { + return false; + } + } + else { + if (!CertificateValidationCallback.Equals + (other.CertificateValidationCallback)) { + return false; + } + } + + if (!Key.Equals(other.Key)) { + return false; + } + + if (Timeout != other.Timeout) { + return false; + } + + return true; + } + return false; + } + + /** + * {@inheritDoc} + */ + public override int GetHashCode() { + return _host.GetHashCode() ^ _port; + } + + /** + * {@inheritDoc} + */ + public override String ToString() { + return "{host="+_host+", port="+_port+"}"; + } + + + } +} diff --git a/DotNetConnectors.sln/Framework/ApiOperations.cs b/DotNetConnectors.sln/Framework/ApiOperations.cs new file mode 100644 index 00000000..bf1ce08f --- /dev/null +++ b/DotNetConnectors.sln/Framework/ApiOperations.cs @@ -0,0 +1,404 @@ +/* + * ==================== + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER. + * + * Copyright 2007-2008 Sun Microsystems, Inc. All rights reserved. + * + * The contents of this file are subject to the terms of the Common Development + * and Distribution License("CDDL") (the "License"). You may not use this file + * except in compliance with the License. + * + * You can obtain a copy of the License at + * http://IdentityConnectors.dev.java.net/legal/license.txt + * See the License for the specific language governing permissions and limitations + * under the License. + * + * When distributing the Covered Code, include this CDDL Header Notice in each file + * and include the License file at identityconnectors/legal/license.txt. + * If applicable, add the following below this CDDL Header, with the fields + * enclosed by brackets [] replaced by your own identifying information: + * "Portions Copyrighted [year] [name of copyright owner]" + * ==================== + */ +using System; +using System.Reflection; +using System.Globalization; +using System.Collections.Generic; + +using Org.IdentityConnectors.Common.Security; +using Org.IdentityConnectors.Framework.Common.Objects; +using Org.IdentityConnectors.Framework.Common.Objects.Filters; + +namespace Org.IdentityConnectors.Framework.Api.Operations +{ + /** + * Base interface for all API operations. + */ + public interface APIOperation { + } + + public interface AuthenticationApiOp : APIOperation { + + /** + * Most basic authentication available. + * + * @param username + * string that represents the account or user id. + * @param password + * string that represents the password for the account or user. + * @throws RuntimeException + * iff the credentials do not pass authentication otherwise + * nothing. + */ + Uid Authenticate(ObjectClass objectClass, string username, GuardedString password, OperationOptions options); + } + + /// + /// Operation to create connector objects based on the attributes provided. + /// + public interface CreateApiOp : APIOperation { + + /// + /// Creates a user based on the ConnectorAttributes provide. The only + /// required attribute is the ObjectClass and those required by the + /// Connector. The API will validate the existence of the + /// ObjectClass attribute and that there are no duplicate name'd + /// attributes. + /// + /// ConnectorAttribtes to create the object. + /// Unique id for the created object. + Uid Create(ObjectClass oclass, ICollection attrs, OperationOptions options); + } + + /// + /// Deletes an object with the specified Uid and ObjectClass on the + /// resource. + /// + public interface DeleteApiOp : APIOperation { + /// + /// Delete the object that the specified Uid identifies (if any). + /// + /// The type of object to delete. + /// The unique identitfier for the object to delete. + /// Throws UnknowUid if the object does not exist. + void Delete(ObjectClass objectClass, Uid uid, OperationOptions options); + } + + /** + * Get a particular {@link ConnectorObject} based on the {@link Uid}. + */ + public interface GetApiOp : APIOperation { + + /** + * Get a particular {@link ConnectorObject} based on the {@link Uid}. + * + * @param uid + * the unique id of the object that to get. + * @return {@link ConnectorObject} based on the {@link Uid} provided. + */ + ConnectorObject GetObject(ObjectClass objClass, Uid uid, OperationOptions options); + } + + /** + * Get the schema from the {@link Connector}. + */ + public interface SchemaApiOp : APIOperation { + /** + * Retrieve the basic schema of this {@link Connector}. + */ + Schema Schema(); + } + + + public interface SearchApiOp : APIOperation { + + /** + * Search the resource for all objects that match the filter. + * + * @param filter + * Reduces the number of entries to only those that match the + * {@link Filter} provided. + * @param handler + * class responsible for working with the objects returned from + * the search. + * @throws RuntimeException + * iff there is problem during the processing of the results. + */ + void Search(ObjectClass oclass, Filter filter, ResultsHandler handler, OperationOptions options); + } + + /** + * Runs a script in the same JVM or .Net Runtime as the Connector. + * That is, if you are using a local framework, the script will be + * run in your JVM. If you are connected to a remote framework, the + * script will be run in the remote JVM or .Net Runtime. + *

+ * This API allows an application to run a script in the context + * of any connector. (A connector need not implement any particular interface + * in order to enable this.) The minimum contract to which each connector + * must adhere is as follows: + *

    + *
  1. Script will run in the same classloader/execution environment + * as the connector, so the script will have access to all the classes + * to which the connector has access. + *
  2. + *
  3. Script will have access to a "connector" variable + * that is equivalent to an initialized instance of a connector. + * Thus, at a minimum the script will be able to access + * {@link Connector#getConfiguration() the configuration of the connector}. + *
  4. + *
  5. Script will have access to any + * {@link ScriptContext#getScriptArguments() script-arguments} + * passed in by the application. + *
  6. + *
+ *

+ * A connector that implements {@link ScriptOnConnectorOp} + * may provide more variables than what is described above. + * A connector also may perform special processing + * for {@link OperationOptions} specific to that connector. + * Consult the javadoc of each particular connector to find out what + * additional capabilities, if any, that connector exposes for use in scripts. + *

+ * NOTE: A caller who wants to execute scripts on a connector + * should assume that a script must not use any method of the connector + * beyond the minimum contract described above, + * unless the connector explicitly documents that method as + * "for use by connector script". The primary function of a connector + * is to implement the SPI in the context of the Connector framework. + * In general, no caller should invoke Connector methods directly + * --whether by a script or by other means. + */ + public interface ScriptOnConnectorApiOp : APIOperation { + + /** + * Runs the script. + * @param request - The script and arguments to run. + * @param options - Additional options that control how the script is + * run. The framework does not currently recognize any options + * but specific connectors might. Consult the documentation + * for each connector to identify supported options. + * @return The result of the script. The return type must be + * a type that the framework supports for serialization. + * @see ObjectSerializerFactory for a list of supported return types. + */ + Object RunScriptOnConnector(ScriptContext request, + OperationOptions options); + } + /** + * Runs a script on the target resource that a connector manages. + * This API operation is supported only for a connector that implements + * {@link ScriptOnResourceOp}. + *

+ * The contract here at the API level is intentionally very loose. + * Each connector decides what script languages it supports, + * what running a script on a target resource actually means, + * and what script options (if any) that connector supports. + * Refer to the javadoc of each particular connector for more information. + */ + public interface ScriptOnResourceApiOp : APIOperation { + + /** + * Runs a script on a specific target resource. + * @param request The script and arguments to run. + * @param options Additional options which control how the script is + * run. Please refer to the connector documentation for supported + * options. + * @return The result of the script. The return type must be + * a type that the connector framework supports for serialization. + * See {@link ObjectSerializerFactory} for a list of supported return types. + */ + Object RunScriptOnResource(ScriptContext request, + OperationOptions options); + } + /** + * Receive synchronization events from the resource. This will be supported by + * connectors that implement {@link SyncOp}. + * + * @see SyncOp + */ + public interface SyncApiOp : APIOperation { + /** + * Perform a synchronization. + * + * @param objClass + * The object class to synchronize. Must not be null. + * @param token + * The token representing the last token from the previous sync. + * Should be null if this is the first sync for the given + * resource. + * @param handler + * The result handler Must not be null. + * @param options + * additional options that impact the way this operation is run. + * May be null. + */ + void Sync(ObjectClass objClass, SyncToken token, + SyncResultsHandler handler, + OperationOptions options); + /** + * Returns the token corresponding to the latest sync delta. + * This is to support applications that may wish to sync starting + * "now". + * @return The latest token or null if there is no sync data. + */ + SyncToken GetLatestSyncToken(ObjectClass objectClass); + } + + /** + * Updates a {@link ConnectorObject}. This operation + * is supported for those connectors that implement + * either {@link UpdateOp} or the more advanced + * {@link UpdateAttributeValuesOp}. + */ + public interface UpdateApiOp : APIOperation { + + /** + * Update the object specified by the {@link ObjectClass} and {@link Uid}, + * replacing the current values of each attribute with the values + * provided. + *

+ * For each input attribute, replace + * all of the current values of that attribute in the target object with + * the values of that attribute. + *

+ * If the target object does not currently contain an attribute that the + * input set contains, then add this + * attribute (along with the provided values) to the target object. + *

+ * If the value of an attribute in the input set is + * {@code null}, then do one of the following, depending on + * which is most appropriate for the target: + *

    + *
  • If possible, remove that attribute from the target + * object entirely.
  • + *
  • Otherwise, replace all of the current values of that + * attribute in the target object with a single value of + * {@code null}.
  • + *
+ * @param objclass + * the type of object to modify. Must not be null. + * @param uid + * the uid of the object to modify. Must not be null. + * @param replaceAttributes + * set of new {@link Attribute}. the values in this set + * represent the new, merged values to be applied to the object. + * This set may also include {@link OperationalAttributes operational attributes}. + * Must not be null. + * @param options + * additional options that impact the way this operation is run. + * May be null. + * @return the {@link Uid} of the updated object in case the update changes + * the formation of the unique identifier. + */ + Uid Update(ObjectClass objclass, + Uid uid, + ICollection replaceAttributes, + OperationOptions options); + + /** + * Update the object specified by the {@link ObjectClass} and {@link Uid}, + * adding to the current values of each attribute the values provided. + *

+ * For each attribute that the input set contains, add to + * the current values of that attribute in the target object all of the + * values of that attribute in the input set. + *

+ * NOTE that this does not specify how to handle duplicate values. + * The general assumption for an attribute of a {@code ConnectorObject} + * is that the values for an attribute may contain duplicates. + * Therefore, in general simply append the provided values + * to the current value for each attribute. + *

+ * IMPLEMENTATION NOTE: for connectors that merely implement {@link UpdateOp} + * and not {@link UpdateAttributeValuesOp} this method will be simulated by + * fetching, merging, and calling + * {@link UpdateOp#update(ObjectClass, Uid, Set, OperationOptions)}. Therefore, + * connector implementations are encourage to implement {@link UpdateAttributeValuesOp} + * from a performance and atomicity standpoint. + * @param objclass + * the type of object to modify. Must not be null. + * @param uid + * the uid of the object to modify. Must not be null. + * @param valuesToAdd + * set of {@link Attribute} deltas. The values for the attributes + * in this set represent the values to add to attributes in the object. + * merged. This set must not include {@link OperationalAttributes operational attributes}. + * Must not be null. + * @param options + * additional options that impact the way this operation is run. + * May be null. + * @return the {@link Uid} of the updated object in case the update changes + * the formation of the unique identifier. + */ + Uid AddAttributeValues(ObjectClass objclass, + Uid uid, + ICollection valuesToAdd, + OperationOptions options); + + /** + * Update the object specified by the {@link ObjectClass} and {@link Uid}, + * removing from the current values of each attribute the values provided. + *

+ * For each attribute that the input set contains, + * remove from the current values of that attribute in the target object + * any value that matches one of the values of the attribute from the input set. + *

+ * NOTE that this does not specify how to handle unmatched values. + * The general assumption for an attribute of a {@code ConnectorObject} + * is that the values for an attribute are merely representational state. + * Therefore, the implementer should simply ignore any provided value + * that does not match a current value of that attribute in the target + * object. Deleting an unmatched value should always succeed. + *

+ * IMPLEMENTATION NOTE: for connectors that merely implement {@link UpdateOp} + * and not {@link UpdateAttributeValuesOp} this method will be simulated by + * fetching, merging, and calling + * {@link UpdateOp#update(ObjectClass, Uid, Set, OperationOptions)}. Therefore, + * connector implementations are encourage to implement {@link UpdateAttributeValuesOp} + * from a performance and atomicity standpoint. + * @param objclass + * the type of object to modify. Must not be null. + * @param uid + * the uid of the object to modify. Must not be null. + * @param valuesToRemove + * set of {@link Attribute} deltas. The values for the attributes + * in this set represent the values to remove from attributes in the object. + * merged. This set must not include {@link OperationalAttributes operational attributes}. + * Must not be null. + * @param options + * additional options that impact the way this operation is run. + * May be null. + * @return the {@link Uid} of the updated object in case the update changes + * the formation of the unique identifier. + */ + Uid RemoveAttributeValues(ObjectClass objclass, + Uid uid, + ICollection valuesToRemove, + OperationOptions options); + + } + + + public interface ValidateApiOp : APIOperation { + /** + * Tests connectivity and validity of the {@link Configuration}. + * + * @throws RuntimeException + * iff the {@link Configuration} is not valid or a + * {@link Connection} to the resource could not be established. + */ + void Validate(); + } + + public interface TestApiOp : APIOperation { + /** + * Tests connectivity and validity of the {@link Configuration}. + * + * @throws RuntimeException + * iff the {@link Configuration} is not valid or a + * {@link Connection} to the resource could not be established. + */ + void Test(); + } +} diff --git a/DotNetConnectors.sln/Framework/AssemblyInfo.cs b/DotNetConnectors.sln/Framework/AssemblyInfo.cs new file mode 100644 index 00000000..c33629ae --- /dev/null +++ b/DotNetConnectors.sln/Framework/AssemblyInfo.cs @@ -0,0 +1,53 @@ +/* + * ==================== + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER. + * + * Copyright 2007-2008 Sun Microsystems, Inc. All rights reserved. + * + * The contents of this file are subject to the terms of the Common Development + * and Distribution License("CDDL") (the "License"). You may not use this file + * except in compliance with the License. + * + * You can obtain a copy of the License at + * http://IdentityConnectors.dev.java.net/legal/license.txt + * See the License for the specific language governing permissions and limitations + * under the License. + * + * When distributing the Covered Code, include this CDDL Header Notice in each file + * and include the License file at identityconnectors/legal/license.txt. + * If applicable, add the following below this CDDL Header, with the fields + * enclosed by brackets [] replaced by your own identifying information: + * "Portions Copyrighted [year] [name of copyright owner]" + * ==================== + */ +#region Using directives + +using System.Reflection; +using System.Runtime.CompilerServices; +using System.Runtime.InteropServices; + +#endregion + +// General Information about an assembly is controlled through the following +// set of attributes. Change these attribute values to modify the information +// associated with an assembly. +[assembly: AssemblyTitle("Open Connectors Framework")] +[assembly: AssemblyDescription("")] +[assembly: AssemblyConfiguration("")] +[assembly: AssemblyCompany("")] +[assembly: AssemblyProduct("Open Connectors Framework")] +[assembly: AssemblyCopyright("")] +[assembly: AssemblyTrademark("")] +[assembly: AssemblyCulture("")] + +// This sets the default COM visibility of types in the assembly to invisible. +// If you need to expose a type to COM, use [ComVisible(true)] on that type. +[assembly: ComVisible(false)] + +// The assembly version has following format : +// +// Major.Minor.Build.Revision +// +// You can specify all the values or you can use the default the Revision and +// Build Numbers by using the '*' as shown below: +[assembly: AssemblyVersion("1.0.0.0")] diff --git a/DotNetConnectors.sln/Framework/Common.cs b/DotNetConnectors.sln/Framework/Common.cs new file mode 100644 index 00000000..2da8fa0d --- /dev/null +++ b/DotNetConnectors.sln/Framework/Common.cs @@ -0,0 +1,287 @@ +/* + * ==================== + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER. + * + * Copyright 2007-2008 Sun Microsystems, Inc. All rights reserved. + * + * The contents of this file are subject to the terms of the Common Development + * and Distribution License("CDDL") (the "License"). You may not use this file + * except in compliance with the License. + * + * You can obtain a copy of the License at + * http://IdentityConnectors.dev.java.net/legal/license.txt + * See the License for the specific language governing permissions and limitations + * under the License. + * + * When distributing the Covered Code, include this CDDL Header Notice in each file + * and include the License file at identityconnectors/legal/license.txt. + * If applicable, add the following below this CDDL Header, with the fields + * enclosed by brackets [] replaced by your own identifying information: + * "Portions Copyrighted [year] [name of copyright owner]" + * ==================== + */ +using System; +using System.Reflection; +using System.Collections.Generic; +using Org.IdentityConnectors.Common; +using Org.IdentityConnectors.Common.Security; +using Org.IdentityConnectors.Framework.Spi; +using Org.IdentityConnectors.Framework.Api.Operations; +using Org.IdentityConnectors.Framework.Spi.Operations; +using Org.IdentityConnectors.Framework.Common.Objects; +namespace Org.IdentityConnectors.Framework.Common +{ + internal static class FrameworkInternalBridge { + private static readonly Object LOCK = new Object(); + private static Assembly _assembly = null; + ///

+ /// Loads a class from the FrameworkInternal module + /// + /// + /// + public static SafeType LoadType(String typeName) where T : class { + + Assembly assembly; + lock(LOCK) { + if (_assembly == null) { + AssemblyName assemName = new AssemblyName(); + assemName.Name = "FrameworkInternal"; + _assembly = Assembly.Load(assemName); + } + assembly = _assembly; + } + + return SafeType.ForRawType(assembly.GetType(typeName,true)); + + } + } + + public static class FrameworkUtil { + private static readonly IDictionary,SafeType> SPI_TO_API; + private static readonly ICollection CONFIG_SUPPORTED_TYPES; + private static readonly ICollection ATTR_SUPPORTED_TYPES; + + static FrameworkUtil() { + IDictionary,SafeType> temp = + new Dictionary,SafeType>(); + temp[SafeType.Get()]= + SafeType.Get(); + temp[SafeType.Get()]= + SafeType.Get(); + temp[SafeType.Get()]= + SafeType.Get(); + temp[SafeType.ForRawType(typeof(SearchOp<>))]= + SafeType.Get(); + temp[SafeType.Get()]= + SafeType.Get(); + temp[SafeType.Get()]= + SafeType.Get(); + temp[SafeType.Get()]= + SafeType.Get(); + temp[SafeType.Get()]= + SafeType.Get(); + temp[SafeType.Get()]= + SafeType.Get(); + temp[SafeType.Get()]= + SafeType.Get(); + temp[SafeType.Get()]= + SafeType.Get(); + SPI_TO_API = CollectionUtil.NewReadOnlyDictionary(temp); + + CONFIG_SUPPORTED_TYPES = CollectionUtil.NewReadOnlySet + ( + typeof(string), + typeof(long), + typeof(long?), + typeof(char), + typeof(char?), + typeof(double), + typeof(double?), + typeof(float), + typeof(float?), + typeof(int), + typeof(int?), + typeof(bool), + typeof(bool?), + typeof(Uri), + typeof(FileName), + typeof(GuardedString) + ); + ATTR_SUPPORTED_TYPES = CollectionUtil.NewReadOnlySet + ( + typeof(string), + typeof(long), + typeof(long?), + typeof(char), + typeof(char?), + typeof(double), + typeof(double?), + typeof(float), + typeof(float?), + typeof(int), + typeof(int?), + typeof(bool), + typeof(bool?), + typeof(byte[]), + typeof(BigDecimal), + typeof(BigInteger), + typeof(GuardedString) + ); + + } + + + /** + * Determines if the class is a supported attribute type. If not it throws + * an {@link IllegalArgumentException}. + * + *
    + *
  • string
  • + *
  • long
  • + *
  • long?
  • + *
  • char
  • + *
  • char?
  • + *
  • double
  • + *
  • double?
  • + *
  • float
  • + *
  • float?
  • + *
  • int
  • + *
  • int?
  • + *
  • bool
  • + *
  • bool?
  • + *
  • byte[]
  • + *
  • BigDecimal
  • + *
  • BigInteger
  • + *
+ * + * @param clazz + * type to check against the support list of types. + * @throws IllegalArgumentException + * iff the type is not on the supported list. + */ + public static void CheckAttributeType(Type type) { + if (!FrameworkUtil.IsSupportedAttributeType(type)) { + String MSG = "Attribute type ''"+type+"'' is not supported."; + throw new ArgumentException(MSG); + } + } + public static void CheckAttributeValue(Object value) { + if ( value != null ) { + CheckAttributeType(value.GetType()); + } + } + public static ICollection> Spi2Apis(SafeType type) { + type = type.GetTypeErasure(); + HashSet> set = new HashSet>(); + set.Add(SPI_TO_API[type]); + // add GetApiOp if search is available.. + + if (type.RawType.Equals(typeof(SearchOp<>))) { + set.Add(SafeType.Get()); + } + return set; + } + public static ICollection> AllSPIOperations() { + return SPI_TO_API.Keys; + } + public static ICollection> AllAPIOperations() { + ICollection> set = + new HashSet>(); + CollectionUtil.AddAll(set, + SPI_TO_API.Values); + // add Get because it doesn't have a corresponding SPI. + set.Add(SafeType.Get()); + set.Add(SafeType.Get()); + return CollectionUtil.AsReadOnlySet(set); + } + public static ICollection> GetDefaultSupportedOperations(SafeType connector) { + ICollection> rv = + new HashSet>(); + ICollection interfaces = + ReflectionUtil.GetTypeErasure(ReflectionUtil.GetAllInterfaces(connector.RawType)); + foreach (SafeType spi in AllSPIOperations()) { + if (interfaces.Contains(spi.RawType)) { + CollectionUtil.AddAll(rv,Spi2Apis(spi)); + } + } + //finally add unconditionally supported ops + CollectionUtil.AddAll(rv,GetUnconditionallySupportedOperations()); + return CollectionUtil.AsReadOnlySet(rv); + } + public static ICollection> GetUnconditionallySupportedOperations() { + HashSet> ret; + ret = new HashSet>(); + //add validate api op always + ret.Add(SafeType.Get()); + //add ScriptOnConnectorApiOp always + ret.Add(SafeType.Get()); + return ret; + } + public static ICollection GetAllSupportedConfigTypes() { + return CONFIG_SUPPORTED_TYPES; + } + public static bool IsSupportedConfigurationType (Type type) { + if ( type.IsArray) { + return IsSupportedConfigurationType(type.GetElementType()); + } + else { + return CONFIG_SUPPORTED_TYPES.Contains(type); + } + } + public static ICollection GetAllSupportedAttributeTypes() { + return ATTR_SUPPORTED_TYPES; + } + public static bool IsSupportedAttributeType(Type clazz) { + return ATTR_SUPPORTED_TYPES.Contains(clazz); + } + + /** + * Determines if the class is a supported type for an OperationOption. If not it throws + * an {@link IllegalArgumentException}. + * + * @param clazz + * type to check against the support list of types. + * @throws IllegalArgumentException + * iff the type is not on the supported list. + */ + public static void CheckOperationOptionType(Type clazz) { + //the set of supported operation option types + //is the same as that for configuration beans plus Name, + //ObjectClass, Uid, and QualifiedUid + + if ( clazz.IsArray ) { + CheckOperationOptionType(clazz.GetElementType()); + return; + } + + if (FrameworkUtil.IsSupportedConfigurationType(clazz)) { + return; //ok + } + + if (typeof(ObjectClass).IsAssignableFrom(clazz)) { + return; //ok + } + + if (typeof(Uid).IsAssignableFrom(clazz)) { + return; //ok + } + + if (typeof(QualifiedUid).IsAssignableFrom(clazz)) { + return; //ok + } + + String MSG = "ConfigurationOption type '+"+clazz.Name+"+' is not supported."; + throw new ArgumentException(MSG); + } + /** + * Determines if the class of the object is a supported attribute type. + * If not it throws an {@link IllegalArgumentException}. + * @param value The value to check or null. + */ + public static void CheckOperationOptionValue(Object val) { + if ( val != null ) { + CheckOperationOptionType(val.GetType()); + } + } + } +} diff --git a/DotNetConnectors.sln/Framework/CommonExceptions.cs b/DotNetConnectors.sln/Framework/CommonExceptions.cs new file mode 100644 index 00000000..08043091 --- /dev/null +++ b/DotNetConnectors.sln/Framework/CommonExceptions.cs @@ -0,0 +1,291 @@ +/* + * ==================== + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER. + * + * Copyright 2007-2008 Sun Microsystems, Inc. All rights reserved. + * + * The contents of this file are subject to the terms of the Common Development + * and Distribution License("CDDL") (the "License"). You may not use this file + * except in compliance with the License. + * + * You can obtain a copy of the License at + * http://IdentityConnectors.dev.java.net/legal/license.txt + * See the License for the specific language governing permissions and limitations + * under the License. + * + * When distributing the Covered Code, include this CDDL Header Notice in each file + * and include the License file at identityconnectors/legal/license.txt. + * If applicable, add the following below this CDDL Header, with the fields + * enclosed by brackets [] replaced by your own identifying information: + * "Portions Copyrighted [year] [name of copyright owner]" + * ==================== + */ +using System; +using Org.IdentityConnectors.Framework.Common.Objects; + +namespace Org.IdentityConnectors.Framework.Common.Exceptions +{ + #region AlreadyExistsException + public class AlreadyExistsException : ConnectorException { + + public AlreadyExistsException() : base() { + } + + public AlreadyExistsException(String message) : base(message) { + + } + + public AlreadyExistsException(Exception ex) : base(ex) { + } + + public AlreadyExistsException(String message, Exception ex) : base(message,ex) { + } + } + #endregion + + #region ConfigurationException + public class ConfigurationException : ConnectorException { + + public ConfigurationException() : base() { + } + + public ConfigurationException(String message) : base(message) { + } + + public ConfigurationException(Exception ex) : base(ex) { + } + + public ConfigurationException(String message, Exception ex) : base(message,ex) { + } + } + #endregion + + #region ConnectionBrokenException + public class ConnectionBrokenException : ConnectorIOException { + + + public ConnectionBrokenException() : base() { + } + + public ConnectionBrokenException(String msg) : base(msg) { + } + + public ConnectionBrokenException(Exception ex) : base(ex) { + } + + public ConnectionBrokenException(String message, Exception ex) : base(message,ex) { + } + } + #endregion + + #region ConnectionFailedException + public class ConnectionFailedException : ConnectorIOException { + + + public ConnectionFailedException() : base() { + } + + public ConnectionFailedException(String msg) : base(msg) { + } + + public ConnectionFailedException(Exception ex) : base(ex) { + } + + public ConnectionFailedException(String message, Exception ex) : base(message,ex) { + } + + } + #endregion + + #region ConnectorException + public class ConnectorException : ApplicationException { + + public ConnectorException() : base() { + } + + /** + * Sets a message for the {@link Exception}. + * + * @param message + * passed to the {@link RuntimeException} message. + */ + public ConnectorException(String message) :base(message) { + } + + /** + * Sets the stack trace to the original exception, so this exception can + * masquerade as the original only be a {@link RuntimeException}. + * + * @param originalException + * the original exception adapted to {@link RuntimeException}. + */ + public ConnectorException(Exception ex) : base(ex.Message,ex) { + } + + /** + * Sets the stack trace to the original exception, so this exception can + * masquerade as the original only be a {@link RuntimeException}. + * + * @param message + * @param originalException + * the original exception adapted to {@link RuntimeException}. + */ + public ConnectorException(String message, Exception originalException) : base(message,originalException) { + } + + } + #endregion + + #region ConnectorIOException + public class ConnectorIOException : ConnectorException { + + public ConnectorIOException() : base() { + } + + public ConnectorIOException(String msg) : base(msg) { + } + + public ConnectorIOException(Exception ex) : base(ex) { + } + + public ConnectorIOException(String message, Exception ex) : base(message,ex) { + } + } + #endregion + + #region ConnectorSecurityException + public class ConnectorSecurityException : ConnectorException { + + public ConnectorSecurityException() : base() { + } + + public ConnectorSecurityException(String message) : base(message) { + } + + public ConnectorSecurityException(Exception ex) : base(ex) { + } + + public ConnectorSecurityException(String message, Exception ex) : base(message,ex) { + } + } + #endregion + + #region InvalidCredentialException + public class InvalidCredentialException : ConnectorSecurityException { + + public InvalidCredentialException() : base() { + } + + public InvalidCredentialException(String message) : base(message) { + } + + public InvalidCredentialException(Exception ex) : base(ex) { + } + + public InvalidCredentialException(String message, Exception ex) : base(message,ex) { + } + } + #endregion + + #region InvalidPasswordException + public class InvalidPasswordException : InvalidCredentialException { + + public InvalidPasswordException() : base() { + } + + public InvalidPasswordException(String message) : base(message) { + } + + public InvalidPasswordException(Exception ex) : base(ex) { + } + + public InvalidPasswordException(String message, Exception ex) : base(message,ex) { + } + } + #endregion + + #region OperationTimeoutException + public class OperationTimeoutException : ConnectorException { + + public OperationTimeoutException() : base() { + } + + public OperationTimeoutException(String msg) : base(msg) { + } + + public OperationTimeoutException(Exception e) : base(e) { + } + + public OperationTimeoutException(String msg, Exception e) : base(msg,e) { + } + + } + #endregion + + #region PasswordExpiredException + public class PasswordExpiredException : InvalidPasswordException { + + private Uid _uid; + + public PasswordExpiredException() : base() { + } + + public PasswordExpiredException(String message) : base(message) { + } + + public PasswordExpiredException(Exception ex) : base(ex) { + } + + public PasswordExpiredException(String message, Exception ex) : base(message,ex) { + } + + public Uid Uid { + get { + return _uid; + } + set { + _uid = value; + } + } + } + #endregion + + #region PermissionDeniedException + public class PermissionDeniedException : ConnectorSecurityException { + + public PermissionDeniedException() : base() { + } + + public PermissionDeniedException(String message) : base(message) { + } + + public PermissionDeniedException(Exception ex) : base(ex) { + } + + public PermissionDeniedException(String message, Exception ex) : base(message,ex) { + } + } + #endregion + + #region UnknownUidException + public class UnknownUidException : InvalidCredentialException { + const string MSG = "Object with Uid '{0}' and ObjectClass '{1}' does not exist!"; + + public UnknownUidException() : base() { + } + + public UnknownUidException(Uid uid, ObjectClass objclass) : + base(String.Format(MSG, uid, objclass)){ + } + + public UnknownUidException(String message) : base(message) { + } + + public UnknownUidException(Exception ex) : base(ex) { + } + + public UnknownUidException(String message, Exception ex) : base(message,ex) { + } + } + #endregion +} diff --git a/DotNetConnectors.sln/Framework/CommonObjects.cs b/DotNetConnectors.sln/Framework/CommonObjects.cs new file mode 100644 index 00000000..fa2cec1a --- /dev/null +++ b/DotNetConnectors.sln/Framework/CommonObjects.cs @@ -0,0 +1,3893 @@ +/* + * ==================== + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER. + * + * Copyright 2007-2008 Sun Microsystems, Inc. All rights reserved. + * + * The contents of this file are subject to the terms of the Common Development + * and Distribution License("CDDL") (the "License"). You may not use this file + * except in compliance with the License. + * + * You can obtain a copy of the License at + * http://IdentityConnectors.dev.java.net/legal/license.txt + * See the License for the specific language governing permissions and limitations + * under the License. + * + * When distributing the Covered Code, include this CDDL Header Notice in each file + * and include the License file at identityconnectors/legal/license.txt. + * If applicable, add the following below this CDDL Header, with the fields + * enclosed by brackets [] replaced by your own identifying information: + * "Portions Copyrighted [year] [name of copyright owner]" + * ==================== + */ +using System; +using System.Security; +using System.Collections; +using System.Collections.Generic; +using System.Globalization; +using System.Text; + +using Org.IdentityConnectors.Common; +using Org.IdentityConnectors.Common.Security; + +using Org.IdentityConnectors.Framework.Spi; +using Org.IdentityConnectors.Framework.Spi.Operations; +using Org.IdentityConnectors.Framework.Api.Operations; +using Org.IdentityConnectors.Framework.Common.Serializer; +using Org.IdentityConnectors.Framework.Common.Objects.Filters; +namespace Org.IdentityConnectors.Framework.Common.Objects +{ + #region ConnectorAttributeUtil + public static class ConnectorAttributeUtil { + + /** + * Gets the string value from the single value attribute. + * + * @param attr + * ConnectorAttribute to retrieve the string value from. + * @return null if the value is null otherwise the string value for the + * attribute. + * @throws ClassCastException + * iff the object in the attribute is not an string. + * @throws IllegalArgumentException + * iff the attribute is a multi valued instead of single valued. + */ + public static string GetStringValue(ConnectorAttribute attr) { + return (string)GetSingleValue(attr); + } + + /** + * Gets the string value from the single value attribute. + * + * @param attr + * ConnectorAttribute to retrieve the string value from. + * @return null if the value is null otherwise the string value for the + * attribute. + * @throws IllegalArgumentException + * iff the attribute is a multi valued instead of single valued. + */ + public static string GetAsStringValue(ConnectorAttribute attr) { + object obj = GetSingleValue(attr); + return obj != null ? obj.ToString() : null; + } + + public static GuardedString GetGuardedStringValue(ConnectorAttribute attr) { + object obj = GetSingleValue(attr); + return obj != null ? (GuardedString)obj : null; + } + /** + * Gets the integer value from the single value attribute. + * + * @param attr + * ConnectorAttribute to retrieve the integer value from. + * @return null if the value is null otherwise the integer value for the + * attribute. + * @throws ClassCastException + * iff the object in the attribute is not an integer. + * @throws IllegalArgumentException + * iff the attribute is a multi valued instead of single valued. + */ + public static int? GetIntegerValue(ConnectorAttribute attr) { + object obj = GetSingleValue(attr); + return obj != null ? (int?) obj : null; + } + + /** + * Gets the long value from the single value attribute. + * + * @param attr + * ConnectorAttribute to retrieve the long value from. + * @return null if the value is null otherwise the long value for the + * attribute. + * @throws ClassCastException + * iff the object in the attribute is not an long. + * @throws IllegalArgumentException + * iff the attribute is a multi valued instead of single valued. + */ + public static long? GetLongValue(ConnectorAttribute attr) { + Object obj = GetSingleValue(attr); + return obj != null ? (long?) obj : null; + } + + /** + * Gets the date value from the single value attribute. + * + * @param attr + * ConnectorAttribute to retrieve the date value from. + * @return null if the value is null otherwise the date value for the + * attribute. + * @throws ClassCastException + * iff the object in the attribute is not an long. + * @throws IllegalArgumentException + * iff the attribute is a multi valued instead of single valued. + */ + public static DateTime? GetDateTimeValue(ConnectorAttribute attr) { + long? val = GetLongValue(attr); + if (val != null) { + return DateTimeUtil.GetDateTimeFromUtcMillis(val.Value); + } + return null; + } + + /** + * Gets the integer value from the single value attribute. + * + * @param attr + * ConnectorAttribute to retrieve the integer value from. + * @return null if the value is null otherwise the integer value for the + * attribute. + * @throws ClassCastException + * iff the object in the attribute is not an integer. + * @throws IllegalArgumentException + * iff the attribute is a multi valued instead of single valued. + */ + public static double? GetDoubleValue(ConnectorAttribute attr) { + Object obj = GetSingleValue(attr); + return obj != null ? (double?) obj : null; + } + + public static bool? GetBooleanValue(ConnectorAttribute attr) { + object obj = GetSingleValue(attr); + return obj != null ? (bool?) obj : null; + } + + /** + * Get the single value from the ConnectorAttribute. + */ + public static object GetSingleValue(ConnectorAttribute attr) { + Object ret = null; + IList val = attr.Value; + if (val != null) { + // make sure this only called for single value.. + if (val.Count > 1) { + const string MSG = "The method is only for single value attributes."; + throw new ArgumentException(MSG); + } + ret = val[0]; + } + return ret; + } + + /// + /// Transform a Collection of ConnectorAttribute} instances into a {@link Map}. + /// The key to each element in the map is the name of an ConnectorAttribute. + /// The value of each element in the map is the ConnectorAttribute instance with that name. + /// + /// + /// + public static IDictionary ToMap( + ICollection attributes) { + IDictionary ret = + new Dictionary( + StringComparer.OrdinalIgnoreCase); + foreach (ConnectorAttribute attr in attributes) { + ret[attr.Name] = attr; + } + return ret; + } + + /** + * Get the {@link Uid} from the attribute set. + * + * @param attrs + * set of {@link ConnectorAttribute}s that may contain a {@link Uid}. + * @return null if the set does not contain a {@link Uid} object the first + * one found. + */ + public static Uid GetUidAttribute(ICollection attrs) { + return (Uid)Find(Uid.NAME, attrs); + } + + /** + * Filters out all special attributes from the set. These special attributes + * include {@link Password}, {@link Uid} etc.. + * + * @param attrs + * set of {@link ConnectorAttribute}s to filter out the operational and + * default attributes. + * @return a set that only contains plain attributes or empty. + */ + public static ICollection GetBasicAttributes(ICollection attrs) { + ICollection ret = new HashSet(); + foreach (ConnectorAttribute attr in attrs) { + // note this is dangerous because we need to be consistent + // in the naming of special attributes. + if (!IsSpecial(attr)) { + ret.Add(attr); + } + } + return ret; + } + /** + * Filter out any basic attributes from the specified set, leaving only + * special attributes. Special attributes include {@link Name}, {@link Uid}, + * and {@link OperationalAttributes}. + * + * @param attrs + * set of {@link Attribute}s to filter out the basic attributes + * @return a set that only contains special attributes or an empty set if + * there are none. + */ + public static ICollection GetSpecialAttributes(ICollection attrs) { + ICollection ret = new HashSet(); + foreach (ConnectorAttribute attr in attrs) { + if (IsSpecial(attr)) { + ret.Add(attr); + } + } + return ret; + } + + /** + * Returns a mutable copy of the original set with the uid attribute removed. + * @param attrs The original set. Must not be null. + * @return A mutable copy of the original set with the uid attribute removed. + */ + public static ICollection FilterUid(ICollection attrs) { + Assertions.NullCheck(attrs, "attrs"); + HashSet ret = new HashSet(); + foreach (ConnectorAttribute attr in attrs) { + if (!(attr is Uid)) { + ret.Add(attr); + } + } + return ret; + } + + /** + * Returns a mutable copy of the original set with the uid attribute added. + * @param attrs The original set. Must not be null. + * @param uid The uid. Must not be null. + * @return A mutable copy of the original set with the uid attribute added. + */ + public static ICollection AddUid(ICollection attrs, Uid uid) { + Assertions.NullCheck(attrs, "attrs"); + Assertions.NullCheck(uid, "uid"); + HashSet ret = new HashSet(attrs); + ret.Add(uid); + return ret; + } + + /** + * Determines if this attribute is a special attribute. + * + * @param attr + * {@link ConnectorAttribute} to test for against. + * @return true iff the attribute value is a {@link Uid}, + * {@link ObjectClass}, {@link Password}, or + * {@link OperationalAttributes}. + * @throws NullPointerException + * iff the attribute parameter is null. + */ + public static bool IsSpecial(ConnectorAttribute attr) { + // note this is dangerous because we need to be consistent + // in the naming of special attributes. + String name = attr.Name; + return IsSpecialName(name); + } + + /** + * Determines if this attribute is a special attribute. + * + * @param attr + * {@link ConnectorAttribute} to test for against. + * @return true iff the attribute value is a {@link Uid}, + * {@link ObjectClass}, {@link Password}, or + * {@link OperationalAttributes}. + * @throws NullPointerException + * iff the attribute parameter is null. + */ + public static bool IsSpecial(ConnectorAttributeInfo attr) { + // note this is dangerous because we need to be consistent + // in the naming of special attributes. + String name = attr.Name; + return IsSpecialName(name); + } + + private static bool IsSpecialName(String name) { + // note this is dangerous because we need to be consistent + // in the naming of special attributes. + return (name.StartsWith("@@") && name.EndsWith("@@")); + } + + /// + /// Creates the special naming for operational type attributes. + /// + /// string to make special + /// name constructed for use as an operational attribute. + public static string CreateSpecialName(string name) { + if (StringUtil.IsBlank(name)) { + const string ERR = "Name parameter must not be blank!"; + throw new ArgumentException(ERR); + } + return "@@" + name + "@@"; + } + + /// + /// Gets the 'Name' attribute from a set of ConnectorAttributes. + /// + /// set of attributes to search against. + /// the 'Name' attribute it if exsist otherwisenull + public static Name GetNameFromAttributes(ICollection attrs) { + return (Name)Find(Name.NAME, attrs); + } + + + /** + * Find the {@link ConnectorAttribute} of the given name in the {@link Set}. + * + * @param name + * {@link ConnectorAttribute}'s name to search for. + * @param attrs + * {@link Set} of attribute to search. + * @return {@link ConnectorAttribute} with the specified otherwise null. + */ + public static ConnectorAttribute Find(string name, ICollection attrs) { + Assertions.NullCheck(name, "name"); + ICollection attributes = CollectionUtil.NullAsEmpty(attrs); + foreach (ConnectorAttribute attr in attributes) { + if (attr.Is(name)) { + return attr; + } + } + return null; + } + /** + * Get the password value from the provided set of {@link ConnectorAttribute}s. + */ + public static GuardedString GetPasswordValue(ICollection attrs) { + ConnectorAttribute pwd = Find(OperationalAttributes.PASSWORD_NAME, attrs); + return (pwd == null) ? null : GetGuardedStringValue(pwd); + } + + /** + * Get the current password value from the provided set of {@link Attribute}s. + * + * @param attrs + * Set of {@link Attribute}s that may contain the current password + * {@link OperationalAttributes#CURRENT_PASSWORD_NAME} + * {@link Attribute}. + * @return null if it does not exist in the {@link Set} else + * the value. + */ + public static GuardedString GetCurrentPasswordValue(ICollection attrs) { + ConnectorAttribute pwd = Find(OperationalAttributes.CURRENT_PASSWORD_NAME, attrs); + return (pwd == null) ? null : GetGuardedStringValue(pwd); + } + /** + * Determine if the {@link ConnectorObject} is locked out. By getting the + * value of the {@link OperationalAttributes#LOCK_OUT_NAME}. + * + * @param obj + * {@link ConnectorObject} object to inspect. + * @throws NullPointerException + * iff the parameter 'obj' is null. + * @return null if the attribute does not exist otherwise to + * value of the {@link ConnectorAttribute}. + */ + public static bool? IsLockedOut(ConnectorObject obj) { + ConnectorAttribute attr = obj.GetAttributeByName(OperationalAttributes.LOCK_OUT_NAME); + return (attr == null) ? null : GetBooleanValue(attr); + } + + /** + * Determine if the {@link ConnectorObject} is enable. By getting the value + * of the {@link OperationalAttributes#ENABLE_NAME}. + * + * @param obj + * {@link ConnectorObject} object to inspect. + * @throws IllegalStateException + * if the object does not contain attribute in question. + * @throws NullPointerException + * iff the parameter 'obj' is null. + * @return null if the attribute does not exist otherwise to + * value of the {@link ConnectorAttribute}. + */ + public static bool? IsEnabled(ConnectorObject obj) { + ConnectorAttribute attr = obj.GetAttributeByName(OperationalAttributes.ENABLE_NAME); + return (attr == null) ? null : GetBooleanValue(attr); + } + + /** + * Retrieve the password expiration date from the {@link ConnectorObject}. + * + * @param obj + * {@link ConnectorObject} object to inspect. + * @throws IllegalStateException + * if the object does not contain attribute in question. + * @throws NullPointerException + * iff the parameter 'obj' is null. + * @return null if the {@link ConnectorAttribute} does not exist + * otherwise the value of the {@link ConnectorAttribute}. + */ + public static DateTime? GetPasswordExpirationDate(ConnectorObject obj) { + DateTime? ret = null; + ConnectorAttribute attr = obj.GetAttributeByName(OperationalAttributes.PASSWORD_EXPIRATION_DATE_NAME); + if (attr != null) { + long? date = GetLongValue(attr); + if (date != null) { + ret = DateTime.FromFileTimeUtc(date.Value); + } + } + return ret; + } + /** + * Get the password expired attribute from a {@link Collection} of + * {@link Attribute}s. + * + * @param attrs + * set of attribute to find the expired password + * {@link Attribute}. + * @return null if the attribute does not exist and the value + * of the {@link Attribute} if it does. + */ + public static bool? GetPasswordExpired(ICollection attrs) { + ConnectorAttribute pwd = Find(OperationalAttributes.PASSWORD_EXPIRED_NAME, attrs); + return (pwd == null) ? null : GetBooleanValue(pwd); + } + + /** + * Determine if the password is expired for this object. + * + * @param obj + * {@link ConnectorObject} that should contain a password expired + * attribute. + * @return null if the attribute does not exist and the value + * of the {@link Attribute} if it does. + */ + public static bool? IsPasswordExpired(ConnectorObject obj) { + ConnectorAttribute pwd = obj.GetAttributeByName(OperationalAttributes.PASSWORD_EXPIRED_NAME); + return (pwd == null) ? null : GetBooleanValue(pwd); + } + + /** + * Get the enable date from the set of attributes. + * + * @param attrs + * set of attribute to find the enable date + * {@link Attribute}. + * @return null if the attribute does not exist and the value + * of the {@link Attribute} if it does. + */ + public static DateTime? GetEnableDate(ICollection attrs) { + ConnectorAttribute attr = Find(OperationalAttributes.ENABLE_DATE_NAME, attrs); + return (attr == null) ? null : GetDateTimeValue(attr); + } + } + #endregion + + #region ConnectorAttributeInfoUtil + public static class ConnectorAttributeInfoUtil { + /** + * Transform a Collection of {@link AttributeInfo} instances into + * a {@link Map}. The key to each element in the map is the name of + * an AttributeInfo. The value of each element in the map is the + * AttributeInfo instance with that name. + * + * @param attributes + * set of AttributeInfo to transform to a map. + * @return a map of string and AttributeInfo. + * @throws NullPointerException + * iff the parameter attributes is + * null. + */ + public static IDictionary ToMap( + ICollection attributes) { + IDictionary + ret = new Dictionary( + StringComparer.OrdinalIgnoreCase); + foreach (ConnectorAttributeInfo attr in attributes) { + ret[attr.Name] = attr; + } + return ret; + } + + /** + * Find the {@link AttributeInfo} of the given name in the {@link Set}. + * + * @param name + * {@link AttributeInfo}'s name to search for. + * @param attrs + * {@link Set} of AttributeInfo to search. + * @return {@link AttributeInfo} with the specified otherwise null. + */ + public static ConnectorAttributeInfo Find(string name, ICollection attrs) { + Assertions.NullCheck(name, "name"); + ICollection attributes = CollectionUtil.NullAsEmpty(attrs); + foreach (ConnectorAttributeInfo attr in attributes) { + if (attr.Is(name)) { + return attr; + } + } + return null; + } + } + #endregion + + #region BigDecimal + /// + /// Placeholder since C# doesn't have a BigInteger + /// + public sealed class BigDecimal { + private BigInteger _unscaledVal; + private int _scale; + public BigDecimal(BigInteger unscaledVal, + int scale) { + if ( unscaledVal == null ) { + throw new ArgumentNullException(); + } + _unscaledVal = unscaledVal; + _scale = scale; + } + public BigInteger UnscaledValue { + get { + return _unscaledVal; + } + } + public int Scale { + get { + return _scale; + } + } + public override bool Equals(object o) { + BigDecimal other = o as BigDecimal; + if ( other != null ) { + return UnscaledValue.Equals(other.UnscaledValue) && + Scale == other.Scale; + } + return false; + } + public override int GetHashCode() { + return _unscaledVal.GetHashCode(); + } + + public override string ToString() + { + return UnscaledValue.ToString(); + } + } + #endregion + + #region BigInteger + /// + /// Placeholder since C# doesn't have a BigInteger + /// + public sealed class BigInteger { + private string _value; + public BigInteger(string val) { + if ( val == null ) { + throw new ArgumentNullException(); + } + _value = val; + } + public string Value { + get { + return _value; + } + } + public override bool Equals(object o) { + BigInteger other = o as BigInteger; + if ( other != null ) { + return Value.Equals(other.Value); + } + return false; + } + public override int GetHashCode() { + return _value.GetHashCode(); + } + public override string ToString() { + return _value; + } + } + #endregion + + #region ConnectorAttribute + /// + /// Represents a named collection of values within a resource object, + /// although the simplest case is a name-value pair (e.g., email, + /// employeeID). Values can be empty, null, or set with various types. + /// Empty and null are supported because it makes a difference on some + /// resources (in particular database resources). The developer of a + /// Connector will use an builder to construct an instance of + /// ConnectorAttribute. + /// + public class ConnectorAttribute { + private readonly string _name; + private readonly IList _value; + + internal ConnectorAttribute(string name, IList val) { + if (StringUtil.IsBlank(name)) { + throw new ArgumentException("Name must not be blank!"); + } + if (OperationalAttributes.PASSWORD_NAME.Equals(name) || + OperationalAttributes.CURRENT_PASSWORD_NAME.Equals(name)) { + // check the value.. + if (val == null || val.Count != 1) { + String MSG = "Must be a single value."; + throw new ArgumentException(MSG); + } + if (!(val[0] is GuardedString)) { + const string MSG = "Password value must be an instance of GuardedString."; + throw new ArgumentException(MSG); + } + } + _name = name; + // copy to prevent corruption preserve null + _value = (val == null) ? null : CollectionUtil.NewReadOnlyList(val); + } + + public string Name { + get { + return _name; + } + } + + public IList Value { + get { + return _value; + } + } + + public bool Is(string name) { + return Name.ToUpper().Equals(name.ToUpper()); + } + + public sealed override bool Equals(Object obj) { + // test identity + if (this == obj) { + return true; + } + // test for null.. + if (obj == null) { + return false; + } + // test that the exact class matches + if (!(GetType().Equals(obj.GetType()))) { + return false; + } + // test name field.. + ConnectorAttribute other = (ConnectorAttribute) obj; + if (!_name.ToUpper().Equals(other._name.ToUpper())) { + return false; + } + + if (!CollectionUtil.Equals(_value,other._value)) { + return false; + } + return true; + } + + public sealed override int GetHashCode() { + return _name.ToUpper().GetHashCode(); + } + + + public override string ToString() { + // poor man's consistent toString impl.. + StringBuilder bld = new StringBuilder(); + bld.Append("ConnectorAttribute: "); + IDictionary map = new Dictionary(); + map["Name"] = Name; + map["Value"] = Value; + bld.Append(map.ToString()); + return bld.ToString(); + } + } + #endregion + + #region ConnectorAttributeBuilder + public sealed class ConnectorAttributeBuilder { + private const String NAME_ERROR = "Name must not be blank!"; + + private string _name; + private IList _value; + + public ConnectorAttributeBuilder() { + } + public static ConnectorAttribute Build(String name) { + return new ConnectorAttributeBuilder(){ Name=name }.Build(); + } + public static ConnectorAttribute Build(String name, + params Object [] args) { + ConnectorAttributeBuilder bld = new ConnectorAttributeBuilder(); + bld.Name = name; + bld.AddValue(args); + return bld.Build(); + } + public static ConnectorAttribute Build(String name, + ICollection val) { + ConnectorAttributeBuilder bld = new ConnectorAttributeBuilder(); + bld.Name = name; + bld.AddValue(val); + return bld.Build(); + } + + public string Name { + get { + return _name; + } + set { + if (StringUtil.IsBlank(value)) { + throw new ArgumentException(NAME_ERROR); + } + _name = value; + } + } + + public IList Value { + get { + return _value == null ? null : CollectionUtil.AsReadOnlyList(_value); + } + } + + public ConnectorAttributeBuilder AddValue(params Object [] args) { + AddValuesInternal(args); + return this; + } + public ConnectorAttributeBuilder AddValue(ICollection values) { + AddValuesInternal(values); + return this; + } + + public ConnectorAttribute Build() { + if (StringUtil.IsBlank(Name)) { + throw new ArgumentException(NAME_ERROR); + } + if (Uid.NAME.Equals(_name)) { + return new Uid(GetSingleStringValue()); + } else if (Org.IdentityConnectors.Framework.Common.Objects.Name.NAME.Equals(_name)) { + return new Name(GetSingleStringValue()); + } + return new ConnectorAttribute(Name, _value); + } + private void CheckSingleValue() { + if (_value == null || _value.Count != 1) { + const String MSG = "Must be a single value."; + throw new ArgumentException(MSG); + } + } + private String GetSingleStringValue() { + CheckSingleValue(); + if (!(_value[0] is String)) { + const String MSG = "Must be single string value."; + throw new ArgumentException(MSG); + } + return (String) _value[0]; + } + private void AddValuesInternal(IEnumerable values) { + if (values != null) { + // make sure the list is ready to receive values. + if (_value == null) { + _value = new List(); + } + // add each value checking to make sure its correct + foreach (Object v in values) { + FrameworkUtil.CheckAttributeValue(v); + _value.Add(v); + } + } + } + + // ======================================================================= + // Operational Attributes + // ======================================================================= + /** + * Builds an password expiration date {@link ConnectorAttribute}. This + * {@link ConnectorAttribute} represents the date/time a password will expire on a + * resource. + * + * @param dateTime + * UTC time in milliseconds. + * @return an {@link ConnectorAttribute} built with the pre-defined name for password + * expiration date. + */ + public static ConnectorAttribute BuildPasswordExpirationDate(DateTime dateTime) { + return BuildPasswordExpirationDate(DateTimeUtil.GetUtcTimeMillis(dateTime)); + } + + /** + * Builds an password expiration date {@link ConnectorAttribute}. This + * {@link ConnectorAttribute} represents the date/time a password will expire on a + * resource. + * + * @param dateTime + * UTC time in milliseconds. + * @return an {@link ConnectorAttribute} built with the pre-defined name for password + * expiration date. + */ + public static ConnectorAttribute BuildPasswordExpirationDate(long dateTime) { + return Build(OperationalAttributes.PASSWORD_EXPIRATION_DATE_NAME, + dateTime); + } + + /** + * Builds the operational attribute password. + * + * @param password + * the string that represents a password. + * @return an attribute that represents a password. + */ + public static ConnectorAttribute BuildPassword(GuardedString password) { + return Build(OperationalAttributes.PASSWORD_NAME, password); + } + + /** + * Builds the operational attribute current password. The current password + * indicates this a password change by the account owner and not an + * administrator. The use case is that an administrator password change may + * not keep history or validate against policy. + * + * @param password + * the string that represents a password. + * @return an attribute that represents a password. + */ + public static ConnectorAttribute BuildCurrentPassword(GuardedString password) { + return Build(OperationalAttributes.CURRENT_PASSWORD_NAME, password); + } + + public static ConnectorAttribute BuildPassword(SecureString password) { + return Build(OperationalAttributes.PASSWORD_NAME, new GuardedString(password)); + } + public static ConnectorAttribute BuildCurrentPassword(SecureString password) { + return Build(OperationalAttributes.CURRENT_PASSWORD_NAME, new GuardedString(password)); + } + /** + * Builds ant operational attribute that either represents the object is + * enabled or sets in disabled depending on where its used for instance on + * {@link CreateApiOp} it could be used to create a disabled account. In + * {@link SearchApiOp} it would show the object is enabled or disabled. + * + * @param value + * true indicates the object is enabled otherwise false. + * @return {@link ConnectorAttribute} that determines the enable/disable state of an + * object. + */ + public static ConnectorAttribute BuildEnabled(bool val) { + return Build(OperationalAttributes.ENABLE_NAME, val); + } + + /** + * Builds out an operational {@link ConnectorAttribute} that determines the enable + * date for an object. + * + * @param date + * The date and time to enable a particular object, or the date + * time an object will be enabled. + * @return {@link ConnectorAttribute} + */ + public static ConnectorAttribute BuildEnableDate(DateTime date) { + return BuildEnableDate(DateTimeUtil.GetUtcTimeMillis(date)); + } + + /** + * Builds out an operational {@link ConnectorAttribute} that determines the enable + * date for an object. The time parameter is UTC in milliseconds. + * + * @param date + * The date and time to enable a particular object, or the date + * time an object will be enabled. + * @return {@link ConnectorAttribute} + */ + public static ConnectorAttribute BuildEnableDate(long date) { + return Build(OperationalAttributes.ENABLE_DATE_NAME, date); + } + + /** + * Builds out an operational {@link ConnectorAttribute} that determines the disable + * date for an object. + * + * @param date + * The date and time to enable a particular object, or the date + * time an object will be enabled. + * @return {@link ConnectorAttribute} + */ + public static ConnectorAttribute BuildDisableDate(DateTime date) { + return BuildDisableDate(DateTimeUtil.GetUtcTimeMillis(date)); + } + + /** + * Builds out an operational {@link ConnectorAttribute} that determines the disable + * date for an object. The time parameter is UTC in milliseconds. + * + * @param date + * The date and time to enable a particular object, or the date + * time an object will be enabled. + * @return {@link ConnectorAttribute} + */ + public static ConnectorAttribute BuildDisableDate(long date) { + return Build(OperationalAttributes.DISABLE_DATE_NAME, date); + } + + /** + * Builds the lock attribute that determines if an object is locked out. + * + * @param lock + * true if the object is locked otherwise false. + * @return {@link ConnectorAttribute} that represents the lock state of an object. + */ + public static ConnectorAttribute BuildLockOut(bool lck) { + return Build(OperationalAttributes.LOCK_OUT_NAME, lck); + } + + /** + * Builds out an operational {@link Attribute} that determines if a password + * is expired or expires a password. + * + * @param value + * from the API true expires and from the SPI its shows its + * either expired or not. + * @return {@link Attribute} + */ + public static ConnectorAttribute BuildPasswordExpired(bool expired) { + return Build(OperationalAttributes.PASSWORD_EXPIRED_NAME, expired); + } + + // ======================================================================= + // Pre-defined Attributes + // ======================================================================= + + /** + * Builds out a pre-defined {@link ConnectorAttribute} that determines the last login + * date for an object. + * + * @param date + * The date and time of the last login. + * @return {@link ConnectorAttribute} + */ + public static ConnectorAttribute BuildLastLoginDate(DateTime date) { + return BuildLastLoginDate(DateTimeUtil.GetUtcTimeMillis(date)); + } + + /** + * Builds out a pre-defined {@link ConnectorAttribute} that determines the last login + * date for an object. The time parameter is UTC in milliseconds. + * + * @param date + * The date and time of the last login. + * @return {@link ConnectorAttribute} + */ + public static ConnectorAttribute BuildLastLoginDate(long date) { + return Build(PredefinedAttributes.LAST_LOGIN_DATE_NAME, date); + } + + /** + * Builds out a pre-defined {@link ConnectorAttribute} that determines the last + * password change date for an object. + * + * @param date + * The date and time the password was changed. + * @return {@link ConnectorAttribute} + */ + public static ConnectorAttribute BuildLastPasswordChangeDate(DateTime date) { + return BuildLastPasswordChangeDate(DateTimeUtil.GetUtcTimeMillis(date)); + } + + /** + * Builds out a pre-defined {@link ConnectorAttribute} that determines the last + * password change date for an object. + * + * @param date + * The date and time the password was changed. + * @return {@link ConnectorAttribute} + */ + public static ConnectorAttribute BuildLastPasswordChangeDate(long date) { + return Build(PredefinedAttributes.LAST_PASSWORD_CHANGE_DATE_NAME, date); + } + + /** + * Common password policy attribute where the password must be changed every + * so often. The value for this attribute is milliseconds since its the + * lowest common denominator. + */ + public static ConnectorAttribute BuildPasswordChangeInterval(long val) { + return Build(PredefinedAttributes.PASSWORD_CHANGE_INTERVAL_NAME, val); + } + } + #endregion + + #region ConnectorMessages + /** + * Message catalog for a given connector. + */ + public interface ConnectorMessages { + /** + * Formats the given message key in the current UI culture. + * @param key The message key to format. + * @param dflt The default message if key is not found. If null, defaults + * to key. + * @param args Parameters with which to format the message. + * @return The formatted string. + */ + String Format(String key, String dflt, params object [] args); + } + #endregion + + #region ConnectorObject + public sealed class ConnectorObject { + private readonly ObjectClass _objectClass; + private readonly IDictionary _attrs; + public ConnectorObject(ObjectClass objectClass, ICollection attrs) { + if (objectClass == null) { + throw new ArgumentException("ObjectClass may not be null"); + } + if ( attrs == null || attrs.Count == 0 ) { + throw new ArgumentException("attrs cannot be empty or null."); + } + _objectClass = objectClass; + _attrs = + CollectionUtil.NewReadOnlyDictionary(attrs, + value => { return value.Name;}); + if (!_attrs.ContainsKey(Uid.NAME)) { + const String MSG = "The ConnectorAttribute set must contain a Uid."; + throw new ArgumentException(MSG); + } + if (!_attrs.ContainsKey(Name.NAME)) { + const string MSG = "The ConnectorAttribute set must contain a Name."; + throw new ArgumentException(MSG); + } + } + public ICollection GetAttributes() { + return _attrs.Values; + } + public ConnectorAttribute GetAttributeByName(string name) { + return CollectionUtil.GetValue(_attrs,name,null); + } + public Uid Uid { + get { + return (Uid)GetAttributeByName(Uid.NAME); + } + } + public Name Name { + get { + return (Name)GetAttributeByName(Name.NAME); + } + } + public ObjectClass ObjectClass { + get { + return _objectClass; + } + } + public override int GetHashCode() { + return CollectionUtil.GetHashCode(_attrs); + } + public override bool Equals(Object o) { + ConnectorObject other = o as ConnectorObject; + if ( other != null ) { + if (!_objectClass.Equals(other.ObjectClass)) { + return false; + } + return CollectionUtil.Equals(_attrs,other._attrs); + } + return false; + } + } + #endregion + + #region ConnectorObjectBuilder + public sealed class ConnectorObjectBuilder { + private IDictionary _attributes; + public ConnectorObjectBuilder() { + _attributes = new Dictionary(); + // default always add the account object class.. + ObjectClass = ObjectClass.ACCOUNT; + } + + public void SetUid(string uid) { + AddAttribute(new Uid(uid)); + } + + public void SetUid(Uid uid) { + AddAttribute(uid); + } + + public void SetName(string name) { + AddAttribute(new Name(name)); + } + + public void SetName(Name name) { + AddAttribute(name); + } + + public ObjectClass ObjectClass { get; set; } + + // ======================================================================= + // Clone basically.. + // ======================================================================= + /** + * Takes all the attribute from a {@link ConnectorObject} and add/overwrite + * the current attributes. + */ + public ConnectorObjectBuilder Add(ConnectorObject obj) { + // simply add all the attributes it will include (Uid, ObjectClass..) + foreach (ConnectorAttribute attr in obj.GetAttributes()) { + AddAttribute(attr); + } + ObjectClass = obj.ObjectClass; + return this; + } + + public ConnectorObjectBuilder AddAttribute(params ConnectorAttribute [] attrs) { + ValidateParameter(attrs, "attrs"); + foreach (ConnectorAttribute a in attrs) { + //DONT use Add - it throws exceptions if already there + _attributes[a.Name] = a; + } + return this; + } + public ConnectorObjectBuilder AddAttributes(ICollection attrs) { + ValidateParameter(attrs, "attrs"); + foreach (ConnectorAttribute a in attrs) { + _attributes[a.Name] = a; + } + return this; + } + /** + * Adds values to the attribute. + */ + public ConnectorObjectBuilder AddAttribute(String name, params object [] objs) { + AddAttribute(ConnectorAttributeBuilder.Build(name, objs)); + return this; + } + + /** + * Adds each object in the collection. + */ + public ConnectorObjectBuilder AddAttribute(String name, ICollection obj) { + AddAttribute(ConnectorAttributeBuilder.Build(name, obj)); + return this; + } + public ConnectorObject Build() { + // check that there are attributes to return.. + if (_attributes.Count == 0) { + throw new InvalidOperationException("No attributes set!"); + } + return new ConnectorObject(ObjectClass,_attributes.Values); + } + private static void ValidateParameter(Object param, String paramName) { + if (param == null) { + String FORMAT = "Parameter "+param+" must not be null!"; + throw new NullReferenceException(FORMAT); + } + } + } + #endregion + + #region ConnectorAttributeInfo + public sealed class ConnectorAttributeInfo { + private readonly string _name; + private readonly Type _type; + private readonly Flags _flags; + + /** + * Enum of modifier flags to use for attributes. Note that + * this enum is designed for configuration by exception such that + * an empty set of flags are the defaults: + *
    + *
  • updateable
  • + *
  • creatable
  • + *
  • returned by default
  • + *
  • readable
  • + *
  • single-valued
  • + *
  • optional
  • + *
+ */ + [FlagsAttribute] + public enum Flags { + NONE = 0, + REQUIRED = 1, + MULTIVALUED = 2, + NOT_CREATABLE = 4, + NOT_UPDATEABLE = 8, + NOT_READABLE = 16, + NOT_RETURNED_BY_DEFAULT = 32 + } + + internal ConnectorAttributeInfo(string name, Type type, + Flags flags) { + if (StringUtil.IsBlank(name)) { + throw new ArgumentException("Name must not be blank!"); + } + if ((OperationalAttributes.PASSWORD_NAME.Equals(name) || + OperationalAttributes.CURRENT_PASSWORD_NAME.Equals(name)) && + !typeof(GuardedString).Equals(type)) { + String MSG = "Password based attributes must be of type GuardedString."; + throw new ArgumentException(MSG); + } + // check the type.. + FrameworkUtil.CheckAttributeType(type); + _name = name; + _type = type; + _flags = flags; + if (!IsReadable && IsReturnedByDefault) { + throw new ArgumentException("Attribute "+name+" is flagged as not-readable, so it should also be as not-returned-by-default."); + } + } + + + /** + * The native name of the attribute. + * + * @return the native name of the attribute its describing. + */ + public string Name { + get { + return _name; + } + } + + /** + * The basic type associated with this attribute. All primitives are + * supported. + * + * @return the native type if uses. + */ + public Type ValueType { + get { + return _type; + } + } + + /** + * Returns the set of flags associated with the attribute. + * @return the set of flags associated with the attribute + */ + public Flags InfoFlags { + get { + return _flags; + } + } + + + public bool Is(string name) { + return Name.ToUpper().Equals(name.ToUpper()); + } + + /** + * Determines if the attribute is readable. + * + * @return true if the attribute is readable else false. + */ + public bool IsReadable { + get { + return (_flags & Flags.NOT_READABLE) == 0; + } + } + + /** + * Determines if the attribute is writable on create. + * + * @return true if the attribute is writable on create else false. + */ + public bool IsCreatable { + get { + return (_flags & Flags.NOT_CREATABLE) == 0; + } + } + + /** + * Determines if the attribute is writable on update. + * + * @return true if the attribute is writable on update else false. + */ + public bool IsUpdateable { + get { + return (_flags & Flags.NOT_UPDATEABLE) == 0; + } + } + + /** + * Determines whether this attribute is required for creates. + * + * @return true if the attribute is required for an object else false. + */ + public bool IsRequired { + get { + return (_flags & Flags.REQUIRED) != 0; + } + } + + /** + * Determines if this attribute can handle multiple values. There is a + * special case with byte[] since in most instances this denotes a single + * object. + * + * @return true if the attribute is multi-value otherwise false. + */ + public bool IsMultiValued { + get { + return (_flags & Flags.MULTIVALUED) != 0; + } + } + + /** + * Determines if the attribute is returned by default. Indicates if an + * {@link ConnectorAttribute} will be returned during {@link SearchApiOp} or + * {@link GetApiOp} inside a {@link ConnectorObject} by default. The default + * value is true. + * + * @return false iff the attribute should not be returned by default. + */ + public bool IsReturnedByDefault { + get { + return (_flags & Flags.NOT_RETURNED_BY_DEFAULT) == 0; + } + } + + public override bool Equals(Object o) { + ConnectorAttributeInfo other = o as ConnectorAttributeInfo; + if ( other != null ) { + if (!Name.ToUpper().Equals(other.Name.ToUpper())) { + return false; + } + if (!ValueType.Equals(other.ValueType)) { + return false; + } + if (_flags != other._flags) { + return false; + } + return true; + } + return false; + } + + public override int GetHashCode() { + return _name.ToUpper().GetHashCode(); + } + + public override string ToString() { + return SerializerUtil.SerializeXmlObject(this,false); + } + } + #endregion + + #region ConnectorAttributeInfoBuilder + /** + * Simplifies the process of building 'AttributeInfo' objects. This class is + * responsible for providing a default implementation of {@link AttributeInfo}. + * + * + * AttributeInfoBuilder bld = new AttributeInfoBuilder("email"); + * bld.setRequired(true); + * AttributeInfo info = bld.build(); + * + * + * @author Will Droste + * @version $Revision: 1.9 $ + * @since 1.0 + */ + public sealed class ConnectorAttributeInfoBuilder { + + private String _name; + private Type _type; + private ConnectorAttributeInfo.Flags _flags; + + /** + * Creates an builder with all the defaults set. The name must be set before + * the 'build' method is called otherwise an {@link IllegalStateException} + * is thrown. + * + *
+         * Name: <not set>
+         * Readable: true
+         * Writeable: true
+         * Required: false
+         * Type: string
+         * MultiValue: false
+         * 
+ */ + public ConnectorAttributeInfoBuilder() { + ValueType=(typeof(String)); + _flags = ConnectorAttributeInfo.Flags.NONE; + } + + /** + * Creates an builder with all the defaults set. The name must be set before + * the 'build' method is called otherwise an {@link IllegalStateException} + * is thrown. + * + *
+         * Name: <not set>
+         * Readable: true
+         * Writeable: true
+         * Required: false
+         * Type: string
+         * MultiValue: false
+         * 
+ */ + public ConnectorAttributeInfoBuilder(String name) : this(name,typeof(String)){ + } + + /** + * Creates an builder with all the defaults set. The name must be set before + * the 'build' method is called otherwise an {@link IllegalStateException} + * is thrown. + * + *
+    	 * Name: <not set>
+    	 * Readable: true
+    	 * Writeable: true
+    	 * Required: false
+    	 * Type: string
+    	 * MultiValue: false
+    	 * 
+ */ + public ConnectorAttributeInfoBuilder(String name, Type type) { + Name=(name); + ValueType=(type); + //noneOf means the defaults + _flags = ConnectorAttributeInfo.Flags.NONE; + } + + /** + * Builds an {@link AttributeInfo} object based on the properties set. + * + * @return {@link AttributeInfo} based on the properties set. + */ + public ConnectorAttributeInfo Build() { + return new ConnectorAttributeInfo(_name, _type, _flags); + } + + /** + * Sets the unique name of the {@link AttributeInfo} object. + * + * @param name + * unique name of the {@link AttributeInfo} object. + */ + public String Name { + set { + if (StringUtil.IsBlank(value)) { + throw new ArgumentException("Argument must not be blank."); + } + _name = value; + } + } + + /** + * Please see {@link FrameworkUtil#checkAttributeType(Class)} for the + * definitive list of supported types. + * + * @param value + * type for an {@link Attribute}'s value. + * @throws IllegalArgumentException + * if the Class is not a supported type. + */ + public Type ValueType { + set { + FrameworkUtil.CheckAttributeType(value); + _type = value; + } + } + + /** + * Determines if the attribute is readable. + */ + public bool Readable { + set { + SetFlag(ConnectorAttributeInfo.Flags.NOT_READABLE,!value); + } + } + + /** + * Determines if the attribute is writable. + */ + public bool Creatable { + set { + SetFlag(ConnectorAttributeInfo.Flags.NOT_CREATABLE,!value); + } + } + + /** + * Determines if this attribute is required. + */ + public bool Required { + set { + SetFlag(ConnectorAttributeInfo.Flags.REQUIRED,value); + } + } + + /** + * Determines if this attribute supports multivalue. + */ + public bool MultiValued { + set { + SetFlag(ConnectorAttributeInfo.Flags.MULTIVALUED,value); + } + } + + /** + * Determines if this attribute writable during update. + */ + public bool Updateable { + set { + SetFlag(ConnectorAttributeInfo.Flags.NOT_UPDATEABLE,!value); + } + } + + public bool ReturnedByDefault { + set { + SetFlag(ConnectorAttributeInfo.Flags.NOT_RETURNED_BY_DEFAULT,!value); + } + } + + /** + * Sets all of the flags for this builder. + * @param flags The set of attribute info flags. Null means clear all flags. + *

+ * NOTE: EnumSet.noneOf(AttributeInfo.Flags.class) results in + * an attribute with the default behavior: + *

    + *
  • updateable
  • + *
  • creatable
  • + *
  • returned by default
  • + *
  • readable
  • + *
  • single-valued
  • + *
  • optional
  • + *
+ */ + public ConnectorAttributeInfo.Flags InfoFlags { + set { + _flags = value; + } + } + + private void SetFlag(ConnectorAttributeInfo.Flags flag, bool value) { + if (value) { + _flags = _flags | flag; + } + else { + _flags = _flags & ~flag; + } + } + + /** + * Convenience method to create an AttributeInfo. Equivalent to + * + * new AttributeInfoBuilder(name,type).setFlags(flags).build() + * + * @param name The name of the attribute + * @param type The type of the attribute + * @param flags The flags for the attribute. Null means clear all flags + * @return The attribute info + */ + public static ConnectorAttributeInfo Build(String name, Type type, + ConnectorAttributeInfo.Flags flags) { + return new ConnectorAttributeInfoBuilder(name,type){InfoFlags=flags}.Build(); + } + /** + * Convenience method to create an AttributeInfo. Equivalent to + * + * AttributeInfoBuilder.build(name,type,null) + * + * @param name The name of the attribute + * @param type The type of the attribute + * @param flags The flags for the attribute + * @return The attribute info + */ + public static ConnectorAttributeInfo Build(String name, Type type) { + return Build(name,type,ConnectorAttributeInfo.Flags.NONE); + } + + /** + * Convenience method to create an AttributeInfo. Equivalent to + * + * AttributeInfoBuilder.build(name,type) + * + * @param name The name of the attribute + * @return The attribute info + */ + public static ConnectorAttributeInfo Build(String name) { + return Build(name,typeof(String)); + } + } + #endregion + + #region FileName + /// + /// Placeholder for java.io.File since C#'s + /// FileInfo class throws exceptions if the + /// file doesn't exist. + /// + public sealed class FileName { + private string _path; + public FileName(string path) { + if ( path == null ) { + throw new ArgumentNullException(); + } + _path = path; + } + public string Path { + get { + return _path; + } + } + public override bool Equals(object o) { + FileName other = o as FileName; + if ( other != null ) { + return Path.Equals(other.Path); + } + return false; + } + public override int GetHashCode() { + return _path.GetHashCode(); + } + public override string ToString() { + return _path; + } + } + #endregion + + #region Name + public sealed class Name : ConnectorAttribute { + + public readonly static string NAME = ConnectorAttributeUtil.CreateSpecialName("NAME"); + public readonly static ConnectorAttributeInfo INFO = + new ConnectorAttributeInfoBuilder(NAME) {Required=true}.Build(); + + public Name(String value) : base(NAME, CollectionUtil.NewReadOnlyList(value)) { + } + + /** + * The single value of the attribute that is the unique id of an object. + * + * @return value that identifies an object. + */ + public String GetNameValue() { + return ConnectorAttributeUtil.GetStringValue(this); + } + } + #endregion + + #region ObjectClass + public sealed class ObjectClass { + public const String ACCOUNT_NAME = "account"; + public const String PERSON_NAME = "person"; + public const String GROUP_NAME = "group"; + public const String ORGANIZATION_NAME = "organization"; + /** + * Denotes an account based object. + */ + public static readonly ObjectClass ACCOUNT = new ObjectClass(ACCOUNT_NAME); + /** + * Denotes a person based object. + */ + public static readonly ObjectClass PERSON = new ObjectClass(PERSON_NAME); + /** + * Denotes a group based object. + */ + public static readonly ObjectClass GROUP = new ObjectClass(GROUP_NAME); + /** + * Denotes a organization based object. + */ + public static readonly ObjectClass ORGANIZATION = new ObjectClass(ORGANIZATION_NAME); + + private readonly String _type; + + public ObjectClass(String type) { + if ( type == null ) { + throw new ArgumentException("Type cannot be null."); + } + _type = type; + } + public String GetObjectClassValue() { + return _type; + } + + /** + * Convenience method to build the display name key for + * an object class. + * + * @return The display name key. + */ + public String GetDisplayNameKey() { + return "MESSAGE_OBJECT_CLASS_"+_type.ToUpper(); + } + + public override int GetHashCode() { + return _type.GetHashCode(); + } + + public override bool Equals(object o) { + if ( o is ObjectClass ) { + ObjectClass other = (ObjectClass)o; + return _type.Equals(other._type); + } + return false; + } + + public override string ToString() + { + return "ObjectClass: " + _type; + } + } + #endregion + + #region ObjectClassInfo + public sealed class ObjectClassInfo { + + private readonly String _type; + private readonly ICollection _info; + private readonly bool _isContainer; + + public ObjectClassInfo(String type, + ICollection attrInfo, + bool isContainer) { + _type = type; + _info = CollectionUtil.NewReadOnlySet(attrInfo); + _isContainer = isContainer; + // check to make sure name exists + IDictionary dict + = ConnectorAttributeInfoUtil.ToMap(attrInfo); + if (!dict.ContainsKey(Name.NAME)) { + const string MSG = "Missing 'Name' connector attribute info."; + throw new ArgumentException(MSG); + } + } + + public ICollection ConnectorAttributeInfos { + get { + return this._info; + } + } + + public String ObjectType { + get { + return this._type; + } + } + + public bool IsContainer { + get { + return this._isContainer; + } + } + + public override int GetHashCode() { + return _type.GetHashCode(); + } + + public override bool Equals(Object o) { + ObjectClassInfo other = o as ObjectClassInfo; + if ( other != null ) { + if (!ObjectType.Equals(other.ObjectType)) { + return false; + } + if (!CollectionUtil.Equals(ConnectorAttributeInfos, + other.ConnectorAttributeInfos)) { + return false; + } + if (_isContainer != other._isContainer) { + return false; + } + return true; + } + return false; + } + + public override string ToString() + { + return SerializerUtil.SerializeXmlObject(this,false); + } + } + #endregion + + #region ObjectClassInfoBuilder + /** + * Used to help facilitate the building of {@link ObjectClassInfo} objects. + */ + public sealed class ObjectClassInfoBuilder { + private bool _isContainer; + private IDictionary _info; + + public ObjectClassInfoBuilder() { + _info = new Dictionary(); + ObjectType = ObjectClass.ACCOUNT_NAME; + } + + public string ObjectType { get; set; } + + /** + * Add each {@link AttributeInfo} object to the {@link ObjectClassInfo}. + */ + public ObjectClassInfoBuilder AddAttributeInfo(ConnectorAttributeInfo info) { + if (_info.ContainsKey(info.Name)) { + const string MSG = "ConnectorAttributeInfo of name ''{0}'' already exists!"; + throw new ArgumentException(String.Format(MSG, info.Name)); + } + _info[info.Name] = info; + return this; + } + + public ObjectClassInfoBuilder AddAllAttributeInfo(ICollection info) { + foreach (ConnectorAttributeInfo cainfo in info) { + AddAttributeInfo(cainfo); + } + return this; + } + + public bool IsContainer { + get { + return _isContainer; + } + set { + _isContainer = value; + } + } + + public ObjectClassInfo Build() { + // determine if name is missing and add it by default + if (!_info.ContainsKey(Name.NAME)) { + _info[Name.NAME] = Name.INFO; + } + return new ObjectClassInfo(ObjectType, _info.Values, _isContainer); + } + } + #endregion + + #region OperationalAttributeInfos + /** + * {@link AttributeInfo} for each operational attribute. + */ + public static class OperationalAttributeInfos { + /** + * Gets/sets the enable status of an object. + */ + public static readonly ConnectorAttributeInfo ENABLE = + ConnectorAttributeInfoBuilder.Build( + OperationalAttributes.ENABLE_NAME, typeof(bool)); + + /** + * Gets/sets the enable date for an object. + */ + public static readonly ConnectorAttributeInfo ENABLE_DATE = + ConnectorAttributeInfoBuilder.Build( + OperationalAttributes.ENABLE_DATE_NAME, typeof(long)); + + /** + * Gets/sets the disable date for an object. + */ + public static readonly ConnectorAttributeInfo DISABLE_DATE = + ConnectorAttributeInfoBuilder.Build( + OperationalAttributes.DISABLE_DATE_NAME, typeof(long)); + + /** + * Gets/sets the lock out attribute for an object. + */ + public static readonly ConnectorAttributeInfo LOCK_OUT = + ConnectorAttributeInfoBuilder.Build( + OperationalAttributes.LOCK_OUT_NAME, typeof(bool)); + + /** + * Gets/sets the password expiration date for an object. + */ + public static readonly ConnectorAttributeInfo PASSWORD_EXPIRATION_DATE = + ConnectorAttributeInfoBuilder.Build( + OperationalAttributes.PASSWORD_EXPIRATION_DATE_NAME, typeof(long)); + + /** + * Normally this is a write-only attribute. Sets the password for an object. + */ + public static readonly ConnectorAttributeInfo PASSWORD = + ConnectorAttributeInfoBuilder.Build( + OperationalAttributes.PASSWORD_NAME, typeof(GuardedString), + ConnectorAttributeInfo.Flags.NOT_READABLE | + ConnectorAttributeInfo.Flags.NOT_RETURNED_BY_DEFAULT); + + /** + * Used in conjunction with password to do an account level password change. + * This is for a non-administrator change of the password and therefore + * requires the current password. + */ + public static readonly ConnectorAttributeInfo CURRENT_PASSWORD = + ConnectorAttributeInfoBuilder.Build( + OperationalAttributes.CURRENT_PASSWORD_NAME, typeof(GuardedString), + ConnectorAttributeInfo.Flags.NOT_READABLE | + ConnectorAttributeInfo.Flags.NOT_RETURNED_BY_DEFAULT); + + /** + * Used to determine if a password is expired or to expire a password. + */ + public static readonly ConnectorAttributeInfo PASSWORD_EXPIRED = + ConnectorAttributeInfoBuilder.Build( + OperationalAttributes.PASSWORD_EXPIRED_NAME, typeof(bool)); + + } + #endregion + + #region OperationalAttributes + /** + * Operational attributes have special meaning and cannot be represented by pure + * operations. For instance some administrators would like to create an account + * in the disabled state. The do not want this to be a two operation process + * since this can leave the door open to abuse. Therefore special attributes + * that can perform operations were introduced. The + * {@link OperationalAttributes#DISABLED} attribute could be added to the set of + * attribute sent to a Connector for the {@link CreateOp} operation. To tell the + * {@link Connector} to create the account with it in the disabled state whether + * the target resource itself has an attribute or an additional method must be + * called. + */ + public static class OperationalAttributes { + /** + * Gets/sets the enable status of an object. + */ + public static readonly string ENABLE_NAME = ConnectorAttributeUtil.CreateSpecialName("ENABLE"); + /** + * Gets/sets the enable date for an object. + */ + public static readonly string ENABLE_DATE_NAME = ConnectorAttributeUtil.CreateSpecialName("ENABLE_DATE"); + /** + * Gets/sets the disable date for an object. + */ + public static readonly string DISABLE_DATE_NAME = ConnectorAttributeUtil.CreateSpecialName("DISABLE_DATE"); + /** + * Gets/sets the lock out attribute for an object. + */ + public static readonly string LOCK_OUT_NAME = ConnectorAttributeUtil.CreateSpecialName("LOCK_OUT"); + /** + * Gets/sets the password expiration date for an object. + */ + public static readonly string PASSWORD_EXPIRATION_DATE_NAME = ConnectorAttributeUtil.CreateSpecialName("PASSWORD_EXPIRATION_DATE"); + /** + * Gets/sets the password expired for an object. + */ + public static readonly string PASSWORD_EXPIRED_NAME = ConnectorAttributeUtil.CreateSpecialName("PASSWORD_EXPIRED"); + /** + * Normally this is a write-only attribute. Sets the password for an object. + */ + public static readonly string PASSWORD_NAME = ConnectorAttributeUtil.CreateSpecialName("PASSWORD"); + /** + * Used in conjunction with password to do an account level password change. + * This is for a non-administrator change of the password and therefore + * requires the current password. + */ + public static readonly string CURRENT_PASSWORD_NAME = ConnectorAttributeUtil.CreateSpecialName("CURRENT_PASSWORD"); + + // ======================================================================= + // Helper Methods.. + // ======================================================================= + public static readonly ICollection OPERATIONAL_ATTRIBUTE_NAMES = + CollectionUtil.NewReadOnlySet ( + LOCK_OUT_NAME, + ENABLE_NAME, + ENABLE_DATE_NAME, + DISABLE_DATE_NAME, + PASSWORD_EXPIRATION_DATE_NAME, + PASSWORD_NAME, + CURRENT_PASSWORD_NAME, + PASSWORD_EXPIRED_NAME + ); + + public static ICollection GetOperationalAttributeNames() { + return CollectionUtil.NewReadOnlySet(OPERATIONAL_ATTRIBUTE_NAMES); + } + public static bool IsOperationalAttribute(ConnectorAttribute attr) { + string name = (attr != null) ? attr.Name : null; + return OPERATIONAL_ATTRIBUTE_NAMES.Contains(name); + } + } + #endregion + + #region PredefinedAttributes + /** + * List of well known or pre-defined attributes. Common attributes that most + * resources have that are not operational in nature. + */ + public static class PredefinedAttributes { + /** + * Attribute that should hold a reasonable value to + * display for the value of an object. If this is not present, then the + * application will have to use the NAME to show the value. + */ + public static readonly String SHORT_NAME = ConnectorAttributeUtil.CreateSpecialName("SHORT_NAME"); + + /** + * Attribute that should hold the value of the object's description, + * if one is available. + */ + public static readonly String DESCRIPTION = ConnectorAttributeUtil.CreateSpecialName("DESCRIPTION"); + + /** + * Read-only attribute that shows the last date/time the password was + * changed. + */ + public static readonly string LAST_PASSWORD_CHANGE_DATE_NAME = ConnectorAttributeUtil.CreateSpecialName("LAST_PASSWORD_CHANGE_DATE"); + + /** + * Common password policy attribute where the password must be changed every + * so often. The value for this attribute is milliseconds since its the + * lowest common denominator. + */ + public static readonly string PASSWORD_CHANGE_INTERVAL_NAME = ConnectorAttributeUtil.CreateSpecialName("PASSWORD_CHANGE_INTERVAL"); + + /** + * Last login date for an account. This is usually used to determine inactivity. + */ + public static readonly string LAST_LOGIN_DATE_NAME = ConnectorAttributeUtil.CreateSpecialName("LAST_LOGIN_DATE"); + + /** + * Groups an account object belongs to. + */ + public static readonly string GROUPS_NAME = ConnectorAttributeUtil.CreateSpecialName("GROUPS"); + + /** + * Accounts that belong to a group or organization. + */ + public static readonly string ACCOUNTS_NAME = ConnectorAttributeUtil.CreateSpecialName("ACCOUNTS"); + + /** + * An organization that that an account/person belongs to. + */ + public static readonly string ORGANIZATION_NAME = ConnectorAttributeUtil.CreateSpecialName("ORGANIZATION"); + } + #endregion + + #region PredefinedAttributeInfos + public static class PredefinedAttributeInfos { + /** + * Attribute that should hold a reasonable value to + * display for the value of an object. If this is not present, then the + * application will have to use the NAME to show the value. + */ + public static readonly ConnectorAttributeInfo SHORT_NAME = + ConnectorAttributeInfoBuilder.Build(PredefinedAttributes.SHORT_NAME); + + /** + * Attribute that should hold the value of the object's description, + * if one is available. + */ + public static readonly ConnectorAttributeInfo DESCRIPTION = + ConnectorAttributeInfoBuilder.Build(PredefinedAttributes.DESCRIPTION); + /** + * Read-only attribute that shows the last date/time the password was + * changed. + */ + public static readonly ConnectorAttributeInfo LAST_PASSWORD_CHANGE_DATE = + ConnectorAttributeInfoBuilder.Build( + PredefinedAttributes.LAST_PASSWORD_CHANGE_DATE_NAME, + typeof(long), + ConnectorAttributeInfo.Flags.NOT_CREATABLE | + ConnectorAttributeInfo.Flags.NOT_UPDATEABLE); + + /** + * Common password policy attribute where the password must be changed every + * so often. The value for this attribute is milliseconds since its the + * lowest common denominator. + */ + public static readonly ConnectorAttributeInfo PASSWORD_CHANGE_INTERVAL = + ConnectorAttributeInfoBuilder.Build( + PredefinedAttributes.PASSWORD_CHANGE_INTERVAL_NAME, typeof(long)); + + /** + * Last login date for an account. This is usually used to determine + * inactivity. + */ + public static readonly ConnectorAttributeInfo LAST_LOGIN_DATE = + ConnectorAttributeInfoBuilder.Build( + PredefinedAttributes.LAST_LOGIN_DATE_NAME, + typeof(long), + ConnectorAttributeInfo.Flags.NOT_CREATABLE | + ConnectorAttributeInfo.Flags.NOT_UPDATEABLE); + + /** + * Groups that an account or person belong to. The Attribute values are the + * UID value of each group that an account has membership in. + */ + public static readonly ConnectorAttributeInfo GROUPS = + ConnectorAttributeInfoBuilder.Build(PredefinedAttributes.GROUPS_NAME, + typeof(String), + ConnectorAttributeInfo.Flags.MULTIVALUED | + ConnectorAttributeInfo.Flags.NOT_RETURNED_BY_DEFAULT); + + + /** + * Accounts that are members of a group or organization. The Attribute + * values are the UID value of each account the has a group or organization + * membership. + */ + public static readonly ConnectorAttributeInfo ACCOUNTS = + ConnectorAttributeInfoBuilder.Build(PredefinedAttributes.ACCOUNTS_NAME, + typeof(String), + ConnectorAttributeInfo.Flags.MULTIVALUED| + ConnectorAttributeInfo.Flags.NOT_RETURNED_BY_DEFAULT); + + + /** + * Organizations that an account or person is a member of. The Attribute + * values are the UID value of each organization that an account or person is + * a member of. + */ + public static readonly ConnectorAttributeInfo ORGANIZATION = + ConnectorAttributeInfoBuilder.Build(PredefinedAttributes.ORGANIZATION_NAME, + typeof(String), + ConnectorAttributeInfo.Flags.NOT_RETURNED_BY_DEFAULT); + } + #endregion + + #region OperationOptions + /** + * Arbitrary options to be passed into various operations. This serves + * as a catch-all for extra options. + */ + public sealed class OperationOptions { + + /** + * An option to use with {@link SearchApiOp} that specified the scope + * under which to perform the search. To be used in conjunction with + * {@link #OP_CONTAINER}. Must be one of the following values + *
    + *
  1. {@link #SCOPE_OBJECT}
  2. + *
  3. {@link #SCOPE_ONE_LEVEL}
  4. + *
  5. {@link #SCOPE_SUBTREE}
  6. + *
+ */ + public const String OP_SCOPE = "SCOPE"; + public const String SCOPE_OBJECT = "object"; + public const String SCOPE_ONE_LEVEL = "onelevel"; + public const String SCOPE_SUBTREE = "subtree"; + + /** + * An option to use with {@link SearchApiOp} that specified the container + * under which to perform the search. Must be of type {@link QualifiedUid}. + * Should be implemented for those object classes whose {@link ObjectClassInfo#isContainer()} + * returns true. + */ + public const String OP_CONTAINER = "CONTAINER"; + + /** + * An option to use with {@link ScriptOnResourceApiOp} and possibly others + * that specifies an account under which to execute the script/operation. + * The specified account will appear to have performed any action that the + * script/operation performs. + *

+ * Check the javadoc for a particular connector to see whether that + * connector supports this option. + */ + public static readonly string OP_RUN_AS_USER = "RUN_AS_USER"; + + /** + * An option to use with {@link ScriptOnResourceApiOp} and possibly others + * that specifies a password under which to execute the script/operation. + */ + public static readonly string OP_RUN_WITH_PASSWORD = "RUN_WITH_PASSWORD"; + + /** + * Determines the attributes to retrieve during {@link SearchApiOp} and + * {@link SyncApiOp}. + */ + public static readonly string OP_ATTRIBUTES_TO_GET = "ATTRS_TO_GET"; + + private readonly IDictionary _operationOptions; + + /** + * Public only for serialization; please use {@link OperationOptionsBuilder}. + * @param operationOptions The options. + */ + public OperationOptions(IDictionary operationOptions) { + foreach (Object val in operationOptions.Values) { + FrameworkUtil.CheckOperationOptionValue(val); + } + //clone options to do a deep copy in case anything + //is an array + IDictionary operationOptionsClone = (IDictionary)SerializerUtil.CloneObject(operationOptions); + _operationOptions = CollectionUtil.NewReadOnlyDictionary(operationOptionsClone); + } + + /** + * Returns a map of options. Each value in the map + * must be of a type that the framework can serialize. + * See {@link ObjectSerializerFactory} for a list of supported types. + * + * @return A map of options. + */ + public IDictionary Options { + get { + return _operationOptions; + } + } + + /** + * Convenience method that returns {@link #OP_SCOPE}. + * @return The value for {@link #OP_SCOPE}. + */ + public String Scope { + get { + return (String) CollectionUtil.GetValue(_operationOptions,OP_SCOPE,null); + } + } + + /** + * Convenience method that returns {@link #OP_CONTAINER}. + * @return The value for {@link #OP_CONTAINER}. + */ + public QualifiedUid getContainer { + get { + return (QualifiedUid) CollectionUtil.GetValue(_operationOptions,OP_CONTAINER,null); + } + } + + /** + * Get the string array of attribute names to return in the object. + */ + public string[] AttributesToGet { + get { + return (string[]) CollectionUtil.GetValue( + _operationOptions, OP_ATTRIBUTES_TO_GET, null); + } + } + + /** + * Get the account to run the operation as.. + */ + public string RunAsUser { + get { + return (string) CollectionUtil.GetValue( + _operationOptions, OP_RUN_AS_USER, null); + } + } + + /** + * Get the password to run the operation as.. + */ + public GuardedString RunWithPassword { + get { + return (GuardedString) CollectionUtil.GetValue( + _operationOptions, OP_RUN_WITH_PASSWORD, null); + } + } + + public override string ToString() + { + StringBuilder bld = new StringBuilder(); + bld.Append("OperationOptions: ").Append(Options); + return bld.ToString(); + } + } + #endregion + + #region OperationOptionsBuilder + /** + * Builder for {@link OperationOptions}. + */ + public sealed class OperationOptionsBuilder { + private readonly IDictionary _options; + + /** + * Create a builder with an empty set of options. + */ + public OperationOptionsBuilder() { + _options = new Dictionary(); + } + + /** + * Create a builder from an existing set of options. + * @param options The existing set of options. Must not be null. + */ + public OperationOptionsBuilder(OperationOptions options) { + Assertions.NullCheck(options, "options"); + // clone options to do a deep copy in case anything + // is an array + IDictionary operationOptionsClone = (IDictionary) SerializerUtil + .CloneObject(options.Options); + _options = CollectionUtil.NewDictionary(operationOptionsClone); + } + + /** + * Sets a given option and a value for that option. + * @param name The name of the option + * @param value The value of the option. Must be one of the types that + * we can serialize. + * See {@link ObjectSerializerFactory} for a list of supported types. + */ + public void SetOption(String name, Object value) { + if ( name == null ) { + throw new ArgumentException("Argument 'value' cannot be null."); + } + //don't validate value here - we do that implicitly when + //we clone in the constructor of OperationOptions + _options[name] = value; + } + + /** + * Returns a mutable reference of the options map. + * @return A mutable reference of the options map. + */ + public IDictionary Options { + get { + //might as well be mutable since it's the builder and + //we don't want to deep copy anyway + return _options; + } + } + + /** + * Creates the OperationOptions. + * @return The newly-created OperationOptions + */ + public OperationOptions Build() { + return new OperationOptions(_options); + } + + /** + * Sets the {@link OperationOptions#OP_ATTRIBUTES_TO_GET} option. + * + * @param attrNames + * list of {@link Attribute} names. + */ + public string[] AttributesToGet { + set { + Assertions.NullCheck(value, "AttributesToGet"); + // don't validate value here - we do that in + // the constructor of OperationOptions - that's + // really the only place we can truly enforce this + _options[OperationOptions.OP_ATTRIBUTES_TO_GET] = value; + } + } + + /** + * Set the run with password option. + */ + public GuardedString RunWithPassword { + set { + Assertions.NullCheck(value, "RunWithPassword"); + _options[OperationOptions.OP_RUN_WITH_PASSWORD] = value; + } + } + + /** + * Set the run as user option. + */ + public string RunAsUser { + set { + Assertions.NullCheck(value, "RunAsUser"); + _options[OperationOptions.OP_RUN_AS_USER] = value; + } + } + /** + * Convenience method to set {@link OperationOptions#OP_SCOPE} + * @param scope The scope. May not be null. + * @return A this reference to allow chaining + */ + public string Scope { + set { + Assertions.NullCheck(value, "scope"); + _options[OperationOptions.OP_SCOPE] = value; + } + } + + /** + * Convenience method to set {@link OperationOptions#OP_CONTAINER} + * @param container The container. May not be null. + * @return A this reference to allow chaining + */ + public QualifiedUid Container { + set { + Assertions.NullCheck(value, "container"); + _options[OperationOptions.OP_CONTAINER] = value; + } + } + + } + #endregion + + #region OperationOptionInfo + public sealed class OperationOptionInfo { + private String _name; + private Type _type; + + public OperationOptionInfo(String name, + Type type) { + Assertions.NullCheck(name, "name"); + Assertions.NullCheck(type, "type"); + FrameworkUtil.CheckOperationOptionType(type); + _name = name; + _type = type; + } + + public String Name { + get { + return _name; + } + } + + public Type OptionType { + get { + return _type; + } + } + + public override bool Equals(Object o) { + if (o is OperationOptionInfo) { + OperationOptionInfo other = + (OperationOptionInfo)o; + if (!_name.Equals(other._name)) { + return false; + } + if (!_type.Equals(other._type)) { + return false; + } + return true; + } + return false; + } + + public override int GetHashCode() { + return _name.GetHashCode(); + } + + public override string ToString() + { + StringBuilder bld = new StringBuilder(); + bld.Append("OperationOptionInfo("); + bld.Append(_name); + bld.Append(_type.ToString()); + bld.Append(')'); + return bld.ToString(); + } + + } + #endregion + + #region OperationOptionInfoBuilder + public sealed class OperationOptionInfoBuilder { + private String _name; + private Type _type; + + public OperationOptionInfoBuilder() { + } + + public OperationOptionInfoBuilder(String name, + Type type) { + _name = name; + _type = type; + } + + public String Name { + get { + return _name; + } + set { + _name = value; + } + } + + public Type OptionType { + get { + return _type; + } + set { + _type = value; + } + } + + public OperationOptionInfo Build() { + return new OperationOptionInfo(_name,_type); + } + + public static OperationOptionInfo Build(String name, Type type) { + return new OperationOptionInfoBuilder(name,type).Build(); + } + + public static OperationOptionInfo Build(String name) { + return Build(name, typeof(string)); + } + + public static OperationOptionInfo BuildAttributesToGet() { + return Build(OperationOptions.OP_ATTRIBUTES_TO_GET, typeof(string[])); + } + + public static OperationOptionInfo BuildRunWithPassword() { + return Build(OperationOptions.OP_RUN_WITH_PASSWORD); + } + + public static OperationOptionInfo BuildRunAsUser() { + return Build(OperationOptions.OP_RUN_AS_USER); + } + public static OperationOptionInfo BuildScope() { + return Build(OperationOptions.OP_SCOPE); + } + + public static OperationOptionInfo BuildContainer() { + return Build(OperationOptions.OP_CONTAINER,typeof(QualifiedUid)); + } + } + #endregion + + #region QualifiedUid + /** + * A fully-qualified uid. That is, a pair of {@link ObjectClass} and + * {@link Uid}. + */ + public sealed class QualifiedUid { + private readonly ObjectClass _objectClass; + private readonly Uid _uid; + + /** + * Create a QualifiedUid. + * @param objectClass The object class. May not be null. + * @param uid The uid. May not be null. + */ + public QualifiedUid(ObjectClass objectClass, + Uid uid) { + Assertions.NullCheck(objectClass,"objectClass"); + Assertions.NullCheck(uid,"uid"); + _objectClass = objectClass; + _uid = uid; + } + + /** + * Returns the object class. + * @return The object class. + */ + public ObjectClass ObjectClass { + get { + return _objectClass; + } + } + + /** + * Returns the uid. + * @return The uid. + */ + public Uid Uid { + get { + return _uid; + } + } + + /** + * Returns true iff o is a QualifiedUid and the object class and uid match. + */ + public override bool Equals(Object o) { + if ( o is QualifiedUid ) { + QualifiedUid other = (QualifiedUid)o; + return ( _objectClass.Equals(other._objectClass) && + _uid.Equals(other._uid) ); + } + return false; + } + + /** + * Returns a hash code based on uid + */ + public override int GetHashCode() { + return _uid.GetHashCode(); + } + + /** + * Returns a string representation acceptible for debugging. + */ + public override String ToString() { + return SerializerUtil.SerializeXmlObject(this, false); + } + + } + #endregion + + #region ResultsHandler + /** + * Encapsulate the handling of each object returned by the search. + */ + public delegate bool ResultsHandler(ConnectorObject obj); + #endregion + + #region Schema + /** + * Determines the objects supported by a + * {@link com.sun.openconnectors.framework.spi.Connector}. + * The {@link Schema} object is used to represent the basic objects that a + * connector supports. This does not prevent a connector from supporting more. + * Rather, this is informational for the caller of the connector to understand + * a minimum support level. + * The schema defines 4 primary data structures + *

    + *
  1. Declared ObjectClasses ({@link #getObjectClassInfo()}).
  2. + *
  3. Declared OperationOptionInfo ({@link #getOperationOptionInfo()}).
  4. + *
  5. Supported ObjectClasses by operation ({@link #getSupportedObjectClassesByOperation()}).
  6. + *
  7. Supported OperationOptionInfo by operation({@link #getSupportedOptionsByOperation()()}).
  8. + *
+ * + * TODO: add more to describe and what is expected from this call and how it is + * used.. based on OperationalAttribute etc.. + */ + public sealed class Schema { + private readonly ICollection _declaredObjectClasses; + private readonly ICollection _declaredOperationOptions; + private readonly IDictionary,ICollection> + _supportedObjectClassesByOperation; + private readonly IDictionary,ICollection> + _supportedOptionsByOperation; + + /** + * Public only for serialization; please use + * SchemaBuilder instead. + * @param info + * @param supportedObjectClassesByOperation + */ + public Schema(ICollection info, + ICollection options, + IDictionary,ICollection> supportedObjectClassesByOperation, + IDictionary,ICollection> supportedOptionsByOperation) { + _declaredObjectClasses = CollectionUtil.NewReadOnlySet(info); + _declaredOperationOptions = CollectionUtil.NewReadOnlySet(options); + + //make read-only + { + IDictionary,ICollection> temp = + new Dictionary,ICollection>(); + foreach (KeyValuePair,ICollection> entry in + supportedObjectClassesByOperation) { + SafeType op = + entry.Key; + ICollection resolvedClasses = + CollectionUtil.NewReadOnlySet(entry.Value); + temp[op] = resolvedClasses; + } + _supportedObjectClassesByOperation = CollectionUtil.AsReadOnlyDictionary(temp); + } + //make read-only + { + IDictionary,ICollection> temp = + new Dictionary,ICollection>(); + foreach (KeyValuePair,ICollection> entry in + supportedOptionsByOperation) { + SafeType op = + entry.Key; + ICollection resolvedClasses = + CollectionUtil.NewReadOnlySet(entry.Value); + temp[op] = resolvedClasses; + } + _supportedOptionsByOperation = CollectionUtil.AsReadOnlyDictionary(temp); + } + } + + /** + * Returns the set of object classes that are defined in the schema, regardless + * of which operations support them. + */ + public ICollection ObjectClassInfo { + get { + return _declaredObjectClasses; + } + } + + /** + * Returns the ObjectClassInfo for the given type. + * @param type The type to find. + * @return the ObjectClassInfo for the given type or null if not found. + */ + public ObjectClassInfo FindObjectClassInfo(String type) { + foreach (ObjectClassInfo info in _declaredObjectClasses) { + if ( info.ObjectType.Equals(type) ) { + return info; + } + } + return null; + } + + /** + * Returns the set of operation options that are defined in the schema, regardless + * of which operations support them. + * @return The options defined in this schema. + */ + public ICollection OperationOptionInfo { + get { + return _declaredOperationOptions; + } + } + + /** + * Returns the OperationOptionInfo for the given name. + * @param name The name to find. + * @return the OperationOptionInfo for the given name or null if not found. + */ + public OperationOptionInfo FindOperationOptionInfo(String name) { + Assertions.NullCheck(name, "name"); + foreach (OperationOptionInfo info in _declaredOperationOptions) { + if ( info.Name.Equals(name) ) { + return info; + } + } + return null; + } + + /** + * Returns the supported object classes for the given operation. + * @param apiop The operation. + * @return the supported object classes for the given operation. + */ + public ICollection GetSupportedObjectClassesByOperation(SafeType apiop) { + ICollection rv = + CollectionUtil.GetValue(_supportedObjectClassesByOperation,apiop,null); + if ( rv == null ) { + ICollection empty = + CollectionUtil.NewReadOnlySet(); + + return empty; + } + else { + return rv; + } + } + + /** + * Returns the supported options for the given operation. + * @param apiop The operation. + * @return the supported options for the given operation. + */ + public ICollection GetSupportedOptionsByOperation(SafeType apiop) { + ICollection rv = + CollectionUtil.GetValue(_supportedOptionsByOperation,apiop,null); + if ( rv == null ) { + ICollection empty = + CollectionUtil.NewReadOnlySet(); + return empty; + } + else { + return rv; + } + } + + /** + * Returns the set of object classes that apply to a particular operation. + * @return the set of object classes that apply to a particular operation. + */ + public IDictionary,ICollection> SupportedObjectClassesByOperation { + get { + return _supportedObjectClassesByOperation; + } + } + /** + * Returns the set of operation options that apply to a particular operation. + * @return the set of operation options that apply to a particular operation. + */ + public IDictionary,ICollection> SupportedOptionsByOperation { + get { + return _supportedOptionsByOperation; + } + } + + + + public override int GetHashCode() { + return CollectionUtil.GetHashCode(_declaredObjectClasses); + } + + public override bool Equals(object o) { + Schema other = o as Schema; + if ( other != null ) { + if (!CollectionUtil.Equals(ObjectClassInfo,other.ObjectClassInfo)) { + return false; + } + if (!CollectionUtil.Equals(OperationOptionInfo,other.OperationOptionInfo)) { + return false; + } + if (!CollectionUtil.Equals(_supportedObjectClassesByOperation, + other._supportedObjectClassesByOperation)) { + return false; + } + if (!CollectionUtil.Equals(_supportedOptionsByOperation, + other._supportedOptionsByOperation)) { + return false; + } + return true; + } + return false; + } + + public override string ToString() + { + return SerializerUtil.SerializeXmlObject(this, false); + } + } + #endregion + + #region SchemaBuilder + /** + * Simple builder class to help facilitate creating a {@link Schema} object. + */ + public sealed class SchemaBuilder { + private readonly SafeType _connectorClass; + private readonly ICollection _declaredObjectClasses + = new HashSet(); + private readonly ICollection _declaredOperationOptions + = new HashSet(); + + private readonly IDictionary,ICollection> + _supportedObjectClassesByOperation = + new Dictionary,ICollection>(); + private readonly IDictionary,ICollection> + _supportedOptionsByOperation = + new Dictionary,ICollection>(); + + + /** + * + */ + public SchemaBuilder(SafeType connectorClass) { + Assertions.NullCheck(connectorClass, "connectorClass"); + _connectorClass = connectorClass; + } + + /** + * Adds another ObjectClassInfo to the schema. Also, adds this + * to the set of supported classes for every operation defined by + * the Connector. + * + * @param info + * @throws IllegalStateException If already defined + */ + public void DefineObjectClass(ObjectClassInfo info) { + Assertions.NullCheck(info, "info"); + if (_declaredObjectClasses.Contains(info)) { + throw new InvalidOperationException("ObjectClass already defined: "+ + info.ObjectType); + } + _declaredObjectClasses.Add(info); + foreach (SafeType op in + FrameworkUtil.GetDefaultSupportedOperations(_connectorClass)) { + ICollection oclasses = + CollectionUtil.GetValue(_supportedObjectClassesByOperation,op,null); + if (oclasses == null) { + oclasses = new HashSet(); + _supportedObjectClassesByOperation[op] = oclasses; + } + oclasses.Add(info); + } + } + /** + * Adds another OperationOptionInfo to the schema. Also, adds this + * to the set of supported options for every operation defined by + * the Connector. + */ + public void DefineOperationOption(OperationOptionInfo info) { + Assertions.NullCheck(info, "info"); + if (_declaredOperationOptions.Contains(info)) { + throw new InvalidOperationException("OperationOption already defined: "+ + info.Name); + } + _declaredOperationOptions.Add(info); + foreach (SafeType op in + FrameworkUtil.GetDefaultSupportedOperations(_connectorClass)) { + ICollection oclasses = + CollectionUtil.GetValue(_supportedOptionsByOperation,op,null); + if (oclasses == null) { + oclasses = new HashSet(); + _supportedOptionsByOperation[op] = oclasses; + } + oclasses.Add(info); + } + } + + /** + * Adds another ObjectClassInfo to the schema. Also, adds this + * to the set of supported classes for every operation defined by + * the Connector. + * @throws IllegalStateException If already defined + */ + public void DefineObjectClass(String type, ICollection attrInfo) { + ObjectClassInfoBuilder bld = new ObjectClassInfoBuilder(); + bld.ObjectType = type; + bld.AddAllAttributeInfo(attrInfo); + ObjectClassInfo obj = bld.Build(); + DefineObjectClass(obj); + } + + /** + * Adds another OperationOptionInfo to the schema. Also, adds this + * to the set of supported options for every operation defined by + * the Connector. + * @throws IllegalStateException If already defined + */ + public void DefineOperationOption(String optionName, Type type) { + OperationOptionInfoBuilder bld = new OperationOptionInfoBuilder(); + bld.Name=(optionName); + bld.OptionType=(type); + OperationOptionInfo info = bld.Build(); + DefineOperationOption(info); + } + + /** + * Adds the given ObjectClassInfo as a supported ObjectClass for + * the given operation. + * @param op The SPI operation + * @param def The ObjectClassInfo + * @throws IllegalArgumentException If the given ObjectClassInfo was + * not already defined using {@link #defineObjectClass(ObjectClassInfo)}. + */ + public void AddSupportedObjectClass(SafeType op, + ObjectClassInfo def) + { + Assertions.NullCheck(op, "op"); + Assertions.NullCheck(def, "def"); + ICollection> apis = + FrameworkUtil.Spi2Apis(op); + if (!_declaredObjectClasses.Contains(def)) { + throw new ArgumentException("ObjectClass "+def.ObjectType+ + " not defined in schema."); + } + foreach (SafeType api in apis) { + ICollection infos = + CollectionUtil.GetValue(_supportedObjectClassesByOperation,api,null); + if ( infos == null ) { + throw new ArgumentException("Operation "+op+ + " not implement by connector."); + } + if ( infos.Contains(def)) { + throw new ArgumentException("ObjectClass "+def.ObjectType+ + " already supported for operation "+op); + } + infos.Add(def); + } + } + + /** + * Removes the given ObjectClassInfo as a supported ObjectClass for + * the given operation. + * @param op The SPI operation + * @param def The ObjectClassInfo + * @throws IllegalArgumentException If the given ObjectClassInfo was + * not already defined using {@link #defineObjectClass(ObjectClassInfo)}. + */ + public void RemoveSupportedObjectClass(SafeType op, + ObjectClassInfo def) + { + Assertions.NullCheck(op, "op"); + Assertions.NullCheck(def, "def"); + ICollection> apis = + FrameworkUtil.Spi2Apis(op); + if (!_declaredObjectClasses.Contains(def)) { + throw new ArgumentException("ObjectClass "+def.ObjectType+ + " not defined in schema."); + } + foreach (SafeType api in apis) { + ICollection infos = + CollectionUtil.GetValue(_supportedObjectClassesByOperation,api,null); + if ( infos == null ) { + throw new ArgumentException("Operation "+op+ + " not implement by connector."); + } + if ( !infos.Contains(def)) { + throw new ArgumentException("ObjectClass "+def.ObjectType + +" already removed for operation "+op); + } + infos.Remove(def); + } + } + /** + * Adds the given OperationOptionInfo as a supported option for + * the given operation. + * @param op The SPI operation + * @param def The OperationOptionInfo + * @throws IllegalArgumentException If the given OperationOptionInfo was + * not already defined using {@link #defineOperationOption(OperationOptionInfo)}. + */ + public void AddSupportedOperationOption(SafeType op, + OperationOptionInfo def) { + Assertions.NullCheck(op, "op"); + Assertions.NullCheck(def, "def"); + ICollection> apis = + FrameworkUtil.Spi2Apis(op); + if (!_declaredOperationOptions.Contains(def)) { + throw new ArgumentException("OperationOption "+def.Name+ + " not defined in schema."); + } + foreach (SafeType api in apis) { + ICollection infos = + CollectionUtil.GetValue(_supportedOptionsByOperation,api,null); + if ( infos == null ) { + throw new ArgumentException("Operation "+op+ + " not implement by connector."); + } + if ( infos.Contains(def) ) { + throw new ArgumentException("OperationOption "+def.Name+ + " already supported for operation "+op); + } + infos.Add(def); + } + } + + /** + * Removes the given OperationOptionInfo as a supported option for + * the given operation. + * @param op The SPI operation + * @param def The OperationOptionInfo + * @throws IllegalArgumentException If the given OperationOptionInfo was + * not already defined using {@link #defineOperationOption(OperationOptionInfo)}. + */ + public void RemoveSupportedOperationOption(SafeType op, + OperationOptionInfo def) { + Assertions.NullCheck(op, "op"); + Assertions.NullCheck(def, "def"); + ICollection> apis = + FrameworkUtil.Spi2Apis(op); + if (!_declaredOperationOptions.Contains(def)) { + throw new ArgumentException("OperationOption "+def.Name+ + " not defined in schema."); + } + foreach (SafeType api in apis) { + ICollection infos = + CollectionUtil.GetValue(_supportedOptionsByOperation,api,null); + if ( infos == null ) { + throw new ArgumentException("Operation "+op+ + " not implement by connector."); + } + if ( !infos.Contains(def) ) { + throw new ArgumentException("OperationOption "+def.Name+ + " already removed for operation "+op); + } + infos.Remove(def); + } + } + + /** + * Clears the operation-specific supported classes. Normally, when + * you add an ObjectClass, using {@link #defineObjectClass(ObjectClassInfo)}, + * it is added to all operations. You may then remove those that you need + * using {@link #removeSupportedObjectClass(Class, ObjectClassInfo)}. You + * may wish, as an alternative to clear everything out and instead add using + * {@link #addSupportedObjectClass(Class, ObjectClassInfo)}. + */ + public void ClearSupportedObjectClassesByOperation() { + foreach (ICollection values in + _supportedObjectClassesByOperation.Values) + { + values.Clear(); + } + } + /** + * Clears the operation-specific supported options. Normally, when + * you add an OperationOptionInfo, using {@link #defineOperationOption(OperationOptionInfo)(ObjectClassInfo)}, + * it is added to all operations. You may then remove those that you need + * using {@link #removeSupportedOperationOption(Class, OperationOptionInfo)}. You + * may wish, as an alternative to clear everything out and instead add using + * {@link #addSupportedOperationOption(Class, OperationOptionInfo)}. + */ + public void ClearSupportedOptionsByOperation() { + foreach (ICollection values in + _supportedOptionsByOperation.Values) + { + values.Clear(); + } + } + + /** + * Builds the {@link Schema} object based on the {@link ObjectClassInfo}s + * added so far. + * + * @return new Schema object based on the info provided. + */ + public Schema Build() { + if (_declaredObjectClasses.Count == 0) { + String ERR = "Must be at least one ObjectClassInfo object!"; + throw new InvalidOperationException(ERR); + } + return new Schema(_declaredObjectClasses, + _declaredOperationOptions, + _supportedObjectClassesByOperation, + _supportedOptionsByOperation); + } + } + #endregion + + #region ScriptContext + /** + * Encapsulates a script and all of its parameters. + * @see com.sun.openconnectors.framework.api.operations.ScriptOnResourceApiOp + * @see com.sun.openconnectors.framework.api.operations.ScriptOnConnectorApiOp + */ + public sealed class ScriptContext { + + private readonly String _scriptLanguage; + private readonly String _scriptText; + private readonly IDictionary _scriptArguments; + + /** + * Public only for serialization; please use {@link ScriptContextBuilder}. + * @param scriptLanguage The script language. Must not be null. + * @param scriptText The script text. Must not be null. + * @param scriptArguments The script arguments. May be null. + */ + public ScriptContext(String scriptLanguage, + String scriptText, + IDictionary scriptArguments) { + + if (scriptLanguage == null) { + throw new ArgumentException("Argument 'scriptLanguage' must be specified"); + } + if (scriptText == null) { + throw new ArgumentException("Argument 'scriptText' must be specified"); + } + //clone script arguments and options - this serves two purposes + //1)makes sure everthing is serializable + //2)does a deep copy + IDictionary scriptArgumentsClone = (IDictionary)SerializerUtil.CloneObject(scriptArguments); + _scriptLanguage = scriptLanguage; + _scriptText = scriptText; + _scriptArguments = CollectionUtil.NewReadOnlyDictionary(scriptArgumentsClone); + } + + /** + * Identifies the language in which the script is written + * (e.g., bash, csh, + * Perl4 or Python). + * @return The script language. + */ + public String ScriptLanguage { + get { + return _scriptLanguage; + } + } + + /** + * Returns the text (i.e., actual characters) of the script. + * @return The text of the script. + */ + public String ScriptText { + get { + return _scriptText; + } + } + + /** + * Returns a map of arguments to be passed to the script. + * Values must be types that the framework can serialize. + * See {@link ObjectSerializerFactory} for a list of supported types. + * @return A map of arguments to be passed to the script. + */ + public IDictionary ScriptArguments { + get { + return _scriptArguments; + } + } + + public override string ToString() + { + StringBuilder bld = new StringBuilder(); + bld.Append("ScriptContext: "); + // poor man's to string method. + IDictionary map = new Dictionary(); + map["Language"] = ScriptLanguage; + map["Text"] = ScriptText; + map["Arguments"] = ScriptArguments; + bld.Append(map.ToString()); + return bld.ToString(); + } + + } + #endregion + + #region ScriptContextBuilder + /** + * Builds an {@link ScriptContext}. + */ + public sealed class ScriptContextBuilder { + private String _scriptLanguage; + private String _scriptText; + private readonly IDictionary _scriptArguments = new + Dictionary(); + + /** + * Creates an empty builder. + */ + public ScriptContextBuilder() { + + } + + /** + * Creates a builder with the required parameters specified. + * @param scriptLanguage a string that identifies the language + * in which the script is written + * (e.g., bash, csh, + * Perl4 or Python). + * @param scriptText The text (i.e., actual characters) of the script. + */ + public ScriptContextBuilder(String scriptLanguage, + String scriptText) { + _scriptLanguage = scriptLanguage; + _scriptText = scriptText; + } + + /** + * Identifies the language in which the script is written + * (e.g., bash, csh, + * Perl4 or Python). + * @return The script language. + */ + public String ScriptLanguage { + get { + return _scriptLanguage; + } + set { + _scriptLanguage = value; + } + } + + /** + * Returns the actual characters of the script. + * @return the actual characters of the script. + */ + public String ScriptText { + get { + return _scriptText; + } + set { + _scriptText = value; + } + } + + /** + * Adds or sets an argument to pass to the script. + * @param name The name of the argument. Must not be null. + * @param value The value of the argument. Must be one of + * type types that the framework can serialize. + * @see ObjectSerializerFactory for a list of supported types. + */ + public ScriptContextBuilder AddScriptArgument(String name, Object value) { + if ( name == null ) { + throw new ArgumentException("Argument 'name' cannot be null."); + } + //don't validate value here - we do that implicitly when + //we clone in the constructor of ScriptRequest + _scriptArguments[name] = value; + return this; + } + + /** + * Removes the given script argument. + * @param name The name of the argument. Must not be null. + */ + public ScriptContextBuilder RemoveScriptArgument(String name) { + if ( name == null ) { + throw new ArgumentException("Argument 'name' cannot be null."); + } + _scriptArguments.Remove(name); + return this; + } + + /** + * Returns a mutable reference of the script arguments map. + * @return A mutable reference of the script arguments map. + */ + public IDictionary ScriptArguments { + get { + //might as well be mutable since it's the builder and + //we don't want to deep copy anyway + return _scriptArguments; + } + } + + /** + * Creates a ScriptContext. + * The scriptLanguage and scriptText + * must be set prior to calling this. + * @return The ScriptContext. + */ + public ScriptContext Build() { + return new ScriptContext(_scriptLanguage, + _scriptText, + _scriptArguments); + } + } + #endregion + + #region SyncDelta + /** + * Represents a change to an object in a resource. + * + * @see SyncApiOp + * @see SyncOp + */ + public sealed class SyncDelta { + private readonly SyncToken _token; + private readonly SyncDeltaType _deltaType; + private readonly Uid _uid; + private readonly ConnectorObject _object; + + /** + * Creates a SyncDelata + * @param token + * The token. Must not be null. + * @param deltaType + * The delta. Must not be null. + * @param uid + * The uid. Must not be null. + * @param object + * The object that has changed. May be null for delete. + */ + internal SyncDelta(SyncToken token, SyncDeltaType deltaType, + Uid uid, + ConnectorObject obj) { + Assertions.NullCheck(token, "token"); + Assertions.NullCheck(deltaType, "deltaType"); + Assertions.NullCheck(uid, "uid"); + + //only allow null object for delete + if ( obj == null && + deltaType != SyncDeltaType.DELETE) { + throw new ArgumentException("ConnectorObject must be specified for anything other than delete."); + } + + //if object not null, make sure its Uid + //matches + if ( obj != null ) { + if (!uid.Equals(obj.Uid)) { + throw new ArgumentException("Uid does not match that of the object."); + } + } + + _token = token; + _deltaType = deltaType; + _uid = uid; + _object = obj; + + } + + /** + * Returns the Uid of the object that changed. + * + * @return the Uid of the object that changed. + */ + public Uid Uid { + get { + return _uid; + } + } + + /** + * Returns the connector object that changed. This + * may be null in the case of delete. + * @return The object or possibly null if this + * represents a delete. + */ + public ConnectorObject Object { + get { + return _object; + } + } + + /** + * Returns the SyncToken of the object that changed. + * + * @return the SyncToken of the object that changed. + */ + public SyncToken Token { + get { + return _token; + } + } + + /** + * Returns the type of the change the occured. + * + * @return The type of change that occured. + */ + public SyncDeltaType DeltaType { + get { + return _deltaType; + } + } + + + public override String ToString() { + IDictionary values = new Dictionary(); + values["Token"] = _token; + values["DeltaType"] = _deltaType; + values["Uid"] = _uid; + values["Object"] = _object; + return values.ToString(); + } + + public override int GetHashCode() { + return _uid.GetHashCode(); + } + + public override bool Equals(Object o) { + if ( o is SyncDelta ) { + SyncDelta other = (SyncDelta)o; + if (!_token.Equals(other._token)) { + return false; + } + if (!_deltaType.Equals(other._deltaType)) { + return false; + } + if (!_uid.Equals(other._uid)) { + return false; + } + if (_object == null) { + if ( other._object != null ) { + return false; + } + } + else if (!_object.Equals(other._object)) { + return false; + } + return true; + } + return false; + } + } + #endregion + + #region SyncDeltaBuilder + /** + * Builder for {@link SyncDelta}. + */ + public sealed class SyncDeltaBuilder { + private SyncToken _token; + private SyncDeltaType _deltaType; + private Uid _uid; + private ConnectorObject _object; + + /** + * Create a new SyncDeltaBuilder + */ + public SyncDeltaBuilder() { + + } + + /** + * Creates a new SyncDeltaBuilder whose + * values are initialized to those of the delta. + * @param delta The original delta. + */ + public SyncDeltaBuilder(SyncDelta delta) { + _token = delta.Token; + _deltaType = delta.DeltaType; + _object = delta.Object; + _uid = delta.Uid; + } + + /** + * Returns the SyncToken of the object that changed. + * + * @return the SyncToken of the object that changed. + */ + public SyncToken Token { + get { + return _token; + } + set { + _token = value; + } + } + + /** + * Returns the type of the change that occurred. + * + * @return The type of change that occurred. + */ + public SyncDeltaType DeltaType { + get { + return _deltaType; + } + set { + _deltaType = value; + } + } + + /** + * Returns the Uid of the object that changed. + * Note that this is implicitly set when you call + * {@link #setObject(ConnectorObject)}. + * + * @return the Uid of the object that changed. + */ + public Uid Uid { + get { + return _uid; + } + set { + _uid = value; + } + } + + /** + * Returns the object that changed. + * Sets the object that changed and implicitly + * sets Uid if object is not null. + * @return The object that changed. May be null for + * deletes. + */ + public ConnectorObject Object { + get { + return _object; + } + set { + _object = value; + if ( value != null ) { + _uid = value.Uid; + } + } + } + + /** + * Creates a SyncDelta. Prior to calling the following must be specified: + *
    + *
  1. {@link #setObject(ConnectorObject) Object} (for anything other than delete)
  2. + *
  3. {@link #setUid(Uid) Uid} (this is implictly set when calling {@link #setObject(ConnectorObject)})
  4. + *
  5. {@link #setToken(SyncToken) Token}
  6. + *
  7. {@link #setDeltaType(SyncDeltaType) DeltaType}
  8. + *
+ */ + public SyncDelta Build() { + return new SyncDelta(_token, _deltaType, _uid, _object); + } + } + #endregion + + #region SyncDeltaType + /** + * The type of change. + */ + public enum SyncDeltaType { + /** + * The change represents either a create or an update in + * the resource. These are combined into a single value because: + *
    + *
  1. Many resources will not be able to distinguish a create from an update. + * Those that have an audit log will be able to. However, many implementations + * will only have the current record and a modification timestamp.
  2. + *
  3. Regardless of whether or not the resource can distinguish the two cases, + * the application needs to distinguish.
  4. + *
+ */ + CREATE_OR_UPDATE, + + /** + * The change represents a DELETE in the resource + */ + DELETE + } + #endregion + + #region SyncResultsHandler + /** + * Called to handle a delta in the stream. Will be called multiple times, + * once for each result. Although a callback, this is still invoked + * synchronously. That is, it is guaranteed that following a call to + * {@link SyncApiOp#sync(ObjectClass, SyncToken, SyncResultsHandler)} no + * more invocations to {@link #handle(SyncDelta)} will be performed. + * + * @param delta + * The change + * @return True iff the application wants to continue processing more + * results. + * @throws RuntimeException + * If the application encounters an exception. This will stop + * the interation and the exception will be propogated back to + * the application. + */ + public delegate bool SyncResultsHandler(SyncDelta delta); + #endregion + + #region SyncToken + /** + * Abstract "place-holder" for synchronization. The application must not make + * any attempt to interpret the value of the token. From the standpoint of the + * application the token is merely a black-box. The application may only persist + * the value of the token for use on subsequent synchronization requests. + *

+ * What this token represents is entirely connector-specific. On some connectors + * this might be a last-modified value. On others, it might be a unique ID of a + * log table entry. On others such as JMS, this might be a dummy value since + * JMS itself keeps track of the state of the sync. + */ + public sealed class SyncToken { + + private Object _value; + + /** + * Creates a new + * + * @param value + * May not be null. TODO: define set of allowed value types + * (currently same as set of allowed attribute values). + */ + public SyncToken(Object value) { + Assertions.NullCheck(value, "value"); + FrameworkUtil.CheckAttributeValue(value); + _value = value; + } + + /** + * Returns the value for the token. + * + * @return The value for the token. + */ + public Object Value { + get { + return _value; + } + } + + public override String ToString() { + return "SyncToken: " + _value.ToString(); + } + + public override int GetHashCode() { + return CollectionUtil.GetHashCode(_value); + } + + public override bool Equals(Object o) { + if ( o is SyncToken ) { + SyncToken other = (SyncToken)o; + return CollectionUtil.Equals(_value, other._value); + } + return false; + } + + + } + #endregion + + #region Uid + public sealed class Uid : ConnectorAttribute { + + public static readonly string NAME = ConnectorAttributeUtil.CreateSpecialName("UID"); + + public Uid(String val) : base(NAME, CollectionUtil.NewReadOnlyList(Check(val))) { + } + private static String Check(String value) { + if (StringUtil.IsBlank(value)) { + String ERR = "Uid value must not be blank!"; + throw new ArgumentException(ERR); + } + return value; + } + /** + * The single value of the attribute that is the unique id of an object. + * + * @return value that identifies an object. + */ + public String GetUidValue() { + return ConnectorAttributeUtil.GetStringValue(this); + } + } + #endregion + + #region ConnectorAttributesAccessor + /** + * Attributes Accessor convenience methods for accessing attributes. + * + * This class wraps a set of attributes to make lookup faster than the + * {@link AttributeUtil#find(String, Set)} method, since that method must + * re-create the map each time. + * + * @author Warren Strange + */ + public class ConnectorAttributesAccessor { + + ICollection _attrs; + IDictionary _attrMap; + + public ConnectorAttributesAccessor(ICollection attrs) { + _attrs = attrs; + _attrMap = ConnectorAttributeUtil.ToMap(attrs); + } + + /** + * Find the named attribute + * + * @param name - + * the attribute name to search for + * @return the Attribute, or null if not found. + */ + public ConnectorAttribute Find(String name) { + return CollectionUtil.GetValue(_attrMap, name, null); + } + + /** + * Get the {@link Name} attribute from the set of attributes. + * + * @return the {@link Name} attribute in the set. + */ + public Name GetName() { + return (Name) Find(Name.NAME); + } + + /** + * Return the enabled status of the account. + * + * If the ENABLE operational attribute is present, it's value takes + * precedence over the current value. If it is missing, the currentlyEnabled + * status is returned instead. + * + * @param dflt + * the default state if enable is not found. + * @return true if the account is enabled, false otherwise + */ + public bool GetEnabled(bool dflt) { + bool e = dflt; + ConnectorAttribute enable = Find(OperationalAttributes.ENABLE_NAME); + if (enable != null) { + e = ConnectorAttributeUtil.GetBooleanValue(enable).Value; + } + return e; + } + + /** + * Get the password as a GuardeString + * + * @return the password as a guarded String + */ + public GuardedString GetPassword() { + ConnectorAttribute a = Find(OperationalAttributes.PASSWORD_NAME); + return a == null ? null : ConnectorAttributeUtil.GetGuardedStringValue(a); + } + + /** + * Return a list of attributes + * + * @param name - + * name of attribute to search for. + * + * @return The List (generic object) iff it exists otherwise null. + */ + public IList FindList(String name) { + ConnectorAttribute a = Find(name); + return (a == null) ? null : a.Value; + } + + /** + * Return the multivalued attribute as a list of strings. This will throw a + * ClassCastException if the underlying attribute list is not of type + * String. + * + * @param name + * the name of the attribute to search for + * @return a List of String values for the attribute + */ + public IList FindStringList(String name) { + IList l = FindList(name); + if (l != null) { + IList ret = new List(l.Count); + foreach (object o in l) { + ret.Add((String) o); + } + return ret; + } + return null; + } + + /** + * Determines if the set as the attribute specified. + * + * @param name + * attribute name + * @return true if the named attribute exists, false otherwise + */ + public bool HasAttribute(String name) { + return Find(name) != null; + } + + /** + * Get the string value from the specified (single-valued) attribute. + * + * @param name + * Attribute from which to retrieve the long value. + * @return null if the value is null otherwise the long value for the + * attribute. + * @throws ClassCastException + * iff the object in the attribute is not an long. + * @throws IllegalArgumentException + * iff the attribute is a multi-valued (rather than + * single-valued). + */ + public String FindString(String name) { + ConnectorAttribute a = Find(name); + return a == null ? null : ConnectorAttributeUtil.GetStringValue(a); + } + + /** + * Get the integer value from the specified (single-valued) attribute. + * + * @param name + * Attribute from which to retrieve the long value. + * @return null if the value is null otherwise the long value for the + * attribute. + * @throws ClassCastException + * iff the object in the attribute is not an long. + * @throws IllegalArgumentException + * iff the attribute is a multi-valued (rather than + * single-valued). + */ + public int? FindInteger(String name) { + ConnectorAttribute a = Find(name); + return (a == null) ? null : ConnectorAttributeUtil.GetIntegerValue(a); + } + + /** + * Get the long value from the specified (single-valued) attribute. + * + * @param name + * Attribute from which to retrieve the long value. + * @return null if the value is null otherwise the long value for the + * attribute. + * @throws ClassCastException + * iff the object in the attribute is not an long. + * @throws IllegalArgumentException + * iff the attribute is a multi-valued (rather than + * single-valued). + */ + public long? FindLong(String name) { + ConnectorAttribute a = Find(name); + return a == null ? null : ConnectorAttributeUtil.GetLongValue(a); + } + + /** + * Get the date value from the specified (single-valued) attribute that + * contains a long. + * + * @param name + * Attribute from which to retrieve the date value. + * @return null if the value is null otherwise the date value for the + * attribute. + * @throws ClassCastException + * iff the object in the attribute is not an long. + * @throws IllegalArgumentException + * iff the attribute is a multi-valued (rather than + * single-valued). + */ + public DateTime? FindDateTime(String name) { + ConnectorAttribute a = Find(name); + return a == null ? null : ConnectorAttributeUtil.GetDateTimeValue(a); + } + + /** + * Get the integer value from the specified (single-valued) attribute. + * + * @param name + * Attribute from which to retrieve the integer value. + * @return null if the value is null otherwise the integer value for the + * attribute. + * @throws ClassCastException + * iff the object in the attribute is not an integer. + * @throws IllegalArgumentException + * iff the attribute is a multi-valued (rather than + * single-valued).. + */ + public double? FindDouble(String name) { + ConnectorAttribute a = Find(name); + return a == null ? null : ConnectorAttributeUtil.GetDoubleValue(a); + } + + /** + * Get the boolean value from the specified (single-valued) attribute. + * + * @param name + * Attribute from which to retrieve the boolean value. + * @return null if the value is null otherwise the boolean value for the + * attribute. + * @throws ClassCastException + * iff the object in the attribute is not an {@link Boolean}. + * @throws IllegalArgumentException + * iff the attribute is a multi-valued (rather than + * single-valued). + */ + public bool? FindBoolean(String name) { + ConnectorAttribute a = Find(name); + return a == null ? null : ConnectorAttributeUtil.GetBooleanValue(a); + } + } + #endregion +} diff --git a/DotNetConnectors.sln/Framework/CommonObjectsFilter.cs b/DotNetConnectors.sln/Framework/CommonObjectsFilter.cs new file mode 100644 index 00000000..2e32a68f --- /dev/null +++ b/DotNetConnectors.sln/Framework/CommonObjectsFilter.cs @@ -0,0 +1,1314 @@ +/* + * ==================== + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER. + * + * Copyright 2007-2008 Sun Microsystems, Inc. All rights reserved. + * + * The contents of this file are subject to the terms of the Common Development + * and Distribution License("CDDL") (the "License"). You may not use this file + * except in compliance with the License. + * + * You can obtain a copy of the License at + * http://IdentityConnectors.dev.java.net/legal/license.txt + * See the License for the specific language governing permissions and limitations + * under the License. + * + * When distributing the Covered Code, include this CDDL Header Notice in each file + * and include the License file at identityconnectors/legal/license.txt. + * If applicable, add the following below this CDDL Header, with the fields + * enclosed by brackets [] replaced by your own identifying information: + * "Portions Copyrighted [year] [name of copyright owner]" + * ==================== + */ +using System; +using System.Text; +using System.Collections.Generic; +using Org.IdentityConnectors.Common; +using Org.IdentityConnectors.Framework.Common.Objects; +namespace Org.IdentityConnectors.Framework.Common.Objects.Filters +{ + #region AbstractFilterTranslator + /** + * Base class to make it easier to implement Search. + * A search filter may contain operators (such as 'contains' or 'in') + * or may contain logical operators (such as 'AND', 'OR' or 'NOT') + * that a connector cannot implement using the native API + * of the target system or application. + * A connector developer should subclass AbstractFilterTranslator + * in order to declare which filter operations the connector does support. + * This allows the FilterTranslator instance to analyze + * a specified search filter and reduce the filter to its most efficient form. + * The default (and worst-case) behavior is to return a null expression, + * which means that the connector should return "everything" + * (that is, should return all values for every requested attribute) + * and rely on the common code in the framework to perform filtering. + * This "fallback" behavior is good (in that it ensures consistency + * of search behavior across connector implementations) but it is + * obviously better for performance and scalability if each connector + * performs as much filtering as the native API of the target can support. + *

+ * A subclass should override each of the following methods where possible: + *

    + *
  1. {@link #createAndExpression}
  2. + *
  3. {@link #createOrExpression}
  4. + *
  5. {@link #createContainsExpression(ContainsFilter, boolean)}
  6. + *
  7. {@link #createEndsWithExpression(EndsWithFilter, boolean)}
  8. + *
  9. {@link #createEqualsExpression(EqualsFilter, boolean)}
  10. + *
  11. {@link #createGreaterThanExpression(GreaterThanFilter, boolean)}
  12. + *
  13. {@link #createGreaterThanOrEqualExpression(GreaterThanOrEqualFilter, boolean)}
  14. + *
  15. {@link #createStartsWithExpression(StartsWithFilter, boolean)}
  16. + *
+ *

+ * Translation can then be performed using {@link #translate(Filter)}. + *

+ * @param The result type of the translator. Commonly this will + * be a string, but there are cases where you might need to return + * a more complex data structure. For example if you are building a SQL + * query, you will need not *just* the base WHERE clause but a list + * of tables that need to be joined together. + */ + abstract public class AbstractFilterTranslator : FilterTranslator + where T : class + { + + /** + * Main method to be called to translate a filter + * @param filter The filter to translate. + * @return The list of queries to be performed. The list + * size() may be one of the following: + *

    + *
  1. 0 - This + * signifies fetch everything. This may occur if your filter + * was null or one of your create* methods returned null.
  2. + *
  3. 1 - List contains a single query that will return the results from the filter. + * Note that the results may be a superset of those specified by + * the filter in the case that one of your create* methods returned null. + * That is OK from a behavior standpoint since ConnectorFacade performs + * a second level of filtering. However it is undesirable from a performance standpoint.
  4. + *
  5. >1 - List contains multiple queries that must be performed in order to + * meet the filter that was passed in. Note that this only occurs if your + * {@link #createOrExpression} method can return null. If this happens, it + * is the responsibility of the connector implementor to perform each query + * and combine the results. In order to eliminate duplicates, the connector + * implementation must keep an in-memory HashSet of those UID + * that have been visited thus far. This will not scale well if your + * result sets are large. Therefore it is recommended that if + * at all possible you implement {@link #createOrExpression}
  6. + *
+ */ + public IList Translate(Filter filter) { + if ( filter == null ) { + return new List(); + } + //this must come first + filter = NormalizeNot(filter); + filter = SimplifyAndDistribute(filter); + //might have simplified it to the everything filter + if ( filter == null ) { + return new List(); + } + IList result = TranslateInternal(filter); + //now "optimize" - we can eliminate exact matches at least + HashSet set = new HashSet(); + IList optimized = new List(result.Count); + foreach (T obj in result) { + if ( set.Add(obj) ) { + optimized.Add(obj); + } + } + return optimized; + } + + /** + * Pushes Not's so that they are just before the leaves of the tree + */ + private Filter NormalizeNot(Filter filter) { + if ( filter is AndFilter ) { + AndFilter af = (AndFilter)filter; + return new AndFilter(NormalizeNot(af.Left), + NormalizeNot(af.Right)); + } + else if ( filter is OrFilter ) { + OrFilter of = (OrFilter)filter; + return new OrFilter(NormalizeNot(of.Left), + NormalizeNot(of.Right)); + } + else if ( filter is NotFilter ) { + NotFilter nf = (NotFilter)filter; + return Negate(NormalizeNot(nf.Filter)); + } + else { + return filter; + } + } + + /** + * Given a filter, create a filter representing its negative. + * This is used by normalizeNot. + */ + private Filter Negate(Filter filter) + { + if ( filter is AndFilter ) { + AndFilter af = (AndFilter)filter; + return new OrFilter(Negate(af.Left), + Negate(af.Right)); + } + else if ( filter is OrFilter ) { + OrFilter of = (OrFilter)filter; + return new AndFilter(Negate(of.Left), + Negate(of.Right)); + } + else if ( filter is NotFilter ) { + NotFilter nf = (NotFilter)filter; + return nf.Filter; + } + else { + return new NotFilter(filter); + } + } + + /** + * Simultaneously prunes those portions of the + * filter than cannot be implemented and distributes + * Ands over Ors where needed if the resource does not + * implement Or. + * + * @param filter Nots must already be normalized + * @return a simplified filter or null to represent the + * "everything" filter. + */ + private Filter SimplifyAndDistribute(Filter filter) { + if ( filter is AndFilter ) { + AndFilter af = (AndFilter)filter; + Filter simplifiedLeft = + SimplifyAndDistribute(af.Left); + Filter simplifiedRight = + SimplifyAndDistribute(af.Right); + if ( simplifiedLeft == null ) { + //left is "everything" - just return the right + return simplifiedRight; + } + else if ( simplifiedRight == null ) { + //right is "everything" - just return the left + return simplifiedLeft; + } + else { + //simulate translation of the left and right + //to see where we end up + IList leftExprs = + TranslateInternal(simplifiedLeft); + IList rightExprs = + TranslateInternal(simplifiedRight); + if (leftExprs.Count == 0) { + //This can happen only when one of the create* methods + //is inconsistent from one invocation to the next + //(simplifiedLeft should have been null + //in the previous 'if' above). + throw new InvalidOperationException("Translation method is inconsistent: "+leftExprs); + } + if (rightExprs.Count == 0) { + //This can happen only when one of the create* methods + //is inconsistent from one invocation to the next + //(simplifiedRight should have been null + //in the previous 'if' above). + throw new InvalidOperationException("Translation method is inconsistent: "+rightExprs); + } + + //Simulate ANDing each pair(left,right). + //If all of them return null (i.e., "everything"), + //then the request cannot be filtered. + bool anyAndsPossible = false; + foreach ( T leftExpr in leftExprs ) { + foreach ( T rightExpr in rightExprs ) { + T test = CreateAndExpression( + leftExpr, + rightExpr); + if ( test != null ) { + anyAndsPossible = true; + break; + } + } + if ( anyAndsPossible ) { + break; + } + } + + //If no AND filtering is possible, + //return whichever of left or right + //contains the fewest expressions. + if (!anyAndsPossible) { + if ( leftExprs.Count <= rightExprs.Count ) { + return simplifiedLeft; + } + else { + return simplifiedRight; + } + } + + //Since AND filtering is possible for at least + //one expression, let's distribute. + if ( leftExprs.Count > 1 ) { + //The left can contain more than one expression + //only if the left-hand side is an unimplemented OR. + //Distribute our AND to the left. + OrFilter left = (OrFilter)simplifiedLeft; + OrFilter newFilter = + new OrFilter(new AndFilter(left.Left, + simplifiedRight), + new AndFilter(left.Right, + simplifiedRight)); + return SimplifyAndDistribute(newFilter); + } + else if ( rightExprs.Count > 1 ) { + //The right can contain more than one expression + //only if the right-hand side is an unimplemented OR. + //Distribute our AND to the right. + OrFilter right = (OrFilter)simplifiedRight; + OrFilter newFilter = + new OrFilter(new AndFilter(simplifiedLeft, + right.Left), + new AndFilter(simplifiedLeft, + right.Right)); + return SimplifyAndDistribute(newFilter); + } + else { + //Each side contains exactly one expression + //and the translator does implement AND + //(anyAndsPossible must be true + //for them to have hit this branch). + if (!anyAndsPossible) { + throw new Exception("expected anyAndsPossible"); + } + return new AndFilter(simplifiedLeft,simplifiedRight); + } + } + } + else if ( filter is OrFilter ) { + OrFilter of = (OrFilter)filter; + Filter simplifiedLeft = + SimplifyAndDistribute(of.Left); + Filter simplifiedRight = + SimplifyAndDistribute(of.Right); + //If either left or right reduces to "everything", + //then simplify the OR to "everything". + if ( simplifiedLeft == null || simplifiedRight == null ) { + return null; + } + //otherwise + return new OrFilter(simplifiedLeft, + simplifiedRight); + } + else { + //Otherwise, it's a NOT(LEAF) or a LEAF. + //Simulate creating it. + T expr = CreateLeafExpression(filter); + if ( expr == null ) { + //If the expression cannot be implemented, + //return the "everything" filter. + return null; + } + else { + //Otherwise, return the filter. + return filter; + } + } + } + + /** + * Translates the filter into a list of expressions. + * The filter must have already been transformed + * using normalizeNot followed by a simplifyAndDistribute. + * @param filter A filter (normalized, simplified, and distibuted) + * @return A list of expressions or empty list for everything. + */ + private IList TranslateInternal(Filter filter) { + if ( filter is AndFilter ) { + T result = TranslateAnd((AndFilter)filter); + IList rv = new List(); + if ( result != null ) { + rv.Add(result); + } + return rv; + } + else if ( filter is OrFilter ) { + return TranslateOr((OrFilter)filter); + } + else { + //otherwise it's either a leaf or a NOT (leaf) + T expr = CreateLeafExpression(filter); + IList exprs = new List(); + if ( expr != null ) { + exprs.Add(expr); + } + return exprs; + } + } + + private T TranslateAnd( AndFilter filter ) { + IList leftExprs = TranslateInternal(filter.Left); + IList rightExprs = TranslateInternal(filter.Right); + if ( leftExprs.Count != 1 ) { + //this can happen only if one of the create* methods + //is inconsistent from one invocation to the next + //(at this point we've already been simplified and + //distributed). + throw new InvalidOperationException("Translation method is inconsistent: "+leftExprs); + } + if ( rightExprs.Count != 1 ) { + //this can happen only if one of the create* methods + //is inconsistent from one invocation to the next + //(at this point we've already been simplified and + //distributed). + throw new InvalidOperationException("Translation method is inconsistent: "+rightExprs); + } + T rv = CreateAndExpression(leftExprs[0], rightExprs[0]); + if ( rv == null ) { + //This could happen only if we're inconsistent + //(since the simplify logic already should have removed + //any expression that cannot be filtered). + throw new InvalidOperationException("createAndExpression is inconsistent"); + } + return rv; + } + + private IList TranslateOr( OrFilter filter ) { + IList leftExprs = TranslateInternal(filter.Left); + IList rightExprs = TranslateInternal(filter.Right); + if ( leftExprs.Count == 0 ) { + //This can happen only if one of the create* methods + //is inconsistent from one invocation to the next. + throw new InvalidOperationException("Translation method is inconsistent"); + } + if ( rightExprs.Count == 0 ) { + //This can happen only if one of the create* methods + //methods is inconsistent from on invocation to the next. + throw new InvalidOperationException("Translation method is inconsistent"); + } + if ( leftExprs.Count == 1 && rightExprs.Count == 1 ) { + //If each side contains exactly one expression, + //try to create a combined expression. + T val = CreateOrExpression(leftExprs[0], rightExprs[0]); + if ( val != null ) { + IList rv = new List(); + rv.Add(val); + return rv; + } + //Otherwise, fall through + } + + //Return a list of queries from the left and from the right + IList rv2 = new List(leftExprs.Count+rightExprs.Count); + CollectionUtil.AddAll(rv2,leftExprs); + CollectionUtil.AddAll(rv2,rightExprs); + return rv2; + } + + /** + * Creates an expression for a LEAF or a NOT(leaf) + * @param filter Must be either a leaf or a NOT(leaf) + * @return The expression + */ + private T CreateLeafExpression(Filter filter) { + Filter leafFilter; + bool not; + if ( filter is NotFilter ) { + NotFilter nf = (NotFilter)filter; + leafFilter = nf.Filter; + not = true; + } + else { + leafFilter = filter; + not = false; + } + T expr = CreateLeafExpression(leafFilter,not); + return expr; + } + + /** + * Creates a Leaf expression + * @param filter Must be a leaf expression + * @param not Is ! to be applied to the leaf expression + * @return The expression or null (for everything) + */ + private T CreateLeafExpression(Filter filter, bool not) { + if ( filter is ContainsFilter ) { + return CreateContainsExpression((ContainsFilter)filter, not); + } + else if (filter is EndsWithFilter) { + return CreateEndsWithExpression((EndsWithFilter)filter,not); + } + else if ( filter is EqualsFilter ) { + return CreateEqualsExpression((EqualsFilter)filter, not); + } + else if ( filter is GreaterThanFilter ) { + return CreateGreaterThanExpression((GreaterThanFilter)filter, not); + } + else if ( filter is GreaterThanOrEqualFilter ) { + return CreateGreaterThanOrEqualExpression((GreaterThanOrEqualFilter)filter, not); + } + else if ( filter is LessThanFilter ) { + return CreateLessThanExpression((LessThanFilter)filter, not); + } + else if ( filter is LessThanOrEqualFilter ) { + return CreateLessThanOrEqualExpression((LessThanOrEqualFilter)filter, not); + } + else if (filter is StartsWithFilter) { + return CreateStartsWithExpression((StartsWithFilter)filter,not); + } + else if (filter is ContainsAllValuesFilter) { + return CreateContainsAllValuesExpression((ContainsAllValuesFilter)filter, not); + } + else { + //unrecognized expression - nothing we can do + return null; + } + } + + /** + * Should be overridden by subclasses to create an AND expression + * if the native resource supports AND. + * @param leftExpression The left expression. Will never be null. + * @param rightExpression The right expression. Will never be null. + * @return The AND expression. A return value of null means + * a native AND query cannot be created for the given expressions. + * In this case, the resulting query will consist of the + * leftExpression only. + */ + protected virtual T CreateAndExpression(T leftExpression, T rightExpression) { + return null; + } + + /** + * Should be overridden by subclasses to create an OR expression + * if the native resource supports OR. + * @param leftExpression The left expression. Will never be null. + * @param rightExpression The right expression. Will never be null. + * @return The OR expression. A return value of null means + * a native OR query cannot be created for the given expressions. + * In this case, {@link #translate} may return multiple queries, each + * of which must be run and results combined. + */ + protected virtual T CreateOrExpression(T leftExpression, T rightExpression) { + return null; + } + + /** + * Should be overridden by subclasses to create a CONTAINS expression + * if the native resource supports CONTAINS. + * @param filter The contains filter. Will never be null. + * @param not True if this should be a NOT CONTAINS + * @return The CONTAINS expression. A return value of null means + * a native CONTAINS query cannot be created for the given filter. + * In this case, {@link #translate} may return an empty query set, meaning + * fetch everything. The filter will be re-applied in memory + * to the resulting object stream. This does not scale well, so + * if possible, you should implement this method. + */ + protected virtual T CreateContainsExpression(ContainsFilter filter, bool not) { + return null; + } + + /** + * Should be overridden by subclasses to create a ENDS-WITH expression + * if the native resource supports ENDS-WITH. + * @param filter The contains filter. Will never be null. + * @param not True if this should be a NOT ENDS-WITH + * @return The ENDS-WITH expression. A return value of null means + * a native ENDS-WITH query cannot be created for the given filter. + * In this case, {@link #translate} may return an empty query set, meaning + * fetch everything. The filter will be re-applied in memory + * to the resulting object stream. This does not scale well, so + * if possible, you should implement this method. + */ + protected virtual T CreateEndsWithExpression(EndsWithFilter filter, bool not) { + return null; + } + + /** + * Should be overridden by subclasses to create a EQUALS expression + * if the native resource supports EQUALS. + * @param filter The contains filter. Will never be null. + * @param not True if this should be a NOT EQUALS + * @return The EQUALS expression. A return value of null means + * a native EQUALS query cannot be created for the given filter. + * In this case, {@link #translate} may return an empty query set, meaning + * fetch everything. The filter will be re-applied in memory + * to the resulting object stream. This does not scale well, so + * if possible, you should implement this method. + */ + protected virtual T CreateEqualsExpression(EqualsFilter filter, bool not) { + return null; + } + + /** + * Should be overridden by subclasses to create a GREATER-THAN expression + * if the native resource supports GREATER-THAN. + * @param filter The contains filter. Will never be null. + * @param not True if this should be a NOT GREATER-THAN + * @return The GREATER-THAN expression. A return value of null means + * a native GREATER-THAN query cannot be created for the given filter. + * In this case, {@link #translate} may return an empty query set, meaning + * fetch everything. The filter will be re-applied in memory + * to the resulting object stream. This does not scale well, so + * if possible, you should implement this method. + */ + protected virtual T CreateGreaterThanExpression(GreaterThanFilter filter, bool not) { + return null; + } + + /** + * Should be overridden by subclasses to create a GREATER-THAN-EQUAL expression + * if the native resource supports GREATER-THAN-EQUAL. + * @param filter The contains filter. Will never be null. + * @param not True if this should be a NOT GREATER-THAN-EQUAL + * @return The GREATER-THAN-EQUAL expression. A return value of null means + * a native GREATER-THAN-EQUAL query cannot be created for the given filter. + * In this case, {@link #translate} may return an empty query set, meaning + * fetch everything. The filter will be re-applied in memory + * to the resulting object stream. This does not scale well, so + * if possible, you should implement this method. + */ + protected virtual T CreateGreaterThanOrEqualExpression(GreaterThanOrEqualFilter filter, bool not) { + return null; + } + + /** + * Should be overridden by subclasses to create a LESS-THAN expression + * if the native resource supports LESS-THAN. + * @param filter The contains filter. Will never be null. + * @param not True if this should be a NOT LESS-THAN + * @return The LESS-THAN expression. A return value of null means + * a native LESS-THAN query cannot be created for the given filter. + * In this case, {@link #translate} may return an empty query set, meaning + * fetch everything. The filter will be re-applied in memory + * to the resulting object stream. This does not scale well, so + * if possible, you should implement this method. + */ + protected virtual T CreateLessThanExpression(LessThanFilter filter, bool not) { + return null; + } + + /** + * Should be overridden by subclasses to create a LESS-THAN-EQUAL expression + * if the native resource supports LESS-THAN-EQUAL. + * @param filter The contains filter. Will never be null. + * @param not True if this should be a NOT LESS-THAN-EQUAL + * @return The LESS-THAN-EQUAL expression. A return value of null means + * a native LESS-THAN-EQUAL query cannot be created for the given filter. + * In this case, {@link #translate} may return an empty query set, meaning + * fetch everything. The filter will be re-applied in memory + * to the resulting object stream. This does not scale well, so + * if possible, you should implement this method. + */ + protected virtual T CreateLessThanOrEqualExpression(LessThanOrEqualFilter filter, bool not) { + return null; + } + + /** + * Should be overridden by subclasses to create a STARTS-WITH expression + * if the native resource supports STARTS-WITH. + * @param filter The contains filter. Will never be null. + * @param not True if this should be a NOT STARTS-WITH + * @return The STARTS-WITH expression. A return value of null means + * a native STARTS-WITH query cannot be created for the given filter. + * In this case, {@link #translate} may return an empty query set, meaning + * fetch everything. The filter will be re-applied in memory + * to the resulting object stream. This does not scale well, so + * if possible, you should implement this method. + */ + protected virtual T CreateStartsWithExpression(StartsWithFilter filter, bool not) { + return null; + } + + protected virtual T CreateContainsAllValuesExpression(ContainsAllValuesFilter filter, bool not) { + return null; + } + } + #endregion + + #region AndFilter + public sealed class AndFilter : CompositeFilter { + + /** + * And the the left and right filters. + */ + public AndFilter(Filter left, Filter right) + : base(left, right) { + } + + /** + * Ands the left and right filters. + * + * @see Filter#accept(ConnectorObject) + */ + public override bool Accept(ConnectorObject obj) { + return Left.Accept(obj) && Right.Accept(obj); + } + + public override string ToString() + { + StringBuilder bld = new StringBuilder(); + bld.Append("AND: ").Append(Left).Append(", ").Append(Right); + return bld.ToString(); + } + } + #endregion + + #region AttributeFilter + public abstract class AttributeFilter : Filter { + private readonly ConnectorAttribute _attribute; + + /** + * Root filter for Attribute testing.. + */ + internal AttributeFilter(ConnectorAttribute attribute) { + _attribute = attribute; + if (attribute == null) { + throw new ArgumentException("Attribute not be null!"); + } + } + + /** + * Get the internal attribute. + */ + public ConnectorAttribute GetAttribute() { + return _attribute; + } + + /** + * Determines if the attribute provided is present in the + * {@link ConnectorObject}. + */ + public bool IsPresent(ConnectorObject obj) { + return obj.GetAttributeByName(_attribute.Name) != null; + } + public abstract bool Accept(ConnectorObject obj); + } + #endregion + + #region ComparableAttributeFilter + /** + * Filter for an attribute value that is comparable. + */ + public abstract class ComparableAttributeFilter : + SingleValueAttributeFilter { + + /** + * Attempt compare attribute values. + */ + internal ComparableAttributeFilter(ConnectorAttribute attr) + : base(attr) { + // determine if this attribute value is comparable.. + if (!(GetValue() is IComparable)) { + String ERR = "Must be a comparable value!"; + throw new ArgumentException(ERR); + } + } + + /** + * Call compareTo on the attribute values. If the attribute is not present + * in the {@link ConnectorObject} return -1. + */ + public int Compare(ConnectorObject obj) { + int ret = -1; + ConnectorAttribute attr = obj.GetAttributeByName(GetName()); + if (attr != null && attr.Value.Count == 1) { + // it must be a comparable because that's were testing against + if (!(attr.Value[0] is IComparable)) { + String ERR = "Attribute value must be comparable!"; + throw new ArgumentException(ERR); + } + // grab this value and the on from the attribute an compare.. + IComparable o1 = (IComparable)attr.Value[0]; + IComparable o2 = (IComparable)GetValue(); + ret = o1.CompareTo(o1); + } + return ret; + } + } + #endregion + + #region CompositeFilter + public abstract class CompositeFilter : Filter { + + public Filter Left { get; set; } + + public Filter Right { get; set; } + + internal CompositeFilter(Filter left, Filter right) { + Left = left; + Right = right; + } + public abstract bool Accept(ConnectorObject obj); + } + #endregion + + #region ContainsFilter + public sealed class ContainsFilter : StringFilter { + + public ContainsFilter(ConnectorAttribute attr) + : base(attr) { + } + + public override bool Accept(String value) { + return value.Contains(GetValue()); + } + + public override string ToString() + { + StringBuilder bld = new StringBuilder(); + bld.Append("CONTAINS: ").Append(GetAttribute()); + return bld.ToString(); + } + } + #endregion + + #region EndsWithFilter + public sealed class EndsWithFilter : StringFilter { + + public EndsWithFilter(ConnectorAttribute attr) + : base(attr) { + } + + public override bool Accept(String value) { + return value.EndsWith(GetValue()); + } + + public override string ToString() { + StringBuilder bld = new StringBuilder(); + bld.Append("ENDSWITH: ").Append(GetAttribute()); + return bld.ToString(); + } + } + #endregion + + #region EqualsFilter + public sealed class EqualsFilter : AttributeFilter { + + /** + * Determines if the attribute inside the {@link ConnectorObject} is equal + * to the {@link Attribute} provided. + */ + public EqualsFilter(ConnectorAttribute attr) + :base(attr) { + } + + /** + * Determines if the attribute exists in the {@link ConnectorObject} and if + * its equal to the one provided. + * + * @see Filter#accept(ConnectorObject) + */ + public override bool Accept(ConnectorObject obj) { + bool ret = false; + ConnectorAttribute thisAttr = GetAttribute(); + ConnectorAttribute attr = obj.GetAttributeByName(thisAttr.Name); + if (attr != null) { + ret = thisAttr.Equals(attr); + } + return ret; + } + + public override string ToString() { + StringBuilder bld = new StringBuilder(); + bld.Append("EQUALS: ").Append(GetAttribute()); + return bld.ToString(); + } + + } + #endregion + + #region Filter + public interface Filter { + bool Accept(ConnectorObject obj); + } + #endregion + + #region FilterBuilder + /** + * FilterBuilder creates a {@link Filter} object, that can determine if a + * ConnectorObject will be filtered or not. + * + * @author Will Droste + * @version $Revision: 1.7 $ + * @since 1.0 + */ + public static class FilterBuilder { + + /** + * Determine if the {@link ConnectorObject} {@link ConnectorAttribute} value ends + * with the {@link ConnectorAttribute} value provided. + * + * @param attr + * {@link ConnectorAttribute} value to test against the + * {@link ConnectorObject} attribute value. + * @return true if the {@link ConnectorObject} attribute value contains the + * attribute value provided. + */ + public static Filter EndsWith(ConnectorAttribute attr) { + return new EndsWithFilter(attr); + } + + /** + * Determine if the {@link ConnectorObject} {@link ConnectorAttribute} value starts + * with the {@link ConnectorAttribute} value provided. + * + * @param attr + * {@link ConnectorAttribute} value to test against the + * {@link ConnectorObject} attribute value. + * @return true if the {@link ConnectorObject} attribute value contains the + * attribute value provided. + */ + public static Filter StartsWith(ConnectorAttribute attr) { + return new StartsWithFilter(attr); + } + + public static Filter ContainsAllValues(ConnectorAttribute attr) { + return new ContainsAllValuesFilter(attr); + } + + /** + * Determine if the {@link ConnectorObject} {@link ConnectorAttribute} value contains + * the {@link ConnectorAttribute} value provided. + * + * @param attr + * {@link ConnectorAttribute} value to test against the + * {@link ConnectorObject} attribute value. + * @return true if the {@link ConnectorObject} attribute value contains the + * attribute value provided. + */ + public static Filter Contains(ConnectorAttribute attr) { + return new ContainsFilter(attr); + } + + /** + * The {@link ConnectorAttribute} value provided is less than or equal to the + * {@link ConnectorObject} attribute value. + * + * @param attr + * ConnectorAttribute to do the comparison. + * @return true if attribute provided is greater than or equal to the one + * provided by the {@link ConnectorObject}. + */ + public static Filter GreaterThanOrEqualTo(ConnectorAttribute attr) { + return new GreaterThanOrEqualFilter(attr); + } + + /** + * The {@link ConnectorAttribute} value provided is less than or equal to the + * {@link ConnectorObject} attribute value. + * + * @param attr + * ConnectorAttribute to do the comparison. + * @return true if attribute provided is less than or equal to the one + * provided by the {@link ConnectorObject}. + */ + public static Filter LessThanOrEqualTo(ConnectorAttribute attr) { + return new LessThanOrEqualFilter(attr); + } + + /** + * The {@link ConnectorAttribute} value provided is less than the + * {@link ConnectorObject} attribute value. + * + * @param attr + * ConnectorAttribute to do the comparison. + * @return true if attribute provided is less than the one provided by the + * {@link ConnectorObject}. + */ + public static Filter LessThan(ConnectorAttribute attr) { + return new LessThanFilter(attr); + } + + /** + * ConnectorAttribute value is greater than the {@link ConnectorObject} attribute + * value. + * + * @param attr + * ConnectorAttribute to do the comparison. + * @return true if attribute provided is greater than the one provided by + * the {@link ConnectorObject}. + */ + public static Filter GreaterThan(ConnectorAttribute attr) { + return new GreaterThanFilter(attr); + } + + /** + * Determines if the {@link ConnectorAttribute} provided exists in the + * {@link ConnectorObject} and is equal. + */ + public static Filter EqualTo(ConnectorAttribute attr) { + return new EqualsFilter(attr); + } + + /** + * Ands the two {@link Filter}. + * + * @param leftOperand + * left side operand. + * @param rightOperand + * right side operand. + * @return the result of leftOperand && rightOperand + */ + public static Filter And(Filter leftOperand, Filter rightOperand) { + return new AndFilter(leftOperand, rightOperand); + } + + /** + * ORs the two {@link Filter}. + * + * @param leftOperand + * left side operand. + * @param rightOperand + * right side operand. + * @return the result of leftOperand || rightOperand + */ + public static Filter Or(Filter leftOperand, Filter rightOperand) { + return new OrFilter(leftOperand, rightOperand); + } + + /** + * NOT the {@link Filter}. + * + * @param filter + * negate the result of {@link Filter}. + * @return the result of not {@link Filter}. + */ + public static Filter Not(Filter filter) { + return new NotFilter(filter); + } + } + #endregion + + #region FilterTranslator + public interface FilterTranslator { + IList Translate(Filter filter); + } + #endregion + + #region GreaterThanFilter + public sealed class GreaterThanFilter : ComparableAttributeFilter { + + /** + * Determine if the {@link ConnectorObject} {@link Attribute} value is + * greater than the one provided in the filter. + */ + public GreaterThanFilter(ConnectorAttribute attr) + : base (attr) { + } + + /** + * Determine if the {@link ConnectorObject} {@link Attribute} value is + * greater than the one provided in the filter. + * + * @see com.sun.openconnectors.framework.common.objects.Filter#accept(ConnectorObject) + */ + public override bool Accept(ConnectorObject obj) { + return IsPresent(obj) && this.Compare(obj) > 0; + } + + public override string ToString() + { + StringBuilder bld = new StringBuilder(); + bld.Append("GREATERTHAN: ").Append(GetAttribute()); + return bld.ToString(); + } + } + #endregion + + #region GreaterThanOrEqualFilter + public sealed class GreaterThanOrEqualFilter : ComparableAttributeFilter { + + /** + * Determine if the {@link ConnectorObject} {@link Attribute} value is + * greater than the one provided in the filter. + */ + public GreaterThanOrEqualFilter(ConnectorAttribute attr) + : base (attr) { + } + + /** + * Determine if the {@link ConnectorObject} {@link Attribute} value is + * greater than the one provided in the filter. + * + * @see com.sun.openconnectors.framework.common.objects.Filter#accept(ConnectorObject) + */ + public override bool Accept(ConnectorObject obj) { + return IsPresent(obj) && this.Compare(obj) >= 0; + } + + public override string ToString() + { + StringBuilder bld = new StringBuilder(); + bld.Append("GREATERTHANOREQUAL: ").Append(GetAttribute()); + return bld.ToString(); + } + } + #endregion + + #region LessThanFilter + public sealed class LessThanFilter : ComparableAttributeFilter { + + /** + * Determine if the {@link ConnectorObject} {@link Attribute} value is + * greater than the one provided in the filter. + */ + public LessThanFilter(ConnectorAttribute attr) + : base (attr) { + } + + /** + * Determine if the {@link ConnectorObject} {@link Attribute} value is + * greater than the one provided in the filter. + * + * @see com.sun.openconnectors.framework.common.objects.Filter#accept(ConnectorObject) + */ + public override bool Accept(ConnectorObject obj) { + return IsPresent(obj) && this.Compare(obj) < 0; + } + + public override string ToString() + { + StringBuilder bld = new StringBuilder(); + bld.Append("LESSTHAN: ").Append(GetAttribute()); + return bld.ToString(); + } + } + #endregion + + #region LessThanOrEqualFilter + public sealed class LessThanOrEqualFilter : ComparableAttributeFilter { + + /** + * Determine if the {@link ConnectorObject} {@link Attribute} value is + * greater than the one provided in the filter. + */ + public LessThanOrEqualFilter(ConnectorAttribute attr) + : base (attr) { + } + + /** + * Determine if the {@link ConnectorObject} {@link Attribute} value is + * greater than the one provided in the filter. + * + * @see com.sun.openconnectors.framework.common.objects.Filter#accept(ConnectorObject) + */ + public override bool Accept(ConnectorObject obj) { + return IsPresent(obj) && this.Compare(obj) <= 0; + } + + public override string ToString() + { + StringBuilder bld = new StringBuilder(); + bld.Append("LESSTHANOREQUAL: ").Append(GetAttribute()); + return bld.ToString(); + } + } + #endregion + + #region NotFilter + /** + * Proxy the filter to return the negative of the value. + */ + public sealed class NotFilter : Filter { + + private readonly Filter _filter; + + /** + * Take the value returned from the internal filter and NOT it. + */ + public NotFilter(Filter filter) { + _filter = filter; + } + + /** + * Get the internal filter that is being negated. + */ + public Filter Filter { + get { + return _filter; + } + } + + /** + * Return the opposite the internal filters return value. + * + * @see Filter#accept(ConnectorObject) + */ + public bool Accept(ConnectorObject obj) { + return !_filter.Accept(obj); + } + + public override string ToString() + { + StringBuilder bld = new StringBuilder(); + bld.Append("NOT: ").Append(Filter); + return bld.ToString(); + } + } + #endregion + + #region OrFilter + public sealed class OrFilter : CompositeFilter { + + /** + * Or the left and right filters. + */ + public OrFilter(Filter left, Filter right) + : base(left, right) { + } + + /** + * ORs the left and right filters. + * + * @see Filter#accept(ConnectorObject) + */ + public override bool Accept(ConnectorObject obj) { + return Left.Accept(obj) || Right.Accept(obj); + } + + public override string ToString() { + StringBuilder bld = new StringBuilder(); + bld.Append("OR: ").Append(Left).Append(", ").Append(Right); + return bld.ToString(); + } + } + #endregion + + #region SingleValueAttributeFilter + /** + * Get a single value out of the attribute to test w/. + */ + public abstract class SingleValueAttributeFilter : AttributeFilter { + + /** + * Attempt to single out the value for comparison. + */ + internal SingleValueAttributeFilter(ConnectorAttribute attr) : + base(attr) { + // make sure this is not a Uid.. + if (Uid.NAME.Equals(attr.Name)) { + String MSG = "Uid can only be used for equals comparison."; + throw new ArgumentException(MSG); + } + // actual runtime.. + if (attr.Value.Count != 1) { + String ERR = "Must only be one value!"; + throw new ArgumentException(ERR); + } + } + + /** + * Value to test against. + */ + public Object GetValue() { + return GetAttribute().Value[0]; + } + /** + * Name of the attribute to find in the {@link ConnectorObject}. + */ + public String GetName() { + return GetAttribute().Name; + } + } + #endregion + + #region StartsWithFilter + public sealed class StartsWithFilter : StringFilter { + + public StartsWithFilter(ConnectorAttribute attr) + : base(attr) { + } + + public override bool Accept(String value) { + return value.StartsWith(GetValue()); + } + + public override string ToString() + { + StringBuilder bld = new StringBuilder(); + bld.Append("STARTSWITH: ").Append(GetAttribute()); + return bld.ToString(); + } + } + #endregion + + #region StringFilter + /** + * Filter based on strings. + */ + public abstract class StringFilter : SingleValueAttributeFilter { + + /** + * Attempts to get a string from the attribute. + */ + internal StringFilter(ConnectorAttribute attr) + : base(attr) { + Object val = base.GetValue(); + if (!(val is string)) { + String MSG = "Value must be a string!"; + throw new ArgumentException(MSG); + } + } + + /** + * Get the string value from the afore mentioned attribute. + * + * @see SingleValueAttributeFilter#getValue() + */ + public new String GetValue() { + return (String) base.GetValue(); + } + + /** + * @throws ClassCastException + * iff the value from the {@link ConnectorObject}'s attribute + * of the same name as provided is not a string. + * @see com.sun.openconnectors.framework.common.objects.Filter#accept(ConnectorObject) + */ + public override bool Accept(ConnectorObject obj) { + bool ret = false; + ConnectorAttribute attr = obj.GetAttributeByName(GetName()); + if (attr != null) { + ret = Accept((string)attr.Value[0]); + } + return ret; + } + + public abstract bool Accept(String value); + } + #endregion + + #region ContainsAllValues Filter + public class ContainsAllValuesFilter : AttributeFilter { + private readonly string _name; + private readonly ICollection _values; + + public ContainsAllValuesFilter(ConnectorAttribute attr) : base(attr) { + _name = attr.Name; + _values = attr.Value; + } + /** + * Determine if the {@link ConnectorObject} contains an {@link Attribute} + * which contains all the values provided in the {@link Attribute} passed + * into the filter. + * + * {@inheritDoc} + */ + public override bool Accept(ConnectorObject obj) { + bool rv = false; + ConnectorAttribute found = obj.GetAttributeByName(_name); + if (found != null) { + // TODO: possible optimization using 'Set' + foreach (object o in _values) { + if (!(rv = found.Value.Contains(o))) { + break; + } + } + } + return rv; + } + } + #endregion + +} diff --git a/DotNetConnectors.sln/Framework/CommonSerializer.cs b/DotNetConnectors.sln/Framework/CommonSerializer.cs new file mode 100644 index 00000000..261429cb --- /dev/null +++ b/DotNetConnectors.sln/Framework/CommonSerializer.cs @@ -0,0 +1,289 @@ +/* + * ==================== + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER. + * + * Copyright 2007-2008 Sun Microsystems, Inc. All rights reserved. + * + * The contents of this file are subject to the terms of the Common Development + * and Distribution License("CDDL") (the "License"). You may not use this file + * except in compliance with the License. + * + * You can obtain a copy of the License at + * http://IdentityConnectors.dev.java.net/legal/license.txt + * See the License for the specific language governing permissions and limitations + * under the License. + * + * When distributing the Covered Code, include this CDDL Header Notice in each file + * and include the License file at identityconnectors/legal/license.txt. + * If applicable, add the following below this CDDL Header, with the fields + * enclosed by brackets [] replaced by your own identifying information: + * "Portions Copyrighted [year] [name of copyright owner]" + * ==================== + */ +using System; +using System.IO; +using System.Collections.Generic; +using Org.IdentityConnectors.Common; +namespace Org.IdentityConnectors.Framework.Common.Serializer +{ + /** + * Interface for reading objects from a stream. + */ + public interface BinaryObjectDeserializer { + /** + * Reads the next object from the stream. Throws + * a wrapped {@link EOFException} if end of stream is reached. + * @return The next object from the stream. + */ + object ReadObject(); + + /** + * Closes the underlying stream + */ + void Close(); + } + /** + * Interface for writing objects to a stream. + */ + public interface BinaryObjectSerializer { + /** + * Writes the next object to the stream. + * @param obj The object to write. + * @see ObjectSerializerFactory for a list of supported types. + */ + void WriteObject(object obj); + + /** + * Flushes the underlying stream. + */ + void Flush(); + + /** + * Closes the underylying stream after first flushing it. + */ + void Close(); + } + + /** + * Serializer factory for serializing connector objects. The list of + * supported types are as follows: + * TODO: list supported types + *
    + *
+ * @see SerializerUtil + */ + public abstract class ObjectSerializerFactory { + // At some point we might make this pluggable, but for now, hard-code + private const string IMPL_NAME = "Org.IdentityConnectors.Framework.Impl.Serializer.ObjectSerializerFactoryImpl"; + + private static ObjectSerializerFactory _instance; + + private static readonly Object LOCK = new Object(); + + + /** + * Get the singleton instance of the {@link ObjectSerializerFactory}. + */ + public static ObjectSerializerFactory GetInstance() { + lock (LOCK) { + if (_instance == null) { + SafeType t = + FrameworkInternalBridge.LoadType(IMPL_NAME); + + _instance = t.CreateInstance(); + } + return _instance; + } + } + + /** + * Creates a BinaryObjectSerializer for writing objects to + * the given stream. + * + * NOTE: consider using {@link SerializerUtil#serializeBinaryObject(Object)} + * for convenience serializing a single object. + * + * NOTE2: do not mix and match {@link SerializerUtil#serializeBinaryObject(Object)} + * with {{@link #newBinaryDeserializer(InputStream)}. This is unsafe since there + * is header information and state associated with the stream. Objects written + * using one method must be read using the proper corresponding method. + * + * @param os The stream + * @return The serializer + */ + public abstract BinaryObjectSerializer NewBinarySerializer(Stream os); + + /** + * Creates a BinaryObjectDeserializer for reading objects from + * the given stream. + * + * NOTE: consider using {@link SerializerUtil#deserializeBinaryObject(byte[])} + * for convenience deserializing a single object. + * + * NOTE2: do not mix and match {@link SerializerUtil#deserializeBinaryObject(Object)} + * with {{@link #newBinarySerializer(OutputStream)}. This is unsafe since there + * is header information and state associated with the stream. Objects written + * using one method must be read using the proper corresponding method. + * + * @param os The stream + * @return The deserializer + */ + public abstract BinaryObjectDeserializer NewBinaryDeserializer(Stream i); + + /** + * Creates a BinaryObjectSerializer for writing objects to + * the given stream. + * + * NOTE: consider using {@link SerializerUtil#serializeXmlObject(Object,boolean)} + * for convenience serializing a single object. + * + * NOTE2: do not mix and match {@link SerializerUtil#serializeXmlObject(Object,boolean)} + * with {{@link #deserializeXmlStream(InputSource, XmlObjectResultsHandler, boolean)}. + * + * @param w The writer + * @param includeHeader True to include the xml header + * @param multiObject Is this to produce a multi-object document. If false, only + * a single object may be written. + * @return The serializer + */ + public abstract XmlObjectSerializer NewXmlSerializer(TextWriter w, + bool includeHeader, + bool multiObject); + + /** + * Deserializes XML objects from a stream + * + * NOTE: Consider using {@link SerializerUtil#deserializeXmlObject(String,boolean)} + * for convenience deserializing a single object. + * + * NOTE2: Do not mix and match {@link SerializerUtil#deserializeXmlObject(Object,boolean)} + * with {{@link #newXmlSerializer(Writer, boolean, boolean)}. + * + * @param is The input source + * @param handler The callback to receive objects from the stream + * @param validate True iff we are to validate + */ + public abstract void DeserializeXmlStream(TextReader reader, + XmlObjectResultsHandler handler, + bool validate); + + } + + /** + * Bag of utilities for serialization + */ + public static class SerializerUtil { + + + /** + * Serializes the given object to bytes + * @param object The object to serialize + * @return The bytes + * @see ObjectSerializerFactory for a list of supported types + */ + public static byte [] SerializeBinaryObject(object obj) { + ObjectSerializerFactory fact = ObjectSerializerFactory.GetInstance(); + MemoryStream mem = new MemoryStream(); + BinaryObjectSerializer ser = fact.NewBinarySerializer(mem); + ser.WriteObject(obj); + ser.Close(); + return mem.ToArray(); + } + + /** + * Deserializes the given object from bytes + * @param bytes The bytes to deserialize + * @return The object + * @see ObjectSerializerFactory for a list of supported types + */ + public static object DeserializeBinaryObject(byte [] bytes) { + ObjectSerializerFactory fact = ObjectSerializerFactory.GetInstance(); + MemoryStream mem = new MemoryStream(bytes); + BinaryObjectDeserializer des = fact.NewBinaryDeserializer(mem); + return des.ReadObject(); + } + + /** + * Serializes the given object to xml + * @param object The object to serialize + * @param includeHeader True if we are to include the xml header. + * @return The xml + * @see ObjectSerializerFactory for a list of supported types + */ + public static String SerializeXmlObject(Object obj, bool includeHeader) { + ObjectSerializerFactory fact = ObjectSerializerFactory.GetInstance(); + StringWriter w = new StringWriter(); + XmlObjectSerializer ser = fact.NewXmlSerializer(w, includeHeader, false); + ser.WriteObject(obj); + ser.Close(true); + return w.ToString(); + } + + /** + * Deserializes the given object from xml + * @param bytes The xml to deserialize + * @param validate True if we are to validate the xml + * @return The object + * @see ObjectSerializerFactory for a list of supported types + */ + public static Object DeserializeXmlObject(String str, bool validate) { + ObjectSerializerFactory fact = ObjectSerializerFactory.GetInstance(); + StringReader source = new StringReader(str); + IList rv = new List(); + fact.DeserializeXmlStream(source, + obj => { + rv.Add(obj); + return true; + },validate); + if ( rv.Count > 0 ) { + return rv[0]; + } + else { + return null; + } + } + + /** + * Clones the given object by serializing it to bytes and then + * deserializing it. + * @param object The object. + * @return A clone of the object + */ + public static object CloneObject(Object obj) { + byte [] bytes = SerializeBinaryObject(obj); + return DeserializeBinaryObject(bytes); + } + + } + + /** + * Callback interface to receive xml objects from a stream of objects. + */ + public delegate bool XmlObjectResultsHandler(Object obj); + + /** + * Interface for writing objects to a stream. + */ + public interface XmlObjectSerializer { + /** + * Writes the next object to the stream. + * @param object The object to write. + * @see ObjectSerializerFactory for a list of supported types. + * @throws ConnectorException if there is more than one object + * and this is not configured for multi-object document. + */ + void WriteObject(Object obj); + + /** + * Flushes the underlying stream. + */ + void Flush(); + + /** + * Adds document end tag and optinally closes the underlying stream + */ + void Close(bool closeUnderlyingStream); + } + + +} diff --git a/DotNetConnectors.sln/Framework/Framework.csproj b/DotNetConnectors.sln/Framework/Framework.csproj new file mode 100644 index 00000000..4cecaa9d --- /dev/null +++ b/DotNetConnectors.sln/Framework/Framework.csproj @@ -0,0 +1,99 @@ + + + + {8B24461B-456A-4032-89A1-CD418F7B5B62} + Debug + AnyCPU + Library + Org.IdentityConnectors.Framework + Framework + v3.5 + False + False + 4 + false + + + prompt + 4 + AnyCPU + bin\Debug\ + true + Full + False + True + DEBUG;TRACE + + + pdbonly + bin\Release\ + False + prompt + 4 + True + False + TRACE + + + False + Auto + 4194304 + AnyCPU + 4096 + + + + + + 3.5 + + + 3.5 + + + + + + + + + + + + + + + + + + + {F140E8DA-52B4-4159-992A-9DA10EA8EEFB} + Common + + + + + + + diff --git a/DotNetConnectors.sln/Framework/Spi.cs b/DotNetConnectors.sln/Framework/Spi.cs new file mode 100644 index 00000000..974246d0 --- /dev/null +++ b/DotNetConnectors.sln/Framework/Spi.cs @@ -0,0 +1,233 @@ +/* + * ==================== + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER. + * + * Copyright 2007-2008 Sun Microsystems, Inc. All rights reserved. + * + * The contents of this file are subject to the terms of the Common Development + * and Distribution License("CDDL") (the "License"). You may not use this file + * except in compliance with the License. + * + * You can obtain a copy of the License at + * http://IdentityConnectors.dev.java.net/legal/license.txt + * See the License for the specific language governing permissions and limitations + * under the License. + * + * When distributing the Covered Code, include this CDDL Header Notice in each file + * and include the License file at identityconnectors/legal/license.txt. + * If applicable, add the following below this CDDL Header, with the fields + * enclosed by brackets [] replaced by your own identifying information: + * "Portions Copyrighted [year] [name of copyright owner]" + * ==================== + */ +using System; +using System.Globalization; +using System.Collections.Generic; +using Org.IdentityConnectors.Common; +using Org.IdentityConnectors.Common.Pooling; +using Org.IdentityConnectors.Framework.Common.Objects; +using Org.IdentityConnectors.Framework.Spi.Operations; +namespace Org.IdentityConnectors.Framework.Spi +{ + #region AttributeNormalizer + /** + * Interface to be implemented by connectors that need + * to normalize certain attributes. This might, for + * example, be used to normalize whitespace within + * DN's to ensure consistent filtering whether that + * filtering is natively on the resource or by the + * connector framework. For connectors implementing + * this interface, the method {@link #normalizeAttribute(ObjectClass, Attribute)} + * will be applied to each of the following: + *
    + *
  1. The filter passed to {@link SearchOp}.
  2. + *
  3. The results returned from {@link SearchOp}.
  4. + *
  5. The results returned from {@link SyncOp}.
  6. + *
  7. The attributes passed to {@link AdvancedUpdateOp}.
  8. + *
  9. The Uid returned from {@link AdvancedUpdateOp}.
  10. + *
  11. The attributes passed to {@link UpdateOp}.
  12. + *
  13. The Uid returned from {@link UpdateOp}.
  14. + *
  15. The attributes passed to {@link CreateOp}.
  16. + *
  17. The Uid returned from {@link CreateOp}.
  18. + *
  19. The Uid passed to {@link DeleteOp}.
  20. + *
+ */ + public interface AttributeNormalizer + { + ConnectorAttribute NormalizeAttribute(ObjectClass oclass, ConnectorAttribute attribute); + } + #endregion + + #region Configuration + /// + /// Configuration information for the Connector. + /// + public interface Configuration { + + ConnectorMessages ConnectorMessages{get;set;} + + /** + * Determine if the configuration is valid based on the values set. + */ + void Validate(); + } + #endregion + + #region AbstractConfiguration + public abstract class AbstractConfiguration : Configuration { + + public ConnectorMessages ConnectorMessages{get;set;} + + public abstract void Validate(); + } + #endregion + + #region ConnectorClassAttribute + [AttributeUsage(AttributeTargets.Class,AllowMultiple=false)] + public class ConnectorClassAttribute : System.Attribute { + + private readonly String _connectorDisplayNameKey; + private readonly SafeType _connectorConfigurationClass; + + public ConnectorClassAttribute(String connectorDisplayNameKey, + Type connectorConfigurationClass) { + _connectorDisplayNameKey = connectorDisplayNameKey; + _connectorConfigurationClass = SafeType.ForRawType(connectorConfigurationClass); + } + + public string ConnectorDisplayNameKey { + get { + return _connectorDisplayNameKey; + } + } + + public SafeType ConnectorConfigurationType { + get { + return _connectorConfigurationClass; + } + } + + public string [] MessageCatalogPaths {get;set;} + + } + #endregion + + #region ConfigurationPropertyAttribute + /// + /// The interface is traversed through reflection. This + /// annotation provides a way to override the default configuration operation for + /// each property. + /// + /// + /// + /// public class MyClass : IConfiguration { + /// [ConfigurationPropertionOptions(Confidential=true)] + /// public string MyProperty {get ; set;} + /// } + /// + /// + [AttributeUsage(AttributeTargets.Property)] + public class ConfigurationPropertyAttribute : System.Attribute { + + /// + /// Order in which this property is displayed. + /// + public int Order {get;set;} + /// + /// Is this a confidential property whose value should be + /// encrypted by the application when persisted? + /// + public bool Confidential {get;set;} + /// + /// Is this a required property? + /// + public bool Required {get;set;} + /// + /// Change the default help message key. + /// + public string HelpMessageKey {get;set;} + /// + /// Change the default display message key. + /// + public string DisplayMessageKey {get;set;} + + /** + * List of operations for which this property must be specified. + * This is used for the case where a connector may or may not + * implement certain operations depending in the configuration. + * The default value of "empty array" is special in that + * it means that this property is applicable to all operations. + * MUST be SPI operations + */ + public Type [] OperationTypes {get;set;} + + /** + * List of operations for which this property must be specified. + * This is used for the case where a connector may or may not + * implement certain operations depending in the configuration. + * The default value of "empty array" is special in that + * it means that this property is applicable to all operations. + */ + public SafeType [] Operations { + get { + Type [] types = OperationTypes; + SafeType [] rv = new SafeType[types.Length]; + for ( int i = 0; i < types.Length; i++ ) { + rv[i] = + SafeType.ForRawType(types[i]); + } + return rv; + } + } + + /// + /// Default constructor + /// + public ConfigurationPropertyAttribute() { + Order = 1; + Confidential = false; + Required = false; + HelpMessageKey = null; + DisplayMessageKey = null; + OperationTypes = new Type[0]; + } + } + #endregion + + #region Connector + /// + /// This is the main interface to declare a connector. Developers must implement + /// this interface. The life-cycle for a is as follows + /// is called then any of the operations implemented + /// in the Connector and finally dispose. The and + /// allow for block operations. For instance bulk creates or + /// deletes and the use of before and after actions. Once is + /// called the object is discarded. + /// + public interface Connector : IDisposable { + + /// + /// Initialize the connector with its configuration. For instance in a JDBC + /// Connector this would include the database URL, password, and user. + /// + /// instance of the object implemented by + /// the developer and populated with information + /// in order to initialize the . + void Init(Configuration configuration); + } + #endregion + + #region PoolableConnector + /** + * To be implemented by Connectors that wish to be pooled. + */ + public interface PoolableConnector : Connector { + /** + * Checks to see if the connector is still alive. + * @throws RuntimeException If no longer alive. + */ + void CheckAlive(); + } + #endregion + +} diff --git a/DotNetConnectors.sln/Framework/SpiOperations.cs b/DotNetConnectors.sln/Framework/SpiOperations.cs new file mode 100644 index 00000000..236f1124 --- /dev/null +++ b/DotNetConnectors.sln/Framework/SpiOperations.cs @@ -0,0 +1,412 @@ +/* + * ==================== + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER. + * + * Copyright 2007-2008 Sun Microsystems, Inc. All rights reserved. + * + * The contents of this file are subject to the terms of the Common Development + * and Distribution License("CDDL") (the "License"). You may not use this file + * except in compliance with the License. + * + * You can obtain a copy of the License at + * http://IdentityConnectors.dev.java.net/legal/license.txt + * See the License for the specific language governing permissions and limitations + * under the License. + * + * When distributing the Covered Code, include this CDDL Header Notice in each file + * and include the License file at identityconnectors/legal/license.txt. + * If applicable, add the following below this CDDL Header, with the fields + * enclosed by brackets [] replaced by your own identifying information: + * "Portions Copyrighted [year] [name of copyright owner]" + * ==================== + */ +using System; +using System.Collections.Generic; + +using Org.IdentityConnectors.Common.Security; +using Org.IdentityConnectors.Framework.Common.Objects; +using Org.IdentityConnectors.Framework.Common.Objects.Filters; + +namespace Org.IdentityConnectors.Framework.Spi.Operations +{ + + + /** + * Authenticate an object based on their unique identifier and password. + */ + public interface AuthenticateOp : SPIOperation { + + /** + * Simple authentication with two parameters presumed to be user name and + * password. The {@link Connector} developer is expected to attempt to + * authenticate these credentials natively. If the authentication fails the + * developer should throw a type of {@link RuntimeException} either + * {@link IllegalArgumentException} or if a native exception is available + * and if its of type {@link RuntimeException} simple throw it. If the + * native exception is not a {@link RuntimeException} wrap it in one and + * throw it. This will provide the most detail for logging problem and + * failed attempts. + *

+ * The developer is of course encourage to try and throw the most + * informative exception as possible. In that regards there are several + * exceptions provided in the exceptions package. For instance one of the + * most common is {@link InvalidPassword}. + * + * @param username + * the name based credential for authentication. + * @param password + * the password based credential for authentication. + * @throws RuntimeException + * iff native authentication fails. If a native exception if + * available attempt to throw it. + */ + Uid Authenticate(ObjectClass objectClass, String username, GuardedString password, OperationOptions options); + } + + /** + * The {@link Connector} developer is responsible for taking the attributes + * given (which always includes the {@link ObjectClass}) and create an object + * and its {@link Uid}. The {@link Connector} developer must return the + * {@link Uid} so that the caller can refer to the created object. + *

+ * The {@link Connector} developer should make a best effort to create the + * object otherwise throw an informative {@link RuntimeException} telling the + * caller why the operation could not be completed. It reasonable to use + * defaults for required {@link Attribute}s as long as they are documented. + * + * @author Will Droste + * @version $Revision $ + * @since 1.0 + */ + public interface CreateOp : SPIOperation { + /** + * The {@link Connector} developer is responsible for taking the attributes + * given (which always includes the {@link ObjectClass}) and create an + * object and its {@link Uid}. The {@link Connector} developer must return + * the {@link Uid} so that the caller can refer to the created object. + * + * @param name + * specifies the name of the object to create. + * @param attrs + * includes all the attributes necessary to create the resource + * object including the {@link ObjectClass} attribute. + * @return the unique id for the object that is created. For instance in + * LDAP this would be the 'dn', for a database this would be the + * primary key, and for 'ActiveDirectory' this would be the GUID. + */ + Uid Create(ObjectClass oclass, ICollection attrs, OperationOptions options); + } + + ///

+ /// Deletes an object with the specified Uid and ObjectClass on the + /// resource. + /// + public interface DeleteOp : SPIOperation { + /// + /// Delete the object that the specified Uid identifies (if any). + /// + /// The type of object to delete. + /// The unique identitfier for the object to delete. + /// Throws UnknowUid if the object does not exist. + void Delete(ObjectClass objClass, Uid uid, OperationOptions options); + } + + public interface SchemaOp : SPIOperation { + + /** + * Determines what types of objects this {@link Connector} supports. This + * method is considered an operation since determining supported objects may + * require configuration information and allows this determination to be + * dynamic. + * + * @return basic schema supported by this {@link Connector}. + */ + Schema Schema(); + } + /** + * Operation that runs a script in the environment of the connector. + * (Compare to {@link ScriptOnResourceOp}, which runs a script + * on the target resource that the connector manages.) + * A connector that intends to provide to scripts + * more than is required by the basic contract + * specified in the javadoc for {@link ScriptOnConnectorApiOp} + * should implement this interface. + *

+ * Each connector that implements this interface must support + * at least the behavior specified by {@link ScriptOnConnectorApiOp}. + * A connector also may expose additional variables for use by scripts + * and may respond to specific {@link OperationOptions options}. + * Each connector that implements this interface + * must describe in its javadoc as available "for use by connector scripts" + * any such additional variables or supported options. + */ + public interface ScriptOnConnectorOp : SPIOperation { + + /** + * Runs the script request. + * @param request The script and arguments to run. + * @param options Additional options that control how the script is + * run. + * @return The result of the script. The return type must be + * a type that the framework supports for serialization. + * See {@link ObjectSerializerFactory} for a list of supported types. + */ + Object RunScriptOnConnector(ScriptContext request, + OperationOptions options); + } + /** + * Operation that runs a script directly on a target resource. + * (Compare to {@link ScriptOnConnectorOp}, which runs a script + * in the context of a particular connector.) + *

+ * A connector that intends to support + * {@link ScriptOnResourceApiOp} + * should implement this interface. Each connector that implements + * this interface must document which script languages the connector supports, + * as well as any supported {@link OperationOptions}. + */ + public interface ScriptOnResourceOp : SPIOperation { + /** + * Run the specified script on the target resource + * that this connector manages. + * @param request The script and arguments to run. + * @param options Additional options that control + * how the script is run. + * @return The result of the script. The return type must be + * a type that the framework supports for serialization. + * See {@link ObjectSerializerFactory} for a list of supported types. + */ + Object RunScriptOnResource(ScriptContext request, + OperationOptions options); + } + + /** + * Implement this interface to allow the Connector to search for resource + * objects. + */ + public interface SearchOp : SPIOperation where T : class { + /** + * Creates a filter translator that will translate + * a specified filter to the native filter. The + * translated filters will be subsequently passed to + * {@link #search(ObjectClass, Object, ResultsHandler)} + * @param oclass The object class for the search. Will never be null. + * @param options + * additional options that impact the way this operation is run. + * If the caller passes null, the framework will convert this into + * an empty set of options, so SPI need not worry + * about this ever being null. + * @return A filter translator. + */ + FilterTranslator CreateFilterTranslator(ObjectClass oclass, OperationOptions options); + /** + * This will be called by ConnectorFacade, once for each native query produced + * by the FilterTranslator. If there is more than one query the results will + * automatically be merged together and duplicates eliminated. NOTE + * that this implies an in-memory data structure that holds a set of + * Uids, so memory usage in the event of multiple queries will be O(N) + * where N is the number of results. That is why it is important that + * the FilterTranslator implement OR if possible. + * @param oclass The object class for the search. Will never be null. + * @param query The native query to run. A value of null means 'return everything for the given object class'. + * @param handler + * Results should be returned to this handler + * @param options + * additional options that impact the way this operation is run. + * If the caller passes null, the framework will convert this into + * an empty set of options, so SPI need not worry + * about this ever being null. + */ + void ExecuteQuery(ObjectClass oclass, T query, ResultsHandler handler, OperationOptions options); + } + /** + * Receive synchronization events from the resource. + * + * @see SyncApiOp + */ + public interface SyncOp : SPIOperation { + /** + * Perform a synchronization. + * + * @param objClass + * The object class to synchronize. Must not be null. + * @param token + * The token representing the last token from the previous sync. + * Should be null if this is the first sync for the given + * resource. + * @param handler + * The result handler Must not be null. + * @param options + * additional options that impact the way this operation is run. + * If the caller passes null, the framework will convert this into + * an empty set of options, so SPI need not worry + * about this ever being null. + */ + void Sync(ObjectClass objClass, SyncToken token, + SyncResultsHandler handler, + OperationOptions options); + /** + * Returns the token corresponding to the latest sync delta. + * This is to support applications that may wish to sync starting + * "now". + * @return The latest token or null if there is no sync data. + */ + SyncToken GetLatestSyncToken(ObjectClass objectClass); + } + + /** + * The developer of a Connector should implement either this interface or the + * {@link UpdateAttributeValuesOp} interface if the Connector will allow an authorized + * caller to update (i.e., modify or replace) objects on the target resource. + *

+ * This update method is simpler to implement than {link UpdateAttributeValuesOp}, + * which must handle any of several different types of update that the caller + * may specify. However a true implementation of {link UpdateAttributeValuesOp} + * offers better performance and atomicity semantics. + * + * @author Will Droste + * @version $Revision $ + * @since 1.0 + */ + public interface UpdateOp : SPIOperation { + /** + * Update the object specified by the {@link ObjectClass} and {@link Uid}, + * replacing the current values of each attribute with the values + * provided. + *

+ * For each input attribute, replace + * all of the current values of that attribute in the target object with + * the values of that attribute. + *

+ * If the target object does not currently contain an attribute that the + * input set contains, then add this + * attribute (along with the provided values) to the target object. + *

+ * If the value of an attribute in the input set is + * {@code null}, then do one of the following, depending on + * which is most appropriate for the target: + *

    + *
  • If possible, remove that attribute from the target + * object entirely.
  • + *
  • Otherwise, replace all of the current values of that + * attribute in the target object with a single value of + * {@code null}.
  • + *
+ * @param objclass + * the type of object to modify. Will never be null. + * @param uid + * the uid of the object to modify. Will never be null. + * @param replaceAttributes + * set of new {@link Attribute}. the values in this set + * represent the new, merged values to be applied to the object. + * This set may also include {@link OperationalAttributes operational attributes}. + * Will never be null. + * @param options + * additional options that impact the way this operation is run. + * Will never be null. + * @return the {@link Uid} of the updated object in case the update changes + * the formation of the unique identifier. + */ + Uid Update(ObjectClass objclass, + Uid uid, + ICollection replaceAttributes, + OperationOptions options); + } + + /** + * More advanced implementation of {@link UpdateOp} to be implemented by + * connectors that wish to offer better performance and atomicity semantics + * for the methods {@link UpdateApiOp#addAttributeValues(ObjectClass, Uid, Set, OperationOptions)} + * and {@link UpdateApiOp#removeAttributeValues(ObjectClass, Uid, Set, OperationOptions)}. + */ + public interface UpdateAttributeValuesOp : UpdateOp { + + /** + * Update the object specified by the {@link ObjectClass} and {@link Uid}, + * adding to the current values of each attribute the values provided. + *

+ * For each attribute that the input set contains, add to + * the current values of that attribute in the target object all of the + * values of that attribute in the input set. + *

+ * NOTE that this does not specify how to handle duplicate values. + * The general assumption for an attribute of a {@code ConnectorObject} + * is that the values for an attribute may contain duplicates. + * Therefore, in general simply append the provided values + * to the current value for each attribute. + *

+ * @param objclass + * the type of object to modify. Will never be null. + * @param uid + * the uid of the object to modify. Will never be null. + * @param valuesToAdd + * set of {@link Attribute} deltas. The values for the attributes + * in this set represent the values to add to attributes in the object. + * merged. This set will never include {@link OperationalAttributes operational attributes}. + * Will never be null. + * @param options + * additional options that impact the way this operation is run. + * Will never be null. + * @return the {@link Uid} of the updated object in case the update changes + * the formation of the unique identifier. + */ + Uid AddAttributeValues(ObjectClass objclass, + Uid uid, + ICollection valuesToAdd, + OperationOptions options); + + /** + * Update the object specified by the {@link ObjectClass} and {@link Uid}, + * removing from the current values of each attribute the values provided. + *

+ * For each attribute that the input set contains, + * remove from the current values of that attribute in the target object + * any value that matches one of the values of the attribute from the input set. + *

+ * NOTE that this does not specify how to handle unmatched values. + * The general assumption for an attribute of a {@code ConnectorObject} + * is that the values for an attribute are merely representational state. + * Therefore, the implementer should simply ignore any provided value + * that does not match a current value of that attribute in the target + * object. Deleting an unmatched value should always succeed. + * @param objclass + * the type of object to modify. Will never be null. + * @param uid + * the uid of the object to modify. Will never be null. + * @param valuesToRemove + * set of {@link Attribute} deltas. The values for the attributes + * in this set represent the values to remove from attributes in the object. + * merged. This set will never include {@link OperationalAttributes operational attributes}. + * Will never be null. + * @param options + * additional options that impact the way this operation is run. + * Will never be null.. + * @return the {@link Uid} of the updated object in case the update changes + * the formation of the unique identifier. + */ + Uid RemoveAttributeValues(ObjectClass objclass, + Uid uid, + ICollection valuesToRemove, + OperationOptions options); + + } + + public interface TestOp : SPIOperation { + + /** + * Tests connectivity and validity of the {@link Configuration}. + * + * @throws RuntimeException + * iff the {@link Configuration} is not valid or a + * {@link Connection} to the resource could not be established. + */ + void Test(); + } + + /** + * Tagging interface for the {@link Connector} SPI. + */ + public interface SPIOperation { + + } +} diff --git a/DotNetConnectors.sln/Framework/Test.cs b/DotNetConnectors.sln/Framework/Test.cs new file mode 100644 index 00000000..ea50962a --- /dev/null +++ b/DotNetConnectors.sln/Framework/Test.cs @@ -0,0 +1,296 @@ +/* + * ==================== + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER. + * + * Copyright 2007-2008 Sun Microsystems, Inc. All rights reserved. + * + * The contents of this file are subject to the terms of the Common Development + * and Distribution License("CDDL") (the "License"). You may not use this file + * except in compliance with the License. + * + * You can obtain a copy of the License at + * http://IdentityConnectors.dev.java.net/legal/license.txt + * See the License for the specific language governing permissions and limitations + * under the License. + * + * When distributing the Covered Code, include this CDDL Header Notice in each file + * and include the License file at identityconnectors/legal/license.txt. + * If applicable, add the following below this CDDL Header, with the fields + * enclosed by brackets [] replaced by your own identifying information: + * "Portions Copyrighted [year] [name of copyright owner]" + * ==================== + */ +using System; + +using System.IO; +using System.Xml; +using System.Collections; +using System.Collections.Generic; +using System.Reflection; +using System.Runtime.CompilerServices; +using Org.IdentityConnectors.Common; +using Org.IdentityConnectors.Framework.Api; +using Org.IdentityConnectors.Framework.Api.Operations; +using Org.IdentityConnectors.Framework.Common; +using Org.IdentityConnectors.Framework.Common.Objects; +using Org.IdentityConnectors.Framework.Common.Objects.Filters; +using Org.IdentityConnectors.Framework.Spi; +using Org.IdentityConnectors.Framework.Spi.Operations; + +namespace Org.IdentityConnectors.Framework.Test +{ + public sealed class ToListResultsHandler { + private IList _objects + = new List(); + public bool Handle(ConnectorObject obj) { + _objects.Add(obj); + return true; + } + + public IList Objects { + get { + return _objects; + } + } + } + public abstract class TestHelpers { + + /** + * Method for convenient testing of local connectors. + */ + public static APIConfiguration CreateTestConfiguration(SafeType clazz, + Configuration config) { + return GetInstance().CreateTestConfigurationImpl(clazz, config); + } + + /** + * Creates an dummy message catalog ideal for unit testing. + * All messages are formatted as follows: + *

+ * message-key: arg0.toString(), ..., argn.toString + * @return A dummy message catalog. + */ + public static ConnectorMessages CreateDummyMessages() { + return GetInstance().CreateDummyMessagesImpl(); + } + + public static IList SearchToList(SearchApiOp search, + ObjectClass oclass, + Filter filter) { + return SearchToList(search, oclass, filter, null); + } + + public static IList SearchToList(SearchApiOp search, + ObjectClass oclass, + Filter filter, + OperationOptions options) { + ToListResultsHandler handler = new + ToListResultsHandler(); + search.Search(oclass,filter, handler.Handle,options); + return handler.Objects; + } + /** + * Performs a raw, unfiltered search at the SPI level, + * eliminating duplicates from the result set. + * @param search The search SPI + * @param oclass The object class - passed through to + * connector so it may be null if the connecor + * allowing it to be null. (This is convenient for + * unit tests, but will not be the case in general) + * @param filter The filter to search on + * @param options The options - may be null - will + * be cast to an empty OperationOptions + * @return The list of results. + */ + public static IList SearchToList(SearchOp search, + ObjectClass oclass, + Filter filter) where T : class{ + return SearchToList(search,oclass,filter,null); + } + /** + * Performs a raw, unfiltered search at the SPI level, + * eliminating duplicates from the result set. + * @param search The search SPI + * @param oclass The object class - passed through to + * connector so it may be null if the connecor + * allowing it to be null. (This is convenient for + * unit tests, but will not be the case in general) + * @param filter The filter to search on + * @param options The options - may be null - will + * be cast to an empty OperationOptions + * @return The list of results. + */ + public static IList SearchToList(SearchOp search, + ObjectClass oclass, + Filter filter, + OperationOptions options) where T : class{ + ToListResultsHandler handler = new + ToListResultsHandler(); + Search(search,oclass,filter, handler.Handle, options); + return handler.Objects; + } + + /** + * Performs a raw, unfiltered search at the SPI level, + * eliminating duplicates from the result set. + * @param search The search SPI + * @param oclass The object class - passed through to + * connector so it may be null if the connecor + * allowing it to be null. (This is convenient for + * unit tests, but will not be the case in general) + * @param filter The filter to search on + * @param handler The result handler + * @param options The options - may be null - will + * be cast to an empty OperationOptions + */ + public static void Search(SearchOp search, + ObjectClass oclass, + Filter filter, + ResultsHandler handler, + OperationOptions options) where T : class { + GetInstance().SearchImpl(search, oclass, filter, handler, options); + } + + + //At some point we might make this pluggable, but for now, hard-code + private const String IMPL_NAME = + "Org.IdentityConnectors.Framework.Impl.Test.TestHelpersImpl"; + private static readonly object LOCK = new object(); + private static TestHelpers _instance; + + /** + * Returns the instance of this factory. + * @return The instance of this factory + */ + private static TestHelpers GetInstance() { + lock(LOCK) { + if (_instance == null) { + SafeType type = FrameworkInternalBridge.LoadType(IMPL_NAME); + _instance = type.CreateInstance(); + } + return _instance; + } + } + + + abstract protected APIConfiguration CreateTestConfigurationImpl(SafeType clazz, + Configuration config); + abstract protected void SearchImpl(SearchOp search, + ObjectClass oclass, + Filter filter, + ResultsHandler handler, + OperationOptions options) where T : class; + + + private static IDictionary _properties = null; + private static readonly string PREFIX = Environment.GetEnvironmentVariable("USERPROFILE") + "/.connectors/"; + public static readonly string GLOBAL_PROPS = "connectors.xml"; + + [MethodImpl(MethodImplOptions.NoInlining)] + public static string GetProperty(string key, string def) { + Assembly asm = Assembly.GetCallingAssembly(); + return CollectionUtil.GetValue(GetProperties(asm), key, def); + } + + private static IDictionary GetProperties(Assembly asm) { + lock(LOCK) { + if (_properties == null) { + _properties = LoadProperties(asm); + } + } + // create a new instance so its not mutable + return CollectionUtil.NewReadOnlyDictionary(_properties); + } + + private static IDictionary LoadProperties(Assembly asm) { + const string ERR = "Unable to load optional XML properties file: "; + string fn = null; + IDictionary props = null; + IDictionary ret = new Dictionary(); + + //load global properties file + try { + fn = Path.Combine(PREFIX, GLOBAL_PROPS); + props = LoadPropertiesFile(fn); + CollectionUtil.AddAll(ret, props); + } catch (Exception e) { + TraceUtil.TraceException(ERR + fn, e); + } + + // load the project properties file + try { + fn = Path.Combine(Environment.CurrentDirectory, "project.xml"); + props = LoadPropertiesFile(fn); + CollectionUtil.AddAll(ret, props); + } catch (Exception e) { + TraceUtil.TraceException(ERR + fn, e); + } + + // private settings are in the "assembly name" folder, as defined in the assembly + string prjName = asm.GetName().Name; + if (!StringUtil.IsBlank(prjName)) { + //load private project properties file + try { + fn = Path.Combine(PREFIX, prjName + "/project.xml"); + props = LoadPropertiesFile(fn); + CollectionUtil.AddAll(ret, props); + } catch (IOException e) { + TraceUtil.TraceException(ERR + fn, e); + } + + string cfg = Environment.GetEnvironmentVariable("configuration"); + if(!StringUtil.IsBlank(cfg)) { + try { + // load a config-specific properties file + fn = Path.Combine(PREFIX, prjName + "/" + cfg + "/project.xml"); + props = LoadPropertiesFile(fn); + CollectionUtil.AddAll(ret, props); + } catch (IOException e) { + TraceUtil.TraceException(ERR + fn, e); + } + } + } else { + TraceUtil.TraceException("Could not infer assembly name.", new Exception()); + } + + // load the environment variables + foreach (DictionaryEntry entry in Environment.GetEnvironmentVariables()) { + ret[entry.Key.ToString()] = entry.Value.ToString(); + } + return ret; + } + + ///

+ /// Format for the xml file is simple <property name='' value=''/> + /// + /// + /// + public static IDictionary LoadPropertiesFile(string filename) { + IDictionary ret = new Dictionary(); + //Environment. + XmlTextReader reader = null; + try { + // Load the reader with the data file and ignore all white space nodes. + reader = new XmlTextReader(filename); + reader.WhitespaceHandling = WhitespaceHandling.None; + // Parse the file and display each of the nodes. + while (reader.Read()) { + if (reader.NodeType == XmlNodeType.Element && + reader.Name.Equals("property")) { + string name = reader.GetAttribute("name"); + string xmlValue = reader.GetAttribute("value"); + if (!StringUtil.IsBlank(name) && xmlValue != null) { + ret[name] = xmlValue; + } + } + } + } finally { + if (reader!=null) + reader.Close(); + } + return ret; + } + + abstract protected ConnectorMessages CreateDummyMessagesImpl(); + } +} diff --git a/DotNetConnectors.sln/FrameworkInternal/Api.cs b/DotNetConnectors.sln/FrameworkInternal/Api.cs new file mode 100644 index 00000000..09f7f320 --- /dev/null +++ b/DotNetConnectors.sln/FrameworkInternal/Api.cs @@ -0,0 +1,889 @@ +/* + * ==================== + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER. + * + * Copyright 2007-2008 Sun Microsystems, Inc. All rights reserved. + * + * The contents of this file are subject to the terms of the Common Development + * and Distribution License("CDDL") (the "License"). You may not use this file + * except in compliance with the License. + * + * You can obtain a copy of the License at + * http://IdentityConnectors.dev.java.net/legal/license.txt + * See the License for the specific language governing permissions and limitations + * under the License. + * + * When distributing the Covered Code, include this CDDL Header Notice in each file + * and include the License file at identityconnectors/legal/license.txt. + * If applicable, add the following below this CDDL Header, with the fields + * enclosed by brackets [] replaced by your own identifying information: + * "Portions Copyrighted [year] [name of copyright owner]" + * ==================== + */ +using System; + +using Org.IdentityConnectors.Framework.Api; +using Org.IdentityConnectors.Framework.Api.Operations; +using Org.IdentityConnectors.Framework.Common; +using Org.IdentityConnectors.Framework.Common.Objects; +using Org.IdentityConnectors.Framework.Common.Objects.Filters; +using Org.IdentityConnectors.Framework.Common.Serializer; +using Org.IdentityConnectors.Framework.Impl.Api.Local; +using Org.IdentityConnectors.Framework.Impl.Api.Remote; +using Org.IdentityConnectors.Framework.Impl.Serializer.Binary; +using Org.IdentityConnectors.Common; +using Org.IdentityConnectors.Common.Pooling; +using Org.IdentityConnectors.Common.Proxy; +using Org.IdentityConnectors.Common.Security; +using System.Linq; +using System.Collections.Generic; +using System.Globalization; +using System.Resources; +using System.Reflection; +using System.Reflection.Emit; +using System.Diagnostics; +using System.Text; + +namespace Org.IdentityConnectors.Framework.Impl.Api +{ + #region ConfigurationPropertyImpl + /// + /// Internal class, public only for unit tests + /// + public class ConfigurationPropertyImpl : ConfigurationProperty { + + private ICollection> _operations; + + public ICollection> Operations { + get { + return _operations; + } + set { + _operations = CollectionUtil.NewReadOnlySet(value); + } + } + + public ConfigurationPropertiesImpl Parent { get; set; } + + public int Order { get; set; } + + public string Name { get; set; } + + public string HelpMessageKey { get; set; } + + public string DisplayMessageKey { get; set; } + + public string GetHelpMessage(string def) { + return FormatMessage(HelpMessageKey,def); + } + + public string GetDisplayName(string def) { + return FormatMessage(DisplayMessageKey,def); + } + + public object Value { get; set; } + + public Type ValueType { get; set; } + + public bool IsConfidential { get; set; } + + public bool IsRequired { get; set; } + + public override int GetHashCode() + { + return Name.GetHashCode(); + } + + public override bool Equals(Object o) { + ConfigurationPropertyImpl other = o as ConfigurationPropertyImpl; + if ( other != null ) { + if (!Name.Equals(other.Name)) { + return false; + } + if (!CollectionUtil.Equals(Value,other.Value)) { + return false; + } + if (Order != other.Order) { + return false; + } + if (!CollectionUtil.Equals(HelpMessageKey,other.HelpMessageKey)) { + return false; + } + if (!CollectionUtil.Equals(DisplayMessageKey,other.DisplayMessageKey)) { + return false; + } + if (IsConfidential != other.IsConfidential) { + return false; + } + if (IsRequired != other.IsRequired) { + return false; + } + if (!CollectionUtil.Equals(ValueType,other.ValueType)) { + return false; + } + if (!CollectionUtil.Equals(_operations,other._operations)) { + return false; + } + + return true; + } + return false; + } + private String FormatMessage(String key, String dflt, params object [] args) { + APIConfigurationImpl apiConfig = Parent.Parent; + ConnectorMessages messages = + apiConfig.ConnectorInfo.Messages; + return messages.Format(key, dflt, args); + } + } + #endregion + + #region ConfigurationPropertiesImpl + /// + /// Internal class, public only for unit tests + /// + public class ConfigurationPropertiesImpl : ConfigurationProperties { + //properties, sorted by "order" + private IList _properties; + //property names, sorted by "order + private IList _propertyNames; + private IDictionary _propertiesByName; + + public IList Properties { + get { + return _properties; + } + set { + ConfigurationPropertyImpl [] arr = + value == null ? new ConfigurationPropertyImpl[0] : value.ToArray(); + + Array.Sort(arr, + (p1,p2)=> {return p1.Order.CompareTo(p2.Order);}); + _properties = + CollectionUtil.NewReadOnlyList(arr); + IList propertyNames = new List(); + IDictionary propertiesByName = + new Dictionary(); + foreach (ConfigurationPropertyImpl property in _properties) { + propertyNames.Add(property.Name); + propertiesByName[property.Name] = property; + property.Parent = this; + } + _propertyNames = CollectionUtil.AsReadOnlyList(propertyNames); + _propertiesByName = CollectionUtil.AsReadOnlyDictionary(propertiesByName); + } + } + public ConfigurationProperty GetProperty(String name) { + return CollectionUtil.GetValue(_propertiesByName,name,null); + } + public IList PropertyNames { + get { + return _propertyNames; + } + } + public APIConfigurationImpl Parent { get; set; } + public void SetPropertyValue(String name, object val) { + ConfigurationProperty property = GetProperty(name); + if ( property == null ) { + String MSG = "Property '"+name+"' does not exist."; + throw new ArgumentException(MSG); + } + property.Value = val; + } + + public override int GetHashCode() + { + return CollectionUtil.GetHashCode(_properties); + } + + public override bool Equals(object o) { + ConfigurationPropertiesImpl other = o as ConfigurationPropertiesImpl; + if ( other != null ) { + return CollectionUtil.SetsEqual(_properties, + other._properties); + } + return false; + } + } + #endregion + + #region APIConfigurationImpl + /// + /// Internal class, public only for unit tests + /// + public class APIConfigurationImpl : APIConfiguration { + + private ObjectPoolConfiguration _connectorPooling; + + private ConfigurationPropertiesImpl _configurationProperties; + private ICollection> _supportedOperations = + CollectionUtil.NewReadOnlySet>(new SafeType[0]); + + private IDictionary, int> _timeoutMap = + new Dictionary, int>(); + + public ConfigurationProperties ConfigurationProperties { + get { + return _configurationProperties; + } + set { + if (_configurationProperties != null) { + _configurationProperties.Parent = null; + } + _configurationProperties = (ConfigurationPropertiesImpl)value; + if (_configurationProperties != null) { + _configurationProperties.Parent = this; + } + } + } + public IDictionary, int> TimeoutMap { + get { + return _timeoutMap; + } + set { + _timeoutMap = value; + } + } + public bool IsConnectorPoolingSupported { get; set;} + public ObjectPoolConfiguration ConnectorPoolConfiguration + { + get { + if (_connectorPooling == null) { + _connectorPooling = new ObjectPoolConfiguration(); + } + return _connectorPooling; + } + set { + _connectorPooling = value; + } + } + public ICollection> SupportedOperations { + get { + return _supportedOperations; + } + set { + _supportedOperations = CollectionUtil.NewReadOnlySet>(value); + } + } + + public int GetTimeout(SafeType operation) { + return CollectionUtil.GetValue(_timeoutMap,operation, + APIConstants.NO_TIMEOUT); + } + public void SetTimeout(SafeType operation, int timeout) { + _timeoutMap[operation] = timeout; + } + + public AbstractConnectorInfo ConnectorInfo { get; set; } + + public int ProducerBufferSize { get; set; } + public APIConfigurationImpl() { + ProducerBufferSize = 100; + } + } + #endregion + + #region AbstractConnectorInfo + /// + /// internal class, public only for unit tests + /// + public class AbstractConnectorInfo : ConnectorInfo { + + private APIConfigurationImpl _defaultAPIConfiguration; + + + public string GetConnectorDisplayName() { + return Messages.Format(ConnectorDisplayNameKey, + ConnectorKey.ConnectorName); + } + + public ConnectorKey ConnectorKey { get; set; } + + public string ConnectorDisplayNameKey { get; set; } + + public ConnectorMessages Messages { get; set; } + + public APIConfigurationImpl DefaultAPIConfiguration { + get { + return _defaultAPIConfiguration; + } + set { + if ( value != null ) { + value.ConnectorInfo = this; + } + _defaultAPIConfiguration = value; + } + } + + public APIConfiguration CreateDefaultAPIConfiguration() { + APIConfigurationImpl rv = + (APIConfigurationImpl) + SerializerUtil.CloneObject(_defaultAPIConfiguration); + rv.ConnectorInfo = this; + return rv; + } + } + #endregion + + #region ConnectorMessagesImpl + /// + /// internal class, public only for unit tests + /// + public class ConnectorMessagesImpl : ConnectorMessages { + + private IDictionary> + _catalogs = new Dictionary>(); + + public String Format(String key, String dflt, params object [] args) { + if ( key == null ) { + return dflt; + } + CultureInfo locale = CultureInfo.CurrentUICulture; + if ( locale == null ) { + locale = CultureInfo.CurrentCulture; + } + if ( dflt == null ) { + dflt = key; + } + CultureInfo foundCulture = locale; + IDictionary + catalog = CollectionUtil.GetValue(_catalogs,foundCulture,null); + //check neutral culture + if ( catalog == null ) { + foundCulture = foundCulture.Parent; + catalog = CollectionUtil.GetValue(_catalogs,foundCulture,null); + } + //check invariant culture + if ( catalog == null ) { + foundCulture = foundCulture.Parent; + catalog = CollectionUtil.GetValue(_catalogs,foundCulture,null); + } + String message = null; + if ( catalog != null ) { + message = CollectionUtil.GetValue(catalog,key,null); + } + if ( message == null ) { + message = GetFrameworkMessage(locale,key); + } + if ( message == null ) { + return dflt; + } + else { + //TODO: think more about this since the formatting + //is slightly different than Java + return String.Format(foundCulture,message,args); + } + } + + private String GetFrameworkMessage(CultureInfo culture, String key) + { + ResourceManager manager = + new ResourceManager("Org.IdentityConnectors.Resources", + typeof(ConnectorMessagesImpl).Assembly); + String contents = (String)manager.GetObject(key,culture); + return contents; + } + + public IDictionary> Catalogs { + get { + return _catalogs; + } + set { + if ( value == null ) { + _catalogs = + new Dictionary>(); + } + else { + _catalogs = value; + } + } + } + } + #endregion + + #region ConnectorInfoManagerFactoryImpl + public sealed class ConnectorInfoManagerFactoryImpl : + ConnectorInfoManagerFactory { + private class RemoteManagerKey { + private readonly String _host; + private readonly int _port; + + public RemoteManagerKey(RemoteFrameworkConnectionInfo info) { + _host = info.Host; + _port = info.Port; + } + + public override bool Equals(Object o) { + if ( o is RemoteManagerKey ) { + RemoteManagerKey other = (RemoteManagerKey)o; + if (!_host.Equals(other._host)) { + return false; + } + if (_port != other._port) { + return false; + } + return true; + } + return false; + } + + public override int GetHashCode() { + return _host.GetHashCode()^_port; + } + + } + + private Object LOCAL_LOCK = new Object(); + private Object REMOTE_LOCK = new Object(); + + private ConnectorInfoManager + _localManagerCache; + + private IDictionary + _remoteManagerCache = new Dictionary(); + + public ConnectorInfoManagerFactoryImpl() { + + } + + + + public override void ClearRemoteCache() { + lock (REMOTE_LOCK) { + _remoteManagerCache.Clear(); + } + } + + + public override ConnectorInfoManager GetLocalManager() + { + lock (LOCAL_LOCK) { + ConnectorInfoManager rv = _localManagerCache; + if ( rv == null ) { + rv = new LocalConnectorInfoManagerImpl(); + } + _localManagerCache = rv; + return rv; + } + } + + public override ConnectorInfoManager GetRemoteManager(RemoteFrameworkConnectionInfo info) + { + RemoteManagerKey key = new RemoteManagerKey(info); + lock (REMOTE_LOCK) { + RemoteConnectorInfoManagerImpl rv = CollectionUtil.GetValue(_remoteManagerCache,key,null); + if ( rv == null ) { + rv = new RemoteConnectorInfoManagerImpl(info); + } + _remoteManagerCache[key] = rv; + return rv.Derive(info); + } + } + + } + #endregion + + #region AbstractConnectorFacade + internal abstract class AbstractConnectorFacade : ConnectorFacade { + + private readonly APIConfigurationImpl _configuration; + + + /** + * Builds up the maps of supported operations and calls. + */ + public AbstractConnectorFacade(APIConfigurationImpl configuration) { + Assertions.NullCheck(configuration,"configuration"); + //clone in case application tries to modify + //after the fact. this is necessary to + //ensure thread-safety of a ConnectorFacade + //also, configuration is used as a key in the + //pool, so it is important that it not be modified. + _configuration = (APIConfigurationImpl)SerializerUtil.CloneObject(configuration); + //parent ref not included in the clone + _configuration.ConnectorInfo=(configuration.ConnectorInfo); + } + + /** + * Return an instance of an API operation. + * + * @return null if the operation is not support otherwise + * return an instance of the operation. + * @see com.sun.openconnectors.framework.api.ConnectorFacade#getOperation(java.lang.Class) + */ + public APIOperation GetOperation(SafeType api) { + if (!SupportedOperations.Contains(api)) { + return null; + } + return GetOperationImplementation(api); + } + + /** + * {@inheritDoc} + */ + public ICollection> SupportedOperations { + get { + return _configuration.SupportedOperations; + } + } + + // ======================================================================= + // Operation API Methods + // ======================================================================= + /** + * {@inheritDoc} + */ + public Schema Schema() { + return ((SchemaApiOp) this.GetOperationCheckSupported(SafeType.Get())) + .Schema(); + } + + /** + * {@inheritDoc} + */ + public Uid Create(ObjectClass oclass, ICollection attrs, OperationOptions options) { + CreateApiOp op = ((CreateApiOp) GetOperationCheckSupported(SafeType.Get())); + return op.Create(oclass,attrs,options); + } + + /** + * {@inheritDoc} + */ + public void Delete(ObjectClass objClass, Uid uid, OperationOptions options) { + ((DeleteApiOp) + this.GetOperationCheckSupported(SafeType.Get())) + .Delete(objClass, uid, options); + } + + /** + * {@inheritDoc} + */ + public void Search(ObjectClass oclass,Filter filter, ResultsHandler handler, OperationOptions options) { + ((SearchApiOp) this.GetOperationCheckSupported(SafeType.Get())).Search( + oclass,filter, handler, options); + } + + /** + * {@inheritDoc} + */ + public Uid Update(ObjectClass objclass, Uid uid, ICollection attrs, OperationOptions options) { + return ((UpdateApiOp) this.GetOperationCheckSupported(SafeType.Get())) + .Update(objclass, uid, attrs, options); + } + + /** + * {@inheritDoc} + */ + public Uid AddAttributeValues( + ObjectClass objclass, + Uid uid, + ICollection attrs, + OperationOptions options) { + return ((UpdateApiOp) this.GetOperationCheckSupported(SafeType.Get())) + .AddAttributeValues(objclass, uid, attrs, options); + } + + /** + * {@inheritDoc} + */ + public Uid RemoveAttributeValues( + ObjectClass objclass, + Uid uid, + ICollection attrs, + OperationOptions options) { + return ((UpdateApiOp) this.GetOperationCheckSupported(SafeType.Get())) + .RemoveAttributeValues(objclass, uid, attrs, options); + } + + /** + * {@inheritDoc} + */ + public Uid Authenticate(ObjectClass objectClass,String username, GuardedString password, OperationOptions options) { + return ((AuthenticationApiOp) this + .GetOperationCheckSupported(SafeType.Get())).Authenticate( + objectClass,username, password, options); + } + + /** + * {@inheritDoc} + */ + public ConnectorObject GetObject(ObjectClass objClass, Uid uid, OperationOptions options) { + return ((GetApiOp) this.GetOperationCheckSupported(SafeType.Get())) + .GetObject(objClass, uid, options); + } + /** + * {@inheritDoc} + */ + public Object RunScriptOnConnector(ScriptContext request, + OperationOptions options) { + return ((ScriptOnConnectorApiOp) this + .GetOperationCheckSupported(SafeType.Get())) + .RunScriptOnConnector(request, options); + } + + /** + * {@inheritDoc} + */ + public Object RunScriptOnResource(ScriptContext request, + OperationOptions options) { + return ((ScriptOnResourceApiOp) this + .GetOperationCheckSupported(SafeType.Get())) + .RunScriptOnResource(request, options); + } + + /** + * {@inheritDoc} + */ + public void Test() { + ((TestApiOp) this.GetOperationCheckSupported(SafeType.Get())).Test(); + } + + /** + * {@inheritDoc} + */ + public void Validate() { + ((ValidateApiOp) this.GetOperationCheckSupported(SafeType.Get())).Validate(); + } + + public void Sync(ObjectClass objClass, SyncToken token, + SyncResultsHandler handler, + OperationOptions options) { + ((SyncApiOp)this.GetOperationCheckSupported(SafeType.Get())) + .Sync(objClass, token, handler, options); + } + + public SyncToken GetLatestSyncToken(ObjectClass objectClass) { + return ((SyncApiOp)this.GetOperationCheckSupported(SafeType.Get())) + .GetLatestSyncToken(objectClass); + } + + private APIOperation GetOperationCheckSupported(SafeType api) { + // check if this operation is supported. + if (!SupportedOperations.Contains(api)) { + String MSG = "Operation ''{0}'' not supported."; + String str = String.Format(MSG, api); + throw new InvalidOperationException(str); + } + return GetOperationImplementation(api); + } + + /** + * Gets the implementation of the given operation + * @param api The operation to implement. + * @return The implementation + */ + protected abstract APIOperation GetOperationImplementation(SafeType api); + + protected APIConfigurationImpl GetAPIConfiguration() { + return _configuration; + } + + /** + * Creates a new {@link APIOperation} proxy given a handler. + */ + protected APIOperation NewAPIOperationProxy(SafeType api, InvocationHandler handler) { + return (APIOperation) Proxy.NewProxyInstance(api.RawType, handler); + } + + private static bool LOGGINGPROXY_ENABLED; + static AbstractConnectorFacade() { + string enabled = System.Configuration. + ConfigurationManager.AppSettings.Get("logging.proxy"); + LOGGINGPROXY_ENABLED = StringUtil.IsTrue(enabled); + } + + protected APIOperation CreateLoggingProxy(SafeType api, APIOperation target) { + APIOperation ret = target; + if (LOGGINGPROXY_ENABLED) { + LoggingProxy logging = new LoggingProxy(api, target); + ret = NewAPIOperationProxy(api, logging); + } + return ret; + } + } + #endregion + + #region ConnectorFacadeFactoryImpl + public class ConnectorFacadeFactoryImpl : ConnectorFacadeFactory { + + + + + /** + * {@inheritDoc} + */ + public override ConnectorFacade NewInstance(APIConfiguration config) { + ConnectorFacade ret = null; + APIConfigurationImpl impl = (APIConfigurationImpl) config; + AbstractConnectorInfo connectorInfo = impl.ConnectorInfo; + if ( connectorInfo is LocalConnectorInfoImpl ) { + LocalConnectorInfoImpl localInfo = + (LocalConnectorInfoImpl)connectorInfo; + // create a new Provisioner.. + ret = new LocalConnectorFacadeImpl(localInfo,impl); + } + else { + ret = new RemoteConnectorFacadeImpl(impl); + } + return ret; + } + + + /** + * Dispose of all object pools and other resources associated with this + * class. + */ + public override void Dispose() { + ConnectorPoolManager.Dispose(); + } + + } + #endregion + + #region ObjectStreamHandler + internal interface ObjectStreamHandler { + bool Handle(Object obj); + } + #endregion + + #region StreamHandlerUtil + internal static class StreamHandlerUtil { + + /** + * Adapts from a ObjectStreamHandler to a ResultsHandler + */ + private class ResultsHandlerAdapter { + private readonly ObjectStreamHandler _target; + public ResultsHandlerAdapter(ObjectStreamHandler target) { + _target = target; + } + public bool Handle(ConnectorObject obj) { + return _target.Handle(obj); + } + } + + /** + * Adapts from a ObjectStreamHandler to a SyncResultsHandler + */ + private class SyncResultsHandlerAdapter { + private readonly ObjectStreamHandler _target; + public SyncResultsHandlerAdapter(ObjectStreamHandler target) { + _target = target; + } + public bool Handle(SyncDelta obj) { + return _target.Handle(obj); + } + } + + /** + * Adapts from a ObjectStreamHandler to a SyncResultsHandler + */ + private class ObjectStreamHandlerAdapter : ObjectStreamHandler { + private readonly Type _targetInterface; + private readonly Object _target; + public ObjectStreamHandlerAdapter(Type targetInterface, Object target) { + Assertions.NullCheck(targetInterface, "targetInterface"); + Assertions.NullCheck(target, "target"); + if (!targetInterface.IsAssignableFrom(target.GetType())) { + throw new ArgumentException("Target" +targetInterface+" "+target); + } + if (!IsAdaptableToObjectStreamHandler(targetInterface)) { + throw new ArgumentException("Target interface not supported: "+targetInterface); + } + _targetInterface = targetInterface; + _target = target; + } + public bool Handle(Object obj) { + if (_targetInterface.Equals( typeof(ResultsHandler) )) { + return ((ResultsHandler)_target)((ConnectorObject)obj); + } + else if (_targetInterface.Equals(typeof(SyncResultsHandler))) { + return ((SyncResultsHandler)_target)((SyncDelta)obj); + } + else { + throw new InvalidOperationException("Unhandled case: "+_targetInterface); + } + } + } + + public static bool IsAdaptableToObjectStreamHandler(Type clazz) { + return ( typeof(ResultsHandler).IsAssignableFrom(clazz) || + typeof(SyncResultsHandler).IsAssignableFrom(clazz)); + } + + public static ObjectStreamHandler AdaptToObjectStreamHandler(Type interfaceType, + Object target) { + return new ObjectStreamHandlerAdapter(interfaceType,target); + } + public static Object AdaptFromObjectStreamHandler(Type interfaceType, + ObjectStreamHandler target) { + if (interfaceType.Equals( typeof(ResultsHandler) ) ){ + return new ResultsHandler(new ResultsHandlerAdapter(target).Handle); + } + else if (interfaceType.Equals( typeof(SyncResultsHandler) ) ) { + return new SyncResultsHandler(new SyncResultsHandlerAdapter(target).Handle); + } + else { + throw new InvalidOperationException("Unhandled case: "+interfaceType); + } + } + + } + #endregion + + #region LoggingProxy + public class LoggingProxy : InvocationHandler { + + private readonly SafeType _op; + private readonly object _target; + + public LoggingProxy(SafeType api, object target) { + _op = api; + _target = target; + } + /// + /// Log all operations. + /// + public Object Invoke(Object proxy, MethodInfo method, Object [] args) { + //do not proxy equals, hashCode, toString + if (method.DeclaringType.Equals(typeof(object))) { + return method.Invoke(this, args); + } + StringBuilder bld = new StringBuilder(); + bld.Append("Enter: "); + AddMethodName(bld, method); + bld.Append('('); + for (int i=0; args != null && i < args.Length; i++) { + if (i != 0) { + bld.Append(", "); + } + bld.Append('{').Append(i).Append('}'); + } + bld.Append(')'); + // write out trace header + Trace.TraceInformation(bld.ToString(), args); + // invoke the method + try { + object ret = method.Invoke(_target, args); + // clear out buffer. + bld.Length = 0; + bld.Append("Return: "); + AddMethodName(bld, method); + bld.Append("({0})"); + Trace.TraceInformation(bld.ToString(), ret); + return ret; + } catch (TargetInvocationException e) { + Exception root = e.InnerException; + throw root; + } + } + + private void AddMethodName(StringBuilder bld, MethodInfo method) { + bld.Append(_op.RawType.Name); + bld.Append('.'); + bld.Append(method.Name); + } + } + #endregion + +} diff --git a/DotNetConnectors.sln/FrameworkInternal/ApiLocal.cs b/DotNetConnectors.sln/FrameworkInternal/ApiLocal.cs new file mode 100644 index 00000000..6f78e65e --- /dev/null +++ b/DotNetConnectors.sln/FrameworkInternal/ApiLocal.cs @@ -0,0 +1,1034 @@ +/* + * ==================== + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER. + * + * Copyright 2007-2008 Sun Microsystems, Inc. All rights reserved. + * + * The contents of this file are subject to the terms of the Common Development + * and Distribution License("CDDL") (the "License"). You may not use this file + * except in compliance with the License. + * + * You can obtain a copy of the License at + * http://IdentityConnectors.dev.java.net/legal/license.txt + * See the License for the specific language governing permissions and limitations + * under the License. + * + * When distributing the Covered Code, include this CDDL Header Notice in each file + * and include the License file at identityconnectors/legal/license.txt. + * If applicable, add the following below this CDDL Header, with the fields + * enclosed by brackets [] replaced by your own identifying information: + * "Portions Copyrighted [year] [name of copyright owner]" + * ==================== + */ +using System; +using System.Diagnostics; +using Org.IdentityConnectors.Common; +using Org.IdentityConnectors.Common.Pooling; +using Org.IdentityConnectors.Common.Proxy; +using Org.IdentityConnectors.Framework.Api; +using Org.IdentityConnectors.Framework.Api.Operations; +using Org.IdentityConnectors.Framework.Common; +using Org.IdentityConnectors.Framework.Common.Objects; +using Org.IdentityConnectors.Framework.Common.Exceptions; +using Org.IdentityConnectors.Framework.Common.Serializer; +using Org.IdentityConnectors.Framework.Impl.Api.Remote; +using Org.IdentityConnectors.Framework.Impl.Api.Local.Operations; +using Org.IdentityConnectors.Framework.Spi; +using Org.IdentityConnectors.Framework.Spi.Operations; +using System.Collections.Generic; +using System.Globalization; +using System.Reflection; +using System.Resources; +using System.Threading; +using System.IO; +using System.Linq; +namespace Org.IdentityConnectors.Framework.Impl.Api.Local +{ + #region ConnectorPoolManager + public class ConnectorPoolManager { + + + private class ConnectorPoolKey { + private readonly ConnectorKey _connectorKey; + private readonly ConfigurationPropertiesImpl _configProperties; + private readonly ObjectPoolConfiguration _poolingConfig; + public ConnectorPoolKey(ConnectorKey connectorKey, + ConfigurationPropertiesImpl configProperties, + ObjectPoolConfiguration poolingConfig) { + _connectorKey = connectorKey; + _configProperties = configProperties; + _poolingConfig = poolingConfig; + } + public override int GetHashCode() { + return _connectorKey.GetHashCode(); + } + public override bool Equals(Object o) { + if ( o is ConnectorPoolKey ) { + ConnectorPoolKey other = (ConnectorPoolKey)o; + if (!_connectorKey.Equals(other._connectorKey)) { + return false; + } + if (!_configProperties.Equals(other._configProperties)) { + return false; + } + if (!_poolingConfig.Equals(other._poolingConfig)) { + return false; + } + return true; + } + return false; + } + } + + private class ConnectorPoolHandler : ObjectPoolHandler { + private readonly APIConfigurationImpl _apiConfiguration; + private readonly LocalConnectorInfoImpl _localInfo; + public ConnectorPoolHandler(APIConfigurationImpl apiConfiguration, + LocalConnectorInfoImpl localInfo) { + _apiConfiguration = apiConfiguration; + _localInfo = localInfo; + } + public PoolableConnector NewObject() { + Configuration config = + CSharpClassProperties.CreateBean((ConfigurationPropertiesImpl)_apiConfiguration.ConfigurationProperties, + _localInfo.ConnectorConfigurationClass); + PoolableConnector connector = + (PoolableConnector)_localInfo.ConnectorClass.CreateInstance(); + connector.Init(config); + return connector; + } + public void TestObject(PoolableConnector obj) { + obj.CheckAlive(); + } + public void DisposeObject(PoolableConnector obj) { + obj.Dispose(); + } + } + + /** + * Cache of the various _pools.. + */ + private static readonly IDictionary> + _pools = new Dictionary>(); + + + /** + * Get a object pool for this connector if it supports connector pooling. + */ + public static ObjectPool GetPool(APIConfigurationImpl impl, + LocalConnectorInfoImpl localInfo) { + ObjectPool pool = null; + // determine if this connector wants generic connector pooling.. + if (impl.IsConnectorPoolingSupported) { + ConnectorPoolKey key = + new ConnectorPoolKey( + impl.ConnectorInfo.ConnectorKey, + (ConfigurationPropertiesImpl)impl.ConfigurationProperties, + impl.ConnectorPoolConfiguration); + + lock (_pools) { + // get the pool associated.. + pool = CollectionUtil.GetValue(_pools,key,null); + // create a new pool if it doesn't exist.. + if (pool == null) { + Trace.TraceInformation("Creating new pool: "+ + impl.ConnectorInfo.ConnectorKey); + // this instance is strictly used for the pool.. + pool = new ObjectPool( + new ConnectorPoolHandler(impl,localInfo), + impl.ConnectorPoolConfiguration); + // add back to the map of _pools.. + _pools[key] = pool; + } + } + } + return pool; + } + + public static void Dispose() { + lock (_pools) { + // close each pool.. + foreach (ObjectPool pool in _pools.Values) { + try { + pool.Shutdown(); + } catch (Exception e) { + TraceUtil.TraceException("Failed to close pool", e); + } + } + // clear the map of all _pools.. + _pools.Clear(); + } + } + + } + #endregion + + #region CSharpClassProperties + internal static class CSharpClassProperties + { + public static ConfigurationPropertiesImpl + CreateConfigurationProperties(Configuration defaultObject) + { + SafeType config = SafeType.Get(defaultObject); + ConfigurationPropertiesImpl properties = + new ConfigurationPropertiesImpl(); + IList temp = + new List(); + IDictionary descs = GetFilteredProperties(config); + + foreach (PropertyInfo desc in descs.Values) { + + String name = desc.Name; + + // get the configuration options.. + ConfigurationPropertyAttribute options = + GetPropertyOptions(desc); + // use the options to set internal properties.. + int order = 0; + String helpKey = name + ".help"; + String displKey = name + ".display"; + bool confidential = false; + bool required = false; + if (options != null) { + // determine the display and help keys.. + if (!StringUtil.IsBlank(options.HelpMessageKey)) { + helpKey = options.HelpMessageKey; + } + if (!StringUtil.IsBlank(options.DisplayMessageKey)) { + displKey = options.DisplayMessageKey; + } + // determine the order.. + order = options.Order; + required = options.Required; + confidential = options.Confidential; + } + Type type = desc.PropertyType; + if (!FrameworkUtil.IsSupportedConfigurationType(type)) { + const String MSG = "Property type ''{0}'' is not supported."; + throw new ArgumentException(String.Format(MSG, type)); + } + + Object value = desc.GetValue(defaultObject,null); + + ConfigurationPropertyImpl prop = new ConfigurationPropertyImpl(); + prop.IsConfidential=confidential; + prop.IsRequired=required; + prop.DisplayMessageKey=displKey; + prop.HelpMessageKey=helpKey; + prop.Name=name; + prop.Order=order; + prop.Value=value; + prop.ValueType=type; + prop.Operations = options == null ? null : TranslateOperations(options.Operations); + + temp.Add(prop); + + } + properties.Properties=(temp); + return properties; + } + + private static ICollection> TranslateOperations(SafeType [] ops) + { + ICollection> set = + new HashSet>(); + foreach (SafeType spi in ops) { + CollectionUtil.AddAll(set,FrameworkUtil.Spi2Apis(spi)); + } + return set; + } + + public static Configuration + CreateBean(ConfigurationPropertiesImpl properties, + SafeType config) { + Configuration rv = config.CreateInstance(); + rv.ConnectorMessages=properties.Parent.ConnectorInfo.Messages; + IDictionary descriptors = + GetFilteredProperties(config); + foreach (ConfigurationPropertyImpl property in properties.Properties) { + String name = property.Name; + PropertyInfo desc = + CollectionUtil.GetValue(descriptors,name,null); + if ( desc == null ) { + String FMT = + "Class ''{0}'' does not have a property ''{1}''."; + String MSG = String.Format(FMT, + config.RawType.Name, + name); + throw new ArgumentException(MSG); + } + object val = property.Value; + //some value types such as arrays + //are mutable. make sure the config object + //has its own copy + val = SerializerUtil.CloneObject(val); + desc.SetValue(rv,val,null); + } + return rv; + } + + private static IDictionary + GetFilteredProperties(SafeType config) + { + IDictionary rv = + new Dictionary(); + PropertyInfo [] descriptors = config.RawType.GetProperties(); + foreach (PropertyInfo descriptor in descriptors) { + String propName = descriptor.Name; + if ( !descriptor.CanWrite ) { + //if there's no setter, ignore it + continue; + } + if ("ConnectorMessages".Equals(propName)) { + continue; + } + if ( !descriptor.CanRead ) { + const String FMT = + "Found setter ''{0}'' but not the corresponding getter."; + String MSG = String.Format(FMT,propName); + throw new ArgumentException(MSG); + } + rv[propName] = descriptor; + } + return rv; + } + + + + /** + * Get the option from the property. + */ + private static ConfigurationPropertyAttribute GetPropertyOptions( + PropertyInfo propertyInfo) { + Object [] objs = + propertyInfo.GetCustomAttributes( + typeof(ConfigurationPropertyAttribute),true); + if ( objs.Length == 0 ) { + return null; + } + else { + return (ConfigurationPropertyAttribute)objs[0]; + } + } + + } + #endregion + + #region LocalConnectorInfoManagerImpl + internal class LocalConnectorInfoManagerImpl : ConnectorInfoManager + { + private IList _connectorInfo; + + public LocalConnectorInfoManagerImpl() + { + _connectorInfo = new List(); + Assembly assembly = Assembly.GetExecutingAssembly(); + FileInfo thisAssemblyFile = + new FileInfo(assembly.Location); + DirectoryInfo directory = + thisAssemblyFile.Directory; + FileInfo [] files = + directory.GetFiles("*.Connector.dll"); + foreach (FileInfo file in files) { + Assembly lib = + Assembly.LoadFrom(file.ToString()); + CollectionUtil.AddAll(_connectorInfo, + ProcessAssembly(lib)); + } + } + + private IList ProcessAssembly(Assembly assembly) { + IList rv = new List(); + + Type [] types = null; + try { + types = assembly.GetTypes(); + } + catch (Exception e) { + TraceUtil.TraceException("Unable to load assembly: "+assembly.FullName+". Assembly will be ignored.",e); + } + + foreach (Type type in types) { + Object [] attributes = type.GetCustomAttributes( + typeof(ConnectorClassAttribute), + false); + if ( attributes.Length > 0 ) { + ConnectorClassAttribute attribute = + (ConnectorClassAttribute)attributes[0]; + LocalConnectorInfoImpl info = + CreateConnectorInfo(assembly,type,attribute); + rv.Add(info); + } + } + return rv; + } + + private LocalConnectorInfoImpl CreateConnectorInfo(Assembly assembly, + Type rawConnectorClass, + ConnectorClassAttribute attribute) { + String fileName = assembly.Location; + if (!typeof(Connector).IsAssignableFrom(rawConnectorClass)) { + String MSG = ( "File "+fileName+ + " declares a connector "+rawConnectorClass+ + " that does not implement Connector."); + throw new ConfigurationException(MSG); + } + SafeType connectorClass = + SafeType.ForRawType(rawConnectorClass); + SafeType connectorConfigurationClass = attribute.ConnectorConfigurationType; + if ( connectorConfigurationClass == null ) { + String MSG = ( "File "+fileName+ + " contains a ConnectorInfo attribute "+ + "with no connector configuration class."); + throw new ConfigurationException(MSG); + } + String connectorDisplayNameKey = + attribute.ConnectorDisplayNameKey; + if ( connectorDisplayNameKey == null ) { + String MSG = ( "File "+fileName+ + " contains a ConnectorInfo attribute "+ + "with no connector display name."); + throw new ConfigurationException(MSG); + } + ConnectorKey key = + new ConnectorKey(assembly.GetName().Name, + assembly.GetName().Version.ToString(), + connectorClass.RawType.Namespace+"."+connectorClass.RawType.Name); + LocalConnectorInfoImpl rv = new LocalConnectorInfoImpl(); + rv.ConnectorClass = connectorClass; + rv.ConnectorConfigurationClass = connectorConfigurationClass; + rv.ConnectorDisplayNameKey = connectorDisplayNameKey; + rv.ConnectorKey = key; + rv.DefaultAPIConfiguration = CreateDefaultAPIConfiguration(rv); + rv.Messages = LoadMessages(assembly,rv,attribute.MessageCatalogPaths); + return rv; + } + + private APIConfigurationImpl + CreateDefaultAPIConfiguration(LocalConnectorInfoImpl localInfo) { + SafeType connectorClass = + localInfo.ConnectorClass; + APIConfigurationImpl rv = new APIConfigurationImpl(); + Configuration config = + localInfo.ConnectorConfigurationClass.CreateInstance(); + bool pooling = IsPoolingSupported(connectorClass); + rv.IsConnectorPoolingSupported=pooling; + rv.ConfigurationProperties=(CSharpClassProperties.CreateConfigurationProperties(config)); + rv.ConnectorInfo=(localInfo); + rv.SupportedOperations=(FrameworkUtil.GetDefaultSupportedOperations(connectorClass)); + return rv; + } + + private static bool IsPoolingSupported(SafeType clazz) { + return ReflectionUtil.IsParentTypeOf(typeof(PoolableConnector),clazz.RawType); + } + /// + /// Given an assembly, returns the list of cultures that + /// it is localized for + /// + /// + /// + private CultureInfo [] GetLocalizedCultures(Assembly assembly) { + FileInfo assemblyFile = + new FileInfo(assembly.Location); + DirectoryInfo directory = + assemblyFile.Directory; + IList temp = new List(); + DirectoryInfo [] subdirs = directory.GetDirectories(); + foreach (DirectoryInfo subdir in subdirs) { + String name = subdir.Name; + CultureInfo cultureInfo; + //get the culture if the directory is the name of the + //culture + try { + cultureInfo = new CultureInfo(name); + } + catch (ArgumentException) { + //invalid culture + continue; + } + //see if there's a satellite assembly for this + try { + assembly.GetSatelliteAssembly(cultureInfo); + } + catch (Exception) { + //invalid assembly + continue; + } + temp.Add(cultureInfo); + } + temp.Add(CultureInfo.InvariantCulture); + return temp.ToArray(); + } + + private ConnectorMessagesImpl LoadMessages(Assembly assembly, + LocalConnectorInfoImpl info, + String [] nameBases) { + if ( nameBases == null || nameBases.Length == 0 ) { + String pkage = + info.ConnectorClass.RawType.Namespace; + nameBases = new String[]{pkage+".Messages"}; + } + ConnectorMessagesImpl rv = new ConnectorMessagesImpl(); + CultureInfo [] cultures = GetLocalizedCultures(assembly); + for ( int i = nameBases.Length - 1; i >= 0; i-- ) { + String nameBase = nameBases[i]; + ResourceManager manager = new ResourceManager(nameBase,assembly); + foreach (CultureInfo culture in cultures) { + ResourceSet resourceSet = manager.GetResourceSet(culture,true,false); + if ( resourceSet != null ) { + IDictionary temp = + CollectionUtil.GetValue(rv.Catalogs,culture,null); + if ( temp == null ) { + temp = new Dictionary(); + rv.Catalogs[culture] = temp; + } + foreach (System.Collections.DictionaryEntry entry in resourceSet) { + String key = ""+entry.Key; + String val = ""+entry.Value; + temp[key] = val; + } + } + } + } + + return rv; + } + + public ConnectorInfo FindConnectorInfo(ConnectorKey key) { + foreach (ConnectorInfo info in _connectorInfo) { + if ( info.ConnectorKey.Equals(key) ) { + return info; + } + } + return null; + } + public IList ConnectorInfos { + get { + return CollectionUtil.AsReadOnlyList(_connectorInfo); + } + } + } + #endregion + + #region LocalConnectorInfoImpl + /// + /// Internal class, public only for unit tests + /// + public class LocalConnectorInfoImpl : AbstractConnectorInfo + { + public RemoteConnectorInfoImpl ToRemote() { + RemoteConnectorInfoImpl rv = new RemoteConnectorInfoImpl(); + rv.ConnectorDisplayNameKey=ConnectorDisplayNameKey; + rv.ConnectorKey=ConnectorKey; + rv.DefaultAPIConfiguration=DefaultAPIConfiguration; + rv.Messages=Messages; + return rv; + } + public SafeType ConnectorClass {get;set;} + public SafeType ConnectorConfigurationClass {get;set;} + } + #endregion + + #region LocalConnectorFacadeImpl + internal class LocalConnectorFacadeImpl : AbstractConnectorFacade { + + // ======================================================================= + // Constants + // ======================================================================= + /** + * Map the API interfaces to their implementation counterparts. + */ + private static readonly IDictionary,ConstructorInfo> API_TO_IMPL= + new Dictionary,ConstructorInfo>(); + + private static void AddImplementation(SafeType inter, + SafeType impl) { + ConstructorInfo info = + impl.RawType.GetConstructor(new Type[]{typeof(ConnectorOperationalContext), + typeof(Connector)}); + if ( info == null ) { + throw new ArgumentException(impl+" does not define the proper constructor"); + } + API_TO_IMPL[inter]= info; + } + + static LocalConnectorFacadeImpl() { + AddImplementation(SafeType.Get(), + SafeType.Get()); + AddImplementation(SafeType.Get(), + SafeType.Get()); + AddImplementation(SafeType.Get(), + SafeType.Get()); + AddImplementation(SafeType.Get(), + SafeType.Get()); + AddImplementation(SafeType.Get(), + SafeType.Get()); + AddImplementation(SafeType.Get(), + SafeType.Get()); + AddImplementation(SafeType.Get(), + SafeType.Get()); + AddImplementation(SafeType.Get(), + SafeType.Get()); + AddImplementation(SafeType.Get(), + SafeType.Get()); + AddImplementation(SafeType.Get(), + SafeType.Get()); + } + + + + // ======================================================================= + // Fields + // ======================================================================= + /** + * Pool used to acquire connection from to use during operations. + */ + + /** + * The connector info + */ + private readonly LocalConnectorInfoImpl connectorInfo; + + /** + * Builds up the maps of supported operations and calls. + */ + public LocalConnectorFacadeImpl(LocalConnectorInfoImpl connectorInfo, + APIConfigurationImpl apiConfiguration) + :base(apiConfiguration) { + this.connectorInfo = connectorInfo; + } + + // ======================================================================= + // ConnectorFacade Interface + // ======================================================================= + + protected override APIOperation GetOperationImplementation(SafeType api) { + APIOperation proxy; + //first create the inner proxy - this is the proxy that obtaining + //a connection from the pool, etc + //NOTE: we want to skip this part of the proxy for + //validate op, but we will want the timeout proxy + if ( api.RawType.Equals(typeof(ValidateApiOp))) { + OperationalContext context = + new OperationalContext(connectorInfo,GetAPIConfiguration()); + proxy = new ValidateImpl(context); + } + else if ( api.RawType.Equals( typeof(GetApiOp) ) ) { + ConstructorInfo constructor = + API_TO_IMPL[SafeType.Get()]; + ConnectorOperationalContext context = + new ConnectorOperationalContext(connectorInfo, + GetAPIConfiguration(), + GetPool()); + + ConnectorAPIOperationRunnerProxy handler = + new ConnectorAPIOperationRunnerProxy(context,constructor); + proxy = + new GetImpl((SearchApiOp)NewAPIOperationProxy(SafeType.Get(),handler)); + } + else { + ConstructorInfo constructor = + API_TO_IMPL[api]; + ConnectorOperationalContext context = + new ConnectorOperationalContext(connectorInfo, + GetAPIConfiguration(), + GetPool()); + + ConnectorAPIOperationRunnerProxy handler = + new ConnectorAPIOperationRunnerProxy(context,constructor); + proxy = + NewAPIOperationProxy(api,handler); + } + + //TODO: timeout + + // add logging proxy.. + proxy = CreateLoggingProxy(api, proxy); + return proxy; + } + private ObjectPool GetPool() + { + return ConnectorPoolManager.GetPool(GetAPIConfiguration(),connectorInfo); + } + } + #endregion + + #region ObjectPool + public class ObjectPool where T : class { + + /** + * Statistics bean + */ + public sealed class Statistics { + private readonly int _numIdle; + private readonly int _numActive; + + internal Statistics(int numIdle, int numActive) { + _numIdle = numIdle; + _numActive = numActive; + } + + /** + * Returns the number of idle objects + */ + public int NumIdle { + get { + return _numIdle; + } + } + + /** + * Returns the number of active objects + */ + public int NumActive { + get { + return _numActive; + } + } + } + + /** + * An object plus additional book-keeping + * information about the object + */ + private class PooledObject where T2 : class { + /** + * The underlying object + */ + private readonly T2 _object; + + /** + * True if this is currently active, false if + * it is idle + */ + private bool _isActive; + + /** + * Last state change (change from active to + * idle or vice-versa) + */ + private long _lastStateChangeTimestamp; + + /** + * Is this a freshly created object (never been pooled)? + */ + private bool _isNew; + + public PooledObject(T2 obj) { + _object = obj; + _isNew = true; + Touch(); + } + + public T2 Object { + get { + return _object; + } + } + + public bool IsActive { + get { + return _isActive; + } + set { + if (_isActive != value) { + Touch(); + _isActive = value; + } + } + } + + public bool IsNew { + get { + return _isNew; + } + set { + _isNew = value; + } + } + + + private void Touch() { + _lastStateChangeTimestamp = DateTimeUtil.GetCurrentUtcTimeMillis(); + } + + public long LastStateChangeTimestamp { + get { + return _lastStateChangeTimestamp; + } + } + } + + /** + * The lock object we use for everything + */ + private readonly Object LOCK = new Object(); + + /** + * Map from the object to the + * PooledObject (use IdentityHashMap so it's + * always object equality) + */ + private readonly IDictionary> + _activeObjects = CollectionUtil.NewIdentityDictionary>(); + + /** + * Queue of idle objects. The one that has + * been idle for the longest comes first in the queue + */ + private readonly LinkedList> + _idleObjects = new LinkedList>(); + + /** + * ObjectPoolHandler we use for managing object lifecycle + */ + private readonly ObjectPoolHandler _handler; + + /** + * Configuration for this pool. + */ + private readonly ObjectPoolConfiguration _config; + + /** + * Is the pool shutdown + */ + private bool _isShutdown; + + /** + * Create a new ObjectPool + * @param handler Handler for objects + * @param config Configuration for the pool + */ + public ObjectPool(ObjectPoolHandler handler, + ObjectPoolConfiguration config) { + + Assertions.NullCheck(handler, "handler"); + Assertions.NullCheck(config, "config"); + + _handler = handler; + //clone it + _config = + (ObjectPoolConfiguration)SerializerUtil.CloneObject(config); + //validate it + _config.Validate(); + + } + + /** + * Return an object to the pool + * @param object + */ + public void ReturnObject(T obj) { + Assertions.NullCheck(obj, "object"); + lock (LOCK) { + //remove it from the active list + PooledObject pooled = + CollectionUtil.GetValue(_activeObjects,obj,null); + + //they are attempting to return something + //we haven't allocated (or that they've + //already returned) + if ( pooled == null ) { + throw new InvalidOperationException("Attempt to return an object not in the pool: "+obj); + } + _activeObjects.Remove(obj); + + //set it to idle and add to idle list + //(this might get evicted right away + //by evictIdleObjects if we're over the + //limit or if we're shutdown) + pooled.IsActive=(false); + pooled.IsNew=(false); + _idleObjects.AddLast(pooled); + + //finally evict idle objects + EvictIdleObjects(); + + //wake anyone up who was waiting on a object + Monitor.PulseAll(LOCK); + } + } + + /** + * Borrow an object from the pool. + * @return An object + */ + public T BorrowObject() { + while ( true ) { + PooledObject rv = BorrowObjectNoTest(); + try { + //make sure we are testing it outside + //of synchronization. otherwise this + //can create an IO bottleneck + _handler.TestObject(rv.Object); + return rv.Object; + } + catch (Exception e) { + //it's bad - remove from active objects + lock (LOCK) { + _activeObjects.Remove(rv.Object); + } + DisposeNoException(rv.Object); + //if it's a new object, break out of the loop + //immediately + if ( rv.IsNew ) { + throw e; + } + } + } + } + + /** + * Borrow an object from the pool, but don't test + * it (it gets tested by the caller *outside* of + * synchronization) + * @return the object + */ + private PooledObject BorrowObjectNoTest() { + //time when the call began + long startTime = DateTimeUtil.GetCurrentUtcTimeMillis(); + + lock (LOCK) { + EvictIdleObjects(); + while ( true ) { + if (_isShutdown) { + throw new InvalidOperationException("Object pool already shutdown"); + } + + PooledObject pooledConn = null; + + //first try to recycle an idle object + if (_idleObjects.Count > 0) { + pooledConn = _idleObjects.First(); + _idleObjects.RemoveFirst(); + } + //otherwise, allocate a new object if + //below the limit + else if (_activeObjects.Count < _config.MaxObjects) { + pooledConn = + new PooledObject(_handler.NewObject()); + } + + //if there's an object available, return it + //and break out of the loop + if ( pooledConn != null ) { + pooledConn.IsActive=(true); + _activeObjects[pooledConn.Object] = + pooledConn; + return pooledConn; + } + + //see if we haven't timed-out yet + long elapsed = + DateTimeUtil.GetCurrentUtcTimeMillis() - startTime; + long remaining = _config.MaxWait - elapsed; + + //wait if we haven't timed out + if (remaining > 0) { + Monitor.Wait(LOCK,(int)remaining); + } + else { + //otherwise throw + throw new ConnectorException("Max objects exceeded"); + } + } + } + } + + /** + * Closes any idle objects in the pool. + * Existing active objects will remain alive and + * be allowed to shutdown gracefully, but no more + * objects will be allocated. + */ + public void Shutdown() { + lock(LOCK) { + _isShutdown = true; + //just evict idle objects + //if there are any active objects still + //going, leave them alone so they can return + //gracefully + EvictIdleObjects(); + //wake anyone up who was waiting on an object + Monitor.PulseAll(LOCK); + } + } + + /** + * Gets a snapshot of the pool's stats at a point in time. + * @return The statistics + */ + public Statistics GetStatistics() { + lock(LOCK) { + return new Statistics(_idleObjects.Count, + _activeObjects.Count); + } + } + + /** + * Evicts idle objects as needed (evicts + * all idle objects if we're shutdown) + */ + private void EvictIdleObjects() { + while (TooManyIdleObjects()) { + PooledObject conn = _idleObjects.First(); + _idleObjects.RemoveFirst(); + DisposeNoException(conn.Object); + } + } + + /** + * Returns true if any of the following are true: + *
    + *
  1. We're shutdown and there are idle objects
  2. + *
  3. Max idle objects exceeded
  4. + *
  5. Min idle objects exceeded and there are old objects
  6. + *
+ */ + private bool TooManyIdleObjects() { + + if (_isShutdown && _idleObjects.Count > 0) { + return true; + } + + if (_config.MaxIdle < _idleObjects.Count) { + return true; + } + if (_config.MinIdle >= _idleObjects.Count) { + return false; + } + + PooledObject oldest = + _idleObjects.First(); + + long age = + ( DateTimeUtil.GetCurrentUtcTimeMillis()-oldest.LastStateChangeTimestamp ); + + + return age > _config.MinEvictableIdleTimeMillis; + } + + /** + * Dispose of an object, but don't throw any exceptions + * @param object + */ + private void DisposeNoException(T obj) { + try { + _handler.DisposeObject(obj); + } + catch (Exception e) { + TraceUtil.TraceException("disposeObject() is not supposed to throw",e); + } + } + } + #endregion + + #region ObjectPoolHandler + public interface ObjectPoolHandler where T : class { + T NewObject(); + void TestObject(T obj); + void DisposeObject(T obj); + } + #endregion +} diff --git a/DotNetConnectors.sln/FrameworkInternal/ApiLocalOperations.cs b/DotNetConnectors.sln/FrameworkInternal/ApiLocalOperations.cs new file mode 100644 index 00000000..6ed4c594 --- /dev/null +++ b/DotNetConnectors.sln/FrameworkInternal/ApiLocalOperations.cs @@ -0,0 +1,1416 @@ +/* + * ==================== + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER. + * + * Copyright 2007-2008 Sun Microsystems, Inc. All rights reserved. + * + * The contents of this file are subject to the terms of the Common Development + * and Distribution License("CDDL") (the "License"). You may not use this file + * except in compliance with the License. + * + * You can obtain a copy of the License at + * http://IdentityConnectors.dev.java.net/legal/license.txt + * See the License for the specific language governing permissions and limitations + * under the License. + * + * When distributing the Covered Code, include this CDDL Header Notice in each file + * and include the License file at identityconnectors/legal/license.txt. + * If applicable, add the following below this CDDL Header, with the fields + * enclosed by brackets [] replaced by your own identifying information: + * "Portions Copyrighted [year] [name of copyright owner]" + * ==================== + */ +using System; + +using Org.IdentityConnectors.Common; +using Org.IdentityConnectors.Common.Pooling; +using Org.IdentityConnectors.Common.Proxy; +using Org.IdentityConnectors.Common.Script; +using Org.IdentityConnectors.Common.Security; +using Org.IdentityConnectors.Framework.Api.Operations; +using Org.IdentityConnectors.Framework.Common.Objects; +using Org.IdentityConnectors.Framework.Common.Objects.Filters; +using Org.IdentityConnectors.Framework.Common.Exceptions; +using Org.IdentityConnectors.Framework.Common.Serializer; +using Org.IdentityConnectors.Framework.Spi; +using Org.IdentityConnectors.Framework.Spi.Operations; +using System.Reflection; +using System.Collections.Generic; +using System.Linq; + +namespace Org.IdentityConnectors.Framework.Impl.Api.Local.Operations +{ + #region APIOperationRunner + /** + * NOTE: internal class, public only for unit tests + * Base class for API operation runners. + */ + public abstract class APIOperationRunner { + + /** + * Context that has all the information required to execute an operation. + */ + private readonly OperationalContext _context; + + + /** + * Creates the API operation so it can called multiple times. + */ + public APIOperationRunner(OperationalContext context) { + _context = context; + //TODO: verify this + // get the APIOperation that this class implements.. + //List> apiOps = getInterfaces(this + //.getClass(), APIOperation.class); + // there should be only one.. + //if (apiOps.size() > 1) { + // final String MSG = "Must only implement one operation."; + // throw new IllegalStateException(MSG); + //} + } + + /** + * Get the current operational context. + */ + public OperationalContext GetOperationalContext() { + return _context; + } + + } + #endregion + + #region ConnectorAPIOperationRunner + /** + * NOTE: internal class, public only for unit tests + * Subclass of APIOperationRunner for operations that require a connector. + */ + public abstract class ConnectorAPIOperationRunner : APIOperationRunner { + + /** + * The connector instance + */ + private readonly Connector _connector; + + /** + * Creates the API operation so it can called multiple times. + */ + public ConnectorAPIOperationRunner(ConnectorOperationalContext context, + Connector connector) : base(context) { + _connector = connector; + } + + public Connector GetConnector() { + return _connector; + } + + public ObjectNormalizerFacade GetNormalizer(ObjectClass oclass) { + AttributeNormalizer norm = null; + Connector connector = GetConnector(); + if ( connector is AttributeNormalizer ) { + norm = (AttributeNormalizer)connector; + } + return new ObjectNormalizerFacade(oclass,norm); + } + } + #endregion + + #region ConnectorAPIOperationRunnerProxy + /** + * Proxy for APIOperationRunner that takes care of setting up underlying + * connector and creating the implementation of APIOperationRunner. + * The implementation of APIOperationRunner gets created whenever the + * actual method is invoked. + */ + internal class ConnectorAPIOperationRunnerProxy : InvocationHandler { + + + /** + * The operational context + */ + private readonly ConnectorOperationalContext _context; + + /** + * The implementation constructor. The instance is lazily created upon + * invocation + */ + private readonly ConstructorInfo _runnerImplConstructor; + + /** + * Create an APIOperationRunnerProxy + * @param context The operational context + * @param runnerImplConstructor The implementation constructor. Implementation + * must define a two-argument constructor(OperationalContext,Connector) + */ + public ConnectorAPIOperationRunnerProxy(ConnectorOperationalContext context, + ConstructorInfo runnerImplConstructor) { + _context = context; + _runnerImplConstructor = runnerImplConstructor; + } + + public Object Invoke(Object proxy, MethodInfo method, object[] args) + { + //do not proxy equals, hashCode, toString + if (method.DeclaringType.Equals(typeof(object))) { + return method.Invoke(this, args); + } + object ret = null; + Connector connector = null; + ObjectPool pool = _context.GetPool(); + // get the connector class.. + SafeType connectorClazz = _context.GetConnectorClass(); + try { + // pooling is implemented get one.. + if (pool != null) { + connector = pool.BorrowObject(); + } + else { + // get a new instance of the connector.. + connector = connectorClazz.CreateInstance(); + // initialize the connector.. + connector.Init(_context.GetConfiguration()); + } + APIOperationRunner runner = + (APIOperationRunner)_runnerImplConstructor.Invoke(new object[]{ + _context, + connector}); + ret = method.Invoke(runner, args); + // call out to the operation.. + } catch (TargetInvocationException e) { + Exception root = e.InnerException; + throw root; + } finally { + // make sure dispose of the connector properly + if (connector != null) { + // determine if there was a pool.. + if (pool != null) { + try { + //try to return it to the pool even though an + //exception may have happened that leaves it in + //a bad state. The contract of checkAlive + //is that it will tell you if the connector is + //still valid and so we leave it up to the pool + //and connector to work it out. + pool.ReturnObject((PoolableConnector)connector); + } catch (Exception e) { + //don't let pool exceptions propogate or mask + //other exceptions. do log it though. + TraceUtil.TraceException(null,e); + } + } + //not pooled - just dispose + else { + //dispose it not supposed to throw, but just in case, + //catch the exception and log it so we know about it + //but don't let the exception prevent additional + //cleanup that needs to happen + try { + connector.Dispose(); + } catch (Exception e) { + //log this though + TraceUtil.TraceException(null,e); + } + } + } + } + return ret; + } + } + #endregion + + #region ConnectorOperationalContext + /** + * NOTE: internal class, public only for unit tests + * Simple structure to pass more variables through the constructor of + * {@link APIOperationRunner}. + */ + public class ConnectorOperationalContext : OperationalContext { + + private readonly ObjectPool _pool; + + public ConnectorOperationalContext(LocalConnectorInfoImpl connectorInfo, + APIConfigurationImpl apiConfiguration, + ObjectPool pool) + :base(connectorInfo,apiConfiguration) { + _pool = pool; + } + + public ObjectPool GetPool() { + return _pool; + } + + + public SafeType GetConnectorClass() { + return GetConnectorInfo().ConnectorClass; + } + + } + #endregion + + #region AuthenticationImpl + internal class AuthenticationImpl : ConnectorAPIOperationRunner, + AuthenticationApiOp { + /** + * Pass the configuration etc to the abstract class. + */ + public AuthenticationImpl(ConnectorOperationalContext context, + Connector connector) + :base(context,connector) { + } + + /** + * Authenticate using the basic credentials. + * + * @see Authentication#authenticate(String, String) + */ + public Uid Authenticate(ObjectClass objectClass, String username, GuardedString password, OperationOptions options) { + Assertions.NullCheck(objectClass,"objectClass"); + Assertions.NullCheck(username, "username"); + Assertions.NullCheck(password, "password"); + //convert null into empty + if ( options == null ) { + options = new OperationOptionsBuilder().Build(); + } + return ((AuthenticateOp) GetConnector()).Authenticate(objectClass,username, password,options); + } + } + #endregion + + #region CreateImpl + internal class CreateImpl : ConnectorAPIOperationRunner, + CreateApiOp { + + /** + * Initializes the operation works. + */ + public CreateImpl(ConnectorOperationalContext context, + Connector connector) + :base(context,connector) { + } + + /** + * Calls the create method on the Connector side. + * + * @see CreateApiOp#create(Set) + */ + public Uid Create(ObjectClass oclass, ICollection attributes, OperationOptions options) { + Assertions.NullCheck(oclass, "oclass"); + Assertions.NullCheck(attributes, "attributes"); + //convert null into empty + if ( options == null ) { + options = new OperationOptionsBuilder().Build(); + } + HashSet dups = new HashSet(); + foreach (ConnectorAttribute attr in attributes) { + if (dups.Contains(attr.Name)) { + throw new ArgumentException("Duplicate attribute name exists: " + attr.Name); + } + dups.Add(attr.Name); + } + if (oclass == null) { + throw new ArgumentException("Required attribute ObjectClass not found!"); + } + Connector connector = GetConnector(); + ObjectNormalizerFacade normalizer = GetNormalizer(oclass); + ICollection normalizedAttributes = + normalizer.NormalizeAttributes(attributes); + // create the object.. + Uid ret = ((CreateOp) connector).Create(oclass,attributes,options); + return (Uid)normalizer.NormalizeAttribute(ret); + } + } + #endregion + + #region DeleteImpl + internal class DeleteImpl : ConnectorAPIOperationRunner , + DeleteApiOp { + + /** + * Initializes the operation works. + */ + public DeleteImpl(ConnectorOperationalContext context, + Connector connector) + :base(context,connector) { + } + /** + * Calls the delete method on the Connector side. + * + * @see com.sun.openconnectors.framework.api.operations.CreateApiOp#create(java.util.Set) + */ + public void Delete(ObjectClass objClass, Uid uid, OperationOptions options) { + Assertions.NullCheck(objClass, "objClass"); + Assertions.NullCheck(uid, "uid"); + //convert null into empty + if ( options == null ) { + options = new OperationOptionsBuilder().Build(); + } + Connector connector = GetConnector(); + ObjectNormalizerFacade normalizer = GetNormalizer(objClass); + // delete the object.. + ((DeleteOp) connector).Delete(objClass, + (Uid)normalizer.NormalizeAttribute(uid), + options); + } + } + #endregion + + #region AttributesToGetResultsHandler + public abstract class AttributesToGetResultsHandler { + // ======================================================================= + // Fields + // ======================================================================= + private readonly string[] _attrsToGet; + + // ======================================================================= + // Constructors + // ======================================================================= + /** + * Keep the attribute to get.. + */ + public AttributesToGetResultsHandler(string[] attrsToGet) { + Assertions.NullCheck(attrsToGet, "attrsToGet"); + _attrsToGet = attrsToGet; + } + + /** + * Simple method that clones the object and remove the attribute thats are + * not in the {@link OperationOptions#OP_ATTRIBUTES_TO_GET} set. + * + * @param attrsToGet + * case insensitive set of attribute names. + */ + public ICollection ReduceToAttrsToGet( + ICollection attrs) { + ICollection ret = new HashSet(); + IDictionary map = ConnectorAttributeUtil.ToMap(attrs); + foreach (string attrName in _attrsToGet) { + ConnectorAttribute attr = CollectionUtil.GetValue(map, attrName, null); + // TODO: Should we throw if the attribute is not yet it was + // requested?? Or do we ignore because the API maybe asking + // for what the resource doesn't have?? + if (attr != null) { + ret.Add(attr); + } + } + return ret; + } + public ConnectorObject ReduceToAttrsToGet(ConnectorObject obj) { + // clone the object and reduce the attributes only the set of + // attributes. + ConnectorObjectBuilder bld = new ConnectorObjectBuilder(); + bld.SetUid(obj.Uid); + bld.SetName(obj.Name); + bld.ObjectClass = obj.ObjectClass; + ICollection objAttrs = obj.GetAttributes(); + ICollection attrs = ReduceToAttrsToGet(objAttrs); + bld.AddAttributes(attrs); + return bld.Build(); + } + } + #endregion + + #region SearchAttributesToGetResultsHandler + public sealed class SearchAttributesToGetResultsHandler : + AttributesToGetResultsHandler { + + // ======================================================================= + // Fields + // ======================================================================= + private readonly ResultsHandler _handler; + + // ======================================================================= + // Constructors + // ======================================================================= + public SearchAttributesToGetResultsHandler( + ResultsHandler handler, string[] attrsToGet) : base(attrsToGet) { + Assertions.NullCheck(handler, "handler"); + this._handler = handler; + } + + public bool Handle(ConnectorObject obj) { + // clone the object and reduce the attributes only the set of + // attributes. + return _handler(ReduceToAttrsToGet(obj)); + } + } + #endregion + + #region SearchAttributesToGetResultsHandler + public sealed class SyncAttributesToGetResultsHandler : + AttributesToGetResultsHandler { + + // ======================================================================= + // Fields + // ======================================================================= + private readonly SyncResultsHandler _handler; + + // ======================================================================= + // Constructors + // ======================================================================= + public SyncAttributesToGetResultsHandler( + SyncResultsHandler handler, string[] attrsToGet) : base(attrsToGet) { + Assertions.NullCheck(handler, "handler"); + this._handler = handler; + } + + public bool Handle(SyncDelta delta) { + SyncDeltaBuilder bld = new SyncDeltaBuilder(); + bld.Uid = delta.Uid; + bld.Token = delta.Token; + bld.DeltaType = delta.DeltaType; + if ( delta.Object != null ) { + bld.Object=ReduceToAttrsToGet(delta.Object); + } + return _handler(bld.Build()); + } + } + #endregion + + #region DuplicateFilteringResultsHandler + public sealed class DuplicateFilteringResultsHandler { + + // ======================================================================= + // Fields + // ======================================================================= + private readonly ResultsHandler _handler; + private readonly HashSet _visitedUIDs = new HashSet(); + + private bool _stillHandling; + + // ======================================================================= + // Constructors + // ======================================================================= + /** + * Filter chain for producers. + * + * @param producer + * Producer to filter. + * + */ + public DuplicateFilteringResultsHandler(ResultsHandler handler) { + // there must be a producer.. + if (handler == null) { + throw new ArgumentException("Handler must not be null!"); + } + this._handler = handler; + } + + public bool Handle(ConnectorObject obj) { + String uid = + obj.Uid.GetUidValue(); + if (!_visitedUIDs.Add(uid)) { + //we've already seen this - don't pass it + //throw + return true; + } + _stillHandling = _handler(obj); + return _stillHandling; + } + + public bool IsStillHandling { + get { + return _stillHandling; + } + } + + } + #endregion + + #region FilteredResultsHandler + public sealed class FilteredResultsHandler { + + // ======================================================================= + // Fields + // ======================================================================= + readonly ResultsHandler handler; + readonly Filter filter; + + // ======================================================================= + // Constructors + // ======================================================================= + /** + * Filter chain for producers. + * + * @param producer + * Producer to filter. + * @param filter + * Filter to use to accept objects. + */ + public FilteredResultsHandler(ResultsHandler handler, Filter filter) { + // there must be a producer.. + if (handler == null) { + throw new ArgumentException("Producer must not be null!"); + } + this.handler = handler; + // use a default pass through filter.. + this.filter = filter == null ? new PassThruFilter() : filter; + } + + public bool Handle(ConnectorObject obj) { + if ( filter.Accept(obj) ) { + return handler(obj); + } + else { + return true; + } + } + + /** + * Use a pass thru filter to use if a null filter is provided. + */ + class PassThruFilter : Filter { + public bool Accept(ConnectorObject obj) { + return true; + } + } + + } + #endregion + + #region GetImpl + /** + * Uses {@link SearchOp} to find the object that is referenced by the + * {@link Uid} provided. + */ + public class GetImpl : GetApiOp { + + readonly SearchApiOp op; + + private class ResultAdapter { + private IList _list = new List(); + public bool Handle(ConnectorObject obj) { + _list.Add(obj); + return false; + } + public ConnectorObject GetResult() { + return _list.Count == 0 ? null : _list[0]; + } + } + + public GetImpl(SearchApiOp search) { + this.op = search; + } + + public ConnectorObject GetObject(ObjectClass objClass, Uid uid, OperationOptions options) { + Assertions.NullCheck(objClass, "objClass"); + Assertions.NullCheck(uid, "uid"); + //convert null into empty + if ( options == null ) { + options = new OperationOptionsBuilder().Build(); + } + Filter filter = FilterBuilder.EqualTo(uid); + ResultAdapter adapter = new ResultAdapter(); + op.Search(objClass,filter,new ResultsHandler(adapter.Handle),options); + return adapter.GetResult(); + } + } + #endregion + + #region OperationalContext + /** + * NOTE: internal class, public only for unit tests + * OperationalContext - base class for operations that do not + * require a connection. + */ + public class OperationalContext { + + /** + * ConnectorInfo + */ + private readonly LocalConnectorInfoImpl connectorInfo; + + /** + * Contains the {@link Connector} {@link Configuration}. + */ + private readonly APIConfigurationImpl apiConfiguration; + + + public OperationalContext(LocalConnectorInfoImpl connectorInfo, + APIConfigurationImpl apiConfiguration) { + this.connectorInfo = connectorInfo; + this.apiConfiguration = apiConfiguration; + } + + public Configuration GetConfiguration() { + return CSharpClassProperties.CreateBean((ConfigurationPropertiesImpl)this.apiConfiguration.ConfigurationProperties, + connectorInfo.ConnectorConfigurationClass); + } + + protected LocalConnectorInfoImpl GetConnectorInfo() { + return connectorInfo; + } + } + #endregion + + #region NormalizingResultsHandler + public class NormalizingResultsHandler { + + private readonly ResultsHandler _target; + private readonly ObjectNormalizerFacade _normalizer; + + public NormalizingResultsHandler(ResultsHandler target, + ObjectNormalizerFacade normalizer) { + Assertions.NullCheck(target, "target"); + Assertions.NullCheck(normalizer, "normalizer"); + _target = target; + _normalizer = normalizer; + } + + + public bool Handle(ConnectorObject obj) { + ConnectorObject normalized = _normalizer.NormalizeObject(obj); + return _target(normalized); + } + + } + #endregion + + #region NormalizingSyncResultsHandler + public class NormalizingSyncResultsHandler { + + private readonly SyncResultsHandler _target; + private readonly ObjectNormalizerFacade _normalizer; + + public NormalizingSyncResultsHandler(SyncResultsHandler target, + ObjectNormalizerFacade normalizer) { + Assertions.NullCheck(target, "target"); + Assertions.NullCheck(normalizer, "normalizer"); + _target = target; + _normalizer = normalizer; + } + + + public bool Handle(SyncDelta delta) { + SyncDelta normalized = _normalizer.NormalizeSyncDelta(delta); + return _target(normalized); + } + + } + #endregion + + #region ObjectNormalizerFacade + public sealed class ObjectNormalizerFacade { + /** + * The (non-null) object class + */ + private readonly ObjectClass _objectClass; + /** + * The (possibly null) attribute normalizer + */ + private readonly AttributeNormalizer _normalizer; + + /** + * Create a new ObjectNormalizer + * @param objectClass The object class + * @param normalizer The normalizer. May be null. + */ + public ObjectNormalizerFacade(ObjectClass objectClass, + AttributeNormalizer normalizer) { + Assertions.NullCheck(objectClass, "objectClass"); + _objectClass = objectClass; + _normalizer = normalizer; + } + + /** + * Returns the normalized value of the attribute. + * If no normalizer is specified, returns the original + * attribute. + * @param attribute The attribute to normalize. + * @return The normalized attribute + */ + public ConnectorAttribute NormalizeAttribute(ConnectorAttribute attribute) { + if ( attribute == null ) { + return null; + } + else if (_normalizer != null) { + return _normalizer.NormalizeAttribute(_objectClass, attribute); + } + else { + return attribute; + } + } + + /** + * Returns the normalized set of attributes or null + * if the original set is null. + * @param attributes The original attributes. + * @return The normalized attributes or null if + * the original set is null. + */ + public ICollection NormalizeAttributes(ICollection attributes) { + if ( attributes == null ) { + return null; + } + ICollection temp = new HashSet(); + foreach (ConnectorAttribute attribute in attributes ) { + temp.Add(NormalizeAttribute(attribute)); + } + return CollectionUtil.AsReadOnlySet(temp); + } + + /** + * Returns the normalized object. + * @param orig The original object + * @return The normalized object. + */ + public ConnectorObject NormalizeObject(ConnectorObject orig) { + return new ConnectorObject(orig.ObjectClass, + NormalizeAttributes(orig.GetAttributes())); + } + + /** + * Returns the normalized sync delta. + * @param delta The original delta. + * @return The normalized delta. + */ + public SyncDelta NormalizeSyncDelta(SyncDelta delta) { + SyncDeltaBuilder builder = new + SyncDeltaBuilder(delta); + if ( delta.Object != null ) { + builder.Object=NormalizeObject(delta.Object); + } + return builder.Build(); + } + + /** + * Returns a filter consisting of the original with + * all attributes normalized. + * @param filter The original. + * @return The normalized filter. + */ + public Filter NormalizeFilter(Filter filter) { + if ( filter is ContainsFilter ) { + AttributeFilter afilter = + (AttributeFilter)filter; + return new ContainsFilter(NormalizeAttribute(afilter.GetAttribute())); + } + else if (filter is EndsWithFilter) { + AttributeFilter afilter = + (AttributeFilter)filter; + return new EndsWithFilter(NormalizeAttribute(afilter.GetAttribute())); + } + else if ( filter is EqualsFilter ) { + AttributeFilter afilter = + (AttributeFilter)filter; + return new EqualsFilter(NormalizeAttribute(afilter.GetAttribute())); + } + else if ( filter is GreaterThanFilter ) { + AttributeFilter afilter = + (AttributeFilter)filter; + return new GreaterThanFilter(NormalizeAttribute(afilter.GetAttribute())); + } + else if ( filter is GreaterThanOrEqualFilter ) { + AttributeFilter afilter = + (AttributeFilter)filter; + return new GreaterThanOrEqualFilter(NormalizeAttribute(afilter.GetAttribute())); + } + else if ( filter is LessThanFilter ) { + AttributeFilter afilter = + (AttributeFilter)filter; + return new LessThanFilter(NormalizeAttribute(afilter.GetAttribute())); + } + else if ( filter is LessThanOrEqualFilter ) { + AttributeFilter afilter = + (AttributeFilter)filter; + return new LessThanOrEqualFilter(NormalizeAttribute(afilter.GetAttribute())); + } + else if (filter is StartsWithFilter) { + AttributeFilter afilter = + (AttributeFilter)filter; + return new StartsWithFilter(NormalizeAttribute(afilter.GetAttribute())); + } + else if (filter is ContainsAllValuesFilter) { + AttributeFilter afilter = + (AttributeFilter)filter; + return new ContainsAllValuesFilter(NormalizeAttribute(afilter.GetAttribute())); + } + else if ( filter is NotFilter ) { + NotFilter notFilter = + (NotFilter)filter; + return new NotFilter(NormalizeFilter(notFilter.Filter)); + } + else if ( filter is AndFilter ) { + AndFilter andFilter = + (AndFilter)filter; + return new AndFilter(NormalizeFilter(andFilter.Left), + NormalizeFilter(andFilter.Right)); + } + else if ( filter is OrFilter ) { + OrFilter orFilter = + (OrFilter)filter; + return new OrFilter(NormalizeFilter(orFilter.Left), + NormalizeFilter(orFilter.Right)); + } + else { + return filter; + } + } + + + } + #endregion + + #region SchemaImpl + internal class SchemaImpl : ConnectorAPIOperationRunner , SchemaApiOp { + /** + * Initializes the operation works. + */ + public SchemaImpl(ConnectorOperationalContext context, + Connector connector) + :base(context,connector) { + } + + /** + * Retrieve the schema from the {@link Connector}. + * + * @see com.sun.openconnectors.framework.api.operations.SchemaApiOp#schema() + */ + public Schema Schema() { + return ((SchemaOp)GetConnector()).Schema(); + } + } + #endregion + + #region ScriptOnConnectorImpl + public class ScriptOnConnectorImpl : ConnectorAPIOperationRunner, + ScriptOnConnectorApiOp { + + public ScriptOnConnectorImpl(ConnectorOperationalContext context, + Connector connector) : + base(context,connector) { + } + + public Object RunScriptOnConnector(ScriptContext request, + OperationOptions options) { + Assertions.NullCheck(request, "request"); + //convert null into empty + if ( options == null ) { + options = new OperationOptionsBuilder().Build(); + } + Object rv; + if ( GetConnector() is ScriptOnConnectorOp ) { + rv = ((ScriptOnConnectorOp)GetConnector()).RunScriptOnConnector(request, options); + } + else { + String language = request.ScriptLanguage; + Assembly assembly = GetConnector().GetType().Assembly; + + ScriptExecutor executor = + ScriptExecutorFactory.NewInstance(language).NewScriptExecutor( + BuildReferenceList(assembly), + request.ScriptText, + false); + IDictionary scriptArgs = + new Dictionary(request.ScriptArguments); + scriptArgs["connector"]=GetConnector(); //add the connector instance itself + rv = executor.Execute(scriptArgs); + } + return SerializerUtil.CloneObject(rv); + } + + private Assembly [] BuildReferenceList(Assembly assembly) + { + List list = new List(); + BuildReferenceList2(assembly,list,new HashSet()); + return list.ToArray(); + } + + private void BuildReferenceList2(Assembly assembly, + List list, + HashSet visited) { + bool notThere = visited.Add(assembly.GetName().FullName); + if (notThere) + { + list.Add(assembly); + foreach (AssemblyName referenced in assembly.GetReferencedAssemblies()) { + Assembly assembly2 = Assembly.Load(referenced); + BuildReferenceList2(assembly2,list,visited); + } + } + } + + } + #endregion + + #region ScriptOnResourceImpl + public class ScriptOnResourceImpl : ConnectorAPIOperationRunner, + ScriptOnResourceApiOp { + + public ScriptOnResourceImpl(ConnectorOperationalContext context, + Connector connector) : + base(context,connector) { + } + + public Object RunScriptOnResource(ScriptContext request, + OperationOptions options) { + Assertions.NullCheck(request, "request"); + //convert null into empty + if ( options == null ) { + options = new OperationOptionsBuilder().Build(); + } + Object rv + = ((ScriptOnResourceOp)GetConnector()).RunScriptOnResource(request, options); + return SerializerUtil.CloneObject(rv); + } + + } + #endregion + + #region SearchImpl + internal class SearchImpl : ConnectorAPIOperationRunner , SearchApiOp { + + /** + * Initializes the operation works. + */ + public SearchImpl(ConnectorOperationalContext context, + Connector connector) + :base(context,connector) { + } + + /** + * Call the SPI search routines to return the results to the + * {@link ResultsHandler}. + * + * @see SearchApiOp#search(Filter, long, long, SearchApiOp.ResultsHandler) + */ + public void Search(ObjectClass oclass, Filter originalFilter, ResultsHandler handler, OperationOptions options) { + Assertions.NullCheck(oclass, "oclass"); + Assertions.NullCheck(handler, "handler"); + //convert null into empty + if ( options == null ) { + options = new OperationOptionsBuilder().Build(); + } + ObjectNormalizerFacade normalizer = + GetNormalizer(oclass); + //chain a normalizing handler (must come before + //filter handler) + handler = + new NormalizingResultsHandler(handler,normalizer).Handle; + Filter normalizedFilter = + normalizer.NormalizeFilter(originalFilter); + + //get the IList interface that this type implements + Type interfaceType = ReflectionUtil.FindInHierarchyOf + (typeof(SearchOp<>),GetConnector().GetType()); + Type [] val = interfaceType.GetGenericArguments(); + if (val.Length != 1) { + throw new Exception("Unexpected type: "+interfaceType); + } + Type queryType = val[0]; + Type searcherRawType = typeof(RawSearcherImpl<>); + Type searcherType = + searcherRawType.MakeGenericType(queryType); + RawSearcher searcher = (RawSearcher)Activator.CreateInstance(searcherType); + // add filtering handler + handler = new FilteredResultsHandler(handler, normalizedFilter).Handle; + // add attributes to get handler + string[] attrsToGet = options.AttributesToGet; + if (attrsToGet != null && attrsToGet.Length > 0) { + handler = new SearchAttributesToGetResultsHandler( + handler, attrsToGet).Handle; + } + searcher.RawSearch(GetConnector(),oclass,normalizedFilter,handler,options); + } + } + #endregion + + #region RawSearcher + internal interface RawSearcher { + /** + * Public because it is used by TestHelpers. Raw, + * SPI-level search. + * @param search The underlying implementation of search + * (generally the connector itself) + * @param oclass The object class + * @param filter The filter + * @param handler The handler + * @param options The options + */ + void RawSearch(Object search, + ObjectClass oclass, + Filter filter, + ResultsHandler handler, + OperationOptions options); + } + #endregion + + #region RawSearcherImpl + internal class RawSearcherImpl : RawSearcher where T : class { + public void RawSearch(Object search, + ObjectClass oclass, + Filter filter, + ResultsHandler handler, + OperationOptions options) { + RawSearch((SearchOp)search,oclass,filter,handler,options); + } + + /** + * Public because it is used by TestHelpers. Raw, + * SPI-level search. + * @param search The underlying implementation of search + * (generally the connector itself) + * @param oclass The object class + * @param filter The filter + * @param handler The handler + * @param options The options + */ + public static void RawSearch(SearchOp search, + ObjectClass oclass, + Filter filter, + ResultsHandler handler, + OperationOptions options) { + + FilterTranslator translator = + search.CreateFilterTranslator(oclass, options); + IList queries = + (IList)translator.Translate(filter); + if ( queries.Count == 0) { + search.ExecuteQuery(oclass, + null, handler, options); + } + else { + //eliminate dups if more than one + bool eliminateDups = + queries.Count > 1; + DuplicateFilteringResultsHandler dups = null; + if (eliminateDups) { + dups = new DuplicateFilteringResultsHandler(handler); + handler = dups.Handle; + } + foreach( T query in queries ) { + search.ExecuteQuery(oclass, + query, handler, options); + //don't run any more queries if the consumer + //has stopped + if (dups != null ) { + if (!dups.IsStillHandling) { + break; + } + } + } + } + } + + } + #endregion + + #region SyncImpl + public class SyncImpl : ConnectorAPIOperationRunner, + SyncApiOp { + + public SyncImpl(ConnectorOperationalContext context, + Connector connector) + : base(context,connector) { + } + + public void Sync(ObjectClass objClass, SyncToken token, + SyncResultsHandler handler, + OperationOptions options) { + //token is allowed to be null, objClass and handler must not be null + Assertions.NullCheck(objClass, "objClass"); + Assertions.NullCheck(handler, "handler"); + //convert null into empty + if ( options == null ) { + options = new OperationOptionsBuilder().Build(); + } + // add a handler in the chain to remove attributes + string[] attrsToGet = options.AttributesToGet; + if (attrsToGet != null && attrsToGet.Length > 0) { + handler = new SyncAttributesToGetResultsHandler( + handler, attrsToGet).Handle; + } + //chain a normalizing results handler + ObjectNormalizerFacade normalizer = + GetNormalizer(objClass); + handler = new NormalizingSyncResultsHandler(handler,normalizer).Handle; + ((SyncOp)GetConnector()).Sync(objClass, token, handler, options); + } + public SyncToken GetLatestSyncToken(ObjectClass objectClass) + { + return ((SyncOp) GetConnector()).GetLatestSyncToken(objectClass); + } + } + #endregion + + #region TestImpl + /** + * Provides a method for the API to call the SPI's test method on the + * connector. The test method is intended to determine if the {@link Connector} + * is ready to perform the various operations it supports. + * + * @author Will Droste + * + */ + internal class TestImpl : ConnectorAPIOperationRunner , TestApiOp { + + public TestImpl(ConnectorOperationalContext context, Connector connector) + :base(context,connector) { + } + + /** + * {@inheritDoc} + */ + public void Test() { + ((TestOp) GetConnector()).Test(); + } + + } + #endregion + + #region UpdateImpl + /** + * NOTE: internal class, public only for unit tests + * Handles both version of update this include simple replace and the advance + * update. + */ + public class UpdateImpl : ConnectorAPIOperationRunner , UpdateApiOp { + /** + * All the operational attributes that can not be added or deleted. + */ + static readonly HashSet OPERATIONAL_ATTRIBUTE_NAMES = new HashSet(); + + const String OPERATIONAL_ATTRIBUTE_ERR = + "Operational attribute '{0}' can not be added or deleted only replaced."; + + static UpdateImpl() { + OPERATIONAL_ATTRIBUTE_NAMES.Add(Name.NAME); + CollectionUtil.AddAll(OPERATIONAL_ATTRIBUTE_NAMES, + OperationalAttributes.OPERATIONAL_ATTRIBUTE_NAMES); + } + + /** + * Determines which type of update a connector supports and then uses that + * handler. + */ + public UpdateImpl(ConnectorOperationalContext context, + Connector connector) + :base(context,connector){ + } + + public Uid Update(ObjectClass objclass, + Uid uid, + ICollection replaceAttributes, + OperationOptions options) { + // validate all the parameters.. + ValidateInput(objclass,uid,replaceAttributes,false); + //cast null as empty + if ( options == null ) { + options = new OperationOptionsBuilder().Build(); + } + + ObjectNormalizerFacade normalizer = + GetNormalizer(objclass); + uid = (Uid)normalizer.NormalizeAttribute(uid); + replaceAttributes = + normalizer.NormalizeAttributes(replaceAttributes); + UpdateOp op = (UpdateOp)GetConnector(); + Uid ret = op.Update(objclass, uid, replaceAttributes, options); + return (Uid)normalizer.NormalizeAttribute(ret); + } + + public Uid AddAttributeValues(ObjectClass objclass, + Uid uid, + ICollection valuesToAdd, + OperationOptions options) { + // validate all the parameters.. + ValidateInput(objclass,uid,valuesToAdd,true); + //cast null as empty + if ( options == null ) { + options = new OperationOptionsBuilder().Build(); + } + + ObjectNormalizerFacade normalizer = + GetNormalizer(objclass); + uid = (Uid)normalizer.NormalizeAttribute(uid); + valuesToAdd = + normalizer.NormalizeAttributes(valuesToAdd); + UpdateOp op = (UpdateOp)GetConnector(); + Uid ret; + if ( op is UpdateAttributeValuesOp ) { + UpdateAttributeValuesOp valueOp = + (UpdateAttributeValuesOp)op; + ret = valueOp.AddAttributeValues(objclass, uid, valuesToAdd, options); + } + else { + ICollection replaceAttributes = + FetchAndMerge(objclass,uid,valuesToAdd,true,options); + ret = op.Update(objclass, uid, replaceAttributes, options); + } + return (Uid)normalizer.NormalizeAttribute(ret); + } + + public Uid RemoveAttributeValues(ObjectClass objclass, + Uid uid, + ICollection valuesToRemove, + OperationOptions options) { + // validate all the parameters.. + ValidateInput(objclass,uid,valuesToRemove,true); + //cast null as empty + if ( options == null ) { + options = new OperationOptionsBuilder().Build(); + } + + ObjectNormalizerFacade normalizer = + GetNormalizer(objclass); + uid = (Uid)normalizer.NormalizeAttribute(uid); + valuesToRemove = + normalizer.NormalizeAttributes(valuesToRemove); + UpdateOp op = (UpdateOp)GetConnector(); + Uid ret; + if ( op is UpdateAttributeValuesOp ) { + UpdateAttributeValuesOp valueOp = + (UpdateAttributeValuesOp)op; + ret = valueOp.RemoveAttributeValues(objclass, uid, valuesToRemove, options); + } + else { + ICollection replaceAttributes = + FetchAndMerge(objclass,uid,valuesToRemove,false,options); + ret = op.Update(objclass, uid, replaceAttributes, options); + } + return (Uid)normalizer.NormalizeAttribute(ret); + } + + private ICollection FetchAndMerge(ObjectClass objclass, Uid uid, + ICollection valuesToChange, + bool add, + OperationOptions options) + { + // check that this connector supports Search.. + if (ReflectionUtil.FindInHierarchyOf(typeof(SearchOp<>),GetConnector().GetType()) != null) { + String MSG = "Connector must support search"; + throw new InvalidOperationException(MSG); + } + + //add attrs to get to operation options, so that the + //object we fetch has exactly the set of attributes we require + //(there may be ones that are not in the default set) + OperationOptionsBuilder builder = new OperationOptionsBuilder(options); + ICollection attrNames = new HashSet(); + foreach (ConnectorAttribute attribute in valuesToChange) { + attrNames.Add(attribute.Name); + } + builder.AttributesToGet=(attrNames.ToArray()); + options = builder.Build(); + + // get the connector object from the resource... + ConnectorObject o = GetConnectorObject(objclass, uid, options); + if (o == null) { + throw new UnknownUidException(uid, objclass); + } + // merge the update data.. + ICollection mergeAttrs = Merge(valuesToChange, o.GetAttributes(),add); + return mergeAttrs; + } + + /** + * Merges two connector objects into a single updated object. + */ + public ICollection Merge(ICollection updateAttrs, + ICollection baseAttrs, bool add) { + // return the merged attributes + ICollection ret = new HashSet(); + // create map that can be modified to get the subset of changes + IDictionary baseAttrMap = ConnectorAttributeUtil.ToMap(baseAttrs); + // run through attributes of the current object.. + foreach (ConnectorAttribute updateAttr in updateAttrs) { + // get the name of the update attributes + String name = updateAttr.Name; + // remove each attribute that is an update attribute.. + ConnectorAttribute baseAttr = CollectionUtil.GetValue(baseAttrMap,name,null); + IList values; + ConnectorAttribute modifiedAttr; + if (add) { + if (baseAttr == null) { + modifiedAttr = updateAttr; + } else { + // create a new list with the base attribute to add to.. + values = CollectionUtil.NewList(baseAttr.Value); + CollectionUtil.AddAll(values,updateAttr.Value); + modifiedAttr = ConnectorAttributeBuilder.Build(name, values); + } + } + else { + if (baseAttr == null) { + // nothing to actually do the attribute do not exist + continue; + } else { + // create a list with the base attribute to remove from.. + values = CollectionUtil.NewList(baseAttr.Value); + foreach (Object val in updateAttr.Value) { + values.Remove(val); + } + // if the values are empty send a null to the connector.. + if (values.Count == 0) { + modifiedAttr = ConnectorAttributeBuilder.Build(name); + } else { + modifiedAttr = ConnectorAttributeBuilder.Build(name, values); + } + } + } + ret.Add(modifiedAttr); + } + return ret; + } + + /** + * Get the {@link ConnectorObject} to modify. + */ + private ConnectorObject GetConnectorObject(ObjectClass oclass, Uid uid, OperationOptions options) { + // attempt to get the connector object.. + GetApiOp get = new GetImpl(new SearchImpl((ConnectorOperationalContext)GetOperationalContext(), + GetConnector())); + return get.GetObject(oclass, uid, options); + } + + /** + * Makes things easier if you can trust the input. + */ + public static void ValidateInput(ObjectClass objclass, + Uid uid, + ICollection attrs, bool isDelta) { + Assertions.NullCheck(uid, "uid"); + Assertions.NullCheck(objclass, "objclass"); + Assertions.NullCheck(attrs, "attrs"); + // check to make sure there's not a uid.. + if (ConnectorAttributeUtil.GetUidAttribute(attrs) != null) { + throw new ArgumentException( + "Parameter 'attrs' contains a uid."); + } + // check for things only valid during ADD/DELETE + if (isDelta) { + foreach (ConnectorAttribute attr in attrs) { + Assertions.NullCheck(attr, "attr"); + // make sure that none of the values are null.. + if (attr.Value == null) { + throw new ArgumentException( + "Can not add or remove a 'null' value."); + } + // make sure that if this an delete/add that it doesn't include + // certain attributes because it doesn't make any sense.. + String name = attr.Name; + if (OPERATIONAL_ATTRIBUTE_NAMES.Contains(name)) { + String msg = String.Format(OPERATIONAL_ATTRIBUTE_ERR, name); + throw new ArgumentException(msg); + } + } + } + } + } + #endregion + + #region ValidateImpl + internal class ValidateImpl : APIOperationRunner, ValidateApiOp { + + public ValidateImpl(OperationalContext context) + :base(context) { + } + + /** + * {@inheritDoc} + */ + public void Validate() { + GetOperationalContext().GetConfiguration().Validate(); + } + } + #endregion + +} diff --git a/DotNetConnectors.sln/FrameworkInternal/ApiRemote.cs b/DotNetConnectors.sln/FrameworkInternal/ApiRemote.cs new file mode 100644 index 00000000..b9ba8dbe --- /dev/null +++ b/DotNetConnectors.sln/FrameworkInternal/ApiRemote.cs @@ -0,0 +1,383 @@ +/* + * ==================== + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER. + * + * Copyright 2007-2008 Sun Microsystems, Inc. All rights reserved. + * + * The contents of this file are subject to the terms of the Common Development + * and Distribution License("CDDL") (the "License"). You may not use this file + * except in compliance with the License. + * + * You can obtain a copy of the License at + * http://IdentityConnectors.dev.java.net/legal/license.txt + * See the License for the specific language governing permissions and limitations + * under the License. + * + * When distributing the Covered Code, include this CDDL Header Notice in each file + * and include the License file at identityconnectors/legal/license.txt. + * If applicable, add the following below this CDDL Header, with the fields + * enclosed by brackets [] replaced by your own identifying information: + * "Portions Copyrighted [year] [name of copyright owner]" + * ==================== + */ +using System; +using System.Collections.Generic; +using System.IO; +using System.Net; +using System.Reflection; +using System.Security.Authentication; +using System.Globalization; +using System.Net.Security; +using System.Net.Sockets; +using System.Security.Cryptography.X509Certificates; +using System.Threading; +using Org.IdentityConnectors.Common; +using Org.IdentityConnectors.Common.Proxy; +using Org.IdentityConnectors.Framework.Api; +using Org.IdentityConnectors.Framework.Api.Operations; +using Org.IdentityConnectors.Framework.Common.Exceptions; +using Org.IdentityConnectors.Framework.Common.Objects; +using Org.IdentityConnectors.Framework.Common.Objects.Filters; +using Org.IdentityConnectors.Framework.Common.Serializer; +using Org.IdentityConnectors.Framework.Impl.Api; +using Org.IdentityConnectors.Framework.Impl.Api.Local.Operations; +using Org.IdentityConnectors.Framework.Impl.Api.Remote.Messages; +using System.Diagnostics; +namespace Org.IdentityConnectors.Framework.Impl.Api.Remote +{ + public class RemoteFrameworkConnection : IDisposable { + + private TcpClient _socket; + private Stream _stream; + + private BinaryObjectSerializer _encoder; + private BinaryObjectDeserializer _decoder; + + public RemoteFrameworkConnection(RemoteFrameworkConnectionInfo info) { + Init(info); + } + + public RemoteFrameworkConnection(TcpClient socket, Stream stream) { + Init(socket,stream); + } + + private void Init(RemoteFrameworkConnectionInfo connectionInfo) + { + IPAddress [] addresses = + Dns.GetHostAddresses(connectionInfo.Host); + TcpClient client = new TcpClient(addresses[0].AddressFamily); + client.SendTimeout = connectionInfo.Timeout; + client.ReceiveTimeout = connectionInfo.Timeout; + client.Connect(addresses[0],connectionInfo.Port); + Stream stream; + try { + stream = client.GetStream(); + } + catch (Exception) { + try { client.Close(); } catch (Exception) {} + throw; + } + try { + if (connectionInfo.UseSSL) { + if (connectionInfo.CertificateValidationCallback != null) { + RemoteCertificateValidationCallback callback = + connectionInfo.CertificateValidationCallback; + + stream = new SslStream( + stream,false,callback); + } + else { + stream = new SslStream(stream, + false); + } + ((SslStream)stream).AuthenticateAsClient(connectionInfo.Host, + new X509CertificateCollection(new X509Certificate[0]), + SslProtocols.Tls, + false); + } + } + catch (Exception) { + try { stream.Close(); } catch (Exception) {} + try { client.Close(); } catch (Exception) {} + throw; + } + Init(client,stream); + } + + private void Init(TcpClient socket, Stream stream) + { + _socket = socket; + _stream = stream; + ObjectSerializerFactory fact = + ObjectSerializerFactory.GetInstance(); + _encoder = fact.NewBinarySerializer(_stream); + _decoder = fact.NewBinaryDeserializer(_stream); + } + + public void Dispose() { + Flush(); + _stream.Close(); + _socket.Close(); + } + + public void Flush() { + _encoder.Flush(); + } + + public void WriteObject(object obj) { + _encoder.WriteObject(obj); + } + + public object ReadObject() { + //flush first in case there is any data in the + //output buffer + Flush(); + return _decoder.ReadObject(); + } + } + + + /// + /// internal class, public only for unit tests + /// + public sealed class RemoteConnectorInfoImpl : AbstractConnectorInfo { + + + public RemoteConnectorInfoImpl() { + + } + + //transient field, not serialized + public RemoteFrameworkConnectionInfo RemoteConnectionInfo {get;set;} + + } + + public class RemoteConnectorInfoManagerImpl : ConnectorInfoManager { + + + private IList _connectorInfo; + + private RemoteConnectorInfoManagerImpl() { + + } + + public RemoteConnectorInfoManagerImpl(RemoteFrameworkConnectionInfo info) + { + using (RemoteFrameworkConnection connection = + new RemoteFrameworkConnection(info)) { + connection.WriteObject(CultureInfo.CurrentUICulture); + connection.WriteObject(info.Key); + connection.WriteObject(new HelloRequest()); + HelloResponse response = (HelloResponse)connection.ReadObject(); + if ( response.Exception != null ) { + throw response.Exception; + } + IList remoteInfos = + response.ConnectorInfos; + //populate transient fields not serialized + foreach (RemoteConnectorInfoImpl remoteInfo in remoteInfos ) { + remoteInfo.RemoteConnectionInfo=info; + } + _connectorInfo = + CollectionUtil.NewReadOnlyList(remoteInfos); + } + + } + + /** + * Derives another RemoteConnectorInfoManagerImpl with + * a different RemoteFrameworkConnectionInfo but with the + * same metadata + * @param info + * @return + */ + public RemoteConnectorInfoManagerImpl Derive(RemoteFrameworkConnectionInfo info) { + RemoteConnectorInfoManagerImpl rv = new RemoteConnectorInfoManagerImpl(); + IList remoteInfosObj = + (IList)SerializerUtil.CloneObject(_connectorInfo); + IList remoteInfos = + CollectionUtil.NewList(remoteInfosObj); + foreach (ConnectorInfo remoteInfo in remoteInfos) { + ((RemoteConnectorInfoImpl)remoteInfo).RemoteConnectionInfo=(info); + } + rv._connectorInfo = + CollectionUtil.AsReadOnlyList(remoteInfos); + return rv; + } + + + public ConnectorInfo FindConnectorInfo(ConnectorKey key) { + foreach (ConnectorInfo info in _connectorInfo) { + if ( info.ConnectorKey.Equals(key) ) { + return info; + } + } + return null; + } + + public IList ConnectorInfos { + get { + return _connectorInfo; + } + } + + } + + internal class RemoteConnectorFacadeImpl : AbstractConnectorFacade { + + /** + * Builds up the maps of supported operations and calls. + */ + public RemoteConnectorFacadeImpl(APIConfigurationImpl configuration) + :base(configuration) { + } + + protected override APIOperation GetOperationImplementation(SafeType api) { + InvocationHandler handler = new RemoteOperationInvocationHandler( + GetAPIConfiguration(), + api); + APIOperation proxy = NewAPIOperationProxy(api, handler); + // add logging.. + proxy = CreateLoggingProxy(api, proxy); + return proxy; + } + } + + + /** + * Invocation handler for all of our operations + */ + public class RemoteOperationInvocationHandler : InvocationHandler { + private readonly APIConfigurationImpl _configuration; + private readonly SafeType _operation; + + public RemoteOperationInvocationHandler(APIConfigurationImpl configuration, + SafeType operation) { + _configuration = configuration; + _operation = operation; + } + + + public Object Invoke(Object proxy, MethodInfo method, Object[] args) + { + //don't proxy toString, hashCode, or equals + if ( method.DeclaringType.Equals(typeof(object))) { + return method.Invoke(this, args); + } + + //partition arguments into arguments that can + //be simply marshalled as part of the request and + //those that are response handlers + IList simpleMarshallArgs = + CollectionUtil.NewList(args); + ObjectStreamHandler streamHandlerArg = + ExtractStreamHandler(ReflectionUtil.GetParameterTypes(method),simpleMarshallArgs); + + //build the request object + RemoteConnectorInfoImpl connectorInfo = + (RemoteConnectorInfoImpl)_configuration.ConnectorInfo; + RemoteFrameworkConnectionInfo connectionInfo = + connectorInfo.RemoteConnectionInfo; + OperationRequest request = new OperationRequest( + connectorInfo.ConnectorKey, + _configuration, + _operation, + method.Name, + simpleMarshallArgs); + + //create the connection + RemoteFrameworkConnection connection = + new RemoteFrameworkConnection(connectionInfo); + try { + connection.WriteObject(CultureInfo.CurrentUICulture); + connection.WriteObject(connectionInfo.Key); + //send the request + connection.WriteObject(request); + + //now process each response stream (if any) + if ( streamHandlerArg != null ) { + HandleStreamResponse(connection,streamHandlerArg); + } + + //finally return the actual return value + OperationResponsePart response = + (OperationResponsePart)connection.ReadObject(); + if ( response.Exception != null ) { + throw response.Exception; + } + return response.Result; + } + finally { + connection.Dispose(); + } + } + /** + * Handles a stream response until the end of the stream + */ + private static void HandleStreamResponse(RemoteFrameworkConnection connection, ObjectStreamHandler streamHandler) { + Object response; + bool handleMore = true; + while ( true ) { + response = connection.ReadObject(); + if ( response is OperationResponsePart ) { + OperationResponsePart part = (OperationResponsePart)response; + if ( part.Exception != null ) { + throw part.Exception; + } + object obj = + part.Result; + if ( handleMore ) { + handleMore = streamHandler.Handle(obj); + } + } + else if ( response is OperationResponsePause ) { + if ( handleMore ) { + connection.WriteObject(new OperationRequestMoreData()); + } + else { + connection.WriteObject(new OperationRequestStopData()); + } + } + else if ( response is OperationResponseEnd ) { + break; + } + else { + throw new ConnectorException("Unexpected response: "+response); + } + } + } + + /** + * Partitions arguments into regular arguments and + * stream arguments. + * @param paramTypes The param types of the method + * @param arguments The passed-in arguments. As a + * side-effect will be set to just the regular arguments. + * @return The stream handler arguments. + */ + private static ObjectStreamHandler ExtractStreamHandler(Type [] paramTypes, + IList arguments) { + ObjectStreamHandler rv = null; + IList filteredArguments = new List(); + for ( int i = 0; i < paramTypes.Length; i++ ) { + Type paramType = paramTypes[i]; + object arg = arguments[i]; + if (StreamHandlerUtil.IsAdaptableToObjectStreamHandler(paramType)) { + ObjectStreamHandler handler = StreamHandlerUtil.AdaptToObjectStreamHandler(paramType, arg); + if ( rv != null ) { + throw new InvalidOperationException("Multiple stream handlers not supported"); + } + rv = handler; + } + else { + filteredArguments.Add(arg); + } + } + arguments.Clear(); + CollectionUtil.AddAll(arguments,filteredArguments); + return rv; + } + } + + +} diff --git a/DotNetConnectors.sln/FrameworkInternal/ApiRemoteMessages.cs b/DotNetConnectors.sln/FrameworkInternal/ApiRemoteMessages.cs new file mode 100644 index 00000000..62c58ab0 --- /dev/null +++ b/DotNetConnectors.sln/FrameworkInternal/ApiRemoteMessages.cs @@ -0,0 +1,243 @@ +/* + * ==================== + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER. + * + * Copyright 2007-2008 Sun Microsystems, Inc. All rights reserved. + * + * The contents of this file are subject to the terms of the Common Development + * and Distribution License("CDDL") (the "License"). You may not use this file + * except in compliance with the License. + * + * You can obtain a copy of the License at + * http://IdentityConnectors.dev.java.net/legal/license.txt + * See the License for the specific language governing permissions and limitations + * under the License. + * + * When distributing the Covered Code, include this CDDL Header Notice in each file + * and include the License file at identityconnectors/legal/license.txt. + * If applicable, add the following below this CDDL Header, with the fields + * enclosed by brackets [] replaced by your own identifying information: + * "Portions Copyrighted [year] [name of copyright owner]" + * ==================== + */ +using System; +using System.Collections.Generic; +using Org.IdentityConnectors.Common; +using Org.IdentityConnectors.Framework.Api; +using Org.IdentityConnectors.Framework.Api.Operations; +namespace Org.IdentityConnectors.Framework.Impl.Api.Remote.Messages +{ + /// + /// internal class, public only for unit tests + /// + public class HelloRequest : Message { + + public HelloRequest() { + } + + } + + /// + /// internal class, public only for unit tests + /// + public class HelloResponse : Message { + + /** + * The exception + */ + private Exception _exception; + + /** + * List of connector infos, containing infos for all the connectors + * on the server. + */ + private IList _connectorInfos; + + public HelloResponse(Exception exception, + IList connectorInfos) { + _exception = exception; + _connectorInfos = CollectionUtil.NewReadOnlyList(connectorInfos); + } + + public Exception Exception { + get { + return _exception; + } + } + + public IList ConnectorInfos { + get { + return _connectorInfos; + } + } + + } + + /// + /// internal class, public only for unit tests + /// + + public interface Message { + + } + + /// + /// internal class, public only for unit tests + /// + public class OperationRequest : Message { + /** + * The key of the connector to operate on. + */ + private readonly ConnectorKey _connectorKey; + + /** + * The configuration information to use. + */ + private readonly APIConfigurationImpl _configuration; + + /** + * The operation to perform. + */ + private readonly SafeType _operation; + + /** + * The name of the method since operations can have more + * than one method. + * NOTE: this is case-insensitive + */ + private readonly String _operationMethodName; + + /** + * The arguments to the operation. In general, these correspond + * to the actual arguments of the method. The one exception is + * search - in this case, the callback is not passed. + */ + private readonly IList _arguments; + + public OperationRequest(ConnectorKey key, + APIConfigurationImpl apiConfiguration, + SafeType operation, + string operationMethodName, + IList arguments) { + _connectorKey = key; + _configuration = apiConfiguration; + _operation = operation; + _operationMethodName = operationMethodName; + _arguments = CollectionUtil.NewReadOnlyList(arguments); + } + + public ConnectorKey ConnectorKey { + get { + return _connectorKey; + } + } + + public APIConfigurationImpl Configuration { + get { + return _configuration; + } + } + + public SafeType Operation { + get { + return _operation; + } + } + + public string OperationMethodName { + get { + return _operationMethodName; + } + } + + public IList Arguments { + get { + return _arguments; + } + } + } + + /// + /// internal class, public only for unit tests + /// + public class OperationRequestMoreData : Message { + + public OperationRequestMoreData() { + } + + } + + /// + /// internal class, public only for unit tests + /// + public class OperationRequestStopData : Message { + + public OperationRequestStopData() { + } + + } + + /// + /// internal class, public only for unit tests + /// + public class OperationResponseEnd : Message { + + public OperationResponseEnd() { + } + + } + + /// + /// internal class, public only for unit tests + /// + public class OperationResponsePart : Message { + private Exception _exception; + private Object _result; + + public OperationResponsePart(Exception ex, Object result) { + _exception = ex; + _result = result; + } + + public Exception Exception { + get { + return _exception; + } + } + + public Object Result { + get { + return _result; + } + } + } + + /// + /// internal class, public only for unit tests + /// + public class OperationResponsePause : Message { + + public OperationResponsePause() { + } + + } + + public class EchoMessage : Message { + private object _object; + private string _objectXml; + public EchoMessage(object obj, string xml) { + _object = obj; + _objectXml = xml; + } + public object Object { + get { + return _object; + } + } + public string ObjectXml { + get { + return _objectXml; + } + } + } +} diff --git a/DotNetConnectors.sln/FrameworkInternal/AssemblyInfo.cs b/DotNetConnectors.sln/FrameworkInternal/AssemblyInfo.cs new file mode 100644 index 00000000..5c1c75d7 --- /dev/null +++ b/DotNetConnectors.sln/FrameworkInternal/AssemblyInfo.cs @@ -0,0 +1,53 @@ +/* + * ==================== + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER. + * + * Copyright 2007-2008 Sun Microsystems, Inc. All rights reserved. + * + * The contents of this file are subject to the terms of the Common Development + * and Distribution License("CDDL") (the "License"). You may not use this file + * except in compliance with the License. + * + * You can obtain a copy of the License at + * http://IdentityConnectors.dev.java.net/legal/license.txt + * See the License for the specific language governing permissions and limitations + * under the License. + * + * When distributing the Covered Code, include this CDDL Header Notice in each file + * and include the License file at identityconnectors/legal/license.txt. + * If applicable, add the following below this CDDL Header, with the fields + * enclosed by brackets [] replaced by your own identifying information: + * "Portions Copyrighted [year] [name of copyright owner]" + * ==================== + */ +#region Using directives + +using System; +using System.Reflection; +using System.Runtime.InteropServices; + +#endregion + +// General Information about an assembly is controlled through the following +// set of attributes. Change these attribute values to modify the information +// associated with an assembly. +[assembly: AssemblyTitle("FrameworkInternal")] +[assembly: AssemblyDescription("")] +[assembly: AssemblyConfiguration("")] +[assembly: AssemblyCompany("")] +[assembly: AssemblyProduct("FrameworkInternal")] +[assembly: AssemblyCopyright("")] +[assembly: AssemblyTrademark("")] +[assembly: AssemblyCulture("")] + +// This sets the default COM visibility of types in the assembly to invisible. +// If you need to expose a type to COM, use [ComVisible(true)] on that type. +[assembly: ComVisible(false)] + +// The assembly version has following format : +// +// Major.Minor.Build.Revision +// +// You can specify all the values or you can use the default the Revision and +// Build Numbers by using the '*' as shown below: +[assembly: AssemblyVersion("1.0.0.0")] diff --git a/DotNetConnectors.sln/FrameworkInternal/FrameworkInternal.csproj b/DotNetConnectors.sln/FrameworkInternal/FrameworkInternal.csproj new file mode 100644 index 00000000..34ca21fd --- /dev/null +++ b/DotNetConnectors.sln/FrameworkInternal/FrameworkInternal.csproj @@ -0,0 +1,103 @@ + + + + {5B011775-B121-4EEE-A410-BA2D2F5BFB8B} + Debug + AnyCPU + Library + Org.IdentityConnectors + FrameworkInternal + v3.5 + True + False + 4 + false + + + prompt + 4 + true + bin\Debug\ + Full + False + True + DEBUG;TRACE + + + pdbonly + bin\Release\ + False + prompt + 4 + True + False + TRACE + + + False + Auto + 4194304 + AnyCPU + 4096 + + + + + + + 3.5 + + + + + + + + + + + + + + + + + + + + + Designer + + + + + {F140E8DA-52B4-4159-992A-9DA10EA8EEFB} + Common + + + {8B24461B-456A-4032-89A1-CD418F7B5B62} + Framework + + + diff --git a/DotNetConnectors.sln/FrameworkInternal/Resources.resx b/DotNetConnectors.sln/FrameworkInternal/Resources.resx new file mode 100644 index 00000000..9d8ea6ed --- /dev/null +++ b/DotNetConnectors.sln/FrameworkInternal/Resources.resx @@ -0,0 +1,492 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + text/microsoft-resx + + + 2.0 + + + System.Resources.ResXResourceReader, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + System.Resources.ResXResourceWriter, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + <?xml version='1.0' encoding='UTF-8'?> + + +<!--=======================================================--> +<!--= =--> +<!--= DTD for Connector Objects =--> +<!--= =--> +<!--=======================================================--> + +<!--=======================================================--> +<!--= =--> +<!--= All XML Objects =--> +<!--= =--> +<!--=======================================================--> + +<!ENTITY % exceptionTypes + "AlreadyExistsException | ConfigurationException | ConnectionBrokenException + | ConnectionFailedException | ConnectorIOException | InvalidPasswordException + | UnknownUidException | InvalidCredentialException | PermissionDeniedException + | ConnectorSecurityException | OperationTimeoutException | ConnectorException + | RuntimeException | Exception | Throwable | PasswordExpiredException + "> + +<!ENTITY % messageTypes + "HelloRequest | HelloResponse | OperationRequest | OperationResponseEnd | + OperationResponsePart | OperationRequestMoreData | OperationRequestStopData | + OperationResponsePause | EchoMessage + "> + +<!ENTITY % filterTypes + "AndFilter | ContainsFilter | EndsWithFilter | EqualsFilter | + GreaterThanFilter | GreaterThanOrEqualFilter | LessThanFilter | + LessThanOrEqualFilter | NotFilter | OrFilter | StartsWithFilter | + ContainsAllValuesFilter + "> + +<!ENTITY % attributeTypes + "Attribute | Uid | Name"> + +<!ENTITY % primitiveTypes + "null | Array | Boolean | boolean | Character | char | Integer | + int | Long | long | Float | float | Double | double | String | + URI | File | BigDecimal | BigInteger | ByteArray | Class | + Map | List | Set | Locale | GuardedString + "> + +<!ENTITY % xmlObject + "%primitiveTypes; | %exceptionTypes; | %messageTypes; | %filterTypes; | %attributeTypes; | +ObjectPoolConfiguration | ConfigurationProperty | ConfigurationProperties | +APIConfiguration | ConnectorMessages | ConnectorKey | ConnectorInfo | +UpdateApiOpType | AttributeInfo | ConnectorObject | ObjectClass | +ObjectClassInfo | Schema | ScriptContext | OperationOptions | +OperationOptionInfo | SyncDeltaType | SyncToken | SyncDelta | QualifiedUid +"> + + + +<!--=======================================================--> +<!--= =--> +<!--= Top Level Element for object streams =--> +<!--= =--> +<!--=======================================================--> + + +<!ELEMENT MultiObject (( + %xmlObject; +)*)> + + +<!--=======================================================--> +<!--= =--> +<!--= Primitives =--> +<!--= =--> +<!--=======================================================--> + + + +<!ELEMENT null EMPTY> +<!ELEMENT Array ((%xmlObject;)*)> +<!ATTLIST Array + componentType CDATA #REQUIRED +> +<!ELEMENT Boolean (#PCDATA)> +<!ELEMENT boolean (#PCDATA)> +<!ELEMENT Character (#PCDATA)> +<!ELEMENT char (#PCDATA)> +<!ELEMENT Integer (#PCDATA)> +<!ELEMENT int (#PCDATA)> +<!ELEMENT Long (#PCDATA)> +<!ELEMENT long (#PCDATA)> +<!ELEMENT Float (#PCDATA)> +<!ELEMENT float (#PCDATA)> +<!ELEMENT Double (#PCDATA)> +<!ELEMENT double (#PCDATA)> +<!ELEMENT String (#PCDATA)> +<!ELEMENT URI (#PCDATA)> +<!ELEMENT File (#PCDATA)> +<!ELEMENT BigDecimal EMPTY> +<!ATTLIST BigDecimal + unscaled CDATA #REQUIRED + scale CDATA #REQUIRED +> +<!ELEMENT BigInteger (#PCDATA)> +<!ELEMENT ByteArray (#PCDATA)> +<!ELEMENT Class (#PCDATA)> +<!ELEMENT Map ((MapEntry)*)> +<!ATTLIST Map + caseInsensitive CDATA #IMPLIED +> +<!ELEMENT MapEntry ((%xmlObject;),(%xmlObject;))> +<!ELEMENT Keys ((%xmlObject;)*)> +<!ELEMENT List ((%xmlObject;)*)> +<!ELEMENT Set ((%xmlObject;)*)> +<!ATTLIST Set + caseInsensitive CDATA #IMPLIED +> +<!ELEMENT Locale EMPTY> +<!ATTLIST Locale + language CDATA #IMPLIED + country CDATA #IMPLIED + variant CDATA #IMPLIED +> +<!ELEMENT GuardedString (#PCDATA)> + +<!--=======================================================--> +<!--= =--> +<!--= APIConfiguration =--> +<!--= =--> +<!--=======================================================--> + +<!ELEMENT ObjectPoolConfiguration EMPTY> +<!ATTLIST ObjectPoolConfiguration + maxObjects CDATA #IMPLIED + maxIdle CDATA #IMPLIED + maxWait CDATA #IMPLIED + minEvictableIdleTimeMillis CDATA #IMPLIED + minIdle CDATA #IMPLIED +> + +<!ELEMENT ConfigurationProperty (value,operations)> +<!ATTLIST ConfigurationProperty + order CDATA #IMPLIED + confidential CDATA #IMPLIED + required CDATA #IMPLIED + name CDATA #REQUIRED + helpMessageKey CDATA #REQUIRED + displayMessageKey CDATA #REQUIRED + type CDATA #REQUIRED +> +<!ELEMENT value (%xmlObject;)> +<!ELEMENT operations (Class)*> +<!ELEMENT ConfigurationProperties ((ConfigurationProperty)*)> + +<!ELEMENT APIConfiguration (connectorPoolConfiguration,ConfigurationProperties,timeoutMap,SupportedOperations)> +<!ATTLIST APIConfiguration + connectorPoolingSupported CDATA #REQUIRED + producerBufferSize CDATA #REQUIRED +> +<!ELEMENT connectorPoolConfiguration ((ObjectPoolConfiguration))> +<!ELEMENT timeoutMap (Map)> +<!ELEMENT SupportedOperations ((Class)*)> +<!ELEMENT ConnectorMessages (catalogs)> +<!ELEMENT catalogs (Map)> +<!ELEMENT ConnectorKey EMPTY> +<!ATTLIST ConnectorKey + bundleName CDATA #REQUIRED + bundleVersion CDATA #REQUIRED + connectorName CDATA #REQUIRED +> +<!ELEMENT ConnectorInfo (ConnectorKey,ConnectorMessages,APIConfiguration)> +<!ATTLIST ConnectorInfo + connectorDisplayNameKey CDATA #REQUIRED +> + +<!--=======================================================--> +<!--= =--> +<!--= Common Objects =--> +<!--= =--> +<!--=======================================================--> +<!ELEMENT Attribute (Values)?> +<!ELEMENT Values ((%xmlObject;)*)> +<!ATTLIST Attribute + name CDATA #REQUIRED +> + +<!ELEMENT Uid (#PCDATA)> +<!ELEMENT Name (#PCDATA)> + + + +<!ELEMENT UpdateApiOpType (#PCDATA)> + + +<!ELEMENT AlreadyExistsException EMPTY> +<!ATTLIST AlreadyExistsException + message CDATA #IMPLIED +> +<!ELEMENT ConfigurationException EMPTY> +<!ATTLIST ConfigurationException + message CDATA #IMPLIED +> +<!ELEMENT ConnectionBrokenException EMPTY> +<!ATTLIST ConnectionBrokenException + message CDATA #IMPLIED +> +<!ELEMENT ConnectionFailedException EMPTY> +<!ATTLIST ConnectionFailedException + message CDATA #IMPLIED +> +<!ELEMENT ConnectorIOException EMPTY> +<!ATTLIST ConnectorIOException + message CDATA #IMPLIED +> +<!ELEMENT InvalidPasswordException EMPTY> +<!ATTLIST InvalidPasswordException + message CDATA #IMPLIED +> +<!ELEMENT PasswordExpiredException (Uid?)> +<!ATTLIST PasswordExpiredException + message CDATA #IMPLIED +> +<!ELEMENT UnknownUidException EMPTY> +<!ATTLIST UnknownUidException + message CDATA #IMPLIED +> +<!ELEMENT InvalidCredentialException EMPTY> +<!ATTLIST InvalidCredentialException + message CDATA #IMPLIED +> +<!ELEMENT PermissionDeniedException EMPTY> +<!ATTLIST PermissionDeniedException + message CDATA #IMPLIED +> +<!ELEMENT ConnectorSecurityException EMPTY> +<!ATTLIST ConnectorSecurityException + message CDATA #IMPLIED +> +<!ELEMENT OperationTimeoutException EMPTY> +<!ATTLIST OperationTimeoutException + message CDATA #IMPLIED +> +<!ELEMENT ConnectorException EMPTY> +<!ATTLIST ConnectorException + message CDATA #IMPLIED +> +<!ELEMENT RuntimeException EMPTY> +<!ATTLIST RuntimeException + message CDATA #IMPLIED +> +<!ELEMENT Exception EMPTY> +<!ATTLIST Exception + message CDATA #IMPLIED +> +<!ELEMENT Throwable EMPTY> +<!ATTLIST Throwable + message CDATA #IMPLIED +> +<!ELEMENT AttributeInfo (AttributeInfoFlag*)> +<!ATTLIST AttributeInfo + name CDATA #REQUIRED + type CDATA #REQUIRED +> +<!ELEMENT AttributeInfoFlag EMPTY> +<!ATTLIST AttributeInfoFlag + value ( REQUIRED | MULTIVALUED | NOT_CREATABLE | NOT_UPDATEABLE | NOT_READABLE | NOT_RETURNED_BY_DEFAULT ) #REQUIRED +> +<!ELEMENT ConnectorObject (ObjectClass,Attributes)> +<!ELEMENT Attributes ((%attributeTypes;)*)> + +<!ELEMENT ObjectClass EMPTY> +<!ATTLIST ObjectClass + type CDATA #REQUIRED +> + +<!ELEMENT ObjectClassInfo (AttributeInfos)> +<!ATTLIST ObjectClassInfo + type CDATA #REQUIRED + container CDATA #IMPLIED +> +<!ELEMENT AttributeInfos ((AttributeInfo)*)> + +<!ELEMENT Schema (ObjectClassInfos,OperationOptionInfos,objectClassesByOperation,optionsByOperation)> +<!ELEMENT ObjectClassInfos ((ObjectClassInfo)*)> +<!ELEMENT OperationOptionInfos ((OperationOptionInfo)*)> +<!ELEMENT objectClassesByOperation (Map)> +<!ELEMENT optionsByOperation (Map)> + +<!ELEMENT ScriptContext (scriptArguments,scriptText)> +<!ATTLIST ScriptContext + scriptLanguage CDATA #REQUIRED +> +<!ELEMENT scriptText (#PCDATA)> +<!ELEMENT scriptArguments (Map)> + +<!ELEMENT OperationOptions (options)> +<!ELEMENT options (Map)> +<!ELEMENT OperationOptionInfo EMPTY> +<!ATTLIST OperationOptionInfo + name CDATA #REQUIRED + type CDATA #REQUIRED +> +<!ELEMENT SyncDeltaType EMPTY> +<!ATTLIST SyncDeltaType + value ( CREATE_OR_UPDATE | DELETE ) #REQUIRED +> + +<!ELEMENT SyncToken (value)> +<!ELEMENT SyncDelta (SyncDeltaType,SyncToken,Uid,ConnectorObject?)> +<!ELEMENT QualifiedUid (ObjectClass,Uid)> + + +<!--=======================================================--> +<!--= =--> +<!--= Filters =--> +<!--= =--> +<!--=======================================================--> + + +<!ELEMENT attribute (%attributeTypes;)> +<!ELEMENT AndFilter ((%filterTypes;),(%filterTypes;))> +<!ELEMENT ContainsFilter (attribute)> +<!ELEMENT EndsWithFilter (attribute)> +<!ELEMENT EqualsFilter (attribute)> +<!ELEMENT GreaterThanFilter (attribute)> +<!ELEMENT GreaterThanOrEqualFilter (attribute)> +<!ELEMENT LessThanFilter (attribute)> +<!ELEMENT LessThanOrEqualFilter (attribute)> +<!ELEMENT NotFilter (%filterTypes;)> +<!ELEMENT OrFilter ((%filterTypes;),(%filterTypes;))> +<!ELEMENT StartsWithFilter (attribute)> +<!ELEMENT ContainsAllValuesFilter (attribute)> + +<!--=======================================================--> +<!--= =--> +<!--= Messages =--> +<!--= =--> +<!--=======================================================--> +<!ELEMENT HelloRequest EMPTY> +<!ELEMENT ConnectorInfos ((ConnectorInfo)*)> +<!ELEMENT exception (%exceptionTypes;)> +<!ELEMENT HelloResponse (exception,ConnectorInfos)> +<!ELEMENT OperationRequest (ConnectorKey,APIConfiguration,Arguments)> +<!ATTLIST OperationRequest + operation CDATA #REQUIRED + operationMethodName CDATA #REQUIRED +> +<!ELEMENT Arguments ((%xmlObject;)*)> +<!ELEMENT OperationResponseEnd EMPTY> +<!ELEMENT OperationResponsePart (exception,result)> +<!ELEMENT result ((%xmlObject;)*)> +<!ELEMENT OperationRequestMoreData EMPTY> +<!ELEMENT OperationRequestStopData EMPTY> +<!ELEMENT OperationResponsePause EMPTY> +<!ELEMENT EchoMessage (value,objectXml?)> +<!ELEMENT objectXml (#PCDATA)> + + + Test Framework Value + + + Account + + + Person + + + Group + + + Organization + + \ No newline at end of file diff --git a/DotNetConnectors.sln/FrameworkInternal/Security.cs b/DotNetConnectors.sln/FrameworkInternal/Security.cs new file mode 100644 index 00000000..9520c701 --- /dev/null +++ b/DotNetConnectors.sln/FrameworkInternal/Security.cs @@ -0,0 +1,124 @@ +/* + * ==================== + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER. + * + * Copyright 2007-2008 Sun Microsystems, Inc. All rights reserved. + * + * The contents of this file are subject to the terms of the Common Development + * and Distribution License("CDDL") (the "License"). You may not use this file + * except in compliance with the License. + * + * You can obtain a copy of the License at + * http://IdentityConnectors.dev.java.net/legal/license.txt + * See the License for the specific language governing permissions and limitations + * under the License. + * + * When distributing the Covered Code, include this CDDL Header Notice in each file + * and include the License file at identityconnectors/legal/license.txt. + * If applicable, add the following below this CDDL Header, with the fields + * enclosed by brackets [] replaced by your own identifying information: + * "Portions Copyrighted [year] [name of copyright owner]" + * ==================== + */ +using System; + +using Org.IdentityConnectors.Common.Security; +using System.Text; +using System.Security.Cryptography; + +namespace Org.IdentityConnectors.Common.Security.Impl +{ + public class EncryptorFactoryImpl : EncryptorFactory { + + private readonly Encryptor _defaultEncryptor; + + public EncryptorFactoryImpl() { + _defaultEncryptor = new EncryptorImpl(); + } + + public override Encryptor GetDefaultEncryptor() { + return _defaultEncryptor; + } + } + + public class EncryptorImpl : Encryptor { + + private readonly static byte [] _defaultKeyBytes = + { + (byte) 0x23,(byte) 0x65,(byte) 0x87,(byte) 0x22, + (byte) 0x59,(byte) 0x78,(byte) 0x54,(byte) 0x43, + (byte) 0x64,(byte) 0x05,(byte) 0x6A,(byte) 0xBD, + (byte) 0x34,(byte) 0xA2,(byte) 0x34,(byte) 0x57, + }; + private readonly static byte [] _defaultIvBytes = + { + (byte) 0x51,(byte) 0x65,(byte) 0x22,(byte) 0x23, + (byte) 0x64,(byte) 0x05,(byte) 0x6A,(byte) 0xBE, + (byte) 0x51,(byte) 0x65,(byte) 0x22,(byte) 0x23, + (byte) 0x64,(byte) 0x05,(byte) 0x6A,(byte) 0xBE, + }; + + + + + public EncryptorImpl() { + } + + public UnmanagedArray Decrypt(byte[] bytes) { + using (SymmetricAlgorithm algo = Aes.Create()) { + algo.Padding = PaddingMode.PKCS7; + algo.Mode = CipherMode.CBC; + algo.Key = _defaultKeyBytes; + algo.IV = _defaultIvBytes; + using (ICryptoTransform transform = algo.CreateDecryptor()) { + return Decrypt2(bytes,transform); + } + } + } + + private unsafe UnmanagedArray Decrypt2(byte [] bytes, ICryptoTransform transform) { + byte [] managedBytes = transform.TransformFinalBlock(bytes,0,bytes.Length); + //pin it ASAP. this is a small race condition that is unavoidable due + //to the way the crypto API's return byte[]. + fixed(byte*dummy = managedBytes) { + try { + UnmanagedByteArray rv = new UnmanagedByteArray(managedBytes.Length); + for ( int i = 0; i < rv.Length; i++ ) { + rv[i] = managedBytes[i]; + } + return rv; + } + finally { + SecurityUtil.Clear(managedBytes); + } + } + } + + public byte[] Encrypt(UnmanagedArray bytes) { + using (SymmetricAlgorithm algo = Aes.Create()) { + algo.Padding = PaddingMode.PKCS7; + algo.Mode = CipherMode.CBC; + algo.Key = _defaultKeyBytes; + algo.IV = _defaultIvBytes; + using (ICryptoTransform transform = algo.CreateEncryptor()) { + return Encrypt2(bytes,transform); + } + } + } + + private unsafe byte[] Encrypt2(UnmanagedArray bytes, ICryptoTransform transform) { + byte [] managedBytes = new byte[bytes.Length]; + fixed(byte*dummy = managedBytes) { + try { + SecurityUtil.UnmanagedBytesToManagedBytes(bytes,managedBytes); + byte [] rv = transform.TransformFinalBlock(managedBytes,0,managedBytes.Length); + return rv; + } + finally { + SecurityUtil.Clear(managedBytes); + } + } + } + + } +} diff --git a/DotNetConnectors.sln/FrameworkInternal/Serializer.cs b/DotNetConnectors.sln/FrameworkInternal/Serializer.cs new file mode 100644 index 00000000..2fdc9a6a --- /dev/null +++ b/DotNetConnectors.sln/FrameworkInternal/Serializer.cs @@ -0,0 +1,2404 @@ +/* + * ==================== + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER. + * + * Copyright 2007-2008 Sun Microsystems, Inc. All rights reserved. + * + * The contents of this file are subject to the terms of the Common Development + * and Distribution License("CDDL") (the "License"). You may not use this file + * except in compliance with the License. + * + * You can obtain a copy of the License at + * http://IdentityConnectors.dev.java.net/legal/license.txt + * See the License for the specific language governing permissions and limitations + * under the License. + * + * When distributing the Covered Code, include this CDDL Header Notice in each file + * and include the License file at identityconnectors/legal/license.txt. + * If applicable, add the following below this CDDL Header, with the fields + * enclosed by brackets [] replaced by your own identifying information: + * "Portions Copyrighted [year] [name of copyright owner]" + * ==================== + */ +using System; +using System.IO; +using System.Collections.Generic; +using System.Security; +using Org.IdentityConnectors.Common; +using Org.IdentityConnectors.Common.Pooling; +using Org.IdentityConnectors.Common.Security; +using Org.IdentityConnectors.Framework.Api; +using Org.IdentityConnectors.Framework.Api.Operations; +using Org.IdentityConnectors.Framework.Common.Objects; +using Org.IdentityConnectors.Framework.Common.Objects.Filters; +using Org.IdentityConnectors.Framework.Common.Exceptions; +using Org.IdentityConnectors.Framework.Common.Serializer; +using Org.IdentityConnectors.Framework.Impl.Api; +using Org.IdentityConnectors.Framework.Impl.Api.Remote; +using Org.IdentityConnectors.Framework.Impl.Api.Remote.Messages; +using Org.IdentityConnectors.Framework.Impl.Serializer.Binary; +using Org.IdentityConnectors.Framework.Impl.Serializer.Xml; +using System.Linq; +using System.Globalization; +namespace Org.IdentityConnectors.Framework.Impl.Serializer +{ + #region Serialization Framework + internal abstract class AbstractObjectSerializationHandler + : ObjectTypeMapperImpl, ObjectSerializationHandler { + + protected AbstractObjectSerializationHandler(Type handledClass, + String type) + :base(handledClass,type) { + } + /** + * Called to serialize the object. + */ + abstract public void Serialize(Object obj, ObjectEncoder encoder) ; + + /** + * Called to deserialize the object. + */ + abstract public Object Deserialize(ObjectDecoder decoder) ; + } + + + internal class EnumSerializationHandler : + AbstractObjectSerializationHandler { + + public EnumSerializationHandler (Type clazz, String name) + :base(clazz,name) { + } + + + public override object Deserialize(ObjectDecoder decoder) { + String val = decoder.ReadStringField("value",null); + Type enumClass = HandledObjectType; + Object rv = Enum.Parse(enumClass, val); + return rv; + } + + public override void Serialize(object obj, ObjectEncoder encoder) + { + Enum e = (Enum)obj; + encoder.WriteStringField("value",Enum.GetName(e.GetType(),e)); + } + + } + + /** + * Interface to abstract away the difference between deserializing + * xml and binary + */ + internal interface ObjectDecoder { + /** + * Reads an object using the appropriate serializer for that object + * @param fieldName. A hint of the field name. Ignored for binary + * serialization. The subelement name for xml serialization + */ + Object ReadObjectField(String fieldName, + Type expectedType, + Object dflt); + + /** + * Reads a bool. + * @param fieldName. A hint of the field name. Ignored for binary + * serialization. The subelement name for xml serialization + */ + bool ReadBooleanField(String fieldName, bool dflt) ; + + /** + * Reads an int. + * @param fieldName. A hint of the field name. Ignored for binary + * serialization. The subelement name for xml serialization + */ + int ReadIntField(String fieldName, int dflt) ; + + /** + * Reads a long. + * @param fieldName. A hint of the field name. Ignored for binary + * serialization. The subelement name for xml serialization + */ + long ReadLongField(String fieldName, long dflt) ; + + /** + * Reads a float. + * @param fieldName. A hint of the field name. Ignored for binary + * serialization. The subelement name for xml serialization + */ + float ReadFloatField(String fieldName, float dflt) ; + + /** + * Reads a double. + * @param fieldName. A hint of the field name. Ignored for binary + * serialization. The subelement name for xml serialization + * @param v. The value to serialize + */ + double ReadDoubleField(String fieldName, double dflt) ; + + /** + * Reads a double. + * @param fieldName. A hint of the field name. Ignored for binary + * serialization. The subelement name for xml serialization + * @param v. The value to serialize + */ + string ReadStringField(String fieldName, string dflt) ; + + /** + * Reads a double. + * @param fieldName. A hint of the field name. Ignored for binary + * serialization. The subelement name for xml serialization + * @param v. The value to serialize + */ + Type ReadClassField(String fieldName, Type dflt) ; + + /** + * Reads the value in-line. + */ + String ReadStringContents() ; + + /** + * Reads the value in-line. + */ + bool ReadBooleanContents() ; + + /** + * Reads the value in-line. + */ + int ReadIntContents() ; + + /** + * reads the value in-line. + */ + long ReadLongContents() ; + + /** + * Reads the value in-line. + */ + float ReadFloatContents() ; + + /** + * reads the value in-line. + */ + double ReadDoubleContents() ; + + /** + * reads the value in-line. + */ + byte [] ReadByteArrayContents() ; + + /** + * reads the value in-line. + */ + Type ReadClassContents() ; + + /** + * Returns the number of anonymous sub-objects. + * @return + */ + int GetNumSubObjects(); + + /** + * Reads a sub-object + */ + Object ReadObjectContents(int index) ; + + } + + /** + * Interface to abstract away the difference between serializing + * xml and binary + */ + internal interface ObjectEncoder { + /** + * Writes an object using the appropriate serializer for that object + * @param fieldName. A hint of the field name. Ignored for binary + * serialization. Becomes the subelement name for xml serialization + * @param object. The object to serialize + */ + void WriteObjectField(String fieldName, Object obj, bool inline) ; + + /** + * Writes a boolean. + * @param fieldName. A hint of the field name. Ignored for binary + * serialization. Becomes the subelement name for xml serialization + * @param v. The value to serialize + */ + void WriteBooleanField(String fieldName, bool v) ; + + /** + * Writes an int. + * @param fieldName. A hint of the field name. Ignored for binary + * serialization. Becomes the subelement name for xml serialization + * @param v. The value to serialize + */ + void WriteIntField(String fieldName, int v) ; + + /** + * Writes a long. + * @param fieldName. A hint of the field name. Ignored for binary + * serialization. Becomes the subelement name for xml serialization + * @param v. The value to serialize + */ + void WriteLongField(String fieldName, long v) ; + + /** + * Writes a float. + * @param fieldName. A hint of the field name. Ignored for binary + * serialization. Becomes the subelement name for xml serialization + * @param v. The value to serialize + */ + void WriteFloatField(String fieldName, float v) ; + + /** + * Writes a double. + * @param fieldName. A hint of the field name. Ignored for binary + * serialization. Becomes the subelement name for xml serialization + * @param v. The value to serialize + */ + void WriteDoubleField(String fieldName, double v) ; + + /** + * Writes a double. + * @param fieldName. A hint of the field name. Ignored for binary + * serialization. Becomes the subelement name for xml serialization + * @param v. The value to serialize + */ + void WriteStringField(String fieldName, string v) ; + + /** + * Writes a double. + * @param fieldName. A hint of the field name. Ignored for binary + * serialization. Becomes the subelement name for xml serialization + * @param v. The value to serialize + */ + void WriteClassField(String fieldName, Type v) ; + + /** + * Writes the value in-line. + */ + void WriteStringContents(String str) ; + + /** + * Writes the value in-line. + */ + void WriteBooleanContents(bool v) ; + + /** + * Writes the value in-line. + */ + void WriteIntContents(int v) ; + + /** + * Writes the value in-line. + */ + void WriteLongContents(long v) ; + + /** + * Writes the value in-line. + */ + void WriteFloatContents(float v) ; + + /** + * Writes the value in-line. + */ + void WriteDoubleContents(double v) ; + + /** + * Special case for byte [] that uses base64 encoding for XML + */ + void WriteByteArrayContents(byte [] v) ; + + /** + * Writes the value in-line. + */ + void WriteClassContents(Type v) ; + + /** + * Writes a sub-object + */ + void WriteObjectContents(object o) ; + } + + /** + * Interface to be implemented to handle the serialization/ + * deserialization of an object. + */ + internal interface ObjectSerializationHandler : ObjectTypeMapper { + + /** + * Called to serialize the object. + */ + void Serialize(Object obj, ObjectEncoder encoder) ; + + /** + * Called to deserialize the object. + */ + Object Deserialize(ObjectDecoder decoder) ; + } + + internal static class ObjectSerializerRegistry { + + private static readonly IList HANDLERS = + new List(); + + + + + private static readonly IDictionary + HANDLERS_BY_SERIAL_TYPE = new Dictionary(); + + static ObjectSerializerRegistry() { + CollectionUtil.AddAll(HANDLERS,Primitives.HANDLERS); + CollectionUtil.AddAll(HANDLERS,OperationMappings.MAPPINGS); + CollectionUtil.AddAll(HANDLERS,APIConfigurationHandlers.HANDLERS); + CollectionUtil.AddAll(HANDLERS,FilterHandlers.HANDLERS); + CollectionUtil.AddAll(HANDLERS,CommonObjectHandlers.HANDLERS); + CollectionUtil.AddAll(HANDLERS,MessageHandlers.HANDLERS); + //object is special - just map the type, but don't actually + //serialize + HANDLERS.Add(new ObjectTypeMapperImpl(typeof(object),"Object")); + + foreach (ObjectTypeMapper handler in HANDLERS) { + if (HANDLERS_BY_SERIAL_TYPE.ContainsKey(handler.HandledSerialType)) { + throw new Exception("More than one handler of the" + + " same type: "+handler.HandledSerialType); + } + HANDLERS_BY_SERIAL_TYPE[handler.HandledSerialType] = + handler; + } + } + + /** + * Mapping by class. Dynamically built since actual class may be + * a subclass. + */ + private static readonly IDictionary + HANDLERS_BY_OBJECT_TYPE = + new Dictionary(); + + + + public static ObjectTypeMapper GetMapperBySerialType(String type) { + return CollectionUtil.GetValue(HANDLERS_BY_SERIAL_TYPE,type,null); + } + + public static ObjectTypeMapper GetMapperByObjectType(Type clazz) { + lock(HANDLERS_BY_OBJECT_TYPE) { + ObjectTypeMapper rv = + CollectionUtil.GetValue(HANDLERS_BY_OBJECT_TYPE,clazz,null); + if ( rv == null ) { + foreach (ObjectTypeMapper handler in HANDLERS) { + IDelegatingObjectTypeMapper delegator = + handler as IDelegatingObjectTypeMapper; + ObjectTypeMapper effectiveHandler; + if ( delegator != null ) { + effectiveHandler = delegator.FindMapperDelegate(clazz); + } + else { + effectiveHandler = handler; + } + if ( effectiveHandler != null ) { + Type handledClass = + effectiveHandler.HandledObjectType; + if ( effectiveHandler.MatchSubclasses ) { + if ( handledClass.IsAssignableFrom(clazz) ) { + rv = effectiveHandler; + break; + } + } + else if ( handledClass.Equals(clazz) ) { + rv = effectiveHandler; + break; + } + } + } + HANDLERS_BY_OBJECT_TYPE[clazz] = rv; + } + return rv; + } + } + public static ObjectSerializationHandler GetHandlerBySerialType(String type) { + ObjectTypeMapper rv = GetMapperBySerialType(type); + return rv as ObjectSerializationHandler; + } + + public static ObjectSerializationHandler GetHandlerByObjectType(Type clazz) { + ObjectTypeMapper rv = GetMapperByObjectType(clazz); + return rv as ObjectSerializationHandler; + } + } + + /// + /// ObjectTypeMappers can implement this interface as well. If + /// they do, they can handle specific generic types as well. + /// + internal interface IDelegatingObjectTypeMapper { + ObjectTypeMapper FindMapperDelegate(Type type); + } + + /** + * Interface to be implemented to handle the serialization/ + * deserialization of an object. + */ + internal interface ObjectTypeMapper { + + /** + * Returns the type of object being serialized. This is + * an abstract type name that is intended to be language + * neutral. + */ + String HandledSerialType {get;} + + /** + * Returns the java class handled by this handler. + */ + Type HandledObjectType {get;} + + /** + * Should we match subclasses of the given class or only + * the exact class? + */ + bool MatchSubclasses {get;} + + } + + internal class ObjectTypeMapperImpl : ObjectTypeMapper { + + private Type _handledClass; + private String _handledType; + + public ObjectTypeMapperImpl(Type handledClass, String handledType) { + _handledClass = handledClass; + _handledType = handledType; + } + + public Type HandledObjectType { + get { + return _handledClass; + } + } + + public String HandledSerialType { + get { + return _handledType; + } + } + + public virtual bool MatchSubclasses { + get { + return false; + } + } + + } + + internal abstract class AbstractExceptionHandler : AbstractObjectSerializationHandler + where T : Exception + { + + protected AbstractExceptionHandler(String typeName) + : base(typeof(T),typeName) { + } + + + public override Object Deserialize(ObjectDecoder decoder) { + String message = decoder.ReadStringField("message",null); + return CreateException(message); + } + + public override void Serialize(Object obj, ObjectEncoder encoder) + { + Exception val = (Exception)obj; + encoder.WriteStringField("message", val.Message); + } + public override bool MatchSubclasses { + get { return true; } + } + protected abstract T CreateException(String message); + } + + + #endregion + + #region Primitives + + internal static class Primitives { + public static readonly IList HANDLERS = + new List(); + static Primitives() { + HANDLERS.Add(new BooleanHandler(typeof(bool?),"Boolean")); + HANDLERS.Add(new BooleanHandler(typeof(bool),"boolean")); + HANDLERS.Add(new CharacterHandler(typeof(char?),"Character")); + HANDLERS.Add(new CharacterHandler(typeof(char),"char")); + HANDLERS.Add(new IntegerHandler(typeof(int?),"Integer")); + HANDLERS.Add(new IntegerHandler(typeof(int),"int")); + HANDLERS.Add(new LongHandler(typeof(long?),"Long")); + HANDLERS.Add(new LongHandler(typeof(long),"long")); + HANDLERS.Add(new FloatHandler(typeof(float?),"Float")); + HANDLERS.Add(new FloatHandler(typeof(float),"float")); + HANDLERS.Add(new DoubleHandler(typeof(double?),"Double")); + HANDLERS.Add(new DoubleHandler(typeof(double),"double")); + HANDLERS.Add(new StringHandler()); + HANDLERS.Add(new URIHandler()); + HANDLERS.Add(new FileHandler()); + HANDLERS.Add(new BigIntegerHandler()); + HANDLERS.Add(new BigDecimalHandler()); + HANDLERS.Add(new ByteArrayHandler()); + HANDLERS.Add(new ClassHandler()); + HANDLERS.Add(new MapEntryHandler()); + HANDLERS.Add(new MapHandler()); + HANDLERS.Add(new ListHandler()); + HANDLERS.Add(new SetHandler()); + HANDLERS.Add(new LocaleHandler()); + HANDLERS.Add(new GuardedStringHandler()); + } + private class BooleanHandler : AbstractObjectSerializationHandler { + public BooleanHandler(Type objectType, String serialType) + : base(objectType,serialType) { + + } + public override Object Deserialize(ObjectDecoder decoder) { + bool val = decoder.ReadBooleanContents(); + return val; + } + + public override void Serialize(Object obj, ObjectEncoder encoder) { + bool val = (bool)obj; + encoder.WriteBooleanContents(val); + } + } + private class CharacterHandler : AbstractObjectSerializationHandler { + public CharacterHandler(Type objectType, String serialType) + : base(objectType,serialType) { + + } + public override Object Deserialize(ObjectDecoder decoder) { + String val = decoder.ReadStringContents(); + return val.ToCharArray()[0]; + } + + public override void Serialize(Object obj, ObjectEncoder encoder) { + char val = (char)obj; + encoder.WriteStringContents(val.ToString()); + } + } + private class IntegerHandler : AbstractObjectSerializationHandler { + public IntegerHandler(Type objectType, String serialType) + : base(objectType,serialType) { + + } + public override Object Deserialize(ObjectDecoder decoder) { + int val = decoder.ReadIntContents(); + return val; + } + + public override void Serialize(Object obj, ObjectEncoder encoder) { + int val = (int)obj; + encoder.WriteIntContents(val); + } + } + private class LongHandler : AbstractObjectSerializationHandler { + public LongHandler(Type objectType, String serialType) + : base(objectType,serialType) { + + } + public override Object Deserialize(ObjectDecoder decoder) { + long val = decoder.ReadLongContents(); + return val; + } + + public override void Serialize(Object obj, ObjectEncoder encoder) { + long val = (long)obj; + encoder.WriteLongContents(val); + } + } + private class FloatHandler : AbstractObjectSerializationHandler { + public FloatHandler(Type objectType, String serialType) + : base(objectType,serialType) { + + } + public override Object Deserialize(ObjectDecoder decoder) { + float val = decoder.ReadFloatContents(); + return val; + } + + public override void Serialize(Object obj, ObjectEncoder encoder) { + float val = (float)obj; + encoder.WriteFloatContents(val); + } + } + private class DoubleHandler : AbstractObjectSerializationHandler { + public DoubleHandler(Type objectType, String serialType) + : base(objectType,serialType) { + + } + public override Object Deserialize(ObjectDecoder decoder) { + double val = decoder.ReadDoubleContents(); + return val; + } + + public override void Serialize(Object obj, ObjectEncoder encoder) { + double val = (double)obj; + encoder.WriteDoubleContents(val); + } + } + private class StringHandler : AbstractObjectSerializationHandler { + public StringHandler() + : base(typeof(string),"String") { + + } + public override Object Deserialize(ObjectDecoder decoder) { + string val = decoder.ReadStringContents(); + return val; + } + + public override void Serialize(Object obj, ObjectEncoder encoder) { + string val = (string)obj; + encoder.WriteStringContents(val); + } + } + private class URIHandler : AbstractObjectSerializationHandler { + public URIHandler() + : base(typeof(Uri),"URI") { + + } + public override Object Deserialize(ObjectDecoder decoder) { + string val = decoder.ReadStringContents(); + return new Uri(val); + } + + public override void Serialize(Object obj, ObjectEncoder encoder) { + Uri val = (Uri)obj; + encoder.WriteStringContents(val.ToString()); + } + } + private class FileHandler : AbstractObjectSerializationHandler { + public FileHandler() + : base(typeof(FileName),"File") { + + } + public override Object Deserialize(ObjectDecoder decoder) { + string val = decoder.ReadStringContents(); + return new FileName(val); + } + + public override void Serialize(Object obj, ObjectEncoder encoder) { + FileName val = (FileName)obj; + encoder.WriteStringContents(val.Path); + } + } + private class BigDecimalHandler : AbstractObjectSerializationHandler { + public BigDecimalHandler() + : base(typeof(BigDecimal),"BigDecimal") { + + } + public override Object Deserialize(ObjectDecoder decoder) { + BigInteger unscaled = + new BigInteger(decoder.ReadStringField("unscaled",null)); + int scale = decoder.ReadIntField("scale",0); + return new BigDecimal(unscaled,scale); + } + + public override void Serialize(Object obj, ObjectEncoder encoder) { + BigDecimal val = (BigDecimal)obj; + encoder.WriteStringField("unscaled", val.UnscaledValue.Value); + encoder.WriteIntField("scale", val.Scale); + } + } + private class BigIntegerHandler : AbstractObjectSerializationHandler { + public BigIntegerHandler() + : base(typeof(BigInteger),"BigInteger") { + + } + public override Object Deserialize(ObjectDecoder decoder) { + string val = decoder.ReadStringContents(); + return new BigInteger(val); + } + + public override void Serialize(Object obj, ObjectEncoder encoder) { + BigInteger val = (BigInteger)obj; + encoder.WriteStringContents(val.Value); + } + } + private class ByteArrayHandler : AbstractObjectSerializationHandler { + public ByteArrayHandler() + : base(typeof(byte[]),"ByteArray") { + + } + public override Object Deserialize(ObjectDecoder decoder) { + return decoder.ReadByteArrayContents(); + } + + public override void Serialize(Object obj, ObjectEncoder encoder) { + byte [] val = (byte[])obj; + encoder.WriteByteArrayContents(val); + } + } + private class ClassHandler : AbstractObjectSerializationHandler { + public ClassHandler() + : base(typeof(Type),"Class") { + + } + public override Object Deserialize(ObjectDecoder decoder) { + return decoder.ReadClassContents(); + } + + public override void Serialize(Object obj, ObjectEncoder encoder) { + Type val = (Type)obj; + encoder.WriteClassContents(val); + } + + /// + /// In C#, the actual Type of Type is RuntimeType + /// + public override bool MatchSubclasses { + get { return true; } + } + + } + + private class MapEntry { + internal object key; + internal object val; + public MapEntry(object key, object val) { + this.key = key; + this.val = val; + } + } + private class MapEntryHandler : + AbstractObjectSerializationHandler { + + public MapEntryHandler() + : base(typeof(MapEntry),"MapEntry") { + } + public override Object Deserialize(ObjectDecoder decoder) { + Object key = decoder.ReadObjectContents(0); + Object val = decoder.ReadObjectContents(1); + return new MapEntry(key,val); + } + + public override void Serialize(Object obj, ObjectEncoder encoder) + { + MapEntry entry = (MapEntry)obj; + encoder.WriteObjectContents(entry.key); + encoder.WriteObjectContents(entry.val); + } + } + private class MapHandler : + AbstractObjectSerializationHandler, + IDelegatingObjectTypeMapper { + + public MapHandler() + : base(typeof(IDictionary),"Map") { + } + public ObjectTypeMapper FindMapperDelegate(Type type) { + //get the IDictionary interface that this type implements + Type interfaceType = ReflectionUtil.FindInHierarchyOf + (typeof(IDictionary<,>),type); + if ( interfaceType != null ) { + Type [] keyAndValue = interfaceType.GetGenericArguments(); + if (keyAndValue.Length != 2) { + throw new Exception("Cannot serialize type: "+type); + } + Type mapHandlerRawType = typeof(MapHandler<,>); + Type mapHandlerType = + mapHandlerRawType.MakeGenericType(keyAndValue); + return (ObjectTypeMapper)Activator.CreateInstance(mapHandlerType); + } + return null; + } + public override Object Deserialize(ObjectDecoder decoder) { + bool caseInsensitive = + decoder.ReadBooleanField("caseInsensitive",false); + if (caseInsensitive) { + IDictionary rv = + CollectionUtil.NewCaseInsensitiveDictionary(); + int count = decoder.GetNumSubObjects(); + for (int i = 0; i < count; i++) { + MapEntry entry = (MapEntry)decoder.ReadObjectContents(i); + rv[""+entry.key] = entry.val; + } + return rv; + } + else { + IDictionary rv = + new Dictionary(); + int count = decoder.GetNumSubObjects(); + for (int i = 0; i < count; i++) { + MapEntry entry = (MapEntry)decoder.ReadObjectContents(i); + rv[entry.key] = entry.val; + } + return rv; + } + } + + public override void Serialize(Object obj, ObjectEncoder encoder) + { + IDictionary map = (IDictionary)obj; + if (CollectionUtil.IsCaseInsensitiveDictionary(map)) { + encoder.WriteBooleanField("caseInsensitive",true); + } + else if ( map is SortedDictionary ) { + throw new Exception("Serialization of SortedDictionary not supported"); + } + foreach (KeyValuePair entry in map) { + MapEntry myEntry = new MapEntry(entry.Key,entry.Value); + encoder.WriteObjectContents(myEntry); + } + } + + public override bool MatchSubclasses { + get { return true; } + } + } + private class ListHandler : + AbstractObjectSerializationHandler, + IDelegatingObjectTypeMapper { + + public ListHandler() + : base(typeof(IList),"List") { + } + public ObjectTypeMapper FindMapperDelegate(Type type) { + //in C#, arrays implement IList + if (type.IsArray) { + return null; + } + //get the IList interface that this type implements + Type interfaceType = ReflectionUtil.FindInHierarchyOf + (typeof(IList<>),type); + if ( interfaceType != null ) { + Type [] val = interfaceType.GetGenericArguments(); + if (val.Length != 1) { + throw new Exception("Cannot serialize type: "+type); + } + Type listHandlerRawType = typeof(ListHandler<>); + Type listHandlerType = + listHandlerRawType.MakeGenericType(val); + return (ObjectTypeMapper)Activator.CreateInstance(listHandlerType); + } + return null; + } + public override Object Deserialize(ObjectDecoder decoder) { + IList rv = + new List(); + int count = decoder.GetNumSubObjects(); + for ( int i = 0; i < count; i++ ){ + rv.Add(decoder.ReadObjectContents(i)); + } + return rv; + } + + public override void Serialize(Object obj, ObjectEncoder encoder) + { + IList list = (IList)obj; + foreach (T o in list) { + encoder.WriteObjectContents(o); + } + } + + public override bool MatchSubclasses { + get { return true; } + } + } + private class SetHandler : + AbstractObjectSerializationHandler, + IDelegatingObjectTypeMapper { + + public SetHandler() + : base(typeof(ICollection),"Set") { + } + public ObjectTypeMapper FindMapperDelegate(Type type) { + //in C#, arrays implement IList + if (type.IsArray) { + return null; + } + + //get the IList interface that this type implements + Type interfaceType = ReflectionUtil.FindInHierarchyOf + (typeof(ICollection<>),type); + if ( interfaceType != null ) { + Type [] val = interfaceType.GetGenericArguments(); + if (val.Length != 1) { + throw new Exception("Cannot serialize type: "+type); + } + Type setHandlerRawType = typeof(SetHandler<>); + Type setHandlerType = + setHandlerRawType.MakeGenericType(val); + return (ObjectTypeMapper)Activator.CreateInstance(setHandlerType); + } + return null; + } + public override Object Deserialize(ObjectDecoder decoder) { + bool caseInsensitive = + decoder.ReadBooleanField("caseInsensitive",false); + if (caseInsensitive) { + ICollection rv = + CollectionUtil.NewCaseInsensitiveSet(); + int count = decoder.GetNumSubObjects(); + for (int i = 0; i < count; i++) { + rv.Add(""+decoder.ReadObjectContents(i)); + } + return rv; + } + else { + ICollection rv = + new HashSet(); + int count = decoder.GetNumSubObjects(); + for (int i = 0; i < count; i++) { + rv.Add(decoder.ReadObjectContents(i)); + } + return rv; + } + } + + public override void Serialize(Object obj, ObjectEncoder encoder) + { + ICollection list = (ICollection)obj; + if (CollectionUtil.IsCaseInsensitiveSet(list)) { + encoder.WriteBooleanField("caseInsensitive",true); + } + foreach (T o in list) { + encoder.WriteObjectContents(o); + } + } + + public override bool MatchSubclasses { + get { return true; } + } + } + private class LocaleHandler : AbstractObjectSerializationHandler { + public LocaleHandler() + : base(typeof(CultureInfo),"Locale") { + + } + public override Object Deserialize(ObjectDecoder decoder) { + string language = decoder.ReadStringField("language",""); + string country = decoder.ReadStringField("country",""); + string variant = decoder.ReadStringField("variant",""); + Locale locale = new Locale(language,country,variant); + return locale.ToCultureInfo(); + } + + public override void Serialize(Object obj, ObjectEncoder encoder) { + CultureInfo cultureInfo = (CultureInfo)obj; + Locale locale = Locale.FindLocale(cultureInfo); + encoder.WriteStringField("language", locale.Language); + encoder.WriteStringField("country", locale.Country); + encoder.WriteStringField("variant", locale.Variant); + + } + } + + private class GuardedStringHandler : AbstractObjectSerializationHandler { + public GuardedStringHandler() + : base(typeof(GuardedString),"GuardedString") { + + } + public override Object Deserialize(ObjectDecoder decoder) { + byte [] encryptedBytes = null; + UnmanagedArray clearBytes = null; + UnmanagedArray clearChars = null; + try { + encryptedBytes = decoder.ReadByteArrayContents(); + clearBytes = EncryptorFactory.GetInstance().GetDefaultEncryptor().Decrypt(encryptedBytes); + clearChars = SecurityUtil.BytesToChars(clearBytes); + GuardedString rv = new GuardedString(); + for ( int i = 0; i < clearChars.Length; i++ ) { + rv.AppendChar(clearChars[i]); + } + return rv; + } + finally { + if ( clearBytes != null ) { + clearBytes.Dispose(); + } + if ( clearChars != null ) { + clearChars.Dispose(); + } + SecurityUtil.Clear(encryptedBytes); + } + } + + public override void Serialize(Object obj, ObjectEncoder encoder) { + GuardedString str = (GuardedString)obj; + str.Access( + clearChars=> + { + UnmanagedArray clearBytes = null; + byte [] encryptedBytes = null; + try { + clearBytes = SecurityUtil.CharsToBytes(clearChars); + encryptedBytes = EncryptorFactory.GetInstance().GetDefaultEncryptor().Encrypt(clearBytes); + encoder.WriteByteArrayContents(encryptedBytes); + } + finally { + if ( clearBytes != null ) { + clearBytes.Dispose(); + } + SecurityUtil.Clear(encryptedBytes); + } + }); + } + } + } + #endregion + + #region APIConfigurationHandlers + internal static class APIConfigurationHandlers { + public static readonly IList HANDLERS = + new List(); + static APIConfigurationHandlers() { + HANDLERS.Add( new ConnectionPoolingConfigurationHandler() ); + HANDLERS.Add( new ConfigurationPropertyHandler() ); + HANDLERS.Add( new ConfigurationPropertiesHandler() ); + HANDLERS.Add( new APIConfigurationHandler() ); + HANDLERS.Add( new ConnectorMessagesHandler() ); + HANDLERS.Add( new ConnectorKeyHandler() ); + HANDLERS.Add( new ConnectorInfoHandler() ); + } + private class ConnectionPoolingConfigurationHandler : AbstractObjectSerializationHandler { + public ConnectionPoolingConfigurationHandler() + : base(typeof(ObjectPoolConfiguration),"ObjectPoolConfiguration") { + + } + public override Object Deserialize(ObjectDecoder decoder) { + ObjectPoolConfiguration rv = + new ObjectPoolConfiguration(); + rv.MaxObjects=(decoder.ReadIntField("maxObjects",rv.MaxObjects)); + rv.MaxIdle=(decoder.ReadIntField("maxIdle",rv.MaxIdle)); + rv.MaxWait=(decoder.ReadLongField("maxWait",rv.MaxWait)); + rv.MinEvictableIdleTimeMillis=( + decoder.ReadLongField("minEvictableIdleTimeMillis",rv.MinEvictableIdleTimeMillis)); + rv.MinIdle=( + decoder.ReadIntField("minIdle",rv.MinIdle)); + return rv; + } + + public override void Serialize(Object obj, ObjectEncoder encoder) { + ObjectPoolConfiguration val = + (ObjectPoolConfiguration)obj; + encoder.WriteIntField("maxObjects", + val.MaxObjects); + encoder.WriteIntField("maxIdle", + val.MaxIdle); + encoder.WriteLongField("maxWait", + val.MaxWait); + encoder.WriteLongField("minEvictableIdleTimeMillis", + val.MinEvictableIdleTimeMillis); + encoder.WriteIntField("minIdle", + val.MinIdle); + } + } + private class ConfigurationPropertyHandler : AbstractObjectSerializationHandler { + public ConfigurationPropertyHandler() + : base(typeof(ConfigurationPropertyImpl),"ConfigurationProperty") { + + } + public override Object Deserialize(ObjectDecoder decoder) { + ConfigurationPropertyImpl rv = new ConfigurationPropertyImpl(); + rv.Order=(decoder.ReadIntField("order",0)); + rv.IsConfidential=(decoder.ReadBooleanField("confidential",false)); + rv.IsRequired=decoder.ReadBooleanField("required",false); + rv.Name=(decoder.ReadStringField("name",null)); + rv.HelpMessageKey=( + decoder.ReadStringField("helpMessageKey",null)); + rv.DisplayMessageKey=( + decoder.ReadStringField("displayMessageKey",null)); + rv.ValueType=( + decoder.ReadClassField("type",null)); + rv.Value=( + decoder.ReadObjectField("value",null,null)); + ICollection operationsObj = + (ICollection)decoder.ReadObjectField("operations",typeof(ICollection),null); + ICollection> operations = + new HashSet>(); + foreach (object o in operationsObj) { + Type type = (Type)o; + operations.Add(SafeType.ForRawType(type)); + } + rv.Operations = operations; + + return rv; + } + + public override void Serialize(Object obj, ObjectEncoder encoder) { + ConfigurationPropertyImpl val = + (ConfigurationPropertyImpl)obj; + encoder.WriteIntField("order", + val.Order); + encoder.WriteBooleanField("confidential", + val.IsConfidential); + encoder.WriteBooleanField("required", + val.IsRequired); + encoder.WriteStringField("name", + val.Name); + encoder.WriteStringField("helpMessageKey", + val.HelpMessageKey); + encoder.WriteStringField("displayMessageKey", + val.DisplayMessageKey); + encoder.WriteClassField("type", + val.ValueType); + encoder.WriteObjectField("value", + val.Value, + false); + ICollection operationsObj = + new HashSet(); + foreach(SafeType op in val.Operations) { + operationsObj.Add(op.RawType); + } + encoder.WriteObjectField("operations",operationsObj,true); + } + } + private class ConfigurationPropertiesHandler : AbstractObjectSerializationHandler { + public ConfigurationPropertiesHandler() + : base(typeof(ConfigurationPropertiesImpl),"ConfigurationProperties") { + + } + public override Object Deserialize(ObjectDecoder decoder) { + ConfigurationPropertiesImpl rv = + new ConfigurationPropertiesImpl(); + IList props = + new List + (); + int count = decoder.GetNumSubObjects(); + for ( int i = 0; i < count; i++ ) { + ConfigurationPropertyImpl prop = + (ConfigurationPropertyImpl)decoder.ReadObjectContents(i); + props.Add(prop); + } + rv.Properties = props; + return rv; + } + + public override void Serialize(Object obj, ObjectEncoder encoder) { + ConfigurationPropertiesImpl val = + (ConfigurationPropertiesImpl)obj; + IList props = + val.Properties; + foreach (ConfigurationPropertyImpl prop in props) { + encoder.WriteObjectContents(prop); + } + } + } + private class APIConfigurationHandler : AbstractObjectSerializationHandler { + public APIConfigurationHandler() + : base(typeof(APIConfigurationImpl),"APIConfiguration") { + + } + public override Object Deserialize(ObjectDecoder decoder) { + APIConfigurationImpl rv = new APIConfigurationImpl(); + rv.IsConnectorPoolingSupported=( + decoder.ReadBooleanField("connectorPoolingSupported",false)); + rv.ConnectorPoolConfiguration=( + (ObjectPoolConfiguration) + decoder.ReadObjectField("connectorPoolConfiguration",null,null)); + rv.ConfigurationProperties=((ConfigurationPropertiesImpl) + decoder.ReadObjectField("ConfigurationProperties",typeof(ConfigurationPropertiesImpl),null)); + IDictionary timeoutMapObj = + (IDictionary)decoder.ReadObjectField("timeoutMap",null,null); + IDictionary,int> timeoutMap = + new Dictionary,int>(); + foreach (KeyValuePair entry in timeoutMapObj) { + Type type = (Type)entry.Key; + int val = (int)entry.Value; + timeoutMap[SafeType.ForRawType(type)] = val; + } + rv.TimeoutMap=timeoutMap; + ICollection supportedOperationsObj = + (ICollection)decoder.ReadObjectField("SupportedOperations",typeof(ICollection),null); + ICollection> supportedOperations = + new HashSet>(); + foreach (object obj in supportedOperationsObj) { + Type type = (Type)obj; + supportedOperations.Add(SafeType.ForRawType(type)); + } + rv.SupportedOperations=supportedOperations; + rv.ProducerBufferSize=(decoder.ReadIntField("producerBufferSize",0)); + return rv; + } + + public override void Serialize(Object obj, ObjectEncoder encoder) { + APIConfigurationImpl val = + (APIConfigurationImpl)obj; + + ICollection supportedOperations = + new HashSet(); + if ( val.SupportedOperations != null ) { + foreach (SafeType op in val.SupportedOperations ) { + supportedOperations.Add(op.RawType); + } + } + IDictionary timeoutMap = + new Dictionary(); + if ( val.TimeoutMap != null ) { + foreach (KeyValuePair, int> entry in val.TimeoutMap) { + timeoutMap[entry.Key.RawType] = entry.Value; + } + } + + encoder.WriteIntField("producerBufferSize", + val.ProducerBufferSize); + encoder.WriteBooleanField("connectorPoolingSupported", + val.IsConnectorPoolingSupported); + encoder.WriteObjectField("connectorPoolConfiguration", + val.ConnectorPoolConfiguration,false); + encoder.WriteObjectField("ConfigurationProperties", + val.ConfigurationProperties,true); + encoder.WriteObjectField("timeoutMap", + timeoutMap,false); + encoder.WriteObjectField("SupportedOperations", + supportedOperations,true); + } + } + private class ConnectorMessagesHandler : AbstractObjectSerializationHandler { + public ConnectorMessagesHandler() + : base(typeof(ConnectorMessagesImpl),"ConnectorMessages") { + + } + public override Object Deserialize(ObjectDecoder decoder) { + ConnectorMessagesImpl rv = new ConnectorMessagesImpl(); + IDictionary catalogsObj = + (IDictionary)decoder.ReadObjectField("catalogs",null,null); + IDictionary> + catalogs = new Dictionary>(); + foreach (KeyValuePair entry in catalogsObj) { + CultureInfo key = (CultureInfo)entry.Key; + IDictionary valObj = (IDictionary)entry.Value; + IDictionary val = + CollectionUtil.NewDictionary(valObj); + catalogs[key] = val; + } + rv.Catalogs=(catalogs); + return rv; + + } + + public override void Serialize(Object obj, ObjectEncoder encoder) { + ConnectorMessagesImpl val = + (ConnectorMessagesImpl)obj; + encoder.WriteObjectField("catalogs", + val.Catalogs,false); + } + } + + private class ConnectorKeyHandler : AbstractObjectSerializationHandler { + public ConnectorKeyHandler() + : base(typeof(ConnectorKey),"ConnectorKey") { + + } + public override Object Deserialize(ObjectDecoder decoder) { + String bundleName = + decoder.ReadStringField("bundleName",null); + String bundleVersion = + decoder.ReadStringField("bundleVersion",null); + String connectorName = + decoder.ReadStringField("connectorName",null); + return new ConnectorKey(bundleName,bundleVersion,connectorName); + } + + public override void Serialize(Object obj, ObjectEncoder encoder) { + ConnectorKey val = (ConnectorKey)obj; + encoder.WriteStringField("bundleName", + val.BundleName); + encoder.WriteStringField("bundleVersion", + val.BundleVersion); + encoder.WriteStringField("connectorName", + val.ConnectorName); + } + } + + private class ConnectorInfoHandler : AbstractObjectSerializationHandler { + public ConnectorInfoHandler() + : base(typeof(RemoteConnectorInfoImpl),"ConnectorInfo") { + + } + public override Object Deserialize(ObjectDecoder decoder) { + RemoteConnectorInfoImpl rv = new RemoteConnectorInfoImpl(); + rv.ConnectorDisplayNameKey=( + decoder.ReadStringField("connectorDisplayNameKey",null)); + rv.ConnectorKey=((ConnectorKey) + decoder.ReadObjectField("ConnectorKey",typeof(ConnectorKey),null)); + rv.Messages=((ConnectorMessagesImpl) + decoder.ReadObjectField("ConnectorMessages",typeof(ConnectorMessagesImpl),null)); + rv.DefaultAPIConfiguration=((APIConfigurationImpl) + decoder.ReadObjectField("APIConfiguration",typeof(APIConfigurationImpl),null)); + return rv; + } + + public override void Serialize(Object obj, ObjectEncoder encoder) { + RemoteConnectorInfoImpl val = + (RemoteConnectorInfoImpl)obj; + encoder.WriteStringField("connectorDisplayNameKey", + val.ConnectorDisplayNameKey); + encoder.WriteObjectField("ConnectorKey", + val.ConnectorKey,true); + encoder.WriteObjectField("ConnectorMessages", + val.Messages,true); + encoder.WriteObjectField("APIConfiguration", + val.DefaultAPIConfiguration,true); + } + } + + } + #endregion + + #region ObjectSerializerFactoryImpl + public class ObjectSerializerFactoryImpl : ObjectSerializerFactory { + + + public override BinaryObjectDeserializer NewBinaryDeserializer(Stream i) { + return new BinaryObjectDecoder(i); + } + + public override BinaryObjectSerializer NewBinarySerializer(Stream os) { + return new BinaryObjectEncoder(os); + } + public override XmlObjectSerializer NewXmlSerializer(TextWriter w, + bool includeHeader, + bool multiObject) { + return new XmlObjectSerializerImpl(w,includeHeader,multiObject); + } + + public override void DeserializeXmlStream(TextReader reader, + XmlObjectResultsHandler handler, + bool validate) { + XmlObjectParser.parse(reader, handler, validate); + } + + } + #endregion + + #region OperationMappings + internal static class OperationMappings { + + public static readonly IList MAPPINGS = + new List(); + + static OperationMappings() { + MAPPINGS.Add(new ObjectTypeMapperImpl(typeof(AuthenticationApiOp), + "AuthenticationApiOp")); + MAPPINGS.Add(new ObjectTypeMapperImpl(typeof(SearchApiOp), + "SearchApiOp")); + MAPPINGS.Add(new ObjectTypeMapperImpl(typeof(ValidateApiOp), + "ValidateApiOp")); + MAPPINGS.Add(new ObjectTypeMapperImpl(typeof(CreateApiOp), + "CreateApiOp")); + MAPPINGS.Add(new ObjectTypeMapperImpl(typeof(SchemaApiOp), + "SchemaApiOp")); + MAPPINGS.Add(new ObjectTypeMapperImpl(typeof(UpdateApiOp), + "UpdateApiOp")); + MAPPINGS.Add(new ObjectTypeMapperImpl(typeof(DeleteApiOp), + "DeleteApiOp")); + MAPPINGS.Add(new ObjectTypeMapperImpl(typeof(GetApiOp), + "GetApiOp")); + MAPPINGS.Add(new ObjectTypeMapperImpl(typeof(TestApiOp), + "TestApiOp")); + MAPPINGS.Add(new ObjectTypeMapperImpl(typeof(ScriptOnConnectorApiOp), + "ScriptOnConnectorApiOp")); + MAPPINGS.Add(new ObjectTypeMapperImpl(typeof(ScriptOnResourceApiOp), + "ScriptOnResourceApiOp")); + MAPPINGS.Add(new ObjectTypeMapperImpl(typeof(SyncApiOp), + "SyncApiOp")); + } + + } + #endregion + + #region CommonObjectHandlers + internal static class CommonObjectHandlers { + public static readonly IList HANDLERS = + new List(); + static CommonObjectHandlers() { + + HANDLERS.Add( new AlreadyExistsExceptionHandler() ); + HANDLERS.Add( new ConfigurationExceptionHandler() ); + HANDLERS.Add( new ConnectionBrokenExceptionHandler() ); + HANDLERS.Add( new ConnectionFailedExceptionHandler() ); + HANDLERS.Add( new ConnectorIOExceptionHandler() ); + HANDLERS.Add( new PasswordExpiredExceptionHandler() ); + HANDLERS.Add( new InvalidPasswordExceptionHandler() ); + HANDLERS.Add( new UnknownUidExceptionHandler() ); + HANDLERS.Add( new InvalidCredentialExceptionHandler() ); + HANDLERS.Add( new PermissionDeniedExceptionHandler() ); + HANDLERS.Add( new ConnectorSecurityExceptionHandler() ); + HANDLERS.Add( new OperationTimeoutExceptionHandler() ); + HANDLERS.Add( new ConnectorExceptionHandler() ); + HANDLERS.Add( new RuntimeExceptionHandler() ); + HANDLERS.Add( new ExceptionHandler() ); + HANDLERS.Add( new ThrowableHandler() ); + HANDLERS.Add( new AttributeHandler() ); + HANDLERS.Add( new AttributeInfoHandler() ); + HANDLERS.Add( new ConnectorObjectHandler() ); + HANDLERS.Add( new NameHandler() ); + HANDLERS.Add( new ObjectClassHandler() ); + HANDLERS.Add( new ObjectClassInfoHandler() ); + HANDLERS.Add( new SchemaHandler() ); + HANDLERS.Add( new UidHandler() ); + HANDLERS.Add( new ScriptContextHandler() ); + HANDLERS.Add( new OperationOptionsHandler() ); + HANDLERS.Add( new OperationOptionInfoHandler() ); + HANDLERS.Add( new EnumSerializationHandler(typeof(ConnectorAttributeInfo.Flags), + "AttributeInfoFlag")); + HANDLERS.Add( new EnumSerializationHandler(typeof(SyncDeltaType), + "SyncDeltaType") ); + HANDLERS.Add( new SyncTokenHandler() ); + HANDLERS.Add( new SyncDeltaHandler() ); + HANDLERS.Add( new QualifiedUidHandler() ); + } + + private class AlreadyExistsExceptionHandler : AbstractExceptionHandler { + public AlreadyExistsExceptionHandler() + : base("AlreadyExistsException") { + + } + protected override AlreadyExistsException CreateException(String msg) { + return new AlreadyExistsException(msg); + } + } + private class ConfigurationExceptionHandler : AbstractExceptionHandler { + public ConfigurationExceptionHandler() + : base("ConfigurationException") { + + } + protected override ConfigurationException CreateException(String msg) { + return new ConfigurationException(msg); + } + } + private class ConnectionBrokenExceptionHandler : AbstractExceptionHandler { + public ConnectionBrokenExceptionHandler() + : base("ConnectionBrokenException") { + + } + protected override ConnectionBrokenException CreateException(String msg) { + return new ConnectionBrokenException(msg); + } + } + private class ConnectionFailedExceptionHandler : AbstractExceptionHandler { + public ConnectionFailedExceptionHandler() + : base("ConnectionFailedException") { + + } + protected override ConnectionFailedException CreateException(String msg) { + return new ConnectionFailedException(msg); + } + } + private class ConnectorIOExceptionHandler : AbstractExceptionHandler { + public ConnectorIOExceptionHandler() + : base("ConnectorIOException") { + + } + protected override ConnectorIOException CreateException(String msg) { + return new ConnectorIOException(msg); + } + } + private class PasswordExpiredExceptionHandler : AbstractExceptionHandler { + public PasswordExpiredExceptionHandler() + : base("PasswordExpiredException") { + + } + + public override Object Deserialize(ObjectDecoder decoder) { + Uid uid = (Uid)decoder.ReadObjectField("Uid", typeof(Uid), null); + PasswordExpiredException ex = + (PasswordExpiredException)base.Deserialize(decoder); + ex.Uid = uid; + return ex; + } + + public override void Serialize(Object obj, ObjectEncoder encoder) + { + base.Serialize(obj, encoder); + PasswordExpiredException val = (PasswordExpiredException)obj; + encoder.WriteObjectField("Uid", val.Uid, true); + } + protected override PasswordExpiredException CreateException(String msg) { + return new PasswordExpiredException(msg); + } + } + private class InvalidPasswordExceptionHandler : AbstractExceptionHandler { + public InvalidPasswordExceptionHandler() + : base("InvalidPasswordException") { + + } + protected override InvalidPasswordException CreateException(String msg) { + return new InvalidPasswordException(msg); + } + } + private class UnknownUidExceptionHandler : AbstractExceptionHandler { + public UnknownUidExceptionHandler() + : base("UnknownUidException") { + + } + protected override UnknownUidException CreateException(String msg) { + return new UnknownUidException(msg); + } + } + private class InvalidCredentialExceptionHandler : AbstractExceptionHandler { + public InvalidCredentialExceptionHandler() + : base("InvalidCredentialException") { + + } + protected override InvalidCredentialException CreateException(String msg) { + return new InvalidCredentialException(msg); + } + } + private class PermissionDeniedExceptionHandler : AbstractExceptionHandler { + public PermissionDeniedExceptionHandler() + : base("PermissionDeniedException") { + + } + protected override PermissionDeniedException CreateException(String msg) { + return new PermissionDeniedException(msg); + } + } + private class ConnectorSecurityExceptionHandler : AbstractExceptionHandler { + public ConnectorSecurityExceptionHandler() + : base("ConnectorSecurityException") { + + } + protected override ConnectorSecurityException CreateException(String msg) { + return new ConnectorSecurityException(msg); + } + } + private class OperationTimeoutExceptionHandler : AbstractExceptionHandler { + public OperationTimeoutExceptionHandler() + : base("OperationTimeoutException") { + + } + protected override OperationTimeoutException CreateException(String msg) { + return new OperationTimeoutException(msg); + } + } + private class ConnectorExceptionHandler : AbstractExceptionHandler { + public ConnectorExceptionHandler() + : base("ConnectorException") { + + } + protected override ConnectorException CreateException(String msg) { + return new ConnectorException(msg); + } + } + //RuntimeException, Exception, and Throwable + //when going from Java to C#, these always become + //Exception. + //when going from C# to Java, these always become + //RuntimeException + private class RuntimeExceptionHandler : AbstractExceptionHandler { + public RuntimeExceptionHandler() + : base("RuntimeException") { + + } + protected override Exception CreateException(String msg) { + return new Exception(msg); + } + } + private class ExceptionHandler : AbstractExceptionHandler { + public ExceptionHandler() + : base("Exception") { + + } + protected override Exception CreateException(String msg) { + return new Exception(msg); + } + } + private class ThrowableHandler : AbstractExceptionHandler { + public ThrowableHandler() + : base("Throwable") { + + } + protected override Exception CreateException(String msg) { + return new Exception(msg); + } + } + + private abstract class AbstractAttributeHandler + : AbstractObjectSerializationHandler + where T : ConnectorAttribute + { + + protected AbstractAttributeHandler(String typeName) + :base(typeof(T),typeName) { + } + + + public override sealed Object Deserialize(ObjectDecoder decoder) { + String name = decoder.ReadStringField("name",null); + IList val = (IList)decoder.ReadObjectField("Values",typeof(IList),null); + return CreateAttribute(name, val); + } + + public override sealed void Serialize(Object obj, ObjectEncoder encoder) + { + ConnectorAttribute val = (ConnectorAttribute)obj; + encoder.WriteStringField("name", val.Name); + encoder.WriteObjectField("Values", val.Value, true); + } + + protected abstract T CreateAttribute(String name, IList val); + } + + private class AttributeHandler + : AbstractAttributeHandler { + public AttributeHandler() + : base("Attribute") { + + } + protected override ConnectorAttribute CreateAttribute(String name, IList value) { + return ConnectorAttributeBuilder.Build(name, value); + } + } + + private class AttributeInfoHandler : AbstractObjectSerializationHandler { + public AttributeInfoHandler() + : base(typeof(ConnectorAttributeInfo),"AttributeInfo") { + + } + public override Object Deserialize(ObjectDecoder decoder) { + ConnectorAttributeInfoBuilder builder = new ConnectorAttributeInfoBuilder(); + builder.Name=( + decoder.ReadStringField("name",null)); + builder.ValueType=( + decoder.ReadClassField("type",null)); + ConnectorAttributeInfo.Flags flags = ConnectorAttributeInfo.Flags.NONE; + int count = decoder.GetNumSubObjects(); + for ( int i = 0; i < count; i++ ) { + object o = decoder.ReadObjectContents(i); + if ( o is ConnectorAttributeInfo.Flags ) { + ConnectorAttributeInfo.Flags f = + (ConnectorAttributeInfo.Flags)o; + flags |= f; + } + } + builder.InfoFlags = flags; + return builder.Build(); + } + + public override void Serialize(Object obj, ObjectEncoder encoder) { + ConnectorAttributeInfo val = (ConnectorAttributeInfo)obj; + encoder.WriteStringField("name", val.Name); + encoder.WriteClassField("type", val.ValueType); + ConnectorAttributeInfo.Flags flags = val.InfoFlags; + foreach (Enum e in Enum.GetValues(typeof(ConnectorAttributeInfo.Flags))) { + ConnectorAttributeInfo.Flags flag = + (ConnectorAttributeInfo.Flags)e; + if ( (flags & flag) != 0 ) { + encoder.WriteObjectContents(flag); + } + } + } + } + + private class ConnectorObjectHandler : AbstractObjectSerializationHandler { + public ConnectorObjectHandler() + : base(typeof(ConnectorObject),"ConnectorObject") { + + } + public override Object Deserialize(ObjectDecoder decoder) { + ObjectClass oclass = + (ObjectClass)decoder.ReadObjectField("ObjectClass",typeof(ObjectClass),null); + ICollection attsObj = + (ICollection)decoder.ReadObjectField("Attributes",typeof(ICollection),null); + ICollection atts = + CollectionUtil.NewSet(attsObj); + return new ConnectorObject(oclass,atts); + } + + public override void Serialize(Object obj, ObjectEncoder encoder) { + ConnectorObject val = (ConnectorObject)obj; + encoder.WriteObjectField("ObjectClass",val.ObjectClass,true); + encoder.WriteObjectField("Attributes", val.GetAttributes(),true); + } + } + private class NameHandler + : AbstractObjectSerializationHandler { + public NameHandler() + : base(typeof(Name),"Name") { + + } + public override Object Deserialize(ObjectDecoder decoder) { + String str = decoder.ReadStringContents(); + return new Name(str); + } + + public override void Serialize(Object obj, ObjectEncoder encoder) { + Name val = (Name)obj; + encoder.WriteStringContents(val.GetNameValue()); + } + } + private class ObjectClassHandler : AbstractObjectSerializationHandler { + public ObjectClassHandler() + : base(typeof(ObjectClass),"ObjectClass") { + + } + public override object Deserialize(ObjectDecoder decoder) { + string type = decoder.ReadStringField("type",null); + return new ObjectClass(type); + } + + public override void Serialize(object obj, ObjectEncoder encoder) { + ObjectClass val = (ObjectClass)obj; + encoder.WriteStringField("type",val.GetObjectClassValue()); + } + } + + private class ObjectClassInfoHandler : AbstractObjectSerializationHandler { + public ObjectClassInfoHandler() + : base(typeof(ObjectClassInfo),"ObjectClassInfo") { + + } + public override Object Deserialize(ObjectDecoder decoder) { + string type = + decoder.ReadStringField("type",null); + bool container = + decoder.ReadBooleanField("container", false); + + ICollection attrInfoObj = + (ICollection)decoder.ReadObjectField("AttributeInfos",typeof(ICollection),null); + + ICollection attrInfo = + CollectionUtil.NewSet(attrInfoObj); + + return new ObjectClassInfo(type,attrInfo,container); + } + + public override void Serialize(Object obj, ObjectEncoder encoder) { + ObjectClassInfo val = (ObjectClassInfo)obj; + encoder.WriteStringField("type",val.ObjectType); + encoder.WriteBooleanField("container",val.IsContainer); + encoder.WriteObjectField("AttributeInfos", val.ConnectorAttributeInfos,true); + } + } + private class SchemaHandler : AbstractObjectSerializationHandler { + public SchemaHandler() + : base(typeof(Schema),"Schema") { + + } + public override Object Deserialize(ObjectDecoder decoder) { + + ICollection objectClassesObj = + (ICollection)decoder.ReadObjectField("ObjectClassInfos",typeof(ICollection),null); + ICollection objectClasses = + CollectionUtil.NewSet(objectClassesObj); + IDictionary objectClassesByName = new Dictionary(); + foreach (ObjectClassInfo info in objectClasses) { + objectClassesByName[info.ObjectType] = info; + } + ICollection operationOptionsObj = + (ICollection)decoder.ReadObjectField("OperationOptionInfos",typeof(ICollection),null); + ICollection operationOptions = + CollectionUtil.NewSet(operationOptionsObj); + IDictionary optionsByName = new Dictionary(); + foreach (OperationOptionInfo info in operationOptions) { + optionsByName[info.Name] = info; + } + IDictionary objectClassNamesByOperationObj = + (IDictionary)decoder.ReadObjectField("objectClassesByOperation",null,null); + IDictionary,ICollection> + objectClassesByOperation = + new Dictionary, ICollection>(); + foreach (KeyValuePair entry in objectClassNamesByOperationObj) { + SafeType op = SafeType.ForRawType((Type)entry.Key); + ICollection namesObj = + (ICollection)entry.Value; + ICollection infos = + new HashSet(); + foreach (object name in namesObj) { + ObjectClassInfo objectClass = CollectionUtil.GetValue(objectClassesByName,(string)name,null); + if ( objectClass != null ) { + infos.Add(objectClass); + } + } + objectClassesByOperation[op] = infos; + } + IDictionary optionsByOperationObj = + (IDictionary)decoder.ReadObjectField("optionsByOperation",null,null); + IDictionary,ICollection> + optionsByOperation = + new Dictionary, ICollection>(); + foreach (KeyValuePair entry in optionsByOperationObj) { + SafeType op = SafeType.ForRawType((Type)entry.Key); + ICollection namesObj = + (ICollection)entry.Value; + ICollection infos = + new HashSet(); + foreach (object name in namesObj) { + OperationOptionInfo info = CollectionUtil.GetValue(optionsByName,(string)name,null); + if ( info != null ) { + infos.Add(info); + } + } + optionsByOperation[op] = infos; + } + return new Schema(objectClasses, + operationOptions, + objectClassesByOperation, + optionsByOperation); + } + + public override void Serialize(Object obj, ObjectEncoder encoder) { + Schema val = (Schema)obj; + encoder.WriteObjectField("ObjectClassInfos", val.ObjectClassInfo,true); + encoder.WriteObjectField("OperationOptionInfos", val.OperationOptionInfo,true); + IDictionary> + objectClassNamesByOperation = new Dictionary>(); + IDictionary> + optionNamesByOperation = new Dictionary>(); + + + foreach (KeyValuePair, ICollection> + entry in val.SupportedObjectClassesByOperation) { + ICollection value = entry.Value; + ICollection names = new HashSet(); + foreach (ObjectClassInfo info in value) { + names.Add(info.ObjectType); + } + objectClassNamesByOperation[entry.Key.RawType] = names; + } + foreach (KeyValuePair, ICollection> + entry in val.SupportedOptionsByOperation) { + ICollection value = entry.Value; + ICollection names = new HashSet(); + foreach (OperationOptionInfo info in value) { + names.Add(info.Name); + } + optionNamesByOperation[entry.Key.RawType] = names; + } + encoder.WriteObjectField("objectClassesByOperation",objectClassNamesByOperation,false); + encoder.WriteObjectField("optionsByOperation",optionNamesByOperation,false); + } + } + private class UidHandler + : AbstractObjectSerializationHandler { + public UidHandler() + : base(typeof(Uid),"Uid") { + + } + public override Object Deserialize(ObjectDecoder decoder) { + String str = decoder.ReadStringContents(); + return new Uid(str); + } + + public override void Serialize(Object obj, ObjectEncoder encoder) { + Uid val = (Uid)obj; + encoder.WriteStringContents(val.GetUidValue()); + } + } + private class ScriptContextHandler : AbstractObjectSerializationHandler { + public ScriptContextHandler() + : base(typeof(ScriptContext),"ScriptContext") { + + } + public override Object Deserialize(ObjectDecoder decoder) { + String scriptLanguage = + decoder.ReadStringField("scriptLanguage",null); + IDictionary arguments = + (IDictionary)decoder.ReadObjectField("scriptArguments",null,null); + String scriptText = + (String)decoder.ReadObjectField("scriptText",typeof(string),null); + IDictionary arguments2 = + CollectionUtil.NewDictionary(arguments); + return new ScriptContext(scriptLanguage,scriptText,arguments2); + } + + public override void Serialize(Object obj, ObjectEncoder encoder) { + ScriptContext val = (ScriptContext)obj; + encoder.WriteStringField("scriptLanguage", val.ScriptLanguage); + encoder.WriteObjectField("scriptArguments", val.ScriptArguments,false); + encoder.WriteObjectField("scriptText", val.ScriptText,true); + } + } + private class OperationOptionsHandler : AbstractObjectSerializationHandler { + public OperationOptionsHandler() + : base(typeof(OperationOptions),"OperationOptions") { + + } + public override Object Deserialize(ObjectDecoder decoder) { + IDictionary options = + (IDictionary)decoder.ReadObjectField("options",null,null); + IDictionary options2 = + CollectionUtil.NewDictionary(options); + return new OperationOptions(options2); + } + + public override void Serialize(Object obj, ObjectEncoder encoder) { + OperationOptions val = (OperationOptions)obj; + encoder.WriteObjectField("options", val.Options,false); + } + } + private class OperationOptionInfoHandler : AbstractObjectSerializationHandler { + public OperationOptionInfoHandler() + : base(typeof(OperationOptionInfo),"OperationOptionInfo") { + + } + public override Object Deserialize(ObjectDecoder decoder) { + String name = decoder.ReadStringField("name",null); + Type type = decoder.ReadClassField("type",null); + return new OperationOptionInfo(name,type); + } + + public override void Serialize(Object obj, ObjectEncoder encoder) { + OperationOptionInfo val = (OperationOptionInfo)obj; + encoder.WriteStringField("name", val.Name); + encoder.WriteClassField("type", val.OptionType); + } + } + private class SyncTokenHandler : AbstractObjectSerializationHandler { + public SyncTokenHandler() + : base(typeof(SyncToken),"SyncToken") { + + } + public override Object Deserialize(ObjectDecoder decoder) { + Object value = decoder.ReadObjectField("value",null,null); + return new SyncToken(value); + } + + public override void Serialize(Object obj, ObjectEncoder encoder) { + SyncToken val = (SyncToken)obj; + encoder.WriteObjectField("value", val.Value,false); + } + } + private class SyncDeltaHandler : AbstractObjectSerializationHandler { + public SyncDeltaHandler() + : base(typeof(SyncDelta),"SyncDelta") { + + } + public override Object Deserialize(ObjectDecoder decoder) { + SyncDeltaBuilder builder = new SyncDeltaBuilder(); + builder.DeltaType=((SyncDeltaType)decoder.ReadObjectField("SyncDeltaType",typeof(SyncDeltaType),null)); + builder.Token=((SyncToken)decoder.ReadObjectField("SyncToken",typeof(SyncToken),null)); + builder.Uid=((Uid)decoder.ReadObjectField("Uid",typeof(Uid),null)); + builder.Object=((ConnectorObject)decoder.ReadObjectField("ConnectorObject",typeof(ConnectorObject),null)); + return builder.Build(); + } + + public override void Serialize(Object obj, ObjectEncoder encoder) { + SyncDelta val = (SyncDelta)obj; + encoder.WriteObjectField("SyncDeltaType", val.DeltaType,true); + encoder.WriteObjectField("SyncToken", val.Token,true); + encoder.WriteObjectField("Uid", val.Uid,true); + encoder.WriteObjectField("ConnectorObject", val.Object, true); + } + } + private class QualifiedUidHandler : AbstractObjectSerializationHandler { + public QualifiedUidHandler() + : base(typeof(QualifiedUid),"QualifiedUid") { + + } + public override Object Deserialize(ObjectDecoder decoder) { + ObjectClass objectClass = (ObjectClass)decoder.ReadObjectField("ObjectClass", typeof(ObjectClass),null); + Uid uid = (Uid)decoder.ReadObjectField("Uid",typeof(Uid),null); + return new QualifiedUid(objectClass,uid); + } + + public override void Serialize(Object obj, ObjectEncoder encoder) { + QualifiedUid val = (QualifiedUid)obj; + encoder.WriteObjectField("ObjectClass", val.ObjectClass, true); + encoder.WriteObjectField("Uid", val.Uid, true); + } + } + + } + #endregion + + #region FilterHandlers + internal static class FilterHandlers { + public static readonly IList HANDLERS = + new List(); + static FilterHandlers() { + HANDLERS.Add(new AndFilterHandler()); + HANDLERS.Add(new ContainsFilterHandler()); + HANDLERS.Add(new EndsWithFilterHandler()); + HANDLERS.Add(new EqualsFilterHandler()); + HANDLERS.Add(new GreaterThanFilterHandler()); + HANDLERS.Add(new GreaterThanOrEqualFilterHandler()); + HANDLERS.Add(new LessThanFilterHandler()); + HANDLERS.Add(new LessThanOrEqualFilterHandler()); + HANDLERS.Add(new NotFilterHandler()); + HANDLERS.Add(new OrFilterHandler()); + HANDLERS.Add(new StartsWithFilterHandler()); + HANDLERS.Add(new ContainsAllValuesFilterHandler()); + } + + private abstract class CompositeFilterHandler + : AbstractObjectSerializationHandler + where T : CompositeFilter { + + protected CompositeFilterHandler(String typeName) + : base(typeof(T),typeName) { + } + + + public override sealed Object Deserialize(ObjectDecoder decoder) { + Filter left = (Filter)decoder.ReadObjectContents(0); + Filter right = (Filter)decoder.ReadObjectContents(1); + return CreateFilter(left, right); + } + + public override sealed void Serialize(Object obj, ObjectEncoder encoder) { + CompositeFilter val = (CompositeFilter)obj; + encoder.WriteObjectContents(val.Left); + encoder.WriteObjectContents(val.Right); + } + + protected abstract T CreateFilter(Filter left, Filter right); + } + + private abstract class AttributeFilterHandler + : AbstractObjectSerializationHandler + where T : AttributeFilter { + + protected AttributeFilterHandler(String typeName) + : base(typeof(T),typeName) { + } + + + public override sealed Object Deserialize(ObjectDecoder decoder) { + ConnectorAttribute attribute = (ConnectorAttribute)decoder.ReadObjectField("attribute",null,null); + return CreateFilter(attribute); + } + + public override sealed void Serialize(Object obj, ObjectEncoder encoder) + { + AttributeFilter val = (AttributeFilter)obj; + encoder.WriteObjectField("attribute", val.GetAttribute(), false); + } + + protected abstract T CreateFilter(ConnectorAttribute attribute); + } + + + + + private class AndFilterHandler : CompositeFilterHandler { + public AndFilterHandler() + : base("AndFilter") { + } + protected override AndFilter CreateFilter(Filter left, Filter right) { + return new AndFilter(left,right); + } + } + + private class ContainsFilterHandler : AttributeFilterHandler { + public ContainsFilterHandler() + : base("ContainsFilter") { + } + protected override ContainsFilter CreateFilter(ConnectorAttribute attribute) { + return new ContainsFilter(attribute); + } + } + + private class EndsWithFilterHandler : AttributeFilterHandler { + public EndsWithFilterHandler() + : base("EndsWithFilter") { + } + protected override EndsWithFilter CreateFilter(ConnectorAttribute attribute) { + return new EndsWithFilter(attribute); + } + } + + private class EqualsFilterHandler : AttributeFilterHandler { + public EqualsFilterHandler() + : base("EqualsFilter") { + } + protected override EqualsFilter CreateFilter(ConnectorAttribute attribute) { + return new EqualsFilter(attribute); + } + } + + private class GreaterThanFilterHandler : AttributeFilterHandler { + public GreaterThanFilterHandler() + : base("GreaterThanFilter") { + } + protected override GreaterThanFilter CreateFilter(ConnectorAttribute attribute) { + return new GreaterThanFilter(attribute); + } + } + + private class GreaterThanOrEqualFilterHandler : AttributeFilterHandler { + public GreaterThanOrEqualFilterHandler() + : base("GreaterThanOrEqualFilter") { + } + protected override GreaterThanOrEqualFilter CreateFilter(ConnectorAttribute attribute) { + return new GreaterThanOrEqualFilter(attribute); + } + } + private class LessThanFilterHandler : AttributeFilterHandler { + public LessThanFilterHandler() + : base("LessThanFilter") { + } + protected override LessThanFilter CreateFilter(ConnectorAttribute attribute) { + return new LessThanFilter(attribute); + } + } + private class LessThanOrEqualFilterHandler : AttributeFilterHandler { + public LessThanOrEqualFilterHandler() + : base("LessThanOrEqualFilter") { + } + protected override LessThanOrEqualFilter CreateFilter(ConnectorAttribute attribute) { + return new LessThanOrEqualFilter(attribute); + } + } + private class NotFilterHandler + : AbstractObjectSerializationHandler { + + public NotFilterHandler() + : base(typeof(NotFilter),"NotFilter") { + } + + + public override sealed Object Deserialize(ObjectDecoder decoder) { + Filter filter = + (Filter)decoder.ReadObjectContents(0); + return new NotFilter(filter); + } + + public override sealed void Serialize(Object obj, ObjectEncoder encoder) + { + NotFilter val = (NotFilter)obj; + encoder.WriteObjectContents(val.Filter); + } + + } + private class OrFilterHandler : CompositeFilterHandler { + public OrFilterHandler() + : base("OrFilter") { + } + protected override OrFilter CreateFilter(Filter left, Filter right) { + return new OrFilter(left,right); + } + } + private class StartsWithFilterHandler : AttributeFilterHandler { + public StartsWithFilterHandler() + : base("StartsWithFilter") { + } + protected override StartsWithFilter CreateFilter(ConnectorAttribute attribute) { + return new StartsWithFilter(attribute); + } + } + + private class ContainsAllValuesFilterHandler : AttributeFilterHandler { + public ContainsAllValuesFilterHandler() + : base("ContainsAllValuesFilter") { + } + protected override ContainsAllValuesFilter CreateFilter(ConnectorAttribute attribute) { + return new ContainsAllValuesFilter(attribute); + } + } + + } + #endregion + + #region MessageHandlers + internal static class MessageHandlers { + public static readonly IList HANDLERS = + new List(); + static MessageHandlers() { + HANDLERS.Add(new HelloRequestHandler()); + HANDLERS.Add(new HelloResponseHandler()); + HANDLERS.Add(new OperationRequestHandler()); + HANDLERS.Add(new OperationResponseEndHandler()); + HANDLERS.Add(new OperationResponsePartHandler()); + HANDLERS.Add(new OperationRequestMoreDataHandler()); + HANDLERS.Add(new OperationRequestStopDataHandler()); + HANDLERS.Add(new OperationResponsePauseHandler()); + HANDLERS.Add(new EchoMessageHandler()); + } + private class HelloRequestHandler + : AbstractObjectSerializationHandler { + + public HelloRequestHandler() + : base(typeof(HelloRequest),"HelloRequest") { + } + + + public override sealed Object Deserialize(ObjectDecoder decoder) { + return new HelloRequest(); + } + + public override sealed void Serialize(Object obj, ObjectEncoder encoder) + { + } + + } + private class HelloResponseHandler + : AbstractObjectSerializationHandler { + + public HelloResponseHandler() + : base(typeof(HelloResponse),"HelloResponse") { + } + + + public override sealed Object Deserialize(ObjectDecoder decoder) { + Exception exception = + (Exception)decoder.ReadObjectField("exception",null,null); + IList connectorInfosObj = + (IList)decoder.ReadObjectField("ConnectorInfos",typeof(IList),null); + IList connectorInfos = + CollectionUtil.NewList(connectorInfosObj); + return new HelloResponse(exception,connectorInfos); + } + + public override sealed void Serialize(Object obj, ObjectEncoder encoder) + { + HelloResponse val = (HelloResponse)obj; + encoder.WriteObjectField("exception", val.Exception, false); + encoder.WriteObjectField("ConnectorInfos", val.ConnectorInfos, true); + } + + } + private class OperationRequestHandler + : AbstractObjectSerializationHandler { + + public OperationRequestHandler() + : base(typeof(OperationRequest),"OperationRequest") { + } + + + public override sealed Object Deserialize(ObjectDecoder decoder) { + ConnectorKey connectorKey = + (ConnectorKey)decoder.ReadObjectField("ConnectorKey",typeof(ConnectorKey),null); + APIConfigurationImpl configuration = + (APIConfigurationImpl)decoder.ReadObjectField("APIConfiguration",typeof(APIConfigurationImpl),null); + Type operation = + decoder.ReadClassField("operation",null); + string operationMethodName = + decoder.ReadStringField("operationMethodName",null); + IList arguments = (IList) + decoder.ReadObjectField("Arguments",typeof(IList),null); + return new OperationRequest(connectorKey, + configuration, + SafeType.ForRawType(operation), + operationMethodName, + arguments); + } + + public override sealed void Serialize(Object obj, ObjectEncoder encoder) + { + OperationRequest val = + (OperationRequest)obj; + encoder.WriteClassField("operation", + val.Operation.RawType); + encoder.WriteStringField("operationMethodName", + val.OperationMethodName); + encoder.WriteObjectField("ConnectorKey", + val.ConnectorKey,true); + encoder.WriteObjectField("APIConfiguration", + val.Configuration,true); + encoder.WriteObjectField("Arguments", + val.Arguments,true); + } + + } + private class OperationResponseEndHandler + : AbstractObjectSerializationHandler { + + public OperationResponseEndHandler() + : base(typeof(OperationResponseEnd),"OperationResponseEnd") { + } + + + public override sealed Object Deserialize(ObjectDecoder decoder) { + return new OperationResponseEnd(); + } + + public override sealed void Serialize(Object obj, ObjectEncoder encoder) + { + } + } + private class OperationResponsePartHandler + : AbstractObjectSerializationHandler { + + public OperationResponsePartHandler() + : base(typeof(OperationResponsePart),"OperationResponsePart") { + } + + + public override sealed Object Deserialize(ObjectDecoder decoder) { + Exception exception = + (Exception)decoder.ReadObjectField("exception",null,null); + Object result = + decoder.ReadObjectField("result",null,null); + + return new OperationResponsePart(exception,result); + } + + public override sealed void Serialize(Object obj, ObjectEncoder encoder) + { + OperationResponsePart val = (OperationResponsePart)obj; + encoder.WriteObjectField("exception", val.Exception, false); + encoder.WriteObjectField("result", val.Result, false); + } + } + private class OperationRequestMoreDataHandler + : AbstractObjectSerializationHandler { + + public OperationRequestMoreDataHandler() + : base(typeof(OperationRequestMoreData),"OperationRequestMoreData") { + } + + + public override sealed Object Deserialize(ObjectDecoder decoder) { + return new OperationRequestMoreData(); + } + + public override sealed void Serialize(Object obj, ObjectEncoder encoder) + { + } + } + private class OperationRequestStopDataHandler + : AbstractObjectSerializationHandler { + + public OperationRequestStopDataHandler() + : base(typeof(OperationRequestStopData),"OperationRequestStopData") { + } + + + public override sealed Object Deserialize(ObjectDecoder decoder) { + return new OperationRequestStopData(); + } + + public override sealed void Serialize(Object obj, ObjectEncoder encoder) + { + } + } + private class OperationResponsePauseHandler + : AbstractObjectSerializationHandler { + + public OperationResponsePauseHandler() + : base(typeof(OperationResponsePause),"OperationResponsePause") { + } + + + public override sealed Object Deserialize(ObjectDecoder decoder) { + return new OperationResponsePause(); + } + + public override sealed void Serialize(Object obj, ObjectEncoder encoder) + { + } + } + private class EchoMessageHandler + : AbstractObjectSerializationHandler { + + public EchoMessageHandler() + : base(typeof(EchoMessage),"EchoMessage") { + } + + + public override sealed Object Deserialize(ObjectDecoder decoder) { + return new EchoMessage(decoder.ReadObjectField("value",null,null), + (String)decoder.ReadObjectField("objectXml",typeof(string),null)); + } + + public override sealed void Serialize(Object obj, ObjectEncoder encoder) + { + EchoMessage val = (EchoMessage)obj; + encoder.WriteObjectField("value",val.Object,false); + encoder.WriteObjectField("objectXml",val.ObjectXml,true); + } + } + + } + #endregion + +} diff --git a/DotNetConnectors.sln/FrameworkInternal/SerializerBinary.cs b/DotNetConnectors.sln/FrameworkInternal/SerializerBinary.cs new file mode 100644 index 00000000..c98692ac --- /dev/null +++ b/DotNetConnectors.sln/FrameworkInternal/SerializerBinary.cs @@ -0,0 +1,775 @@ +/* + * ==================== + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER. + * + * Copyright 2007-2008 Sun Microsystems, Inc. All rights reserved. + * + * The contents of this file are subject to the terms of the Common Development + * and Distribution License("CDDL") (the "License"). You may not use this file + * except in compliance with the License. + * + * You can obtain a copy of the License at + * http://IdentityConnectors.dev.java.net/legal/license.txt + * See the License for the specific language governing permissions and limitations + * under the License. + * + * When distributing the Covered Code, include this CDDL Header Notice in each file + * and include the License file at identityconnectors/legal/license.txt. + * If applicable, add the following below this CDDL Header, with the fields + * enclosed by brackets [] replaced by your own identifying information: + * "Portions Copyrighted [year] [name of copyright owner]" + * ==================== + */ +using System; +using System.Collections.Generic; +using Org.IdentityConnectors.Common; +using Org.IdentityConnectors.Framework.Common.Exceptions; +using Org.IdentityConnectors.Framework.Common.Serializer; +using Org.IdentityConnectors.Framework.Impl.Serializer; +using System.IO; +using System.Text; +namespace Org.IdentityConnectors.Framework.Impl.Serializer.Binary +{ + + internal class InternalEncoder { + + + /** + * Mapping from type name to the ID we serialize so we only have to + */ + private IDictionary _constantPool = + new Dictionary(); + + private IList _constantBuffer = new List(); + + private IList _outputBufferStack = new List(); + internal Stream _rootOutput; + private bool _firstObject = true; + + public InternalEncoder(Stream output) { + _rootOutput = output; + } + + public void WriteObject(ObjectEncoder encoder, Object obj) { + + if (_firstObject) { + WriteInt(BinaryObjectEncoder.OBJECT_MAGIC); + WriteInt(BinaryObjectEncoder.ENCODING_VERSION); + _firstObject = false; + } + + MemoryStream objectBuffer = new MemoryStream(); + _outputBufferStack.Add(objectBuffer); + + if ( obj == null ) { + WriteByte(BinaryObjectEncoder.OBJECT_TYPE_NULL); + } + else { + Type clazz = obj.GetType(); + WriteClass(clazz); + ObjectSerializationHandler handler = + ObjectSerializerRegistry.GetHandlerByObjectType(clazz); + if ( handler == null ) { + //we may have special handlers for certain types of arrays + //if handler is null, treat like any other array + if ( obj is Array ) { + Array array = (Array)obj; + int length = array.Length; + for ( int i = 0; i < length; i++ ) { + Object val = array.GetValue(i); + StartAnonymousField(); + WriteObject(encoder,val); + EndField(); + } + } + else { + throw new ConnectorException("No serializer for class: "+clazz); + } + } + else { + handler.Serialize(obj, encoder); + } + } + + //write end-object into the current obj buffer + WriteByte(BinaryObjectEncoder.FIELD_TYPE_END_OBJECT); + + //pop the stack + _outputBufferStack.RemoveAt(_outputBufferStack.Count-1); + + //it's a top-level object, flush the constant pool + if (_outputBufferStack.Count == 0) { + WriteInt(_constantBuffer.Count); + foreach (String constant in _constantBuffer) { + WriteString(constant,false); + WriteInt(_constantPool[constant]); + } + _constantBuffer.Clear(); + } + + //now write the actual object + objectBuffer.Close(); + byte [] bytes = objectBuffer.ToArray(); + WriteBytes(bytes); + } + + public void WriteClass(Type clazz) + { + ObjectSerializationHandler handler = + ObjectSerializerRegistry.GetHandlerByObjectType(clazz); + ObjectTypeMapper mapper = + ObjectSerializerRegistry.GetMapperByObjectType(clazz); + if ( handler == null && clazz.IsArray ) { + //we may have special handlers for certain types of arrays + //if handler is null, treat like any other array + WriteByte(BinaryObjectEncoder.OBJECT_TYPE_ARRAY); + WriteClass(clazz.GetElementType()); + } + else if ( mapper == null ) { + throw new ConnectorException("No serializer for class: "+clazz); + } + else { + String typeName = mapper.HandledSerialType; + WriteByte(BinaryObjectEncoder.OBJECT_TYPE_CLASS); + WriteString(typeName,true); + } + } + + public void StartAnonymousField() { + WriteByte(BinaryObjectEncoder.FIELD_TYPE_ANONYMOUS_FIELD); + MemoryStream buf = new MemoryStream(); + _outputBufferStack.Add(buf); + } + + public void StartField(String name) { + WriteByte(BinaryObjectEncoder.FIELD_TYPE_NAMED_FIELD); + WriteString(name,true); + MemoryStream buf = new MemoryStream(); + _outputBufferStack.Add(buf); + } + + public void EndField() { + MemoryStream buf = _outputBufferStack[_outputBufferStack.Count-1]; + _outputBufferStack.RemoveAt(_outputBufferStack.Count-1); + buf.Close(); + byte [] bytes = buf.ToArray(); + WriteByteArray(bytes); + } + + public void WriteInt(int v) + { + Stream output = GetCurrentOutput(); + output.WriteByte((byte)(0xff & (v >> 24))); + output.WriteByte((byte)(0xff & (v >> 16))); + output.WriteByte((byte)(0xff & (v >> 8))); + output.WriteByte((byte)(0xff & v)); + } + + public void WriteLong(long v) + { + Stream output = GetCurrentOutput(); + output.WriteByte((byte)(0xff & (v >> 56))); + output.WriteByte((byte)(0xff & (v >> 48))); + output.WriteByte((byte)(0xff & (v >> 40))); + output.WriteByte((byte)(0xff & (v >> 32))); + output.WriteByte((byte)(0xff & (v >> 24))); + output.WriteByte((byte)(0xff & (v >> 16))); + output.WriteByte((byte)(0xff & (v >> 8))); + output.WriteByte((byte)(0xff & v)); + } + + public void WriteDouble(double l) + { + long val = BitConverter.DoubleToInt64Bits(l); + WriteLong(val); + } + + public void WriteByteArray(byte[] v) + { + WriteInt(v.Length); + WriteBytes(v); + } + + public void WriteByte(byte b) + { + GetCurrentOutput().WriteByte(b); + } + + public void WriteBoolean(bool b) + { + WriteByte(b ? (byte)1 : (byte)0); + } + + public void WriteString(String str, bool intern) + { + if ( intern ) { + int code = InternIdentifier(str); + WriteInt(code); + return; + } + byte [] bytes = Encoding.UTF8.GetBytes(str); + WriteByteArray(bytes); + } + + private int InternIdentifier(String name) { + int code = CollectionUtil.GetValue(_constantPool,name,-1); + if ( code == -1 ) { + code = _constantPool.Count; + _constantPool[name] = code; + _constantBuffer.Add(name); + } + return code; + } + + private void WriteBytes(byte [] v) + { + //only write if length > 0 - C# seems to have a problem with + //zero-length byte arrays + if ( v.Length > 0 ) { + GetCurrentOutput().Write(v,0,v.Length); + } + } + + private Stream GetCurrentOutput() { + if (_outputBufferStack.Count == 0) { + return _rootOutput; + } + else { + MemoryStream buf = _outputBufferStack[_outputBufferStack.Count-1]; + return buf; + } + } + + } + + + + internal class BinaryObjectEncoder : ObjectEncoder,BinaryObjectSerializer { + + + + public const int ENCODING_VERSION = 1; + + public const int OBJECT_MAGIC = 0xFAFB; + + public const byte OBJECT_TYPE_NULL = 60; + public const byte OBJECT_TYPE_CLASS = 61; + public const byte OBJECT_TYPE_ARRAY = 62; + + public const byte FIELD_TYPE_ANONYMOUS_FIELD = 70; + public const byte FIELD_TYPE_NAMED_FIELD = 71; + public const byte FIELD_TYPE_END_OBJECT = 72; + + + private InternalEncoder _internalEncoder; + + + + + public BinaryObjectEncoder(Stream output) { + _internalEncoder = new InternalEncoder(new BufferedStream(output,4096)); + } + + public void Flush() { + _internalEncoder._rootOutput.Flush(); + } + + public void Close() { + _internalEncoder._rootOutput.Close(); + } + + public void WriteObject(Object o) { + _internalEncoder.WriteObject(this,o); + } + + public void WriteBooleanContents(bool v) { + _internalEncoder.StartAnonymousField(); + _internalEncoder.WriteBoolean(v); + _internalEncoder.EndField(); + } + + public void WriteBooleanField(String fieldName, bool v) { + _internalEncoder.StartField(fieldName); + _internalEncoder.WriteBoolean(v); + _internalEncoder.EndField(); + } + + public void WriteByteArrayContents(byte[] v) { + _internalEncoder.StartAnonymousField(); + _internalEncoder.WriteByteArray(v); + _internalEncoder.EndField(); + } + + public void WriteClassContents(Type v) { + _internalEncoder.StartAnonymousField(); + _internalEncoder.WriteClass(v); + _internalEncoder.EndField(); + } + + public void WriteClassField(string fieldName, Type v) { + if ( v != null ) { + _internalEncoder.StartField(fieldName); + _internalEncoder.WriteClass(v); + _internalEncoder.EndField(); + } + } + + public void WriteDoubleContents(double v) { + _internalEncoder.StartAnonymousField(); + _internalEncoder.WriteDouble(v); + _internalEncoder.EndField(); + } + + public void WriteDoubleField(String fieldName, double v) { + _internalEncoder.StartField(fieldName); + _internalEncoder.WriteDouble(v); + _internalEncoder.EndField(); + } + + public void WriteFloatContents(float v) { + _internalEncoder.StartAnonymousField(); + //write as double since C# only knows how to deal with that + _internalEncoder.WriteDouble((double)v); + _internalEncoder.EndField(); + } + + public void WriteFloatField(String fieldName, float v) { + _internalEncoder.StartField(fieldName); + //write as double since C# only knows how to deal with that + _internalEncoder.WriteDouble((double)v); + _internalEncoder.EndField(); + } + + public void WriteIntContents(int v) { + _internalEncoder.StartAnonymousField(); + _internalEncoder.WriteInt(v); + _internalEncoder.EndField(); + } + + public void WriteIntField(String fieldName, int v) { + _internalEncoder.StartField(fieldName); + _internalEncoder.WriteInt(v); + _internalEncoder.EndField(); + } + + public void WriteLongContents(long v) { + _internalEncoder.StartAnonymousField(); + _internalEncoder.WriteLong(v); + _internalEncoder.EndField(); + } + + public void WriteLongField(String fieldName, long v) { + _internalEncoder.StartField(fieldName); + _internalEncoder.WriteLong(v); + _internalEncoder.EndField(); + } + + public void WriteObjectContents(Object obj) { + _internalEncoder.StartAnonymousField(); + _internalEncoder.WriteObject(this,obj); + _internalEncoder.EndField(); + } + + public void WriteObjectField(String fieldName, Object obj, bool inline) { + _internalEncoder.StartField(fieldName); + _internalEncoder.WriteObject(this,obj); + _internalEncoder.EndField(); + } + + public void WriteStringContents(String str) { + _internalEncoder.StartAnonymousField(); + _internalEncoder.WriteString(str,false); + _internalEncoder.EndField(); + } + + public void WriteStringField(String fieldName, String val) { + if ( val != null ) { + _internalEncoder.StartField(fieldName); + _internalEncoder.WriteString(val,false); + _internalEncoder.EndField(); + } + } + } + + internal class ReadState { + public IDictionary objectFields = new Dictionary(); + public IList anonymousFields = new List(); + public Stream currentInput; + public ReadState() { + } + public bool StartField(String name) { + currentInput = null; + byte [] content = CollectionUtil.GetValue(objectFields,name,null); + if ( content == null ) { + return false; + } + else { + currentInput = new MemoryStream(content); + return true; + } + } + public void StartAnonymousField(int index) { + if ( index >= anonymousFields.Count ) { + throw new ConnectorException("Anonymous content not found"); + } + currentInput = new MemoryStream(anonymousFields[index]); + } + } + + internal class InternalDecoder { + private readonly byte [] _int_buf = new byte[4]; + private readonly byte [] _long_buf = new byte[8]; + private readonly byte [] _byte_buf = new byte[1]; + + private bool _firstObject = true; + + private readonly IDictionary _constantPool = + new Dictionary(); + + private readonly IList _readStateStack = new List(); + internal readonly Stream _rootInput; + + public InternalDecoder(Stream input) { + _rootInput = input; + } + + public Object ReadObject(ObjectDecoder decoder) { + + if (_firstObject) { + int magic = ReadInt(); + if ( magic != BinaryObjectEncoder.OBJECT_MAGIC) { + throw new ConnectorException("Bad magic number: "+magic); + } + int version = ReadInt(); + if ( version != BinaryObjectEncoder.ENCODING_VERSION ) { + throw new ConnectorException("Unexpected version: "+version); + } + _firstObject = false; + } + + //if it's a top-level object, it's proceeded by a constant pool + if (_readStateStack.Count == 0) + { + int size = ReadInt(); + for ( int i = 0; i < size; i++ ) { + String constant = ReadString(false); + int code = ReadInt(); + _constantPool[code] = constant; + } + } + + Type clazz = ReadClass(); + + ReadState state = new ReadState(); + while(true) { + byte type = ReadByte(); + if ( type == BinaryObjectEncoder.FIELD_TYPE_END_OBJECT ) { + break; + } + else if ( type == BinaryObjectEncoder.FIELD_TYPE_ANONYMOUS_FIELD ) { + byte [] bytes = ReadByteArray(); + state.anonymousFields.Add(bytes); + } + else if ( type == BinaryObjectEncoder.FIELD_TYPE_NAMED_FIELD ) { + String fieldName = ReadString(true); + byte [] bytes = ReadByteArray(); + state.objectFields[fieldName] = bytes; + } + else { + throw new ConnectorException("Unknown type: "+type); + } + } + _readStateStack.Add(state); + + Object rv; + if ( clazz == null ) { + rv = null; + } + else { + ObjectSerializationHandler handler = + ObjectSerializerRegistry.GetHandlerByObjectType(clazz); + if ( handler == null ) { + //we may have special handlers for certain types of arrays + //if handler is null, treat like any other array + if ( clazz.IsArray ) { + int length = GetNumAnonymousFields(); + Array array = Array.CreateInstance(clazz.GetElementType(), + length); + for ( int i = 0; i < length; i++) { + StartAnonymousField(i); + Object element = ReadObject(decoder); + array.SetValue( element, i ); + } + rv = array; + } + else { + throw new ConnectorException("No deserializer for type: "+clazz); + } + } + else { + rv = handler.Deserialize(decoder); + } + } + _readStateStack.RemoveAt(_readStateStack.Count-1); + return rv; + } + + public Type ReadClass() { + int type = ReadByte(); + if ( type == BinaryObjectEncoder.OBJECT_TYPE_NULL ) { + return null; + } + else if ( type == BinaryObjectEncoder.OBJECT_TYPE_ARRAY ) { + Type componentClass = ReadClass(); + return componentClass.MakeArrayType(); + } + else if ( type == BinaryObjectEncoder.OBJECT_TYPE_CLASS ) { + String typeName = ReadString(true); + ObjectTypeMapper mapper = + ObjectSerializerRegistry.GetMapperBySerialType(typeName); + if ( mapper == null ) { + throw new ConnectorException("No deserializer for type: "+typeName); + } + return mapper.HandledObjectType; + } + else { + throw new ConnectorException("Bad type value: "+type); + } + } + + public int GetNumAnonymousFields() { + ReadState readState = _readStateStack[_readStateStack.Count-1]; + return readState.anonymousFields.Count; + } + + public void StartAnonymousField(int index) { + ReadState readState = _readStateStack[_readStateStack.Count-1]; + readState.StartAnonymousField(index); + } + + public bool StartField(String name) { + ReadState readState = _readStateStack[_readStateStack.Count-1]; + return readState.StartField(name); + } + + public int ReadInt() { + ReadByteArrayFully(_int_buf); + return (((_int_buf[0] & 0xff) << 24) | ((_int_buf[1] & 0xff) << 16) | + ((_int_buf[2] & 0xff) << 8) | (_int_buf[3] & 0xff)); + } + + public long ReadLong() { + ReadByteArrayFully(_long_buf); + return (((long)(_long_buf[0] & 0xff) << 56) | + ((long)(_long_buf[1] & 0xff) << 48) | + ((long)(_long_buf[2] & 0xff) << 40) | + ((long)(_long_buf[3] & 0xff) << 32) | + ((long)(_long_buf[4] & 0xff) << 24) | + ((long)(_long_buf[5] & 0xff) << 16) | + ((long)(_long_buf[6] & 0xff) << 8) | + ((long)(_long_buf[7] & 0xff))); + } + + public double ReadDouble() { + long v = ReadLong(); + return BitConverter.Int64BitsToDouble(v); + } + + public byte [] ReadByteArray() { + int len = ReadInt(); + byte [] bytes = new byte[len]; + ReadByteArrayFully(bytes); + return bytes; + } + + public byte ReadByte() + { + ReadByteArrayFully(_byte_buf); + return _byte_buf[0]; + } + + public bool ReadBoolean() { + byte b = ReadByte(); + return b != 0; + } + + public String ReadString(bool interned) { + if ( interned ) { + int code = ReadInt(); + String name = CollectionUtil.GetValue(_constantPool,code,null); + if ( name == null ) { + throw new ConnectorException("Undeclared code: "+code); + } + return name; + } + byte [] bytes = ReadByteArray(); + return Encoding.UTF8.GetString(bytes); + } + + private Stream GetCurrentInput() { + if (_readStateStack.Count > 0) { + ReadState state = _readStateStack[_readStateStack.Count-1]; + return state.currentInput; + } + else { + return _rootInput; + } + } + + private void ReadByteArrayFully(byte [] bytes) + { + int pos = 0; + while ( pos < bytes.Length ) { + int count = GetCurrentInput().Read(bytes,pos,bytes.Length-pos); + if ( count <= 0 ) { + throw new EndOfStreamException(); + } + pos+=count; + } + } + } + + internal class BinaryObjectDecoder : ObjectDecoder,BinaryObjectDeserializer { + + private InternalDecoder _internalDecoder; + + public BinaryObjectDecoder(Stream inp) { + _internalDecoder = new InternalDecoder(new BufferedStream(inp,4096)); + } + + public void Close() { + _internalDecoder._rootInput.Close(); + } + + public Object ReadObject() { + return _internalDecoder.ReadObject(this); + } + + public bool ReadBooleanContents() { + _internalDecoder.StartAnonymousField(0); + return _internalDecoder.ReadBoolean(); + } + + public bool ReadBooleanField(String fieldName, bool dflt) { + if (_internalDecoder.StartField(fieldName)) { + return _internalDecoder.ReadBoolean(); + } + else { + return dflt; + } + } + + public byte[] ReadByteArrayContents() { + _internalDecoder.StartAnonymousField(0); + return _internalDecoder.ReadByteArray(); + } + + public Type ReadClassContents() { + _internalDecoder.StartAnonymousField(0); + return _internalDecoder.ReadClass(); + } + + public Type ReadClassField(string fieldName, Type dflt) { + if (_internalDecoder.StartField(fieldName)) { + return _internalDecoder.ReadClass(); + } + else { + return dflt; + } + } + + public double ReadDoubleContents() { + _internalDecoder.StartAnonymousField(0); + return _internalDecoder.ReadDouble(); + } + + public double ReadDoubleField(String fieldName, double dflt) { + if (_internalDecoder.StartField(fieldName)) { + return ReadDoubleContents(); + } + else { + return dflt; + } + } + + public float ReadFloatContents() { + _internalDecoder.StartAnonymousField(0); + //read as double since C# only knows how to deal with that + return (float)_internalDecoder.ReadDouble(); + } + + public float ReadFloatField(String fieldName, float dflt) { + if (_internalDecoder.StartField(fieldName)) { + //read as double since C# only knows how to deal with that + return (float)_internalDecoder.ReadDouble(); + } + else { + return dflt; + } + } + + public int ReadIntContents() { + _internalDecoder.StartAnonymousField(0); + return _internalDecoder.ReadInt(); + } + + public int ReadIntField(String fieldName, int dflt) { + if (_internalDecoder.StartField(fieldName)) { + return _internalDecoder.ReadInt(); + } + else { + return dflt; + } + } + + public long ReadLongContents() { + _internalDecoder.StartAnonymousField(0); + return _internalDecoder.ReadLong(); + } + + public long ReadLongField(String fieldName, long dflt) { + if (_internalDecoder.StartField(fieldName)) { + return _internalDecoder.ReadLong(); + } + else { + return dflt; + } + } + + public int GetNumSubObjects() + { + return _internalDecoder.GetNumAnonymousFields(); + } + + public Object ReadObjectContents(int index) { + _internalDecoder.StartAnonymousField(index); + return _internalDecoder.ReadObject(this); + } + + public Object ReadObjectField(String fieldName, Type expected, Object dflt) { + if (_internalDecoder.StartField(fieldName)) { + return _internalDecoder.ReadObject(this); + } + else { + return dflt; + } + } + + public String ReadStringContents() { + _internalDecoder.StartAnonymousField(0); + return _internalDecoder.ReadString(false); + } + + public String ReadStringField(String fieldName, String dflt) { + if (_internalDecoder.StartField(fieldName)) { + return _internalDecoder.ReadString(false); + } + else { + return dflt; + } + } + + } + + +} diff --git a/DotNetConnectors.sln/FrameworkInternal/SerializerXml.cs b/DotNetConnectors.sln/FrameworkInternal/SerializerXml.cs new file mode 100644 index 00000000..44556848 --- /dev/null +++ b/DotNetConnectors.sln/FrameworkInternal/SerializerXml.cs @@ -0,0 +1,794 @@ +/* + * ==================== + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER. + * + * Copyright 2007-2008 Sun Microsystems, Inc. All rights reserved. + * + * The contents of this file are subject to the terms of the Common Development + * and Distribution License("CDDL") (the "License"). You may not use this file + * except in compliance with the License. + * + * You can obtain a copy of the License at + * http://IdentityConnectors.dev.java.net/legal/license.txt + * See the License for the specific language governing permissions and limitations + * under the License. + * + * When distributing the Covered Code, include this CDDL Header Notice in each file + * and include the License file at identityconnectors/legal/license.txt. + * If applicable, add the following below this CDDL Header, with the fields + * enclosed by brackets [] replaced by your own identifying information: + * "Portions Copyrighted [year] [name of copyright owner]" + * ==================== + */ +using System; +using System.Collections.Generic; +using System.IO; +using System.Resources; +using System.Text; +using System.Net; +using System.Xml; +using Org.IdentityConnectors.Common; +using Org.IdentityConnectors.Framework.Common.Exceptions; +using Org.IdentityConnectors.Framework.Common.Serializer; +namespace Org.IdentityConnectors.Framework.Impl.Serializer.Xml +{ + public class XmlObjectEncoder : ObjectEncoder { + + private StringBuilder _rootBuilder; + private XmlWriter _writer; + + public XmlObjectEncoder(StringBuilder builder) { + Assertions.NullCheck(builder, "builder"); + _rootBuilder = builder; + } + + public String WriteObject(Object o) { + XmlWriterSettings settings = new XmlWriterSettings(); + settings.Indent = true; + settings.OmitXmlDeclaration = true; + _writer = XmlWriter.Create(_rootBuilder, settings); + String rv = WriteObjectInternal(o,false); + _writer.Close(); + return rv; + } + + public void WriteBooleanContents(bool v) { + WriteStringContentsInternal(EncodeBoolean(v)); + } + + public void WriteBooleanField(String fieldName, bool v) { + WriteAttributeInternal(fieldName,EncodeBoolean(v)); + } + + public void WriteByteArrayContents(byte[] v) { + WriteStringContentsInternal(EncodeByteArray(v)); + } + + public void WriteClassContents(Type v) { + WriteStringContentsInternal(EncodeClass(v)); + } + + public void WriteClassField(String name, Type v) { + if ( v != null ) { + WriteAttributeInternal(name, EncodeClass(v)); + } + } + + public void WriteDoubleContents(double v) { + WriteStringContentsInternal(EncodeDouble(v)); + } + + public void WriteDoubleField(String fieldName, double v) { + WriteAttributeInternal(fieldName,EncodeDouble(v)); + } + + public void WriteFloatContents(float v) { + WriteStringContentsInternal(EncodeFloat(v)); + } + + public void WriteFloatField(String fieldName, float v) { + WriteAttributeInternal(fieldName,EncodeFloat(v)); + } + + public void WriteIntContents(int v) { + WriteStringContentsInternal(EncodeInt(v)); + } + + public void WriteIntField(String fieldName, int v) { + WriteAttributeInternal(fieldName,EncodeInt(v)); + } + + public void WriteLongContents(long v) { + WriteStringContentsInternal(EncodeLong(v)); + } + + public void WriteLongField(String fieldName, long v) { + WriteAttributeInternal(fieldName,EncodeLong(v)); + } + + public void WriteObjectContents(Object o) { + WriteObjectInternal(o,false); + } + + public void WriteObjectField(String fieldName, Object obj, bool inline) { + if (inline && obj == null) + { + return; //don't write anything + } + BeginElement(fieldName); + WriteObjectInternal(obj,inline); + EndElement(); + } + + public void WriteStringContents(String str) { + WriteStringContentsInternal(str); + } + + public void WriteStringField(String fieldName, String str) { + if ( str != null ) { + WriteAttributeInternal(fieldName, str); + } + } + + internal static String EncodeBoolean(bool b) { + return b.ToString(); + } + + private static String EncodeByteArray(byte [] bytes) { + return Convert.ToBase64String(bytes); + } + + private static String EncodeClass(Type clazz) { + ObjectSerializationHandler handler = + ObjectSerializerRegistry.GetHandlerByObjectType(clazz); + ObjectTypeMapper mapper = + ObjectSerializerRegistry.GetMapperByObjectType(clazz); + if ( handler == null && clazz.IsArray ) { + //we may have special handlers for certain types of arrays + //if handler is null, treat like any other array + return EncodeClass(clazz.GetElementType())+"[]"; + } + else if ( mapper == null ) { + throw new ConnectorException("No serializer for class: "+clazz); + } + else { + String typeName = mapper.HandledSerialType; + return typeName; + } + } + + internal static String EncodeDouble(double d) { + return d.ToString("R"); + } + + internal static String EncodeFloat(float d) { + return d.ToString("R"); + } + + internal static String EncodeInt(int d) { + return d.ToString(); + } + + internal static String EncodeLong(long d) { + return d.ToString(); + } + + /** + * Writes the object + * @param object + * @param inline + * @return The type name (regardless of whether it was inlined) + */ + String WriteObjectInternal(Object obj, bool inline) { + if ( obj == null ) { + if ( inline ) { + throw new ArgumentException("null cannot be inlined"); + } + BeginElement("null"); + EndElement(); + return "null"; + } + else { + Type clazz = obj.GetType(); + ObjectSerializationHandler handler = + ObjectSerializerRegistry.GetHandlerByObjectType(clazz); + if ( handler == null ) { + //we may have special handlers for certain types of arrays + //if handler is null, treat like any other array + if ( clazz.IsArray ) { + if (!inline) { + String componentTypeName = EncodeClass(clazz.GetElementType()); + BeginElement("Array"); + WriteAttributeInternal("componentType", componentTypeName); + } + Array array = (Array)obj; + int length = array.Length; + for ( int i = 0; i < length; i++ ) { + Object val = array.GetValue(i); + WriteObjectInternal(val,false); + } + if (!inline) { + EndElement(); + } + return "Array"; + } + else { + throw new ConnectorException("No serializer for class: "+clazz); + } + } + else { + String typeName = EncodeClass(clazz); + if (!inline) { + BeginElement(typeName); + } + handler.Serialize(obj, this); + if (!inline) { + EndElement(); + } + return typeName; + } + } + + } + + ////////////////////////////////////////////////////////////////// + // + // xml encoding + // + ///////////////////////////////////////////////////////////////// + + + + private void BeginElement(String name) { + _writer.WriteStartElement(name); + } + + private void EndElement() { + _writer.WriteEndElement(); + } + private void WriteAttributeInternal(String fieldName, String str) { + _writer.WriteAttributeString(fieldName,str); + } + private void WriteStringContentsInternal(String str) { + _writer.WriteString(str); + } + } + + public class XmlObjectDecoder : ObjectDecoder { + + private readonly XmlElement _node; + private readonly Type _expectedClass; + + public XmlObjectDecoder(XmlElement node, Type expectedClass) { + _node = node; + _expectedClass = expectedClass; + } + + public Object ReadObject() { + return ReadObjectInternal(); + } + + public bool ReadBooleanContents() { + return DecodeBoolean(ReadStringContentsInternal()); + } + + public bool ReadBooleanField(String fieldName, bool dflt) { + return DecodeBoolean(ReadStringAttributeInternal(fieldName,XmlObjectEncoder.EncodeBoolean(dflt))); + } + + public byte[] ReadByteArrayContents() { + return DecodeByteArray(ReadStringContentsInternal()); + } + + public Type ReadClassContents() { + return DecodeClass(ReadStringContentsInternal()); + } + + public Type ReadClassField(String name, Type dflt) { + String val = ReadStringAttributeInternal(name, null); + if ( val == null ) { + return dflt; + } + else { + return DecodeClass(val); + } + } + + public double ReadDoubleContents() { + return DecodeDouble(ReadStringContentsInternal()); + } + + public double ReadDoubleField(String fieldName, double dflt) { + return DecodeDouble(ReadStringAttributeInternal(fieldName,XmlObjectEncoder.EncodeDouble(dflt))); + } + + public float ReadFloatContents() { + return DecodeFloat(ReadStringContentsInternal()); + } + + public float ReadFloatField(String fieldName, float dflt) { + return DecodeFloat(ReadStringAttributeInternal(fieldName,XmlObjectEncoder.EncodeFloat(dflt))); + } + + public int ReadIntContents() { + return DecodeInt(ReadStringContentsInternal()); + } + + public int ReadIntField(String fieldName, int dflt) { + return DecodeInt(ReadStringAttributeInternal(fieldName,XmlObjectEncoder.EncodeInt(dflt))); + } + + public long ReadLongContents() { + return DecodeLong(ReadStringContentsInternal()); + } + + public long ReadLongField(String fieldName, long dflt) { + return DecodeLong(ReadStringAttributeInternal(fieldName,XmlObjectEncoder.EncodeLong(dflt))); + } + + public int GetNumSubObjects() { + int count = 0; + for (XmlElement subElement = XmlUtil.GetFirstChildElement(_node); + subElement != null; + subElement = XmlUtil.GetNextElement(subElement)) { + count++; + } + return count; + } + + public Object ReadObjectContents(int index) { + XmlElement subElement = XmlUtil.GetFirstChildElement(_node); + for ( int i = 0; i < index; i++) { + subElement = XmlUtil.GetNextElement(subElement); + } + + if ( subElement == null ) { + throw new ConnectorException("Missing subelement number: "+index); + } + + return new XmlObjectDecoder(subElement,null).ReadObject(); + } + + public Object ReadObjectField(String fieldName, Type expected, Object dflt) { + XmlElement child = XmlUtil.FindImmediateChildElement(_node, fieldName); + if ( child == null ) { + return dflt; + } + if ( expected != null ) { + return new XmlObjectDecoder(child,expected).ReadObject(); + } + XmlElement subElement = XmlUtil.GetFirstChildElement(child); + if ( subElement == null ) { + return dflt; + } + //if they specify null, don't apply defaults + return new XmlObjectDecoder(subElement,null).ReadObject(); + } + + public String ReadStringContents() { + String rv = ReadStringContentsInternal(); + return rv == null ? "" : rv; + } + + public String ReadStringField(String fieldName, String dflt) { + return ReadStringAttributeInternal(fieldName, dflt); + } + + private String ReadStringContentsInternal() { + String xml = XmlUtil.GetContent(_node); + return xml; + } + + private String ReadStringAttributeInternal(String name, String dflt) { + XmlAttribute attr = _node.GetAttributeNode(name); + if ( attr == null ) { + return dflt; + } + return attr.Value; + } + + private bool DecodeBoolean(String v) { + return Boolean.Parse(v); + } + + private byte [] DecodeByteArray(String base64) { + return Convert.FromBase64String(base64); + } + + private Type DecodeClass(String type) { + if ( type.EndsWith("[]") ) { + String componentName = type.Substring(0,type.Length-"[]".Length); + Type componentClass = + DecodeClass(componentName); + Type arrayClass = + componentClass.MakeArrayType(); + return arrayClass; + } + else { + ObjectTypeMapper mapper = + ObjectSerializerRegistry.GetMapperBySerialType(type); + if ( mapper == null ) { + throw new ConnectorException("No deserializer for type: "+type); + } + Type clazz = mapper.HandledObjectType; + return clazz; + } + } + + private double DecodeDouble(String val) { + return Double.Parse(val); + } + + private float DecodeFloat(String val) { + return Single.Parse(val); + } + + private int DecodeInt(String val) { + return Int32.Parse(val); + } + + private long DecodeLong(String val) { + return Int64.Parse(val); + } + + private Object ReadObjectInternal() { + if (_expectedClass != null) { + ObjectSerializationHandler handler = + ObjectSerializerRegistry.GetHandlerByObjectType(_expectedClass); + if ( handler == null ) { + if (_expectedClass.IsArray) { + IList temp = new List(); + for (XmlElement child = XmlUtil.GetFirstChildElement(_node); child != null; + child = XmlUtil.GetNextElement(child)) { + XmlObjectDecoder sub = new XmlObjectDecoder(child,null); + Object obj = sub.ReadObject(); + temp.Add(obj); + } + int length = temp.Count; + Array array = Array.CreateInstance(_expectedClass.GetElementType(),length); + for ( int i = 0; i < length; i++) { + Object element = temp[i]; + array.SetValue(element,i); + } + return array; + } + else { + throw new ConnectorException("No deserializer for type: "+_expectedClass); + } + } + else { + return handler.Deserialize(this); + } + } + else if ( _node.LocalName.Equals("null") ) { + return null; + } + else if (_node.LocalName.Equals("Array")) { + String componentType = XmlUtil.GetAttribute(_node, "componentType"); + if ( componentType == null ) { + componentType = "Object"; + } + Type componentClass = DecodeClass(componentType); + IList temp = new List(); + for (XmlElement child = XmlUtil.GetFirstChildElement(_node); child != null; + child = XmlUtil.GetNextElement(child)) { + XmlObjectDecoder sub = new XmlObjectDecoder(child,null); + Object obj = sub.ReadObject(); + temp.Add(obj); + } + int length = temp.Count; + Array array = Array.CreateInstance(componentClass, + length); + for ( int i = 0; i < length; i++) { + Object element = temp[i]; + array.SetValue(element,i); + } + return array; + } + else { + Type clazz = + DecodeClass(_node.LocalName); + ObjectSerializationHandler handler = + ObjectSerializerRegistry.GetHandlerByObjectType(clazz); + if ( handler == null ) { + throw new ConnectorException("No deserializer for type: "+clazz); + } + else { + return handler.Deserialize(this); + } + } + } + + } + + public class XmlObjectParser { + + public static void parse(TextReader inputSource, + XmlObjectResultsHandler handler, + bool validate) + { + XmlReaderSettings mySettings = + new XmlReaderSettings(); + if ( validate ) { + mySettings.ValidationType = ValidationType.DTD; + } + mySettings.ProhibitDtd = false; + mySettings.XmlResolver = new MyEntityResolver(validate); + XmlReader reader = XmlReader.Create(inputSource,mySettings); + MyParser parser = new MyParser(handler); + parser.Parse(reader); + } + + + private class MyEntityResolver : XmlResolver + { + private readonly bool _validate; + + public MyEntityResolver(bool validate) + { + _validate = validate; + } + + public override Object GetEntity(Uri absoluteUri, string role, Type ofObject) + { + String text = null; + if (absoluteUri.AbsolutePath.EndsWith(XmlObjectSerializerImpl.CONNECTORS_DTD)) { + if (!_validate) { + text = ""; + } + else { + text = GetDTD(); + } + } + if ( text != null ) { + byte [] bytes = Encoding.UTF8.GetBytes(text); + return new MemoryStream(bytes); + } + return null; + } + + public override ICredentials Credentials { + set { + + } + } + private static String GetDTD() + { + ResourceManager manager = + new ResourceManager("Org.IdentityConnectors.Resources", + typeof(XmlObjectParser).Assembly); + String contents = (String)manager.GetObject(XmlObjectSerializerImpl.CONNECTORS_DTD); + return contents; + } + } + + private class MyParser + { + /** + * The document for the current top-level element. with each top-level element, + * we discard the previous to avoid accumulating memory + */ + private XmlDocument _currentTopLevelElementDocument; + + + /** + * Stack of elements we are creating + */ + private IList _elementStack = new List(10); + + /** + * Results handler that we write our objects to + */ + private readonly XmlObjectResultsHandler _handler; + + /** + * Is the handler still handing + */ + private bool _stillHandling = true; + + + public MyParser(XmlObjectResultsHandler handler) + { + _handler = handler; + } + + public void Parse(XmlReader reader) + { + while ( _stillHandling && reader.Read() ) { + XmlNodeType nodeType = reader.NodeType; + switch (nodeType) { + case XmlNodeType.Element: + StartElement(reader.LocalName); + bool empty = reader.IsEmptyElement; + if (reader.MoveToFirstAttribute()) { + AddAttribute(reader.LocalName,reader.Value); + while (reader.MoveToNextAttribute()) { + AddAttribute(reader.LocalName,reader.Value); + } + } + if ( empty ) { + EndElement(); + } + break; + case XmlNodeType.Text: + case XmlNodeType.CDATA: + case XmlNodeType.Whitespace: + case XmlNodeType.SignificantWhitespace: + AddText(reader.Value); + break; + case XmlNodeType.EndElement: + EndElement(); + break; + } + } + } + + private XmlElement GetCurrentElement() + { + if (_elementStack.Count > 0 ) + { + return _elementStack[_elementStack.Count-1]; + } + else + { + return null; + } + } + + private void AddText(String text) + { + XmlElement currentElement = GetCurrentElement(); + if ( currentElement != null ) + { + currentElement.AppendChild(_currentTopLevelElementDocument.CreateTextNode(text)); + } + } + + private void EndElement() + { + if (_elementStack.Count > 0) //we don't push the top-level MULTI_OBJECT_ELEMENT on the stack + { + XmlElement element = _elementStack[_elementStack.Count-1]; + _elementStack.RemoveAt(_elementStack.Count-1); + if (_elementStack.Count == 0) { + _currentTopLevelElementDocument = null; + if (_stillHandling) { + XmlObjectDecoder decoder = new XmlObjectDecoder(element,null); + Object obj = decoder.ReadObject(); + _stillHandling = _handler(obj); + } + } + } + } + + + private void StartElement(String localName) + { + XmlElement element = null; + if (_elementStack.Count == 0) + { + if (!XmlObjectSerializerImpl.MULTI_OBJECT_ELEMENT.Equals(localName)) + { + _currentTopLevelElementDocument = new XmlDocument(); + element = _currentTopLevelElementDocument.CreateElement(localName); + } + } + else + { + element = + _currentTopLevelElementDocument.CreateElement(localName); + GetCurrentElement().AppendChild(element); + } + if (element != null) + { + _elementStack.Add(element); + } + } + + private void AddAttribute(String name, String val) + { + XmlElement element = GetCurrentElement(); + if ( element != null ) + { + element.SetAttribute(name, val); + } + } + } + } + + public class XmlObjectSerializerImpl : XmlObjectSerializer { + + public const String MULTI_OBJECT_ELEMENT = "MultiObject"; + public const String CONNECTORS_DTD = "connectors.dtd"; + + private readonly TextWriter _output; + + private readonly bool _multiObject; + + private readonly bool _includeHeader; + + private bool _firstObjectWritten; + + private bool _documentEnded; + + public XmlObjectSerializerImpl(TextWriter output, bool includeHeader, bool multiObject) { + _output = output; + _includeHeader = includeHeader; + _multiObject = multiObject; + } + + + /** + * Writes the next object to the stream. + * @param object The object to write. + * @see ObjectSerializerFactory for a list of supported types. + * @throws ConnectorException if there is more than one object + * and this is not configured for multi-object document. + */ + public void WriteObject(Object obj) { + if (_documentEnded) { + throw new InvalidOperationException("Attempt to writeObject after the document is already closed"); + } + StringBuilder buf = new StringBuilder(); + XmlObjectEncoder encoder = new XmlObjectEncoder(buf); + String elementName = encoder.WriteObject(obj); + if (!_firstObjectWritten) { + StartDocument(elementName); + } + else { + if (!_multiObject) { + throw new InvalidOperationException("Attempt to write multiple objects on a single-object document"); + } + } + Write(buf.ToString()); + _firstObjectWritten = true; + } + + public void Flush() { + _output.Flush(); + } + + public void Close( bool closeStream ) { + if (!_documentEnded) { + if (!_firstObjectWritten) { + if (!_multiObject) { + throw new InvalidOperationException("Attempt to write zero objects on a single-object document"); + } + StartDocument(null); + } + WriteEndDocument(); + _documentEnded = true; + } + if (closeStream) { + _output.Close(); + } + } + + private void StartDocument(String firstElement) { + if (_includeHeader) { + String docType = _multiObject ? MULTI_OBJECT_ELEMENT : firstElement; + String line1 = "\n"; + String line2 = "\n"; + Write(line1); + Write(line2); + } + if (_multiObject) { + String line3 = "<"+MULTI_OBJECT_ELEMENT+">\n"; + Write(line3); + } + } + + private void WriteEndDocument() { + if (_multiObject) { + String line1 = "\n"; + Write(line1); + } + } + + private void Write(String str) { + _output.Write(str); + } + + } + +} diff --git a/DotNetConnectors.sln/FrameworkInternal/Server.cs b/DotNetConnectors.sln/FrameworkInternal/Server.cs new file mode 100644 index 00000000..70f3a7a3 --- /dev/null +++ b/DotNetConnectors.sln/FrameworkInternal/Server.cs @@ -0,0 +1,900 @@ +/* + * ==================== + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER. + * + * Copyright 2007-2008 Sun Microsystems, Inc. All rights reserved. + * + * The contents of this file are subject to the terms of the Common Development + * and Distribution License("CDDL") (the "License"). You may not use this file + * except in compliance with the License. + * + * You can obtain a copy of the License at + * http://IdentityConnectors.dev.java.net/legal/license.txt + * See the License for the specific language governing permissions and limitations + * under the License. + * + * When distributing the Covered Code, include this CDDL Header Notice in each file + * and include the License file at identityconnectors/legal/license.txt. + * If applicable, add the following below this CDDL Header, with the fields + * enclosed by brackets [] replaced by your own identifying information: + * "Portions Copyrighted [year] [name of copyright owner]" + * ==================== + */ +using System; +using System.Collections.Generic; +using System.Diagnostics; +using System.Globalization; +using System.Net; +using System.Net.Security; +using System.Security; +using System.Security.Cryptography.X509Certificates; +using System.Net.Sockets; +using System.IO; +using System.Linq; +using System.Threading; +using System.Reflection; +using System.Security.Authentication; +using System.Text; +using Org.IdentityConnectors.Common; +using Org.IdentityConnectors.Common.Security; +using Org.IdentityConnectors.Framework.Api; +using Org.IdentityConnectors.Framework.Api.Operations; +using Org.IdentityConnectors.Framework.Common.Objects; +using Org.IdentityConnectors.Framework.Common.Exceptions; +using Org.IdentityConnectors.Framework.Common.Objects.Filters; +using Org.IdentityConnectors.Framework.Common.Serializer; +using Org.IdentityConnectors.Framework.Server; +using Org.IdentityConnectors.Framework.Impl.Api; +using Org.IdentityConnectors.Framework.Impl.Api.Remote.Messages; +using Org.IdentityConnectors.Framework.Impl.Api.Local; +using Org.IdentityConnectors.Framework.Impl.Api.Local.Operations; +using Org.IdentityConnectors.Framework.Impl.Api.Remote; +namespace Org.IdentityConnectors.Framework.Server +{ + /** + * Connector server interface. + */ + public abstract class ConnectorServer { + + // At some point we might make this pluggable, but for now, hard-code + private const String IMPL_NAME + = "Org.IdentityConnectors.Framework.Impl.Server.ConnectorServerImpl"; + + + /** + * The port to listen on; + */ + private int _port = 0; + + /** + * Base 64 sha1 hash of the connector server key + */ + private String _keyHash; + + /** + * The number of connections to queue + */ + private int _maxConnections = 300; + + /** + * The minimum number of worker threads + */ + private int _minWorkers = 10; + + /** + * The maximum number of worker threads + */ + private int _maxWorkers = 100; + + /** + * The network interface address to use. + */ + private IPAddress _ifAddress = null; + + /** + * Listen on SSL + */ + private bool _useSSL = false; + + /** + * The server certificate to use + */ + private X509Certificate _serverCertificate = null; + + /** + * Get the singleton instance of the {@link ConnectorServer}. + */ + public static ConnectorServer NewInstance() { + SafeType type = + SafeType.ForRawType(Type.GetType(IMPL_NAME,true)); + return type.CreateInstance(); + } + + private void AssertNotStarted() { + if ( IsStarted() ) { + throw new InvalidOperationException("Operation cannot be performed " + + "while server is running"); + } + } + + /** + * Returns the port to listen on. + * @return The port to listen on. + */ + public int Port { + get { + return _port; + } + set { + AssertNotStarted(); + _port = value; + } + } + + /** + * Returns the max connections to queue + * @return The max connections to queue + */ + public int MaxConnections { + get { + return _maxConnections; + } + set { + AssertNotStarted(); + _maxConnections = value; + } + } + + + /** + * Returns the max worker threads to allow. + * @return The max worker threads to allow. + */ + public int MaxWorkers { + get { + return _maxWorkers; + } + set { + AssertNotStarted(); + _maxWorkers = value; + } + } + + + + /** + * Returns the min worker threads to allow. + * @return The min worker threads to allow. + */ + public int MinWorkers { + get { + return _minWorkers; + } + set { + AssertNotStarted(); + _minWorkers = value; + } + } + + + + /** + * Returns the network interface address to bind to. May be null. + * @return The network interface address to bind to or null. + */ + public IPAddress IfAddress { + get { + return _ifAddress; + } + set { + AssertNotStarted(); + _ifAddress = value; + } + } + + /** + * Returns true iff we are to use SSL. + * @return true iff we are to use SSL. + */ + public bool UseSSL { + get { + return _useSSL; + } + set { + AssertNotStarted(); + _useSSL = value; + } + } + + + /** + * Returns the certificate to use for the SSL connection. + */ + public X509Certificate ServerCertificate { + get { + return _serverCertificate; + } + set { + AssertNotStarted(); + _serverCertificate = value; + } + } + + public String KeyHash { + get { + return _keyHash; + } + set { + AssertNotStarted(); + _keyHash = value; + } + } + + /** + * Produces a thread dump of all pending requests + */ + abstract public void DumpRequests(); + + /** + * Starts the server. All server settings must be configured prior + * to calling. The following methods are required to be called: + *
    + *
  • {@link #Port(int)}
  • + *
  • {@link #KeyHash(String)}
  • + *
+ */ + abstract public void Start(); + + /** + * Stops the server gracefully. Returns when all in-progress connections + * have been serviced. + */ + abstract public void Stop(); + + /** + * Return true iff the server is started. Note that started is a + * logical state (start method has been called). It does not necessarily + * reflect the health of the server + * @return true iff the server is started. + */ + abstract public bool IsStarted(); + } +} + +namespace Org.IdentityConnectors.Framework.Impl.Server +{ + public class ConnectionProcessor { + + + private class RemoteResultsHandler : ObjectStreamHandler + { + private const int PAUSE_INTERVAL = 200; + + private readonly RemoteFrameworkConnection _connection; + private long _count = 0; + public RemoteResultsHandler(RemoteFrameworkConnection conn) + { + _connection = conn; + } + + public bool Handle(Object obj) { + try { + OperationResponsePart part = + new OperationResponsePart(null,obj); + _connection.WriteObject(part); + _count++; + if ( _count % PAUSE_INTERVAL == 0 ) { + _connection.WriteObject(new OperationResponsePause()); + Object message = + _connection.ReadObject(); + return message is OperationRequestMoreData; + } + else { + return true; + } + } + catch (IOException e) { + throw new BrokenConnectionException(e); + } + catch (Exception) { + throw; + } + } + } + + private readonly ConnectorServerImpl _server; + private readonly RemoteFrameworkConnection _connection; + + public ConnectionProcessor(ConnectorServerImpl server, + RemoteFrameworkConnection connection) { + _server = server; + _connection = connection; + } + + public void Run() { + try { + _server.BeginRequest(); + try { + while ( true ) { + bool keepGoing = ProcessRequest(); + if (!keepGoing) { + break; + } + } + } + finally { + try { + _connection.Dispose(); + } + catch (Exception e) { + TraceUtil.TraceException(null,e); + } + } + } + catch (Exception e) { + TraceUtil.TraceException(null,e); + } + finally { + _server.EndRequest(); + } + } + + private bool ProcessRequest() + { + + CultureInfo locale; + try { + locale = (CultureInfo)_connection.ReadObject(); + } + catch (EndOfStreamException) { + return false; + } + + //We can't set this because C# does not like language-neutral + //cultures for CurrentCulture - this tends to blow up + //TODO: think more about this... + //Thread.CurrentThread.CurrentCulture = locale; + Thread.CurrentThread.CurrentUICulture = locale; + + GuardedString key = (GuardedString)_connection.ReadObject(); + + bool authorized; + try { + authorized = key.VerifyBase64SHA1Hash(_server.KeyHash); + } + finally { + key.Dispose(); + } + Org.IdentityConnectors.Framework.Common.Exceptions.InvalidCredentialException authException = null; + if (!authorized) { + authException = new Org.IdentityConnectors.Framework.Common.Exceptions.InvalidCredentialException("Remote framework key is invalid"); + } + Object requestObject = _connection.ReadObject(); + if ( requestObject is HelloRequest ) { + if ( authException != null ) { + HelloResponse response = + new HelloResponse(authException,null); + _connection.WriteObject(response); + } + else { + HelloResponse response = + ProcessHelloRequest((HelloRequest)requestObject); + _connection.WriteObject(response); + } + } + else if ( requestObject is OperationRequest ) { + if ( authException != null ) { + OperationResponsePart part = + new OperationResponsePart(authException,null); + _connection.WriteObject(part); + } + else { + OperationRequest opRequest = + (OperationRequest)requestObject; + OperationResponsePart part = + ProcessOperationRequest(opRequest); + _connection.WriteObject(part); + } + } + else if (requestObject is EchoMessage) { + if ( authException != null ) { + //echo message probably doesn't need auth, but + //it couldn't hurt - actually it does for test connection + EchoMessage part = + new EchoMessage(authException,null); + _connection.WriteObject(part); + } + else { + EchoMessage message = (EchoMessage)requestObject; + Object obj = message.Object; + String xml = message.ObjectXml; + if ( xml != null ) { + Console.WriteLine("xml: \n"+xml); + Object xmlClone = + SerializerUtil.DeserializeXmlObject(xml,true); + xml = + SerializerUtil.SerializeXmlObject(xmlClone,true); + } + EchoMessage message2 = new EchoMessage(obj,xml); + _connection.WriteObject(message2); + } + } + else { + throw new Exception("Unexpected request: "+requestObject); + } + return true; + } + + private ConnectorInfoManager GetConnectorInfoManager() { + return ConnectorInfoManagerFactory.GetInstance().GetLocalManager(); + } + + private HelloResponse ProcessHelloRequest(HelloRequest request) { + IList connectorInfo; + Exception exception = null; + try { + ConnectorInfoManager manager = + GetConnectorInfoManager(); + IList localInfos = + manager.ConnectorInfos; + connectorInfo = new List(); + foreach (ConnectorInfo localInfo in localInfos) { + LocalConnectorInfoImpl localInfoImpl = + (LocalConnectorInfoImpl)localInfo; + RemoteConnectorInfoImpl remoteInfo = + localInfoImpl.ToRemote(); + connectorInfo.Add(remoteInfo); + } + } + catch (Exception e) { + TraceUtil.TraceException(null,e); + exception = e; + connectorInfo = null; + } + return new HelloResponse(exception,connectorInfo); + } + + private MethodInfo GetOperationMethod(OperationRequest request) { + MethodInfo [] methods = + request.Operation.RawType.GetMethods(); + MethodInfo found = null; + foreach (MethodInfo m in methods) { + if ( m.Name.ToUpper().Equals(request.OperationMethodName.ToUpper()) ) { + if ( found != null ) { + throw new ConnectorException("APIOperations are expected " + +"to have exactly one method of a given name: "+request.Operation); + } + found = m; + } + } + + if ( found == null ) { + throw new ConnectorException("APIOperations are expected " + +"to have exactly one method of a given name: "+request.Operation+" "+methods.Length); + } + return found; + } + + private OperationResponsePart + ProcessOperationRequest(OperationRequest request) { + Object result; + Exception exception = null; + try { + MethodInfo method = GetOperationMethod(request); + APIOperation operation = GetAPIOperation(request); + IList arguments = request.Arguments; + IList argumentsAndStreamHandlers = + PopulateStreamHandlers(ReflectionUtil.GetParameterTypes(method), + arguments); + try { + Object [] args = argumentsAndStreamHandlers.ToArray(); + FixupArguments(method,args); + result = method.Invoke(operation, args); + } + catch (TargetInvocationException e) { + throw e.InnerException; + } + bool anyStreams = + argumentsAndStreamHandlers.Count > arguments.Count; + if ( anyStreams ) { + try { + _connection.WriteObject(new OperationResponseEnd()); + } + catch (IOException e) { + throw new BrokenConnectionException(e); + } + } + } + catch (BrokenConnectionException w) { + //at this point the stream is broken - just give up + throw w.GetIOException(); + } + catch (Exception e) { + TraceUtil.TraceException(null,e); + exception = e; + result = null; + } + return new OperationResponsePart(exception,result); + } + + private IList PopulateStreamHandlers(Type [] paramTypes, IList arguments) { + IList rv = new List(); + bool firstStream = true; + IEnumerator argIt = arguments.GetEnumerator(); + foreach (Type paramType in paramTypes) { + if ( StreamHandlerUtil.IsAdaptableToObjectStreamHandler(paramType) ) { + if (!firstStream) { + throw new InvalidOperationException("At most one stream handler is supported"); + } + ObjectStreamHandler osh = + new RemoteResultsHandler(_connection); + rv.Add(StreamHandlerUtil.AdaptFromObjectStreamHandler(paramType, osh)); + firstStream = false; + } + else { + argIt.MoveNext(); + rv.Add(argIt.Current); + } + } + return rv; + } + + /// + /// When arguments are serialized, we loose the + /// generic-type of collections. We must fix + /// the arguments + /// + /// + /// + private void FixupArguments(MethodInfo method, + object [] args) + { + Type [] paramTypes = + ReflectionUtil.GetParameterTypes(method); + if (paramTypes.Length != args.Length) { + throw new ArgumentException("Number of arguments does not match for method: "+method); + } + for ( int i = 0; i < args.Length; i++ ) { + args[i] = FixupArgument(paramTypes[i], + args[i]); + } + } + + private object FixupArgument(Type expectedType, + object argument) + { + //at some point, we might want this to be more general-purpose + //for now we just handle those cases that we need to + if (typeof(ICollection).Equals(expectedType)) { + ICollection val = + (ICollection)argument; + return CollectionUtil.NewSet(val); + } + else { + return argument; + } + } + + private APIOperation GetAPIOperation(OperationRequest request) + { + ConnectorInfoManager manager = + GetConnectorInfoManager(); + ConnectorInfo info = manager.FindConnectorInfo( + request.ConnectorKey); + if ( info == null ) { + throw new Exception("No such connector: " + +request.ConnectorKey); + } + APIConfigurationImpl config = + request.Configuration; + + //re-wire the configuration with its connector info + config.ConnectorInfo=(AbstractConnectorInfo)info; + + ConnectorFacade facade = + ConnectorFacadeFactory.GetInstance().NewInstance(config); + + return facade.GetOperation(request.Operation); + } + + private class BrokenConnectionException : Exception { + + + public BrokenConnectionException(IOException ex) + : base("",ex) { + } + + public IOException GetIOException() { + return (IOException)InnerException; + } + } + + } + + class ConnectionListener { + + /** + * This is the size of our internal queue. For now I have this + * relatively small because I want the OS to manage the connect + * queue coming in. That way it can properly turn away excessive + * requests + */ + private const int INTERNAL_QUEUE_SIZE = 2; + + + /** + * The server object that we are using + */ + private readonly ConnectorServerImpl _server; + + /** + * The server socket. This must be bound at the time + * of creation. + */ + private readonly TcpListener _socket; + + /** + * Pool of executors + */ + //TODO: add a thread pool + //private readonly ExecutorService _threadPool; + + /** + * Set to indicated we need to start shutting down + */ + private bool _stopped = false; + + private Thread _thisThread; + + private readonly Object MUTEX = new Object(); + + /** + * Creates the listener thread + * @param server The server object + * @param socket The socket (should already be bound) + */ + public ConnectionListener(ConnectorServerImpl server, + TcpListener socket) { + _server = server; + _socket = socket; + //TODO: thread pool +/* _threadPool = + new ThreadPoolExecutor + (server.getMinWorkers(), + server.getMaxWorkers(), + 30, //idle time timeout + TimeUnit.SECONDS, + new ArrayBlockingQueue( + INTERNAL_QUEUE_SIZE, + true)); //fair*/ + } + + + + public void Run(Object o) { + Trace.TraceInformation("Server started on port: "+_server.Port); + _thisThread = (Thread)o; + while (!IsStopped()) { + try { + TcpClient connection = null; + Stream stream = null; + try { + connection = _socket.AcceptTcpClient(); + stream = connection.GetStream(); + if ( _server.UseSSL ) + { + SslStream sslStream = new SslStream(stream,false); + stream = sslStream; + sslStream.AuthenticateAsServer(_server.ServerCertificate, + false, + SslProtocols.Tls, + false); + } + + ConnectionProcessor processor = + new ConnectionProcessor(_server, + new RemoteFrameworkConnection(connection,stream)); + Thread thread = new Thread(processor.Run); + thread.IsBackground = false; + thread.Start(); + } + catch (Exception) { + if ( stream != null ) + { + try { stream.Close(); } catch (Exception) {} + } + if ( connection != null ) + { + try { connection.Close(); } catch (Exception) {} + } + throw; + } + } + catch (Exception e) { + //log the error unless it's because we've stopped + if (!IsStopped() || !(e is SocketException)) { + TraceUtil.TraceException("Error processing request",e); + } + //wait a second before trying again + if (!IsStopped()) { + Thread.Sleep(1000); + } + } + } + } + + private void MarkStopped() { + lock(MUTEX) { + _stopped = true; + } + } + + private bool IsStopped() { + lock(MUTEX) { + return _stopped; + } + } + + public void Shutdown() { + if (Object.ReferenceEquals(Thread.CurrentThread,_thisThread)) { + throw new ArgumentException("Shutdown may not be called from this thread"); + } + if (!IsStopped()) { + //set the stopped flag so we no its a normal + //shutdown and don't log the SocketException + MarkStopped(); + //close the socket - this causes accept to throw an exception + _socket.Stop(); + //wait for the main listener thread to die so we don't + //get any new requests + _thisThread.Join(); + //TODO: shutdown thread pool + //wait for all in-progress requests to finish + //_threadPool.shutdown(); + } + } + } + + internal class RequestStats { + public RequestStats() { + } + public Thread RequestThread { get; set; } + public long StartTimeMillis { get; set; } + public long RequestID { get; set; } + } + + public class ConnectorServerImpl : ConnectorServer { + + private readonly IDictionary + _pendingRequests = CollectionUtil.NewIdentityDictionary(); + private ConnectionListener _listener; + private Object COUNT_LOCK = new Object(); + private long _requestCount = 0; + + public override bool IsStarted() { + return _listener != null; + } + + public void BeginRequest() { + long requestID; + lock(COUNT_LOCK) { + requestID = _requestCount++; + } + Thread requestThread = Thread.CurrentThread; + RequestStats stats = new RequestStats(); + stats.StartTimeMillis = + DateTimeUtil.GetCurrentUtcTimeMillis(); + stats.RequestThread = Thread.CurrentThread; + stats.RequestID = requestID; + lock (_pendingRequests) { + _pendingRequests[stats.RequestThread] + = stats; + } + } + + public void EndRequest() { + lock (_pendingRequests) { + _pendingRequests.Remove(Thread.CurrentThread); + } + } + public override void DumpRequests() { + long currentTime = DateTimeUtil.GetCurrentUtcTimeMillis(); + IDictionary + pending; + lock(_pendingRequests) { + pending = new Dictionary(_pendingRequests); + } + StringBuilder builder = new StringBuilder(); + builder.Append("****Pending Requests Summary*****"); + foreach (RequestStats stats in pending.Values) { + DumpStats(stats,builder,currentTime); + } + //here we purposefully use write line since + //we always want to see it. in general, don't + //use this method + Trace.WriteLine(builder.ToString()); + } + + private void DumpStats(RequestStats stats, + StringBuilder builder, + long currentTime) + { + builder.AppendLine("**Request #"+stats.RequestID+" pending for "+(currentTime-stats.StartTimeMillis)+" millis."); + StackTrace stackTrace = GetStackTrace(stats.RequestThread); + if ( stackTrace == null ) { + builder.AppendLine(" "); + } + else { + builder.AppendLine(stackTrace.ToString()); + } + } + + private static StackTrace GetStackTrace(Thread thread) { + bool suspended = false; + try { + thread.Suspend(); + suspended = true; + return new StackTrace(thread,true); + } + catch (ThreadStateException) { + return null; //we missed this one + } + finally { + if ( suspended ) { + thread.Resume(); + } + } + } + + public override void Start() { + if ( IsStarted() ) { + throw new InvalidOperationException("Server is already running."); + } + if ( Port == 0 ) { + throw new InvalidOperationException("Port must be set prior to starting server."); + } + if (KeyHash == null) { + throw new InvalidOperationException("Key hash must be set prior to starting server."); + } + if ( UseSSL && ServerCertificate == null ) { + throw new InvalidOperationException("ServerCertificate must be set if using SSL."); + } + //make sure we are configured properly + ConnectorInfoManagerFactory.GetInstance().GetLocalManager(); + _requestCount = 0; + _pendingRequests.Clear(); + TcpListener socket = + CreateServerSocket(); + ConnectionListener listener = new ConnectionListener(this,socket); + Thread thread = new Thread(listener.Run); + thread.Name="ConnectionListener"; + thread.Start(thread); + thread.IsBackground = false; + _listener = listener; + } + + private TcpListener CreateServerSocket() { + IPAddress addr = IfAddress; + + if ( addr == null ) { + addr = IOUtil.GetIPAddress("0.0.0.0"); + } + TcpListener rv = new TcpListener(addr,Port); + //TODO: specify accept count + rv.Start(); + return rv; + } + + + public override void Stop() { + if (_listener != null) { + _listener.Shutdown(); + _listener = null; + } + ConnectorFacadeFactory.GetInstance().Dispose(); + } + + } + + +} diff --git a/DotNetConnectors.sln/FrameworkInternal/Test.cs b/DotNetConnectors.sln/FrameworkInternal/Test.cs new file mode 100644 index 00000000..553155d3 --- /dev/null +++ b/DotNetConnectors.sln/FrameworkInternal/Test.cs @@ -0,0 +1,121 @@ +/* + * ==================== + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER. + * + * Copyright 2007-2008 Sun Microsystems, Inc. All rights reserved. + * + * The contents of this file are subject to the terms of the Common Development + * and Distribution License("CDDL") (the "License"). You may not use this file + * except in compliance with the License. + * + * You can obtain a copy of the License at + * http://IdentityConnectors.dev.java.net/legal/license.txt + * See the License for the specific language governing permissions and limitations + * under the License. + * + * When distributing the Covered Code, include this CDDL Header Notice in each file + * and include the License file at identityconnectors/legal/license.txt. + * If applicable, add the following below this CDDL Header, with the fields + * enclosed by brackets [] replaced by your own identifying information: + * "Portions Copyrighted [year] [name of copyright owner]" + * ==================== + */ +using System; +using System.Text; + +using Org.IdentityConnectors.Common; +using Org.IdentityConnectors.Framework.Api; +using Org.IdentityConnectors.Framework.Impl.Api; +using Org.IdentityConnectors.Framework.Impl.Api.Local; +using Org.IdentityConnectors.Framework.Impl.Api.Local.Operations; +using Org.IdentityConnectors.Framework.Common; +using Org.IdentityConnectors.Framework.Common.Objects; +using Org.IdentityConnectors.Framework.Common.Objects.Filters; +using Org.IdentityConnectors.Framework.Spi; +using Org.IdentityConnectors.Framework.Spi.Operations; +using Org.IdentityConnectors.Framework.Test; + +namespace Org.IdentityConnectors.Framework.Impl.Test +{ + public class TestHelpersImpl : TestHelpers { + + /** + * Method for convenient testing of local connectors. + */ + protected override APIConfiguration CreateTestConfigurationImpl(SafeType clazz, + Configuration config) { + LocalConnectorInfoImpl info = new LocalConnectorInfoImpl(); + info.ConnectorConfigurationClass=SafeType.Get(config); + info.ConnectorClass=(clazz); + info.ConnectorDisplayNameKey=("DUMMY_DISPLAY_NAME"); + info.ConnectorKey=( + new ConnectorKey(clazz.RawType.Name+".bundle", + "1.0", + clazz.RawType.Name)); + info.Messages=(CreateDummyMessages()); + APIConfigurationImpl rv = new APIConfigurationImpl(); + rv.IsConnectorPoolingSupported=( + IsConnectorPoolingSupported(clazz)); + ConfigurationPropertiesImpl properties = + CSharpClassProperties.CreateConfigurationProperties(config); + rv.ConfigurationProperties=(properties); + rv.ConnectorInfo=(info); + rv.SupportedOperations=( + FrameworkUtil.GetDefaultSupportedOperations(clazz)); + info.DefaultAPIConfiguration=( + rv); + return rv; + } + + + private static bool IsConnectorPoolingSupported(SafeType clazz) { + return ReflectionUtil.IsParentTypeOf(typeof(PoolableConnector),clazz.RawType); + } + + /** + * Performs a raw, unfiltered search at the SPI level, + * eliminating duplicates from the result set. + * @param search The search SPI + * @param oclass The object class - passed through to + * connector so it may be null if the connecor + * allowing it to be null. (This is convenient for + * unit tests, but will not be the case in general) + * @param filter The filter to search on + * @param handler The result handler + * @param options The options - may be null - will + * be cast to an empty OperationOptions + */ + protected override void SearchImpl(SearchOp search, + ObjectClass oclass, + Filter filter, + ResultsHandler handler, + OperationOptions options) { + if ( options == null ) { + options = new OperationOptionsBuilder().Build(); + } + RawSearcherImpl.RawSearch( + search, oclass, filter, handler, options); + } + + protected override ConnectorMessages CreateDummyMessagesImpl() { + return new DummyConnectorMessages(); + } + + private class DummyConnectorMessages : ConnectorMessages { + public String Format(String key, String dflt, params Object [] args) { + StringBuilder builder = new StringBuilder(); + builder.Append(key); + builder.Append(": "); + String sep = ""; + foreach (Object arg in args ) { + builder.Append(sep); + builder.Append(arg); + sep=", "; + } + return builder.ToString(); + } + } + + } + +} diff --git a/DotNetConnectors.sln/FrameworkTests/AssemblyInfo.cs b/DotNetConnectors.sln/FrameworkTests/AssemblyInfo.cs new file mode 100644 index 00000000..61f6c635 --- /dev/null +++ b/DotNetConnectors.sln/FrameworkTests/AssemblyInfo.cs @@ -0,0 +1,53 @@ +/* + * ==================== + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER. + * + * Copyright 2007-2008 Sun Microsystems, Inc. All rights reserved. + * + * The contents of this file are subject to the terms of the Common Development + * and Distribution License("CDDL") (the "License"). You may not use this file + * except in compliance with the License. + * + * You can obtain a copy of the License at + * http://IdentityConnectors.dev.java.net/legal/license.txt + * See the License for the specific language governing permissions and limitations + * under the License. + * + * When distributing the Covered Code, include this CDDL Header Notice in each file + * and include the License file at identityconnectors/legal/license.txt. + * If applicable, add the following below this CDDL Header, with the fields + * enclosed by brackets [] replaced by your own identifying information: + * "Portions Copyrighted [year] [name of copyright owner]" + * ==================== + */ +#region Using directives + +using System; +using System.Reflection; +using System.Runtime.InteropServices; + +#endregion + +// General Information about an assembly is controlled through the following +// set of attributes. Change these attribute values to modify the information +// associated with an assembly. +[assembly: AssemblyTitle("FrameworkTests")] +[assembly: AssemblyDescription("")] +[assembly: AssemblyConfiguration("")] +[assembly: AssemblyCompany("")] +[assembly: AssemblyProduct("FrameworkTests")] +[assembly: AssemblyCopyright("")] +[assembly: AssemblyTrademark("")] +[assembly: AssemblyCulture("")] + +// This sets the default COM visibility of types in the assembly to invisible. +// If you need to expose a type to COM, use [ComVisible(true)] on that type. +[assembly: ComVisible(false)] + +// The assembly version has following format : +// +// Major.Minor.Build.Revision +// +// You can specify all the values or you can use the default the Revision and +// Build Numbers by using the '*' as shown below: +[assembly: AssemblyVersion("1.0.0.0")] diff --git a/DotNetConnectors.sln/FrameworkTests/CollectionUtilTests.cs b/DotNetConnectors.sln/FrameworkTests/CollectionUtilTests.cs new file mode 100644 index 00000000..289a91ca --- /dev/null +++ b/DotNetConnectors.sln/FrameworkTests/CollectionUtilTests.cs @@ -0,0 +1,160 @@ +/* + * ==================== + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER. + * + * Copyright 2007-2008 Sun Microsystems, Inc. All rights reserved. + * + * The contents of this file are subject to the terms of the Common Development + * and Distribution License("CDDL") (the "License"). You may not use this file + * except in compliance with the License. + * + * You can obtain a copy of the License at + * http://IdentityConnectors.dev.java.net/legal/license.txt + * See the License for the specific language governing permissions and limitations + * under the License. + * + * When distributing the Covered Code, include this CDDL Header Notice in each file + * and include the License file at identityconnectors/legal/license.txt. + * If applicable, add the following below this CDDL Header, with the fields + * enclosed by brackets [] replaced by your own identifying information: + * "Portions Copyrighted [year] [name of copyright owner]" + * ==================== + */ +using System; +using System.Collections.Generic; +using NUnit.Framework; +using NUnit.Framework.SyntaxHelpers; +using Org.IdentityConnectors.Common; +namespace FrameworkTests +{ + [TestFixture] + public class CollectionUtilTests + { + [Test] + public void TestEquals() { + Assert.IsTrue(CollectionUtil.Equals(null, null)); + Assert.IsFalse(CollectionUtil.Equals(null, "str")); + Assert.IsTrue(CollectionUtil.Equals("str", "str")); + + byte [] arr1 = new byte[] { 1,2,3 }; + byte [] arr2 = new byte[] { 1,2,3 }; + byte [] arr3 = new byte[] { 1,2,4 }; + byte [] arr4 = new byte[] { 1,2 }; + int [] arr5 = new int[] {1,2,3}; + + Assert.IsTrue(CollectionUtil.Equals(arr1, arr2)); + Assert.IsFalse(CollectionUtil.Equals(arr2, arr3)); + Assert.IsFalse(CollectionUtil.Equals(arr2, arr4)); + Assert.IsFalse(CollectionUtil.Equals(arr2, arr5)); + + IList list1 = new List(); + IList list2 = new List(); + list1.Add(arr1); + list2.Add(arr2); + + Assert.IsTrue(CollectionUtil.Equals(list1, list2)); + + list2.Add(arr2); + Assert.IsFalse(CollectionUtil.Equals(list1, list2)); + + list1.Add(arr1); + Assert.IsTrue(CollectionUtil.Equals(list1, list2)); + + list1.Add(arr1); + list2.Add(arr3); + Assert.IsFalse(CollectionUtil.Equals(list1, list2)); + + IDictionary map1 = new Dictionary(); + IDictionary map2 = new Dictionary(); + map1["key1"] = arr1; + map2["key1"] = arr2; + Assert.IsTrue(CollectionUtil.Equals(map1, map2)); + map2["key2"] = arr2; + Assert.IsFalse(CollectionUtil.Equals(map1, map2)); + map1["key2"] = arr1; + Assert.IsTrue(CollectionUtil.Equals(map1, map2)); + map1["key2"] = arr3; + Assert.IsFalse(CollectionUtil.Equals(map1, map2)); + + ICollection set1 = new HashSet(); + ICollection set2 = new HashSet(); + set1.Add("val"); + set2.Add("val"); + Assert.IsTrue(CollectionUtil.Equals(set1, set2)); + set2.Add("val2"); + Assert.IsFalse(CollectionUtil.Equals(set1, set2)); + set1.Add("val2"); + Assert.IsTrue(CollectionUtil.Equals(set1, set2)); + } + [Test] + public void TestHashCode() { + Assert.AreEqual(0,CollectionUtil.GetHashCode(null)); + Assert.AreEqual("str".GetHashCode(),CollectionUtil.GetHashCode("str")); + + byte [] arr1 = new byte[] { 1,2,3 }; + byte [] arr2 = new byte[] { 1,2,3 }; + byte [] arr3 = new byte[] { 1,2,4 }; + byte [] arr4 = new byte[] { 1,2 }; + int [] arr5 = new int[] {1,2,3}; + + Assert.AreEqual(CollectionUtil.GetHashCode(arr1), + CollectionUtil.GetHashCode(arr2)); + Assert.IsFalse(CollectionUtil.GetHashCode(arr2) == + CollectionUtil.GetHashCode(arr3)); + Assert.IsFalse(CollectionUtil.GetHashCode(arr2) == + CollectionUtil.GetHashCode(arr4)); + Assert.IsTrue(CollectionUtil.GetHashCode(arr2) == + CollectionUtil.GetHashCode(arr5)); + + List list1 = new List(); + List list2 = new List(); + list1.Add(arr1); + list2.Add(arr2); + + Assert.IsTrue(CollectionUtil.GetHashCode(list1) == + CollectionUtil.GetHashCode(list2)); + + list2.Add(arr2); + Assert.IsFalse(CollectionUtil.GetHashCode(list1) == + CollectionUtil.GetHashCode(list2)); + + list1.Add(arr1); + Assert.IsTrue(CollectionUtil.GetHashCode(list1) == + CollectionUtil.GetHashCode(list2)); + + list1.Add(arr1); + list2.Add(arr3); + Assert.IsFalse(CollectionUtil.GetHashCode(list1) == + CollectionUtil.GetHashCode(list2)); + + Dictionary map1 = new Dictionary(); + Dictionary map2 = new Dictionary(); + map1["key1"] = arr1; + map2["key1"] = arr2; + Assert.IsTrue(CollectionUtil.GetHashCode(map1) == + CollectionUtil.GetHashCode(map2)); + map2["key2"] = arr2; + Assert.IsFalse(CollectionUtil.GetHashCode(map1) == + CollectionUtil.GetHashCode(map2)); + map1["key2"] = arr1; + Assert.IsTrue(CollectionUtil.GetHashCode(map1) == + CollectionUtil.GetHashCode(map2)); + map1["key2"] = arr3; + Assert.IsFalse(CollectionUtil.GetHashCode(map1) == + CollectionUtil.GetHashCode(map2)); + + HashSet set1 = new HashSet(); + HashSet set2 = new HashSet(); + set1.Add("val"); + set2.Add("val"); + Assert.IsTrue(CollectionUtil.GetHashCode(set1) == + CollectionUtil.GetHashCode(set2)); + set2.Add("val2"); + Assert.IsFalse(CollectionUtil.GetHashCode(set1) == + CollectionUtil.GetHashCode(set2)); + set1.Add("val2"); + Assert.IsTrue(CollectionUtil.GetHashCode(set1) == + CollectionUtil.GetHashCode(set2)); + } + } +} diff --git a/DotNetConnectors.sln/FrameworkTests/ConnectorInfoManagerTests.cs b/DotNetConnectors.sln/FrameworkTests/ConnectorInfoManagerTests.cs new file mode 100644 index 00000000..16b8f43f --- /dev/null +++ b/DotNetConnectors.sln/FrameworkTests/ConnectorInfoManagerTests.cs @@ -0,0 +1,592 @@ +/* + * ==================== + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER. + * + * Copyright 2007-2008 Sun Microsystems, Inc. All rights reserved. + * + * The contents of this file are subject to the terms of the Common Development + * and Distribution License("CDDL") (the "License"). You may not use this file + * except in compliance with the License. + * + * You can obtain a copy of the License at + * http://IdentityConnectors.dev.java.net/legal/license.txt + * See the License for the specific language governing permissions and limitations + * under the License. + * + * When distributing the Covered Code, include this CDDL Header Notice in each file + * and include the License file at identityconnectors/legal/license.txt. + * If applicable, add the following below this CDDL Header, with the fields + * enclosed by brackets [] replaced by your own identifying information: + * "Portions Copyrighted [year] [name of copyright owner]" + * ==================== + */ +using System; +using NUnit.Framework; +using NUnit.Framework.SyntaxHelpers; +using System.Collections.Generic; +using System.Diagnostics; +using Org.IdentityConnectors.Common; +using Org.IdentityConnectors.Common.Security; +using Org.IdentityConnectors.Framework.Api; +using Org.IdentityConnectors.Framework.Api.Operations; +using Org.IdentityConnectors.Framework.Common.Exceptions; +using Org.IdentityConnectors.Framework.Common.Objects; +using Org.IdentityConnectors.Framework.Server; +using Org.IdentityConnectors.Framework.Impl.Api; +using Org.IdentityConnectors.Framework.Impl.Api.Local; +using System.Security; +using System.Threading; +using System.Globalization; +using System.Net; +using System.Net.Security; +using System.Security.Cryptography.X509Certificates; +namespace FrameworkTests +{ + [TestFixture] + public class ConnectorInfoManagerTests + { + private static ConnectorInfo FindConnectorInfo + (ConnectorInfoManager manager, + string version, + string connectorName) { + foreach (ConnectorInfo info in manager.ConnectorInfos) { + ConnectorKey key = info.ConnectorKey; + if ( version.Equals(key.BundleVersion) && + connectorName.Equals(key.ConnectorName) ) { + //intentionally ineffecient to test + //more code + return manager.FindConnectorInfo(key); + } + } + return null; + } + + [Test] + public void TestClassLoading() { + ConnectorInfoManager manager = + GetConnectorInfoManager(); + ConnectorInfo info1 = + FindConnectorInfo(manager, + "1.0.0.0", + "org.identityconnectors.testconnector.TstConnector"); + Assert.IsNotNull(info1); + ConnectorInfo info2 = + FindConnectorInfo(manager, + "2.0.0.0", + "org.identityconnectors.testconnector.TstConnector"); + + Assert.IsNotNull(info2); + + ConnectorFacade facade1 = + ConnectorFacadeFactory.GetInstance().NewInstance(info1.CreateDefaultAPIConfiguration()); + + ConnectorFacade facade2 = + ConnectorFacadeFactory.GetInstance().NewInstance(info2.CreateDefaultAPIConfiguration()); + + ICollection attrs = new HashSet(); + Assert.AreEqual("1.0", facade1.Create(ObjectClass.ACCOUNT,attrs,null).GetUidValue()); + Assert.AreEqual("2.0", facade2.Create(ObjectClass.ACCOUNT,attrs,null).GetUidValue()); + + ShutdownConnnectorInfoManager(); + } + + [Test] + public void TestAPIConfiguration() + { + ConnectorInfoManager manager = + GetConnectorInfoManager(); + ConnectorInfo info = + FindConnectorInfo(manager, + "1.0.0.0", + "org.identityconnectors.testconnector.TstConnector"); + Assert.IsNotNull(info); + APIConfiguration api = info.CreateDefaultAPIConfiguration(); + + ConfigurationProperties props = api.ConfigurationProperties; + ConfigurationProperty property = props.GetProperty("tstField"); + + Assert.IsNotNull(property); + ICollection> operations = + property.Operations; + Assert.AreEqual(1, operations.Count); + Assert.IsTrue(operations.Contains(SafeType.Get())); + + Thread.CurrentThread.CurrentUICulture = new CultureInfo("en"); + Assert.AreEqual("Help for test field.",property.GetHelpMessage(null)); + Assert.AreEqual("Display for test field.",property.GetDisplayName(null)); + Assert.AreEqual("Test Framework Value", + info.Messages.Format("TEST_FRAMEWORK_KEY", "empty")); + + CultureInfo eslocale = new CultureInfo("es"); + Thread.CurrentThread.CurrentUICulture = eslocale; + Assert.AreEqual("tstField.help_es",property.GetHelpMessage(null)); + Assert.AreEqual("tstField.display_es",property.GetDisplayName(null)); + + CultureInfo esESlocale = new CultureInfo("es-ES"); + Thread.CurrentThread.CurrentUICulture = esESlocale; + Assert.AreEqual("tstField.help_es-ES",property.GetHelpMessage(null)); + Assert.AreEqual("tstField.display_es-ES",property.GetDisplayName(null)); + + CultureInfo esARlocale = new CultureInfo("es-AR"); + Thread.CurrentThread.CurrentUICulture = esARlocale; + Assert.AreEqual("tstField.help_es",property.GetHelpMessage(null)); + Assert.AreEqual("tstField.display_es",property.GetDisplayName(null)); + + ConnectorFacadeFactory facf = ConnectorFacadeFactory.GetInstance(); + ConnectorFacade facade = facf.NewInstance(api); + // call the various create/update/delete commands.. + facade.Schema(); + ShutdownConnnectorInfoManager(); + } + + [Test] + public void TestValidate() { + ConnectorInfoManager manager = + GetConnectorInfoManager(); + ConnectorInfo info = + FindConnectorInfo(manager, + "1.0.0.0", + "org.identityconnectors.testconnector.TstConnector"); + + APIConfiguration api = info.CreateDefaultAPIConfiguration(); + + ConfigurationProperties props = api.ConfigurationProperties; + ConfigurationProperty property = props.GetProperty("failValidation"); + property.Value=false; + ConnectorFacadeFactory facf = ConnectorFacadeFactory.GetInstance(); + ConnectorFacade facade = facf.NewInstance(api); + facade.Validate(); + property.Value=true; + facade = facf.NewInstance(api); + try { + Thread.CurrentThread.CurrentUICulture = new CultureInfo("en"); + facade.Validate(); + Assert.Fail("exception expected"); + } + catch (ConnectorException e) { + Assert.AreEqual("validation failed en", e.Message); + } + try { + Thread.CurrentThread.CurrentUICulture = new CultureInfo("es"); + facade.Validate(); + Assert.Fail("exception expected"); + } + catch (ConnectorException e) { + Assert.AreEqual("validation failed es", e.Message); + } + ShutdownConnnectorInfoManager(); + } + + + + /** + * Main purpose of this is to test searching with + * many results and that we can properly handle + * stopping in the middle of this. There's a bunch of + * code in the remote stuff that is there to handle this + * in particular that we want to excercise. + */ + [Test] + public void TestSearchWithManyResults() { + ConnectorInfoManager manager = + GetConnectorInfoManager(); + ConnectorInfo info = + FindConnectorInfo(manager, + "1.0.0.0", + "org.identityconnectors.testconnector.TstConnector"); + + APIConfiguration api = info.CreateDefaultAPIConfiguration(); + + ConfigurationProperties props = api.ConfigurationProperties; + ConfigurationProperty property = props.GetProperty("numResults"); + + //1000 is several times the remote size between pauses + property.Value=1000; + + ConnectorFacadeFactory facf = ConnectorFacadeFactory.GetInstance(); + ConnectorFacade facade = facf.NewInstance(api); + + IList results = new List(); + + facade.Search(ObjectClass.ACCOUNT,null, + obj => { + results.Add(obj); + return true; + },null); + + Assert.AreEqual(1000,results.Count); + for ( int i = 0; i < results.Count; i++ ) { + ConnectorObject obj = results[i]; + Assert.AreEqual(i.ToString(), + obj.Uid.GetUidValue()); + } + + results.Clear(); + + facade.Search(ObjectClass.ACCOUNT,null, + obj => { + if ( results.Count < 500 ) { + results.Add(obj); + return true; + } + else { + return false; + } + },null); + + Assert.AreEqual(500,results.Count); + for ( int i = 0; i < results.Count; i++ ) { + ConnectorObject obj = results[i]; + Assert.AreEqual(i.ToString(), + obj.Uid.GetUidValue()); + } + + ShutdownConnnectorInfoManager(); + } + /** + * Main purpose of this is to test sync with + * many results and that we can properly handle + * stopping in the middle of this. There's a bunch of + * code in the remote stuff that is there to handle this + * in particular that we want to excercise. + */ + [Test] + public void TestSyncWithManyResults() { + ConnectorInfoManager manager = + GetConnectorInfoManager(); + ConnectorInfo info = + FindConnectorInfo(manager, + "1.0.0.0", + "org.identityconnectors.testconnector.TstConnector"); + + APIConfiguration api = info.CreateDefaultAPIConfiguration(); + + ConfigurationProperties props = api.ConfigurationProperties; + ConfigurationProperty property = props.GetProperty("numResults"); + + //1000 is several times the remote size between pauses + property.Value=(1000); + + ConnectorFacadeFactory facf = ConnectorFacadeFactory.GetInstance(); + ConnectorFacade facade = facf.NewInstance(api); + + SyncToken latest = facade.GetLatestSyncToken(ObjectClass.ACCOUNT); + Assert.AreEqual("mylatest",latest.Value); + IList results = new List(); + + facade.Sync(ObjectClass.ACCOUNT, null, obj => { + results.Add(obj); + return true; + },null); + + Assert.AreEqual(1000,results.Count); + for ( int i = 0; i < results.Count; i++ ) { + SyncDelta obj = results[i]; + Assert.AreEqual(i.ToString(), + obj.Uid.GetUidValue()); + } + + results.Clear(); + + facade.Sync(ObjectClass.ACCOUNT, + null, + obj => { + if ( results.Count < 500 ) { + results.Add(obj); + return true; + } + else { + return false; + } + },null); + + Assert.AreEqual(500,results.Count); + for ( int i = 0; i < results.Count; i++ ) { + SyncDelta obj = results[i]; + Assert.AreEqual(i.ToString(), + obj.Uid.GetUidValue()); + } + + ShutdownConnnectorInfoManager(); + } + + [Test] + public void TestConnectionPooling() { + ConnectorPoolManager.Dispose(); + ConnectorInfoManager manager = + GetConnectorInfoManager(); + ConnectorInfo info1 = + FindConnectorInfo(manager, + "1.0.0.0", + "org.identityconnectors.testconnector.TstConnector"); + Assert.IsNotNull(info1); + //reset connection count + { + //trigger TstConnection.init to be called + APIConfiguration config2 = + info1.CreateDefaultAPIConfiguration(); + config2.ConfigurationProperties.GetProperty("resetConnectionCount").Value=(true); + ConnectorFacade facade2 = + ConnectorFacadeFactory.GetInstance().NewInstance(config2); + facade2.Schema(); //force instantiation + } + + APIConfiguration config = + info1.CreateDefaultAPIConfiguration(); + + config.ConnectorPoolConfiguration.MinIdle=(0); + config.ConnectorPoolConfiguration.MaxIdle=(0); + + ConnectorFacade facade1 = + ConnectorFacadeFactory.GetInstance().NewInstance(config); + + OperationOptionsBuilder builder = new OperationOptionsBuilder(); + builder.SetOption("testPooling", "true"); + OperationOptions options = builder.Build(); + ICollection attrs = CollectionUtil.NewReadOnlySet(); + Assert.AreEqual("1", facade1.Create(ObjectClass.ACCOUNT,attrs,options).GetUidValue()); + Assert.AreEqual("2", facade1.Create(ObjectClass.ACCOUNT,attrs,options).GetUidValue()); + Assert.AreEqual("3", facade1.Create(ObjectClass.ACCOUNT,attrs,options).GetUidValue()); + Assert.AreEqual("4", facade1.Create(ObjectClass.ACCOUNT,attrs,options).GetUidValue()); + config = + info1.CreateDefaultAPIConfiguration(); + config.ConnectorPoolConfiguration.MinIdle=(1); + config.ConnectorPoolConfiguration.MaxIdle=(2); + facade1 = + ConnectorFacadeFactory.GetInstance().NewInstance(config); + Assert.AreEqual("5", facade1.Create(ObjectClass.ACCOUNT,attrs,options).GetUidValue()); + Assert.AreEqual("5", facade1.Create(ObjectClass.ACCOUNT,attrs,options).GetUidValue()); + Assert.AreEqual("5", facade1.Create(ObjectClass.ACCOUNT,attrs,options).GetUidValue()); + Assert.AreEqual("5", facade1.Create(ObjectClass.ACCOUNT,attrs,options).GetUidValue()); + ShutdownConnnectorInfoManager(); + } + + [Test] + public void TestScripting() + { + ConnectorInfoManager manager = + GetConnectorInfoManager(); + ConnectorInfo info = + FindConnectorInfo(manager, + "1.0.0.0", + "org.identityconnectors.testconnector.TstConnector"); + APIConfiguration api = info.CreateDefaultAPIConfiguration(); + + + ConnectorFacadeFactory facf = ConnectorFacadeFactory.GetInstance(); + ConnectorFacade facade = facf.NewInstance(api); + + ScriptContextBuilder builder = new ScriptContextBuilder(); + builder.AddScriptArgument("arg1", "value1"); + builder.AddScriptArgument("arg2", "value2"); + builder.ScriptLanguage=("BOO"); + + //test that they can run the script and access the + //connector object + { + String SCRIPT = + "connector.concat(arg1,arg2)"; + builder.ScriptText=(SCRIPT); + String result = (String)facade.RunScriptOnConnector(builder.Build(), + null); + + Assert.AreEqual("value1value2", result); + } + + //test that they can access a class in the class loader + { + String SCRIPT = + "import org.identityconnectors.testconnector\n"+ + "TstConnector.getVersion()"; + builder.ScriptText=(SCRIPT); + String result = (String)facade.RunScriptOnConnector(builder.Build(), + null); + Assert.AreEqual("1.0", result); + } + + //test that they cannot access a class in internal + { + Type clazz = typeof(ConfigurationPropertyImpl); + + String SCRIPT = + "import "+clazz.Namespace+"\n"+ + clazz.Name+"()"; + builder.ScriptText=(SCRIPT); + try { + facade.RunScriptOnConnector(builder.Build(), + null); + Assert.Fail("exception expected"); + } + catch (Exception e) { + String msg = e.Message; + String expectedMessage = + "Namespace '"+clazz.Namespace+"' not found"; + Assert.IsTrue( + msg.Contains(expectedMessage), + "Unexpected message: "+msg); + } + } + + // test that they can access a class in common + { + Type clazz = typeof(ConnectorAttributeBuilder); + String SCRIPT = + "import "+clazz.Namespace+"\n"+ + clazz.Name + ".Build(\"myattr\")"; + builder.ScriptText=(SCRIPT); + ConnectorAttribute attr = (ConnectorAttribute) facade.RunScriptOnConnector(builder.Build(), null); + Assert.AreEqual("myattr", attr.Name); + } + ShutdownConnnectorInfoManager(); + } + + protected virtual ConnectorInfoManager GetConnectorInfoManager() { + ConnectorInfoManagerFactory fact = ConnectorInfoManagerFactory.GetInstance(); + ConnectorInfoManager manager = fact.GetLocalManager(); + return manager; + } + + protected virtual void ShutdownConnnectorInfoManager() { + + } + } + + [TestFixture] + public class RemoteConnectorInfoManagerClearTests : ConnectorInfoManagerTests { + + private ConnectorServer _server; + + protected override ConnectorInfoManager GetConnectorInfoManager() { + TestUtil.InitializeLogging(); + + ShutdownConnnectorInfoManager(); + + GuardedString str = new GuardedString(); + str.AppendChar('c'); + str.AppendChar('h'); + str.AppendChar('a'); + str.AppendChar('n'); + str.AppendChar('g'); + str.AppendChar('e'); + str.AppendChar('i'); + str.AppendChar('t'); + +#if DEBUG + const int PORT = 8758; +#else + const int PORT = 8759; +#endif + _server = ConnectorServer.NewInstance(); + _server.Port=PORT; + _server.IfAddress=(IOUtil.GetIPAddress("127.0.0.1")); + _server.KeyHash=str.GetBase64SHA1Hash(); + _server.Start(); + //while ( true ) { + // Thread.Sleep(1000); + //} + ConnectorInfoManagerFactory fact = ConnectorInfoManagerFactory.GetInstance(); + + RemoteFrameworkConnectionInfo connInfo = new + RemoteFrameworkConnectionInfo("127.0.0.1",PORT,str); + + ConnectorInfoManager manager = fact.GetRemoteManager(connInfo); + + return manager; + } + + protected override void ShutdownConnnectorInfoManager() { + if (_server != null) { + _server.Stop(); + _server = null; + } + } + } + + internal class MyCertificateValidationCallback + { + public bool Validate(Object sender, + X509Certificate certificate, + X509Chain chain, + SslPolicyErrors sslPolicyErrors) { + Trace.TraceInformation("validating: "+certificate.Subject); + return true; + } + } + + [TestFixture] + public class RemoteConnectorInfoManagerSSLTests : ConnectorInfoManagerTests { + + //To generate test certificate do the following: + // + //makecert -r -pe -n "CN=localhost" -ss TestCertificateStore -sr currentuser -sky exchange + // + //In MMC, go to the certificate, export + private ConnectorServer _server; + private const String CERT_PATH = "../../../server.pfx"; + protected override ConnectorInfoManager GetConnectorInfoManager() { + TestUtil.InitializeLogging(); + + ShutdownConnnectorInfoManager(); + GuardedString str = new GuardedString(); + str.AppendChar('c'); + str.AppendChar('h'); + str.AppendChar('a'); + str.AppendChar('n'); + str.AppendChar('g'); + str.AppendChar('e'); + str.AppendChar('i'); + str.AppendChar('t'); + +#if DEBUG + const int PORT = 8762; +#else + const int PORT = 8761; +#endif + + /*X509Store store = new X509Store("TestCertificateStore", + StoreLocation.CurrentUser); + store.Open(OpenFlags.ReadOnly|OpenFlags.OpenExistingOnly); + X509Certificate certificate = store.Certificates[0]; + store.Close();*/ + + X509Certificate2 certificate = new + X509Certificate2(CERT_PATH, + "changeit"); + //Trace.TraceInformation("certificate: "+certificate); + _server = ConnectorServer.NewInstance(); + _server.Port=PORT; + _server.KeyHash=str.GetBase64SHA1Hash(); + _server.IfAddress=(IOUtil.GetIPAddress("localhost")); + _server.UseSSL = true; + _server.ServerCertificate = certificate; + _server.Start(); + //while ( true ) { + // Thread.Sleep(1000); + //} + ConnectorInfoManagerFactory fact = ConnectorInfoManagerFactory.GetInstance(); + MyCertificateValidationCallback + callback = new MyCertificateValidationCallback(); + RemoteFrameworkConnectionInfo connInfo = new + RemoteFrameworkConnectionInfo("localhost", + PORT, + str, + true, + callback.Validate, + 60000); + + ConnectorInfoManager manager = fact.GetRemoteManager(connInfo); + + return manager; + } + + protected override void ShutdownConnnectorInfoManager() { + if (_server != null) { + _server.Stop(); + _server = null; + } + } + } + + + +} diff --git a/DotNetConnectors.sln/FrameworkTests/FilterTranslatorTests.cs b/DotNetConnectors.sln/FrameworkTests/FilterTranslatorTests.cs new file mode 100644 index 00000000..f545c6b2 --- /dev/null +++ b/DotNetConnectors.sln/FrameworkTests/FilterTranslatorTests.cs @@ -0,0 +1,609 @@ +/* + * ==================== + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER. + * + * Copyright 2007-2008 Sun Microsystems, Inc. All rights reserved. + * + * The contents of this file are subject to the terms of the Common Development + * and Distribution License("CDDL") (the "License"). You may not use this file + * except in compliance with the License. + * + * You can obtain a copy of the License at + * http://IdentityConnectors.dev.java.net/legal/license.txt + * See the License for the specific language governing permissions and limitations + * under the License. + * + * When distributing the Covered Code, include this CDDL Header Notice in each file + * and include the License file at identityconnectors/legal/license.txt. + * If applicable, add the following below this CDDL Header, with the fields + * enclosed by brackets [] replaced by your own identifying information: + * "Portions Copyrighted [year] [name of copyright owner]" + * ==================== + */ +using System; +using System.Collections.Generic; +using NUnit.Framework; +using NUnit.Framework.SyntaxHelpers; +using Org.IdentityConnectors.Common; +using Org.IdentityConnectors.Framework.Common.Objects; +using Org.IdentityConnectors.Framework.Common.Objects.Filters; +namespace FrameworkTests +{ + + internal class AllFiltersTranslator : AbstractFilterTranslator + { + protected override String CreateAndExpression(String leftExpression, String rightExpression) { + return "( & " + leftExpression+" "+rightExpression+" )"; + } + + protected override String CreateOrExpression(String leftExpression, String rightExpression) { + return "( | " + leftExpression+" "+rightExpression+" )"; + } + + protected override String CreateContainsExpression(ContainsFilter filter, bool not) { + String rv = "( CONTAINS "+filter.GetName()+" "+filter.GetValue()+" )"; + return Not(rv,not); + } + + protected override String CreateEndsWithExpression(EndsWithFilter filter, bool not) { + String rv = "( ENDS-WITH "+filter.GetName()+" "+filter.GetValue()+" )"; + return Not(rv,not); + } + + protected override String CreateEqualsExpression(EqualsFilter filter, bool not) { + String rv = "( = "+filter.GetAttribute().Name+" ["+filter.GetAttribute().Value[0]+"] )"; + return Not(rv,not); + } + + protected override String CreateGreaterThanExpression(GreaterThanFilter filter, bool not) { + String rv = "( > "+filter.GetName()+" "+filter.GetValue()+" )"; + return Not(rv,not); + } + + protected override String CreateGreaterThanOrEqualExpression(GreaterThanOrEqualFilter filter, bool not) { + String rv = "( >= "+filter.GetName()+" "+filter.GetValue()+" )"; + return Not(rv,not); + } + + protected override String CreateLessThanExpression(LessThanFilter filter, bool not) { + String rv = "( < "+filter.GetName()+" "+filter.GetValue()+" )"; + return Not(rv,not); + } + + protected override String CreateLessThanOrEqualExpression(LessThanOrEqualFilter filter, bool not) { + String rv = "( <= "+filter.GetName()+" "+filter.GetValue()+" )"; + return Not(rv,not); + } + + protected override String CreateStartsWithExpression(StartsWithFilter filter, bool not) { + String rv = "( STARTS-WITH "+filter.GetName()+" "+filter.GetValue()+" )"; + return Not(rv,not); + } + protected override String CreateContainsAllValuesExpression(ContainsAllValuesFilter filter, bool not) { + String rv = "( CONTAINS-ALL-VALUES "+filter.GetAttribute()+" )"; + return Not(rv,not); + } + private String Not(String orig, bool not) { + if ( not ) { + return "( ! " + orig + " )"; + } + else { + return orig; + } + } + + } + + /** + * Everything but Or + */ + internal class NoOrTranslator : AllFiltersTranslator + { + protected override String CreateOrExpression(String leftExpression, String rightExpression) { + return null; + } + } + /** + * Everything but EndsWith + */ + internal class NoEndsWithTranslator : AllFiltersTranslator + { + protected override String CreateEndsWithExpression(EndsWithFilter filter, bool not) { + return null; + } + } + /** + * Everything but EndsWith,Or + */ + internal class NoEndsWithNoOrTranslator : AllFiltersTranslator + { + protected override String CreateOrExpression(String leftExpression, String rightExpression) { + return null; + } + protected override String CreateEndsWithExpression(EndsWithFilter filter, bool not) { + return null; + } + } + + /** + * Everything but And + */ + internal class NoAndTranslator : AllFiltersTranslator + { + protected override String CreateAndExpression(String leftExpression, String rightExpression) { + return null; + } + } + /** + * Everything but And + */ + internal class NoAndNoEndsWithTranslator : AllFiltersTranslator + { + protected override String CreateAndExpression(String leftExpression, String rightExpression) { + return null; + } + protected override String CreateEndsWithExpression(EndsWithFilter filter, bool not) { + return null; + } + + } + /** + * Everything but And + */ + internal class NoAndNoOrNoEndsWithTranslator : AllFiltersTranslator + { + protected override String CreateAndExpression(String leftExpression, String rightExpression) { + return null; + } + protected override String CreateEndsWithExpression(EndsWithFilter filter, bool not) { + return null; + } + protected override String CreateOrExpression(String leftExpression, String rightExpression) { + return null; + } + + } + [TestFixture] + public class FilterTranslatorTests + { + + /** + * Test all operations when everything is fully implemented. + * Test not normalization as well. + */ + [Test] + public void TestBasics() { + ConnectorAttribute attribute = + ConnectorAttributeBuilder.Build("att-name", "att-value"); + ConnectorAttribute attribute2 = + ConnectorAttributeBuilder.Build("att-name2", "att-value2"); + AllFiltersTranslator translator = new + AllFiltersTranslator(); + + { + Filter filter = + FilterBuilder.Contains(attribute); + String expected = "( CONTAINS att-name att-value )"; + String actual = + TranslateSingle(translator, filter); + Assert.AreEqual(expected, actual); + + filter = FilterBuilder.Not(filter); + expected = "( ! "+expected+" )"; + actual = + TranslateSingle(translator, filter); + Assert.AreEqual(expected, actual); + } + + { + Filter filter = + FilterBuilder.EndsWith(attribute); + String expected = "( ENDS-WITH att-name att-value )"; + String actual = + TranslateSingle(translator, filter); + Assert.AreEqual(expected, actual); + + filter = FilterBuilder.Not(filter); + expected = "( ! "+expected+" )"; + actual = + TranslateSingle(translator, filter); + Assert.AreEqual(expected, actual); + } + + { + Filter filter = + FilterBuilder.EqualTo(attribute); + String expected = "( = att-name [att-value] )"; + String actual = + TranslateSingle(translator, filter); + Assert.AreEqual(expected, actual); + + filter = FilterBuilder.Not(filter); + expected = "( ! "+expected+" )"; + actual = + TranslateSingle(translator, filter); + Assert.AreEqual(expected, actual); + } + + { + Filter filter = + FilterBuilder.GreaterThan(attribute); + String expected = "( > att-name att-value )"; + String actual = + TranslateSingle(translator, filter); + Assert.AreEqual(expected, actual); + + filter = FilterBuilder.Not(filter); + expected = "( ! "+expected+" )"; + actual = + TranslateSingle(translator, filter); + Assert.AreEqual(expected, actual); + } + + { + Filter filter = + FilterBuilder.GreaterThanOrEqualTo(attribute); + String expected = "( >= att-name att-value )"; + String actual = + TranslateSingle(translator, filter); + Assert.AreEqual(expected, actual); + + filter = FilterBuilder.Not(filter); + expected = "( ! "+expected+" )"; + actual = + TranslateSingle(translator, filter); + Assert.AreEqual(expected, actual); + } + + { + Filter filter = + FilterBuilder.LessThan(attribute); + String expected = "( < att-name att-value )"; + String actual = + TranslateSingle(translator, filter); + Assert.AreEqual(expected, actual); + + filter = FilterBuilder.Not(filter); + expected = "( ! "+expected+" )"; + actual = + TranslateSingle(translator, filter); + Assert.AreEqual(expected, actual); + } + + { + Filter filter = + FilterBuilder.LessThanOrEqualTo(attribute); + String expected = "( <= att-name att-value )"; + String actual = + TranslateSingle(translator, filter); + Assert.AreEqual(expected, actual); + + filter = FilterBuilder.Not(filter); + expected = "( ! "+expected+" )"; + actual = + TranslateSingle(translator, filter); + Assert.AreEqual(expected, actual); + } + + { + Filter filter = + FilterBuilder.StartsWith(attribute); + String expected = "( STARTS-WITH att-name att-value )"; + String actual = + TranslateSingle(translator, filter); + Assert.AreEqual(expected, actual); + + filter = FilterBuilder.Not(filter); + expected = "( ! "+expected+" )"; + actual = + TranslateSingle(translator, filter); + Assert.AreEqual(expected, actual); + } + + { + Filter filter = + FilterBuilder.ContainsAllValues(attribute); + String expected = "( CONTAINS-ALL-VALUES "+attribute+" )"; + String actual = + TranslateSingle(translator, filter); + Assert.AreEqual(expected, actual); + + filter = FilterBuilder.Not(filter); + expected = "( ! "+expected+" )"; + actual = + TranslateSingle(translator, filter); + Assert.AreEqual(expected, actual); + } + + //and + { + Filter left = + FilterBuilder.Contains(attribute); + Filter right = + FilterBuilder.Contains(attribute2); + String expectedLeft = "( CONTAINS att-name att-value )"; + String expectedRight = "( CONTAINS att-name2 att-value2 )"; + Filter filter = + FilterBuilder.And(left, right); + String expected = + "( & "+expectedLeft+" "+expectedRight+" )"; + String actual = + TranslateSingle(translator, filter); + Assert.AreEqual(expected, actual); + + filter = FilterBuilder.Not(filter); + expectedLeft = "( ! "+expectedLeft+" )"; + expectedRight = "( ! "+expectedRight+" )"; + expected = + "( | "+expectedLeft+" "+expectedRight+" )"; + actual = + TranslateSingle(translator, filter); + Assert.AreEqual(expected, actual); + + } + + //or + { + Filter left = + FilterBuilder.Contains(attribute); + Filter right = + FilterBuilder.Contains(attribute2); + String expectedLeft = "( CONTAINS att-name att-value )"; + String expectedRight = "( CONTAINS att-name2 att-value2 )"; + Filter filter = + FilterBuilder.Or(left, right); + String expected = + "( | "+expectedLeft+" "+expectedRight+" )"; + String actual = + TranslateSingle(translator, filter); + Assert.AreEqual(expected, actual); + + filter = FilterBuilder.Not(filter); + expectedLeft = "( ! "+expectedLeft+" )"; + expectedRight = "( ! "+expectedRight+" )"; + expected = + "( & "+expectedLeft+" "+expectedRight+" )"; + actual = + TranslateSingle(translator, filter); + Assert.AreEqual(expected, actual); + } + + //double-negative + { + Filter filter = + FilterBuilder.Contains(attribute); + filter = FilterBuilder.Not(filter); + filter = FilterBuilder.Not(filter); + String expected = "( CONTAINS att-name att-value )"; + String actual = + TranslateSingle(translator, filter); + Assert.AreEqual(expected, actual); + } + + } + + /** + * (a OR b) AND ( c OR d) needs to become + * (a AND c) OR ( a AND d) OR (b AND c) OR (b AND d) is + * OR is not implemented. Otherwise it should stay + * as-is. + */ + [Test] + public void TestDistribution() + { + Filter a = + FilterBuilder.Contains(ConnectorAttributeBuilder.Build("a", "a")); + Filter b = + FilterBuilder.Contains(ConnectorAttributeBuilder.Build("b", "b")); + Filter c = + FilterBuilder.Contains(ConnectorAttributeBuilder.Build("c", "c")); + Filter d = + FilterBuilder.Contains(ConnectorAttributeBuilder.Build("d", "d")); + + Filter filter = + FilterBuilder.And( + FilterBuilder.Or(a, b), + FilterBuilder.Or(c, d)); + String expected = "( & ( | ( CONTAINS a a ) ( CONTAINS b b ) ) ( | ( CONTAINS c c ) ( CONTAINS d d ) ) )"; + String actual = + TranslateSingle(new AllFiltersTranslator(),filter); + + Assert.AreEqual(expected, actual); + + IList results = + new NoOrTranslator().Translate(filter); + Assert.AreEqual(4, results.Count); + + Assert.AreEqual("( & ( CONTAINS a a ) ( CONTAINS c c ) )", + results[0]); + Assert.AreEqual("( & ( CONTAINS a a ) ( CONTAINS d d ) )", + results[1]); + Assert.AreEqual("( & ( CONTAINS b b ) ( CONTAINS c c ) )", + results[2]); + Assert.AreEqual("( & ( CONTAINS b b ) ( CONTAINS d d ) )", + results[3]); + } + + //test simplification + //-no leaf + [Test] + public void TestSimplifyNoLeaf() { + Filter a = + FilterBuilder.Contains(ConnectorAttributeBuilder.Build("a", "a")); + Filter b = + FilterBuilder.Contains(ConnectorAttributeBuilder.Build("b", "b")); + Filter c = + FilterBuilder.EndsWith(ConnectorAttributeBuilder.Build("c", "c")); + Filter d = + FilterBuilder.Contains(ConnectorAttributeBuilder.Build("d", "d")); + + Filter filter = + FilterBuilder.And( + FilterBuilder.Or(a, b), + FilterBuilder.Or(c, d)); + String expected = "( | ( CONTAINS a a ) ( CONTAINS b b ) )"; + String actual = + TranslateSingle(new NoEndsWithTranslator(),filter); + Assert.AreEqual(expected, actual); + + } + //-no leaf + no or + [Test] + public void TestSimplifyNoLeafNoOr() { + Filter a = + FilterBuilder.Contains(ConnectorAttributeBuilder.Build("a", "a")); + Filter b = + FilterBuilder.Contains(ConnectorAttributeBuilder.Build("b", "b")); + Filter c = + FilterBuilder.EndsWith(ConnectorAttributeBuilder.Build("c", "c")); + Filter d = + FilterBuilder.Contains(ConnectorAttributeBuilder.Build("d", "d")); + + Filter filter = + FilterBuilder.And( + FilterBuilder.Or(a, b), + FilterBuilder.Or(c, d)); + IList results = + new NoEndsWithNoOrTranslator().Translate(filter); + Assert.AreEqual(2, results.Count); + Assert.AreEqual("( CONTAINS a a )", + results[0]); + Assert.AreEqual("( CONTAINS b b )", + results[1]); + + } + + //-no and + [Test] + public void TestSimplifyNoAnd() { + Filter a = + FilterBuilder.Contains(ConnectorAttributeBuilder.Build("a", "a")); + Filter b = + FilterBuilder.Contains(ConnectorAttributeBuilder.Build("b", "b")); + Filter c = + FilterBuilder.EndsWith(ConnectorAttributeBuilder.Build("c", "c")); + Filter d = + FilterBuilder.Contains(ConnectorAttributeBuilder.Build("d", "d")); + + Filter filter = + FilterBuilder.And( + FilterBuilder.Or(a, b), + FilterBuilder.Or(c, d)); + String expected = "( | ( CONTAINS a a ) ( CONTAINS b b ) )"; + String actual = + TranslateSingle(new NoAndTranslator(),filter); + Assert.AreEqual(expected, actual); + } + + //-no and+no leaf + [Test] + public void TestSimplifyNoAndNoLeaf() { + Filter a = + FilterBuilder.Contains(ConnectorAttributeBuilder.Build("a", "a")); + Filter b = + FilterBuilder.Contains(ConnectorAttributeBuilder.Build("b", "b")); + Filter c = + FilterBuilder.EndsWith(ConnectorAttributeBuilder.Build("c", "c")); + Filter d = + FilterBuilder.Contains(ConnectorAttributeBuilder.Build("d", "d")); + + Filter filter = + FilterBuilder.And( + FilterBuilder.Or(a, b), + FilterBuilder.Or(c, d)); + String expected = "( | ( CONTAINS a a ) ( CONTAINS b b ) )"; + String actual = + TranslateSingle(new NoAndNoEndsWithTranslator(),filter); + Assert.AreEqual(expected, actual); + + a = + FilterBuilder.Contains(ConnectorAttributeBuilder.Build("a", "a")); + b = + FilterBuilder.EndsWith(ConnectorAttributeBuilder.Build("b", "b")); + c = + FilterBuilder.Contains(ConnectorAttributeBuilder.Build("c", "c")); + d = + FilterBuilder.Contains(ConnectorAttributeBuilder.Build("d", "d")); + + filter = + FilterBuilder.And( + FilterBuilder.Or(a, b), + FilterBuilder.Or(c, d)); + expected = "( | ( CONTAINS c c ) ( CONTAINS d d ) )"; + actual = + TranslateSingle(new NoAndNoEndsWithTranslator(),filter); + Assert.AreEqual(expected, actual); + + a = + FilterBuilder.Contains(ConnectorAttributeBuilder.Build("a", "a")); + b = + FilterBuilder.EndsWith(ConnectorAttributeBuilder.Build("b", "b")); + c = + FilterBuilder.Contains(ConnectorAttributeBuilder.Build("c", "c")); + d = + FilterBuilder.EndsWith(ConnectorAttributeBuilder.Build("d", "d")); + + filter = + FilterBuilder.And( + FilterBuilder.Or(a, b), + FilterBuilder.Or(c, d)); + IList results = + new NoAndNoEndsWithTranslator().Translate(filter); + Assert.AreEqual(0, results.Count); + } + + //-no and, no or, no leaf + [Test] + public void TestSimplifyNoAndNoOrNoLeaf() { + Filter a = + FilterBuilder.Contains(ConnectorAttributeBuilder.Build("a", "a")); + Filter b = + FilterBuilder.Contains(ConnectorAttributeBuilder.Build("b", "b")); + Filter c = + FilterBuilder.EndsWith(ConnectorAttributeBuilder.Build("c", "c")); + Filter d = + FilterBuilder.Contains(ConnectorAttributeBuilder.Build("d", "d")); + + Filter filter = + FilterBuilder.And( + FilterBuilder.Or(a, b), + FilterBuilder.Or(c, d)); + IList results = + new NoAndNoOrNoEndsWithTranslator().Translate(filter); + Assert.AreEqual(2, results.Count); + Assert.AreEqual("( CONTAINS a a )", + results[0]); + Assert.AreEqual("( CONTAINS b b )", + results[1]); + + a = + FilterBuilder.Contains(ConnectorAttributeBuilder.Build("a", "a")); + b = + FilterBuilder.EndsWith(ConnectorAttributeBuilder.Build("b", "b")); + c = + FilterBuilder.Contains(ConnectorAttributeBuilder.Build("c", "c")); + d = + FilterBuilder.Contains(ConnectorAttributeBuilder.Build("d", "d")); + filter = + FilterBuilder.And( + FilterBuilder.Or(a, b), + FilterBuilder.Or(c, d)); + results = + new NoAndNoOrNoEndsWithTranslator().Translate(filter); + Assert.AreEqual(2, results.Count); + Assert.AreEqual("( CONTAINS c c )", + results[0]); + Assert.AreEqual("( CONTAINS d d )", + results[1]); + + } + + private static String TranslateSingle(AbstractFilterTranslator translator, + Filter filter) { + IList translated = + translator.Translate(filter); + Assert.AreEqual(1, translated.Count); + return translated[0]; + } + } +} diff --git a/DotNetConnectors.sln/FrameworkTests/FrameworkTests.csproj b/DotNetConnectors.sln/FrameworkTests/FrameworkTests.csproj new file mode 100644 index 00000000..8137402f --- /dev/null +++ b/DotNetConnectors.sln/FrameworkTests/FrameworkTests.csproj @@ -0,0 +1,153 @@ + + + + {32804F5A-812C-4FA6-835C-BDAE5B24D355} + Debug + AnyCPU + Library + FrameworkTests + FrameworkTests + v3.5 + + + bin\Debug\ + True + Full + False + True + DEBUG;TRACE + + + bin\Release\ + False + None + True + False + TRACE + + + + + + + 3.5 + + + + 3.5 + + + + 3.5 + + + + + + + + + + + + + + + + + + + + Always + + + Always + + + + + {F140E8DA-52B4-4159-992A-9DA10EA8EEFB} + Common + + + {8B24461B-456A-4032-89A1-CD418F7B5B62} + Framework + + + {5B011775-B121-4EEE-A410-BA2D2F5BFB8B} + FrameworkInternal + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/DotNetConnectors.sln/FrameworkTests/GuardedStringTests.cs b/DotNetConnectors.sln/FrameworkTests/GuardedStringTests.cs new file mode 100644 index 00000000..789cfc60 --- /dev/null +++ b/DotNetConnectors.sln/FrameworkTests/GuardedStringTests.cs @@ -0,0 +1,100 @@ +/* + * ==================== + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER. + * + * Copyright 2007-2008 Sun Microsystems, Inc. All rights reserved. + * + * The contents of this file are subject to the terms of the Common Development + * and Distribution License("CDDL") (the "License"). You may not use this file + * except in compliance with the License. + * + * You can obtain a copy of the License at + * http://IdentityConnectors.dev.java.net/legal/license.txt + * See the License for the specific language governing permissions and limitations + * under the License. + * + * When distributing the Covered Code, include this CDDL Header Notice in each file + * and include the License file at identityconnectors/legal/license.txt. + * If applicable, add the following below this CDDL Header, with the fields + * enclosed by brackets [] replaced by your own identifying information: + * "Portions Copyrighted [year] [name of copyright owner]" + * ==================== + */ +using System; +using System.Security; +using System.Runtime.InteropServices; +using System.Text; +using Org.IdentityConnectors.Common.Security; +using Org.IdentityConnectors.Framework.Common.Serializer; +using NUnit.Framework; +using NUnit.Framework.SyntaxHelpers; + +namespace FrameworkTests +{ + [TestFixture] + public class GuardedStringTests + { + [Test] + public void TestBasics() + { + GuardedString ss = new GuardedString(); + ss.AppendChar('f'); + ss.AppendChar('o'); + ss.AppendChar('o'); + ss.AppendChar('b'); + ss.AppendChar('a'); + ss.AppendChar('r'); + String decrypted = DecryptToString(ss); + Assert.AreEqual("foobar",decrypted); + String hash = ss.GetBase64SHA1Hash(); + Assert.IsTrue(ss.VerifyBase64SHA1Hash(hash)); + ss.AppendChar('2'); + Assert.IsFalse(ss.VerifyBase64SHA1Hash(hash)); + } + [Test] + public void TestUnicode() { + + for ( int i = 0; i < 0xFFFF; i++) { + int expected = i; + char c = (char)i; + GuardedString gs = new GuardedString(); + gs = (GuardedString)SerializerUtil.CloneObject(gs); + gs.AppendChar(c); + gs.Access(clearChars=> { + int v = (int)clearChars[0]; + Assert.AreEqual(expected, v); + }); + + } + } + + [Test] + public void TestEquals() { + GuardedString str1 = new GuardedString(); + GuardedString str2 = new GuardedString(); + Assert.AreEqual(str1, str2); + str2.AppendChar('2'); + Assert.AreNotEqual(str1,str2); + str1.AppendChar('2'); + Assert.AreEqual(str1, str2); + } + + + /** + * Highly insecure method! Do not do this in production + * code. This is only for test purposes + */ + private String DecryptToString(GuardedString str) { + StringBuilder buf = new StringBuilder(); + str.Access( + array=> + { + for ( int i = 0 ; i < array.Length; i++) + { + buf.Append(array[i]); + } + }); + return buf.ToString(); + } + } +} diff --git a/DotNetConnectors.sln/FrameworkTests/LocaleTests.cs b/DotNetConnectors.sln/FrameworkTests/LocaleTests.cs new file mode 100644 index 00000000..0176a1af --- /dev/null +++ b/DotNetConnectors.sln/FrameworkTests/LocaleTests.cs @@ -0,0 +1,210 @@ +/* + * ==================== + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER. + * + * Copyright 2007-2008 Sun Microsystems, Inc. All rights reserved. + * + * The contents of this file are subject to the terms of the Common Development + * and Distribution License("CDDL") (the "License"). You may not use this file + * except in compliance with the License. + * + * You can obtain a copy of the License at + * http://IdentityConnectors.dev.java.net/legal/license.txt + * See the License for the specific language governing permissions and limitations + * under the License. + * + * When distributing the Covered Code, include this CDDL Header Notice in each file + * and include the License file at identityconnectors/legal/license.txt. + * If applicable, add the following below this CDDL Header, with the fields + * enclosed by brackets [] replaced by your own identifying information: + * "Portions Copyrighted [year] [name of copyright owner]" + * ==================== + */ +using System; +using NUnit.Framework; +using NUnit.Framework.SyntaxHelpers; +using System.Globalization; +using System.Collections.Generic; +using Org.IdentityConnectors.Common; +namespace FrameworkTests +{ + [TestFixture] + public class LocaleTests + { + + + [Test] + public void TestJava2CSharp() + { + HashSet + cultures = new HashSet(CultureInfo.GetCultures(CultureTypes.AllCultures)); + + TestJavaLocale(cultures, new Locale("ar", "", ""), "Arabic"); + TestJavaLocale(cultures, new Locale("be", "", ""), "Belarusian"); + TestJavaLocale(cultures, new Locale("bg", "", ""), "Bulgarian"); + TestJavaLocale(cultures, new Locale("ca", "", ""), "Catalan"); + TestJavaLocale(cultures, new Locale("cs", "", ""), "Czech"); + TestJavaLocale(cultures, new Locale("da", "", ""), "Danish"); + TestJavaLocale(cultures, new Locale("de", "", ""), "German"); + TestJavaLocale(cultures, new Locale("el", "", ""), "Greek"); + TestJavaLocale(cultures, new Locale("en", "", ""), "English"); + TestJavaLocale(cultures, new Locale("es", "", ""), "Spanish"); + TestJavaLocale(cultures, new Locale("et", "", ""), "Estonian"); + TestJavaLocale(cultures, new Locale("fi", "", ""), "Finnish"); + TestJavaLocale(cultures, new Locale("fr", "", ""), "French"); + TestJavaLocale(cultures, new Locale("hr", "", ""), "Croatian"); + TestJavaLocale(cultures, new Locale("hu", "", ""), "Hungarian"); + TestJavaLocale(cultures, new Locale("is", "", ""), "Icelandic"); + TestJavaLocale(cultures, new Locale("it", "", ""), "Italian"); + TestJavaLocale(cultures, new Locale("iw", "", ""), "Hebrew"); + TestJavaLocale(cultures, new Locale("ja", "", ""), "Japanese"); + TestJavaLocale(cultures, new Locale("ko", "", ""), "Korean"); + TestJavaLocale(cultures, new Locale("lt", "", ""), "Lithuanian"); + TestJavaLocale(cultures, new Locale("lv", "", ""), "Latvian"); + TestJavaLocale(cultures, new Locale("mk", "", ""), "Macedonian"); + TestJavaLocale(cultures, new Locale("nl", "", ""), "Dutch"); + TestJavaLocale(cultures, new Locale("no", "", ""), "Norwegian"); + TestJavaLocale(cultures, new Locale("pl", "", ""), "Polish"); + TestJavaLocale(cultures, new Locale("pt", "", ""), "Portuguese"); + TestJavaLocale(cultures, new Locale("ro", "", ""), "Romanian"); + TestJavaLocale(cultures, new Locale("ru", "", ""), "Russian"); + TestJavaLocale(cultures, new Locale("sk", "", ""), "Slovak"); + TestJavaLocale(cultures, new Locale("sl", "", ""), "Slovenian"); + TestJavaLocale(cultures, new Locale("sq", "", ""), "Albanian"); + TestJavaLocale(cultures, new Locale("sr", "", ""), "Serbian"); + TestJavaLocale(cultures, new Locale("sv", "", ""), "Swedish"); + TestJavaLocale(cultures, new Locale("th", "", ""), "Thai"); + TestJavaLocale(cultures, new Locale("tr", "", ""), "Turkish"); + TestJavaLocale(cultures, new Locale("uk", "", ""), "Ukrainian"); + TestJavaLocale(cultures, new Locale("vi", "", ""), "Vietnamese"); + TestJavaLocale(cultures, new Locale("zh", "", ""), "Chinese"); + TestJavaLocale(cultures, new Locale("ar", "AE", ""), "Arabic (United Arab Emirates)"); + TestJavaLocale(cultures, new Locale("ar", "BH", ""), "Arabic (Bahrain)"); + TestJavaLocale(cultures, new Locale("ar", "DZ", ""), "Arabic (Algeria)"); + TestJavaLocale(cultures, new Locale("ar", "EG", ""), "Arabic (Egypt)"); + TestJavaLocale(cultures, new Locale("ar", "IQ", ""), "Arabic (Iraq)"); + TestJavaLocale(cultures, new Locale("ar", "JO", ""), "Arabic (Jordan)"); + TestJavaLocale(cultures, new Locale("ar", "KW", ""), "Arabic (Kuwait)"); + TestJavaLocale(cultures, new Locale("ar", "LB", ""), "Arabic (Lebanon)"); + TestJavaLocale(cultures, new Locale("ar", "LY", ""), "Arabic (Libya)"); + TestJavaLocale(cultures, new Locale("ar", "MA", ""), "Arabic (Morocco)"); + TestJavaLocale(cultures, new Locale("ar", "OM", ""), "Arabic (Oman)"); + TestJavaLocale(cultures, new Locale("ar", "QA", ""), "Arabic (Qatar)"); + TestJavaLocale(cultures, new Locale("ar", "SA", ""), "Arabic (Saudi Arabia)"); + TestJavaLocale(cultures, + new Locale("ar", "SD", ""), + "Arabic (Sudan)", + new Locale("ar")); + TestJavaLocale(cultures, new Locale("ar", "SY", ""), "Arabic (Syria)"); + TestJavaLocale(cultures, new Locale("ar", "TN", ""), "Arabic (Tunisia)"); + TestJavaLocale(cultures, new Locale("ar", "YE", ""), "Arabic (Yemen)"); + TestJavaLocale(cultures, new Locale("be", "BY", ""), "Belarusian (Belarus)"); + TestJavaLocale(cultures, new Locale("bg", "BG", ""), "Bulgarian (Bulgaria)"); + TestJavaLocale(cultures, new Locale("ca", "ES", ""), "Catalan (Spain)"); + TestJavaLocale(cultures, new Locale("cs", "CZ", ""), "Czech (Czech Republic)"); + TestJavaLocale(cultures, new Locale("da", "DK", ""), "Danish (Denmark)"); + TestJavaLocale(cultures, new Locale("de", "AT", ""), "German (Austria)"); + TestJavaLocale(cultures, new Locale("de", "CH", ""), "German (Switzerland)"); + TestJavaLocale(cultures, new Locale("de", "DE", ""), "German (Germany)"); + TestJavaLocale(cultures, new Locale("de", "LU", ""), "German (Luxembourg)"); + TestJavaLocale(cultures, new Locale("el", "GR", ""), "Greek (Greece)"); + TestJavaLocale(cultures, new Locale("en", "AU", ""), "English (Australia)"); + TestJavaLocale(cultures, new Locale("en", "CA", ""), "English (Canada)"); + TestJavaLocale(cultures, new Locale("en", "GB", ""), "English (United Kingdom)"); + TestJavaLocale(cultures, new Locale("en", "IE", ""), "English (Ireland)"); + TestJavaLocale(cultures, new Locale("en", "IN", ""), + "English (India)", + new Locale("en")); + TestJavaLocale(cultures, new Locale("en", "NZ", ""), "English (New Zealand)"); + TestJavaLocale(cultures, new Locale("en", "US", ""), "English (United States)"); + TestJavaLocale(cultures, new Locale("en", "ZA", ""), "English (South Africa)"); + TestJavaLocale(cultures, new Locale("es", "AR", ""), "Spanish (Argentina)"); + TestJavaLocale(cultures, new Locale("es", "BO", ""), "Spanish (Bolivia)"); + TestJavaLocale(cultures, new Locale("es", "CL", ""), "Spanish (Chile)"); + TestJavaLocale(cultures, new Locale("es", "CO", ""), "Spanish (Colombia)"); + TestJavaLocale(cultures, new Locale("es", "CR", ""), "Spanish (Costa Rica)"); + TestJavaLocale(cultures, new Locale("es", "DO", ""), "Spanish (Dominican Republic)"); + TestJavaLocale(cultures, new Locale("es", "EC", ""), "Spanish (Ecuador)"); + TestJavaLocale(cultures, new Locale("es", "ES", ""), "Spanish (Spain)"); + TestJavaLocale(cultures, new Locale("es", "GT", ""), "Spanish (Guatemala)"); + TestJavaLocale(cultures, new Locale("es", "HN", ""), "Spanish (Honduras)"); + TestJavaLocale(cultures, new Locale("es", "MX", ""), "Spanish (Mexico)"); + TestJavaLocale(cultures, new Locale("es", "NI", ""), "Spanish (Nicaragua)"); + TestJavaLocale(cultures, new Locale("es", "PA", ""), "Spanish (Panama)"); + TestJavaLocale(cultures, new Locale("es", "PE", ""), "Spanish (Peru)"); + TestJavaLocale(cultures, new Locale("es", "PR", ""), "Spanish (Puerto Rico)"); + TestJavaLocale(cultures, new Locale("es", "PY", ""), "Spanish (Paraguay)"); + TestJavaLocale(cultures, new Locale("es", "SV", ""), "Spanish (El Salvador)"); + TestJavaLocale(cultures, new Locale("es", "UY", ""), "Spanish (Uruguay)"); + TestJavaLocale(cultures, new Locale("es", "VE", ""), "Spanish (Venezuela)"); + TestJavaLocale(cultures, new Locale("et", "EE", ""), "Estonian (Estonia)"); + TestJavaLocale(cultures, new Locale("fi", "FI", ""), "Finnish (Finland)"); + TestJavaLocale(cultures, new Locale("fr", "BE", ""), "French (Belgium)"); + TestJavaLocale(cultures, new Locale("fr", "CA", ""), "French (Canada)"); + TestJavaLocale(cultures, new Locale("fr", "CH", ""), "French (Switzerland)"); + TestJavaLocale(cultures, new Locale("fr", "FR", ""), "French (France)"); + TestJavaLocale(cultures, new Locale("fr", "LU", ""), "French (Luxembourg)"); + TestJavaLocale(cultures, new Locale("hi", "IN", ""), "Hindi (India)"); + TestJavaLocale(cultures, new Locale("hr", "HR", ""), "Croatian (Croatia)"); + TestJavaLocale(cultures, new Locale("hu", "HU", ""), "Hungarian (Hungary)"); + TestJavaLocale(cultures, new Locale("is", "IS", ""), "Icelandic (Iceland)"); + TestJavaLocale(cultures, new Locale("it", "CH", ""), "Italian (Switzerland)"); + TestJavaLocale(cultures, new Locale("it", "IT", ""), "Italian (Italy)"); + TestJavaLocale(cultures, new Locale("iw", "IL", ""), "Hebrew (Israel)"); + TestJavaLocale(cultures, new Locale("ja", "JP", ""), "Japanese (Japan)"); + TestJavaLocale(cultures, new Locale("ko", "KR", ""), "Korean (South Korea)"); + TestJavaLocale(cultures, new Locale("lt", "LT", ""), "Lithuanian (Lithuania)"); + TestJavaLocale(cultures, new Locale("lv", "LV", ""), "Latvian (Latvia)"); + TestJavaLocale(cultures, new Locale("mk", "MK", ""), "Macedonian (Macedonia)"); + TestJavaLocale(cultures, new Locale("nl", "BE", ""), "Dutch (Belgium)"); + TestJavaLocale(cultures, new Locale("nl", "NL", ""), "Dutch (Netherlands)"); + TestJavaLocale(cultures, new Locale("no", "NO", ""), "Norwegian (Norway)"); + TestJavaLocale(cultures, new Locale("no", "NO", "NY"), "Norwegian (Norway,Nynorsk)"); + TestJavaLocale(cultures, new Locale("pl", "PL", ""), "Polish (Poland)"); + TestJavaLocale(cultures, new Locale("pt", "BR", ""), "Portuguese (Brazil)"); + TestJavaLocale(cultures, new Locale("pt", "PT", ""), "Portuguese (Portugal)"); + TestJavaLocale(cultures, new Locale("ro", "RO", ""), "Romanian (Romania)"); + TestJavaLocale(cultures, new Locale("ru", "RU", ""), "Russian (Russia)"); + TestJavaLocale(cultures, new Locale("sk", "SK", ""), "Slovak (Slovakia)"); + TestJavaLocale(cultures, new Locale("sl", "SI", ""), "Slovenian (Slovenia)"); + TestJavaLocale(cultures, new Locale("sq", "AL", ""), "Albanian (Albania)"); + TestJavaLocale(cultures, new Locale("sr", "BA", ""), + "Serbian (Bosnia and Herzegovina)", + new Locale("sr")); + TestJavaLocale(cultures, new Locale("sr", "CS", ""), + "Serbian (Serbia and Montenegro)", + new Locale("sr")); + TestJavaLocale(cultures, new Locale("sv", "SE", ""), "Swedish (Sweden)"); + TestJavaLocale(cultures, new Locale("th", "TH", ""), "Thai (Thailand)"); + TestJavaLocale(cultures, new Locale("th", "TH", "TH"), + "Thai (Thailand,TH)", + new Locale("th","TH")); + TestJavaLocale(cultures, new Locale("tr", "TR", ""), "Turkish (Turkey)"); + TestJavaLocale(cultures, new Locale("uk", "UA", ""), "Ukrainian (Ukraine)"); + TestJavaLocale(cultures, new Locale("vi", "VN", ""), "Vietnamese (Vietnam)"); + TestJavaLocale(cultures, new Locale("zh", "CN", ""), "Chinese (China)"); + TestJavaLocale(cultures, new Locale("zh", "HK", ""), "Chinese (Hong Kong)"); + TestJavaLocale(cultures, new Locale("zh", "TW", ""), "Chinese (Taiwan)"); + + //foreach (CultureInfo info in cultures) { + // Console.WriteLine("remaining: "+info+" "+info.DisplayName+" "+info.TwoLetterISOLanguageName); + //} + } + private void TestJavaLocale(HashSet cultures, + Locale original, + String display) { + TestJavaLocale(cultures,original,display,null); + } + private void TestJavaLocale(HashSet cultures, + Locale original, + String display, + Locale expected) { + if ( expected == null ) { + expected = original; + } + CultureInfo cinfo = original.ToCultureInfo(); + Locale actual = Locale.FindLocale(cinfo); + Assert.AreEqual(expected,actual,display+" ("+original+") "+" didn't map"); + } + } +} diff --git a/DotNetConnectors.sln/FrameworkTests/ObjectNormalizerFacadeTests.cs b/DotNetConnectors.sln/FrameworkTests/ObjectNormalizerFacadeTests.cs new file mode 100644 index 00000000..475f770e --- /dev/null +++ b/DotNetConnectors.sln/FrameworkTests/ObjectNormalizerFacadeTests.cs @@ -0,0 +1,246 @@ +/* + * ==================== + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER. + * + * Copyright 2007-2008 Sun Microsystems, Inc. All rights reserved. + * + * The contents of this file are subject to the terms of the Common Development + * and Distribution License("CDDL") (the "License"). You may not use this file + * except in compliance with the License. + * + * You can obtain a copy of the License at + * http://IdentityConnectors.dev.java.net/legal/license.txt + * See the License for the specific language governing permissions and limitations + * under the License. + * + * When distributing the Covered Code, include this CDDL Header Notice in each file + * and include the License file at identityconnectors/legal/license.txt. + * If applicable, add the following below this CDDL Header, with the fields + * enclosed by brackets [] replaced by your own identifying information: + * "Portions Copyrighted [year] [name of copyright owner]" + * ==================== + */ +using System; +using NUnit.Framework; +using NUnit.Framework.SyntaxHelpers; +using Org.IdentityConnectors.Framework.Common.Objects; +using Org.IdentityConnectors.Framework.Common.Objects.Filters; +using Org.IdentityConnectors.Framework.Common.Serializer; +using Org.IdentityConnectors.Framework.Spi; +using Org.IdentityConnectors.Framework.Impl.Api.Local.Operations; +namespace FrameworkTests +{ + [TestFixture] + public class ObjectNormalizerFacadeTests + { + public class MyAttributeNormalizer : AttributeNormalizer { + public ConnectorAttribute NormalizeAttribute(ObjectClass oclass, ConnectorAttribute attribute) { + if ( attribute.Is("foo")) { + String val = ConnectorAttributeUtil.GetStringValue(attribute); + return ConnectorAttributeBuilder.Build("foo",val.Trim()); + } + else { + return attribute; + } + } + } + + private ConnectorAttribute CreateTestAttribute() + { + return ConnectorAttributeBuilder.Build("foo"," bar "); + } + + private ConnectorAttribute CreateNormalizedTestAttribute() + { + return ConnectorAttributeBuilder.Build("foo","bar"); + } + + private ObjectNormalizerFacade CreateTestNormalizer() + { + ObjectNormalizerFacade facade = new + ObjectNormalizerFacade(ObjectClass.ACCOUNT, + new MyAttributeNormalizer()); + return facade; + } + + private void AssertNormalizedFilter(Filter expectedNormalizedFilter, + Filter filter) { + ObjectNormalizerFacade facade = + CreateTestNormalizer(); + filter = facade.NormalizeFilter(filter); + String expectedXml = SerializerUtil.SerializeXmlObject(expectedNormalizedFilter, false); + String actualXml = SerializerUtil.SerializeXmlObject(filter, false); + Assert.AreEqual(expectedXml, actualXml); + } + + + + [Test] + public void TestEndsWith() + { + Filter expected = + FilterBuilder.EndsWith(CreateNormalizedTestAttribute()); + Filter filter = + FilterBuilder.EndsWith(CreateTestAttribute()); + AssertNormalizedFilter(expected,filter); + } + [Test] + public void TestStartsWith() + { + Filter expected = + FilterBuilder.StartsWith(CreateNormalizedTestAttribute()); + Filter filter = + FilterBuilder.StartsWith(CreateTestAttribute()); + AssertNormalizedFilter(expected,filter); + } + [Test] + public void TestContains() + { + Filter expected = + FilterBuilder.Contains(CreateNormalizedTestAttribute()); + Filter filter = + FilterBuilder.Contains(CreateTestAttribute()); + AssertNormalizedFilter(expected,filter); + } + [Test] + public void TestEqualTo() + { + Filter expected = + FilterBuilder.EqualTo(CreateNormalizedTestAttribute()); + Filter filter = + FilterBuilder.EqualTo(CreateTestAttribute()); + AssertNormalizedFilter(expected,filter); + } + [Test] + public void TestGreaterThanOrEqualTo() + { + Filter expected = + FilterBuilder.GreaterThanOrEqualTo(CreateNormalizedTestAttribute()); + Filter filter = + FilterBuilder.GreaterThanOrEqualTo(CreateTestAttribute()); + AssertNormalizedFilter(expected,filter); + } + [Test] + public void TestLessThanOrEqualTo() + { + Filter expected = + FilterBuilder.LessThanOrEqualTo(CreateNormalizedTestAttribute()); + Filter filter = + FilterBuilder.LessThanOrEqualTo(CreateTestAttribute()); + AssertNormalizedFilter(expected,filter); + } + [Test] + public void TestLessThan() + { + Filter expected = + FilterBuilder.LessThan(CreateNormalizedTestAttribute()); + Filter filter = + FilterBuilder.LessThan(CreateTestAttribute()); + AssertNormalizedFilter(expected,filter); + } + [Test] + public void TestGreaterThan() + { + Filter expected = + FilterBuilder.GreaterThan(CreateNormalizedTestAttribute()); + Filter filter = + FilterBuilder.GreaterThan(CreateTestAttribute()); + AssertNormalizedFilter(expected,filter); + } + [Test] + public void TestAnd() + { + Filter expected = + FilterBuilder.And(FilterBuilder.Contains(CreateNormalizedTestAttribute()), + FilterBuilder.Contains(CreateNormalizedTestAttribute())); + Filter filter = + FilterBuilder.And(FilterBuilder.Contains(CreateTestAttribute()), + FilterBuilder.Contains(CreateTestAttribute())); + AssertNormalizedFilter(expected,filter); + } + [Test] + public void TestOr() + { + Filter expected = + FilterBuilder.Or(FilterBuilder.Contains(CreateNormalizedTestAttribute()), + FilterBuilder.Contains(CreateNormalizedTestAttribute())); + Filter filter = + FilterBuilder.Or(FilterBuilder.Contains(CreateTestAttribute()), + FilterBuilder.Contains(CreateTestAttribute())); + AssertNormalizedFilter(expected,filter); + } + [Test] + public void TestNot() + { + Filter expected = + FilterBuilder.Not(FilterBuilder.Contains(CreateNormalizedTestAttribute())); + Filter filter = + FilterBuilder.Not(FilterBuilder.Contains(CreateTestAttribute())); + AssertNormalizedFilter(expected,filter); + } + [Test] + public void TestContainsAllValues() + { + Filter expected = + FilterBuilder.ContainsAllValues(CreateNormalizedTestAttribute()); + Filter filter = + FilterBuilder.ContainsAllValues(CreateTestAttribute()); + AssertNormalizedFilter(expected,filter); + } + [Test] + public void TestConnectorObject() + { + ConnectorObjectBuilder builder = + new ConnectorObjectBuilder(); + builder.SetName("myname"); + builder.SetUid("myuid"); + builder.AddAttribute(CreateTestAttribute()); + ConnectorObject v1 = builder.Build(); + ConnectorObject v2 = CreateTestNormalizer().NormalizeObject(v1); + builder = + new ConnectorObjectBuilder(); + builder.SetName("myname"); + builder.SetUid("myuid"); + builder.AddAttribute(CreateNormalizedTestAttribute()); + ConnectorObject expected = builder.Build(); + Assert.AreEqual(expected, v2); + Assert.IsFalse(expected.Equals(v1)); + } + + [Test] + public void TestSyncDelta() + { + ConnectorObjectBuilder objbuilder = + new ConnectorObjectBuilder(); + objbuilder.SetName("myname"); + objbuilder.SetUid("myuid"); + objbuilder.AddAttribute(CreateTestAttribute()); + ConnectorObject obj = objbuilder.Build(); + + SyncDeltaBuilder builder = + new SyncDeltaBuilder(); + builder.DeltaType=(SyncDeltaType.DELETE); + builder.Token=(new SyncToken("mytoken")); + builder.Uid=(new Uid("myuid")); + builder.Object=(obj); + SyncDelta v1 = builder.Build(); + SyncDelta v2 = CreateTestNormalizer().NormalizeSyncDelta(v1); + builder = + new SyncDeltaBuilder(); + builder.DeltaType=(SyncDeltaType.DELETE); + builder.Token=(new SyncToken("mytoken")); + builder.Uid=(new Uid("myuid")); + objbuilder = + new ConnectorObjectBuilder(); + objbuilder.SetName("myname"); + objbuilder.SetUid("myuid"); + objbuilder.AddAttribute(CreateNormalizedTestAttribute()); + builder.Object = objbuilder.Build(); + SyncDelta expected = builder.Build(); + Assert.AreEqual(expected, v2); + Assert.IsFalse(expected.Equals(v1)); + + } + + } +} diff --git a/DotNetConnectors.sln/FrameworkTests/ObjectPoolTests.cs b/DotNetConnectors.sln/FrameworkTests/ObjectPoolTests.cs new file mode 100644 index 00000000..3c09f8ec --- /dev/null +++ b/DotNetConnectors.sln/FrameworkTests/ObjectPoolTests.cs @@ -0,0 +1,252 @@ +/* + * ==================== + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER. + * + * Copyright 2007-2008 Sun Microsystems, Inc. All rights reserved. + * + * The contents of this file are subject to the terms of the Common Development + * and Distribution License("CDDL") (the "License"). You may not use this file + * except in compliance with the License. + * + * You can obtain a copy of the License at + * http://IdentityConnectors.dev.java.net/legal/license.txt + * See the License for the specific language governing permissions and limitations + * under the License. + * + * When distributing the Covered Code, include this CDDL Header Notice in each file + * and include the License file at identityconnectors/legal/license.txt. + * If applicable, add the following below this CDDL Header, with the fields + * enclosed by brackets [] replaced by your own identifying information: + * "Portions Copyrighted [year] [name of copyright owner]" + * ==================== + */ +using System; +using System.Threading; +using NUnit.Framework; +using NUnit.Framework.SyntaxHelpers; + +using Org.IdentityConnectors.Common.Pooling; +using Org.IdentityConnectors.Framework.Common.Exceptions; +using Org.IdentityConnectors.Framework.Common.Objects; +using Org.IdentityConnectors.Framework.Spi; +using Org.IdentityConnectors.Framework.Impl.Api.Local; + +namespace FrameworkTests +{ + [TestFixture] + public class ObjectPoolTests + { + private class MyTestConnection { + private bool _isGood = true; + + public void Test() { + if (!_isGood) { + throw new ConnectorException("Connection is bad"); + } + } + + public void Dispose() { + _isGood = false; + } + + public bool IsGood { + get { + return _isGood; + } + } + } + + private class MyTestConnectionFactory : ObjectPoolHandler { + + private bool _createBadConnection = false; + private int _totalCreatedConnections = 0; + + public MyTestConnection NewObject() { + _totalCreatedConnections++; + MyTestConnection rv = new MyTestConnection(); + if (_createBadConnection) { + rv.Dispose(); + } + return rv; + } + public void TestObject(MyTestConnection obj) { + obj.Test(); + } + public void DisposeObject(MyTestConnection obj) { + obj.Dispose(); + } + + public int TotalCreatedConnections { + get { + return _totalCreatedConnections; + } + } + + public bool CreateBadConnection { + set { + _createBadConnection = value; + } + } + } + + private class MyTestThread { + private readonly ObjectPool _pool; + private readonly int _numIterations; + private Exception _exception; + private Thread _thisThread; + public MyTestThread(ObjectPool pool, + int numIterations) { + _pool = pool; + _numIterations = numIterations; + } + public void Run(object o) { + _thisThread = (Thread)o; + try { + for ( int i = 0; i < _numIterations; i++ ) { + MyTestConnection con = + _pool.BorrowObject(); + Thread.Sleep(300); + _pool.ReturnObject(con); + } + } + catch (Exception e) { + _exception = e; + } + } + public void Shutdown() { + _thisThread.Join(); + if (_exception != null) { + throw _exception; + } + } + + } + + [Test] + public void TestWithManyThreads() + { + int NUM_ITERATIONS = 10; + int NUM_THREADS = 10; + int MAX_CONNECTIONS = NUM_THREADS-3; //make sure we get some waiting + ObjectPoolConfiguration config = new ObjectPoolConfiguration(); + config.MaxObjects=(MAX_CONNECTIONS); + config.MaxIdle=(MAX_CONNECTIONS); + config.MinIdle=(MAX_CONNECTIONS); + config.MinEvictableIdleTimeMillis=(60*1000); + config.MaxWait=(60*1000); + MyTestConnectionFactory fact = new MyTestConnectionFactory(); + + ObjectPool pool = new ObjectPool(fact,config); + + MyTestThread [] threads = new MyTestThread[NUM_THREADS]; + for (int i = 0; i < threads.Length; i++) { + threads[i] = new MyTestThread(pool,NUM_ITERATIONS); + Thread thread = new Thread(threads[i].Run); + thread.Start(thread); + } + + foreach (MyTestThread thread in threads) { + thread.Shutdown(); + } + + //these should be the same since we never + //should have disposed anything + Assert.AreEqual(MAX_CONNECTIONS, fact.TotalCreatedConnections); + ObjectPool.Statistics stats = pool.GetStatistics(); + Assert.AreEqual(0, stats.NumActive); + Assert.AreEqual(MAX_CONNECTIONS, stats.NumIdle); + + pool.Shutdown(); + stats = pool.GetStatistics(); + Assert.AreEqual(0, stats.NumActive); + Assert.AreEqual(0, stats.NumIdle); + + } + + [Test] + public void TestBadConnection() + { + int MAX_CONNECTIONS = 3; + ObjectPoolConfiguration config = new ObjectPoolConfiguration(); + config.MaxObjects=(MAX_CONNECTIONS); + config.MaxIdle=(MAX_CONNECTIONS); + config.MinIdle=(MAX_CONNECTIONS); + config.MinEvictableIdleTimeMillis=(60*1000); + config.MaxWait=(60*1000); + MyTestConnectionFactory fact = new MyTestConnectionFactory(); + + ObjectPool pool = new ObjectPool(fact,config); + + //borrow first connection and return + MyTestConnection conn = pool.BorrowObject(); + Assert.AreEqual(1, fact.TotalCreatedConnections); + pool.ReturnObject(conn); + Assert.AreEqual(1, fact.TotalCreatedConnections); + + //re-borrow same connection and return + conn = pool.BorrowObject(); + Assert.AreEqual(1, fact.TotalCreatedConnections); + pool.ReturnObject(conn); + Assert.AreEqual(1, fact.TotalCreatedConnections); + + //dispose and make sure we get a new connection + conn.Dispose(); + conn = pool.BorrowObject(); + Assert.AreEqual(2, fact.TotalCreatedConnections); + pool.ReturnObject(conn); + Assert.AreEqual(2, fact.TotalCreatedConnections); + } + + [Test] + public void TestIdleCleanup() + { + ObjectPoolConfiguration config = new ObjectPoolConfiguration(); + config.MaxObjects=(3); + config.MaxIdle=(2); + config.MinIdle=(1); + config.MinEvictableIdleTimeMillis=(3000); + config.MaxWait=(60*1000); + MyTestConnectionFactory fact = new MyTestConnectionFactory(); + + ObjectPool pool = new ObjectPool(fact,config); + + MyTestConnection conn1 = (MyTestConnection)pool.BorrowObject(); + MyTestConnection conn2 = (MyTestConnection)pool.BorrowObject(); + MyTestConnection conn3 = (MyTestConnection)pool.BorrowObject(); + + Assert.AreEqual(3, fact.TotalCreatedConnections); + pool.ReturnObject(conn1); + Assert.AreEqual(1, pool.GetStatistics().NumIdle); + pool.ReturnObject(conn2); + Assert.AreEqual(2, pool.GetStatistics().NumIdle); + pool.ReturnObject(conn3); + Assert.AreEqual(2, pool.GetStatistics().NumIdle); + Assert.AreEqual(false, conn1.IsGood); + Assert.AreEqual(true, conn2.IsGood); + Assert.AreEqual(true, conn3.IsGood); + Thread.Sleep(((int)(config.MinEvictableIdleTimeMillis+1000))); + MyTestConnection conn4 = (MyTestConnection)pool.BorrowObject(); + Assert.AreSame(conn3, conn4); + Assert.AreEqual(false, conn1.IsGood); + Assert.AreEqual(false, conn2.IsGood); + Assert.AreEqual(true, conn3.IsGood); + Assert.AreEqual(true, conn4.IsGood); + } + + [Test] + public void TestCreateBadConnection() + { + MyTestConnectionFactory fact = new MyTestConnectionFactory(); + fact.CreateBadConnection=(true); + + ObjectPool pool = new ObjectPool(fact,new ObjectPoolConfiguration()); + try { + pool.BorrowObject(); + Assert.Fail("expected exception"); + } + catch (ConnectorException e) { + Assert.AreEqual("Connection is bad", e.Message); + } + } + } +} diff --git a/DotNetConnectors.sln/FrameworkTests/ObjectSerializationTests.cs b/DotNetConnectors.sln/FrameworkTests/ObjectSerializationTests.cs new file mode 100644 index 00000000..637d68f2 --- /dev/null +++ b/DotNetConnectors.sln/FrameworkTests/ObjectSerializationTests.cs @@ -0,0 +1,1128 @@ +/* + * ==================== + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER. + * + * Copyright 2007-2008 Sun Microsystems, Inc. All rights reserved. + * + * The contents of this file are subject to the terms of the Common Development + * and Distribution License("CDDL") (the "License"). You may not use this file + * except in compliance with the License. + * + * You can obtain a copy of the License at + * http://IdentityConnectors.dev.java.net/legal/license.txt + * See the License for the specific language governing permissions and limitations + * under the License. + * + * When distributing the Covered Code, include this CDDL Header Notice in each file + * and include the License file at identityconnectors/legal/license.txt. + * If applicable, add the following below this CDDL Header, with the fields + * enclosed by brackets [] replaced by your own identifying information: + * "Portions Copyrighted [year] [name of copyright owner]" + * ==================== + */ +using System; +using System.IO; +using System.Text; +using NUnit.Framework; +using NUnit.Framework.SyntaxHelpers; +using System.Collections.Generic; +using System.Globalization; +using System.Security; +using System.Linq; +using System.Xml; +using Org.IdentityConnectors.Framework.Impl.Serializer.Binary; +using Org.IdentityConnectors.Common; +using Org.IdentityConnectors.Common.Pooling; +using Org.IdentityConnectors.Common.Security; +using Org.IdentityConnectors.Framework.Api; +using Org.IdentityConnectors.Framework.Api.Operations; +using Org.IdentityConnectors.Framework.Common; +using Org.IdentityConnectors.Framework.Common.Exceptions; +using Org.IdentityConnectors.Framework.Common.Objects; +using Org.IdentityConnectors.Framework.Common.Objects.Filters; +using Org.IdentityConnectors.Framework.Common.Serializer; +using Org.IdentityConnectors.Framework.Impl.Api; +using Org.IdentityConnectors.Framework.Impl.Api.Remote; +using Org.IdentityConnectors.Framework.Impl.Api.Remote.Messages; +using Org.IdentityConnectors.Framework.Impl.Serializer.Xml; +namespace FrameworkTests +{ + [TestFixture] + public class ObjectSerializationTests + { + [Test] + public void TestBoolean() + { + bool v1 = true; + bool v2 = (bool)CloneObject(v1); + Assert.AreEqual(v1, v2); + + v1 = false; + v2 = (bool)CloneObject(v1); + Assert.AreEqual(v1, v2); + } + [Test] + public void TestCharacter() { + char v1 = 'h'; + char v2 = (char)CloneObject(v1); + Assert.AreEqual(v1, v2); + } + [Test] + public void TestInteger() { + int v1 = 12345; + int v2 = (int)CloneObject(v1); + Assert.AreEqual(v1,v2); + + v1 = Int32.MinValue; + v2 = (int)CloneObject(v1); + Assert.AreEqual(v1,v2); + + v1 = Int32.MaxValue; + v2 = (int)CloneObject(v1); + Assert.AreEqual(v1,v2); + + v1 = -1; + v2 = (int)CloneObject(v1); + Assert.AreEqual(v1,v2); + } + + [Test] + public void TestLong() { + long v1 = 12345; + long v2 = (long)CloneObject(v1); + Assert.AreEqual(v1,v2); + + v1 = Int64.MinValue; + v2 = (long)CloneObject(v1); + Assert.AreEqual(v1,v2); + + v1 = Int64.MaxValue; + v2 = (long)CloneObject(v1); + Assert.AreEqual(v1,v2); + + v1 = -1; + v2 = (long)CloneObject(v1); + Assert.AreEqual(v1,v2); + } + + [Test] + public void TestFloat() { + float v1 = 1.1F; + float v2 = (float)CloneObject(v1); + Assert.IsTrue(!Object.ReferenceEquals(v1,v2)); + Assert.AreEqual(v1,v2); + + v1 = Single.Epsilon; + v2 = (float)CloneObject(v1); + Assert.AreEqual(v1,v2); + + v1 = Single.NaN; + v2 = (float)CloneObject(v1); + Assert.AreEqual(v1,v2); + + v1 = Single.NegativeInfinity; + v2 = (float)CloneObject(v1); + Assert.AreEqual(v1,v2); + + v1 = Single.PositiveInfinity; + v2 = (float)CloneObject(v1); + Assert.AreEqual(v1,v2); + + v1 = Single.MinValue; + v2 = (float)CloneObject(v1); + Assert.AreEqual(v1,v2); + + v1 = Single.MaxValue; + v2 = (float)CloneObject(v1); + Assert.AreEqual(v1,v2); + } + + [Test] + public void TestDouble() { + double v1 = 1.1; + double v2 = (double)CloneObject(v1); + Assert.IsTrue(!Object.ReferenceEquals(v1,v2)); + Assert.AreEqual(v1,v2); + + v1 = Double.Epsilon; + v2 = (double)CloneObject(v1); + Assert.AreEqual(v1,v2); + + v1 = Double.NaN; + v2 = (double)CloneObject(v1); + Assert.AreEqual(v1,v2); + + v1 = Double.NegativeInfinity; + v2 = (double)CloneObject(v1); + Assert.AreEqual(v1,v2); + + v1 = Double.PositiveInfinity; + v2 = (double)CloneObject(v1); + Assert.AreEqual(v1,v2); + + v1 = Double.MaxValue; + v2 = (double)CloneObject(v1); + Assert.AreEqual(v1,v2); + + v1 = Double.MinValue; + v2 = (double)CloneObject(v1); + Assert.AreEqual(v1,v2); + } + + [Test] + public void TestString() { + string v1 = "abcd"; + string v2 = (string)CloneObject(v1); + Assert.IsTrue(!Object.ReferenceEquals(v1,v2)); + Assert.AreEqual(v1,v2); + } + + [Test] + public void TestURI() { + Uri v1 = new Uri("mailto:java-net@java.sun.com"); + Uri v2 = (Uri)CloneObject(v1); + Assert.IsTrue(!Object.ReferenceEquals(v1,v2)); + Assert.AreEqual(v1, v2); + } + + [Test] + public void TestFile() { + FileName v1 = new FileName("c:/foo.txt"); + FileName v2 = (FileName)CloneObject(v1); + Assert.IsTrue(!Object.ReferenceEquals(v1,v2)); + Assert.AreEqual(v1, v2); + } + + [Test] + public void TestBigInteger() { + BigInteger v1 = new BigInteger("983423442347324324324"); + BigInteger v2 = (BigInteger)CloneObject(v1); + Assert.AreEqual(v1, v2); + } + + [Test] + public void TestBigDecimal() { + BigDecimal v1 = new BigDecimal(new BigInteger("9847324324324"),45); + BigDecimal v2 = (BigDecimal)CloneObject(v1); + Assert.AreEqual(v1, v2); + } + + [Test] + public void TestByteArray() { + byte [] v1 = {1,2,3}; + byte [] v2 = (byte[])CloneObject(v1); + Assert.AreEqual(3,v2.Length); + Assert.AreEqual(1,v2[0]); + Assert.AreEqual(2,v2[1]); + Assert.AreEqual(3,v2[2]); + Assert.AreEqual(v1,v2); + } + + [Test] + public void TestClasses() { + Assert.AreEqual(typeof(bool), + CloneObject(typeof(bool))); + Assert.AreEqual(typeof(bool?), + CloneObject(typeof(bool?))); + Assert.AreEqual(typeof(bool[][]), + CloneObject(typeof(bool[][]))); + Assert.AreEqual(typeof(bool?[][]), + CloneObject(typeof(bool?[][]))); + Assert.AreEqual(typeof(char), + CloneObject(typeof(char))); + Assert.AreEqual(typeof(char?), + CloneObject(typeof(char?))); + //if this fails, we have added a new type and we need to add + //a serializer for it + Assert.IsTrue( + CollectionUtil.SetsEqual( + CollectionUtil.NewSet(FrameworkUtil.GetAllSupportedConfigTypes()), + (ICollection)CloneObject(FrameworkUtil.GetAllSupportedConfigTypes()))); + //if this fails, we have added a new type and we need to add + //a serializer for it + Assert.IsTrue( + CollectionUtil.SetsEqual( + CollectionUtil.NewSet(FrameworkUtil.GetAllSupportedAttributeTypes()), + (ICollection)CloneObject(FrameworkUtil.GetAllSupportedAttributeTypes()))); + ICollection apiOperations = + new HashSet(); + foreach (SafeType op in FrameworkUtil.AllAPIOperations()) { + apiOperations.Add(op.RawType); + } + //if this fails, need to add to the OperationMappings class + Assert.IsTrue( + CollectionUtil.SetsEqual( + CollectionUtil.NewSet(apiOperations), + (ICollection)CloneObject(apiOperations))); + + } + + [Test] + public void TestArrays() { + int [] v1 = {1,2,3}; + int [] v2 = (int[])CloneObject(v1); + Assert.AreEqual(3, v2.Length); + Assert.AreEqual(1,v2[0]); + Assert.AreEqual(2,v2[1]); + Assert.AreEqual(3,v2[2]); + } + + + [Test] + public void TestObjectArrays() { + object [] v1 = {"1","2","3"}; + object [] v2 = (object[])CloneObject(v1); + Assert.AreEqual(3, v2.Length); + Assert.AreEqual("1",v2[0]); + Assert.AreEqual("2",v2[1]); + Assert.AreEqual("3",v2[2]); + } + + [Test] + public void TestDictionary() { + IDictionary map = new Dictionary(); + map["key1"] = "val1"; + map["key2"] = "val2"; + IDictionary map2 = (IDictionary)CloneObject(map); + Assert.AreEqual("val1",map["key1"]); + Assert.AreEqual("val2",map["key2"]); + + IDictionary map3 = new Dictionary(); + map3["key1"] = "val1"; + map3["key2"] = "val2"; + IDictionary map4 = (IDictionary)CloneObject(map3); + Assert.AreEqual("val1",map4["key1"]); + Assert.AreEqual("val2",map4["key2"]); + } + [Test] + public void TestCaseInsensitiveMap() { + HashSet set = new HashSet(); + set.Add(ConnectorAttributeBuilder.Build("foo1")); + set.Add(ConnectorAttributeBuilder.Build("foo2")); + IDictionary map = ConnectorAttributeUtil.ToMap(set); + Assert.IsTrue(map.ContainsKey("Foo1")); + Assert.IsTrue(map.ContainsKey("Foo2")); + IDictionary map2 = (IDictionary)CloneObject(map); + Assert.IsTrue(map2.ContainsKey("Foo1")); + Assert.IsTrue(map2.ContainsKey("Foo2")); + } + + [Test] + public void TestList() { + IList v1 = new List(); + v1.Add("val1"); + v1.Add("val2"); + IList v2 = (IList)CloneObject(v1); + Assert.AreEqual(2,v2.Count); + Assert.AreEqual("val1",v2[0]); + Assert.AreEqual("val2",v2[1]); + + IList v3 = new List(); + v3.Add("val1"); + v3.Add("val2"); + + IList v4 = (IList)CloneObject(v3); + Assert.AreEqual(2,v4.Count); + Assert.AreEqual("val1",v4[0]); + Assert.AreEqual("val2",v4[1]); + } + + [Test] + public void TestSet() { + //underneath the covers, this creates an + //ICollection - we need to test that ICollection + //becomes a Set + ICollection v1 = + CollectionUtil.NewReadOnlySet("val1","val2"); + HashSet v2 = (HashSet)CloneObject(v1); + Assert.AreEqual(2,v2.Count); + Assert.IsTrue(v2.Contains("val1")); + Assert.IsTrue(v2.Contains("val2")); + + HashSet v3 = new HashSet(); + v3.Add("val1"); + v3.Add("val2"); + + HashSet v4 = (HashSet)CloneObject(v3); + Assert.AreEqual(2,v4.Count); + Assert.IsTrue(v4.Contains("val1")); + Assert.IsTrue(v4.Contains("val2")); + } + + [Test] + public void TestCaseInsensitiveSet() { + ICollection v1 = CollectionUtil.NewCaseInsensitiveSet(); + v1.Add("foo"); + v1.Add("foo2"); + ICollection v2 = (ICollection)CloneObject(v1); + Assert.IsTrue(v2.Contains("Foo")); + Assert.IsTrue(v2.Contains("Foo2")); + } + + [Test] + public void TestCultureInfo() { + //use this one since it uses all 3 fields of locale + CultureInfo v1 = new CultureInfo("nn-NO"); + CultureInfo v2 = (CultureInfo)CloneObject(v1); + Assert.AreEqual(v1,v2); + } + + [Test] + public void TestObjectPoolConfiguration() { + ObjectPoolConfiguration v1 = new ObjectPoolConfiguration(); + v1.MaxObjects = 1; + v1.MaxIdle = 2; + v1.MaxWait = 3; + v1.MinEvictableIdleTimeMillis = 4; + v1.MinIdle = 5; + ObjectPoolConfiguration v2 = + (ObjectPoolConfiguration)CloneObject(v1); + Assert.IsTrue(!Object.ReferenceEquals(v1,v2)); + + Assert.AreEqual(v1,v2); + + Assert.AreEqual(1, v2.MaxObjects); + Assert.AreEqual(2, v2.MaxIdle); + Assert.AreEqual(3, v2.MaxWait); + Assert.AreEqual(4, v2.MinEvictableIdleTimeMillis); + Assert.AreEqual(5, v2.MinIdle); + } + [Test] + public void TestConfigurationProperty() { + ConfigurationPropertyImpl v1 = new ConfigurationPropertyImpl(); + v1.Order = (1); + v1.IsConfidential=(true); + v1.IsRequired=true; + v1.Name=("foo"); + v1.HelpMessageKey=("help key"); + v1.DisplayMessageKey=("display key"); + v1.Value=("bar"); + v1.ValueType=(typeof(string)); + v1.Operations = FrameworkUtil.AllAPIOperations(); + + ConfigurationPropertyImpl v2 = (ConfigurationPropertyImpl) + CloneObject(v1); + Assert.AreEqual(1, v2.Order); + Assert.IsTrue(v2.IsConfidential); + Assert.IsTrue(v2.IsRequired); + Assert.AreEqual("foo", v2.Name); + Assert.AreEqual("help key", v2.HelpMessageKey); + Assert.AreEqual("display key", v2.DisplayMessageKey); + Assert.AreEqual("bar", v2.Value); + Assert.AreEqual(typeof(string), v2.ValueType); + Assert.IsTrue(CollectionUtil.Equals( + FrameworkUtil.AllAPIOperations(),v2.Operations)); + } + + [Test] + public void TestConfigurationProperties() { + ConfigurationPropertyImpl prop1 = new ConfigurationPropertyImpl(); + prop1.Order = (1); + prop1.IsConfidential=(true); + prop1.Name=("foo"); + prop1.HelpMessageKey=("help key"); + prop1.DisplayMessageKey=("display key"); + prop1.Value=("bar"); + prop1.ValueType=(typeof(string)); + prop1.Operations=null; + + ConfigurationPropertiesImpl v1 = new ConfigurationPropertiesImpl(); + v1.Properties=(CollectionUtil.NewReadOnlyList(prop1)); + v1.SetPropertyValue("foo", "bar"); + + ConfigurationPropertiesImpl v2 = (ConfigurationPropertiesImpl) + CloneObject(v1); + Assert.AreEqual("bar", v2.GetProperty("foo").Value); + } + + [Test] + public void TestAPIConfiguration() { + ConfigurationPropertyImpl prop1 = new ConfigurationPropertyImpl(); + prop1.Order = (1); + prop1.IsConfidential=(true); + prop1.Name=("foo"); + prop1.HelpMessageKey=("help key"); + prop1.DisplayMessageKey=("display key"); + prop1.Value=("bar"); + prop1.ValueType=(typeof(string)); + prop1.Operations=null; + + ConfigurationPropertiesImpl props1 = new ConfigurationPropertiesImpl(); + props1.Properties=(CollectionUtil.NewReadOnlyList(prop1)); + + APIConfigurationImpl v1 = new APIConfigurationImpl(); + v1.ConnectorPoolConfiguration=(new ObjectPoolConfiguration()); + v1.ConfigurationProperties=(props1); + v1.IsConnectorPoolingSupported=(true); + v1.ProducerBufferSize=(200); + v1.SupportedOperations=(FrameworkUtil.AllAPIOperations()); + IDictionary,int> map = + CollectionUtil.NewDictionary,int>(SafeType.Get(),6); + v1.TimeoutMap=(map); + + APIConfigurationImpl v2 = (APIConfigurationImpl) + CloneObject(v1); + Assert.IsTrue(!Object.ReferenceEquals(v1, v2)); + Assert.IsNotNull(v2.ConnectorPoolConfiguration); + Assert.IsNotNull(v2.ConfigurationProperties); + Assert.AreEqual(v1.ConnectorPoolConfiguration,v2.ConnectorPoolConfiguration); + Assert.AreEqual(v1.ConfigurationProperties,v2.ConfigurationProperties); + Assert.IsTrue(v2.IsConnectorPoolingSupported); + Assert.AreEqual(200, v2.ProducerBufferSize); + Assert.IsTrue(CollectionUtil.SetsEqual( + FrameworkUtil.AllAPIOperations(), + v2.SupportedOperations)); + Assert.AreEqual(map, v2.TimeoutMap); + } + + [Test] + public void TestConnectorMessages() { + ConnectorMessagesImpl v1 = new ConnectorMessagesImpl(); + IDictionary defaultMap = new Dictionary(); + defaultMap["key1"]="val1"; + IDictionary> messages = + new Dictionary>(); + messages[new CultureInfo("en")]=defaultMap; + messages[new CultureInfo("")]=defaultMap; + v1.Catalogs=(messages); + + ConnectorMessagesImpl v2 = (ConnectorMessagesImpl) + CloneObject(v1); + Assert.IsTrue( + CollectionUtil.SetsEqual(messages[new CultureInfo("")], + v2.Catalogs[new CultureInfo("")])); + Assert.IsTrue( + CollectionUtil.SetsEqual(messages[new CultureInfo("en")], + v2.Catalogs[new CultureInfo("en")])); + } + + [Test] + public void TestRemoteConnectorInfo() { + RemoteConnectorInfoImpl v1 = new RemoteConnectorInfoImpl(); + v1.Messages=(new ConnectorMessagesImpl()); + v1.ConnectorKey=(new ConnectorKey("my bundle", + "my version", + "my connector")); + ConfigurationPropertiesImpl configProperties = new ConfigurationPropertiesImpl(); + configProperties.Properties=(new List()); + APIConfigurationImpl apiImpl = new APIConfigurationImpl(); + apiImpl.ConfigurationProperties=(configProperties); + v1.DefaultAPIConfiguration=(apiImpl); + v1.ConnectorDisplayNameKey=("mykey"); + + RemoteConnectorInfoImpl v2 = (RemoteConnectorInfoImpl) + CloneObject(v1); + + Assert.IsNotNull(v2.Messages); + Assert.AreEqual("my bundle", v2.ConnectorKey.BundleName); + Assert.AreEqual("my version", v2.ConnectorKey.BundleVersion); + Assert.AreEqual("my connector", v2.ConnectorKey.ConnectorName); + Assert.AreEqual("mykey",v2.ConnectorDisplayNameKey); + Assert.IsNotNull(v2.DefaultAPIConfiguration); + } + + [Test] + public void TestAttribute() { + ConnectorAttribute v1 = ConnectorAttributeBuilder.Build("foo", "val1", "val2"); + ConnectorAttribute v2 = (ConnectorAttribute)CloneObject(v1); + Assert.AreEqual(v1,v2); + } + + [Test] + public void TestAttributeInfo() { + + ConnectorAttributeInfoBuilder builder = new ConnectorAttributeInfoBuilder(); + builder.Name=("foo"); + builder.ValueType=(typeof(String)); + builder.Required=(true); + builder.Readable=(true); + builder.Creatable=(true); + builder.Updateable=(true); + builder.MultiValued=(true); + builder.ReturnedByDefault = false; + ConnectorAttributeInfo v1 = builder.Build(); + ConnectorAttributeInfo v2 = (ConnectorAttributeInfo)CloneObject(v1); + Assert.AreEqual(v1,v2); + Assert.AreEqual("foo", v2.Name); + Assert.AreEqual(typeof(String), v2.ValueType); + Assert.IsTrue(v2.IsMultiValued); + Assert.IsTrue(v2.IsReadable); + Assert.IsTrue(v2.IsRequired); + Assert.IsTrue(v2.IsUpdateable); + Assert.IsTrue(v2.IsCreatable); + Assert.IsFalse(v2.IsReturnedByDefault); + + builder.InfoFlags = AllFlags(); + v1 = builder.Build(); + v2 = (ConnectorAttributeInfo)CloneObject(v1); + Assert.AreEqual(v1,v2); + } + + private ConnectorAttributeInfo.Flags AllFlags() + { + ConnectorAttributeInfo.Flags flags = + ConnectorAttributeInfo.Flags.NONE; + foreach (Enum e in Enum.GetValues(typeof(ConnectorAttributeInfo.Flags))) { + ConnectorAttributeInfo.Flags f = + (ConnectorAttributeInfo.Flags)e; + flags |= f; + } + return flags; + } + + [Test] + public void TestConnectorObject() { + ConnectorObjectBuilder bld = new ConnectorObjectBuilder(); + bld.SetUid("foo"); + bld.SetName("fooToo"); + + ConnectorObject v1 = bld.Build(); + ConnectorObject v2 = (ConnectorObject)CloneObject(v1); + + Assert.AreEqual(v1, v2); + } + + [Test] + public void TestName() { + Name v1 = new Name("Test"); + Name v2 = (Name)CloneObject(v1); + Assert.AreEqual(v1, v2); + } + + [Test] + public void TestObjectClass() { + ObjectClass v1 = new ObjectClass("test"); + ObjectClass v2 = (ObjectClass)CloneObject(v1); + Assert.AreEqual(v1, v2); + } + + [Test] + public void TestObjectClassInfo() { + ConnectorAttributeInfoBuilder builder = new ConnectorAttributeInfoBuilder(); + builder.Name=("foo"); + builder.ValueType=(typeof(String)); + builder.Required=(true); + builder.Readable=(true); + builder.Updateable=(true); + builder.MultiValued=(true); + ObjectClassInfoBuilder obld = new ObjectClassInfoBuilder(); + obld.ObjectType = ObjectClass.ORGANIZATION_NAME; + obld.IsContainer = true; + obld.AddAttributeInfo(builder.Build()); + ObjectClassInfo v1 = obld.Build(); + ObjectClassInfo v2 = (ObjectClassInfo)CloneObject(v1); + Assert.AreEqual(v1, v2); + Assert.IsTrue(v2.IsContainer); + } + + [Test] + public void TestUid() { + Uid v1 = new Uid("test"); + Uid v2 = (Uid)CloneObject(v1); + Assert.AreEqual(v1, v2); + } + + [Test] + public void testOperationOptionInfo() { + OperationOptionInfo v1 = + new OperationOptionInfo("name",typeof(int?)); + OperationOptionInfo v2 = + (OperationOptionInfo)CloneObject(v1); + Assert.AreEqual(v1, v2); + } + + [Test] + public void TestSchema() { + OperationOptionInfo opInfo = + new OperationOptionInfo("name",typeof(int?)); + ObjectClassInfoBuilder bld = new ObjectClassInfoBuilder(); + bld.ObjectType = ObjectClass.ORGANIZATION_NAME; + ObjectClassInfo info = bld.Build(); + ICollection temp = CollectionUtil.NewSet(info); + IDictionary,ICollection> map = + new Dictionary,ICollection>(); + map[SafeType.Get()] = temp; + ICollection temp2 = CollectionUtil.NewSet(opInfo); + IDictionary,ICollection> map2 = + new Dictionary,ICollection>(); + map2[SafeType.Get()] = temp2; + Schema v1 = new Schema(CollectionUtil.NewSet(info), + CollectionUtil.NewSet(opInfo), + map, + map2); + Schema v2 = (Schema)CloneObject(v1); + Assert.AreEqual(v1, v2); + Assert.AreEqual(info, v2.ObjectClassInfo.First()); + Assert.AreEqual(1, v2.SupportedObjectClassesByOperation.Count); + Assert.AreEqual(1, v2.SupportedOptionsByOperation.Count); + Assert.AreEqual(1, v2.OperationOptionInfo.Count); + } + + [Test] + public void TestContainsFilter() { + ContainsFilter v1 = new ContainsFilter(ConnectorAttributeBuilder.Build("foo", "bar")); + ContainsFilter v2 = (ContainsFilter)CloneObject(v1); + Assert.AreEqual(v1.GetAttribute(), v2.GetAttribute()); + } + + [Test] + public void TestAndFilter() { + ContainsFilter left1 = new ContainsFilter(ConnectorAttributeBuilder.Build("foo", "bar")); + ContainsFilter right1 = new ContainsFilter(ConnectorAttributeBuilder.Build("foo2", "bar2")); + AndFilter v1 = new AndFilter(left1,right1); + AndFilter v2 = (AndFilter)CloneObject(v1); + ContainsFilter left2 = (ContainsFilter)v2.Left; + ContainsFilter right2 = (ContainsFilter)v2.Right; + Assert.AreEqual(left1.GetAttribute(),left2.GetAttribute()); + Assert.AreEqual(right1.GetAttribute(),right2.GetAttribute()); + } + + [Test] + public void TestEndsWithFilter() { + EndsWithFilter v1 = new EndsWithFilter(ConnectorAttributeBuilder.Build("foo", "bar")); + EndsWithFilter v2 = (EndsWithFilter)CloneObject(v1); + Assert.AreEqual(v1.GetAttribute(), v2.GetAttribute()); + } + + [Test] + public void TestEqualsFilter() { + EqualsFilter v1 = new EqualsFilter(ConnectorAttributeBuilder.Build("foo", "bar")); + EqualsFilter v2 = (EqualsFilter)CloneObject(v1); + Assert.AreEqual(v1.GetAttribute(), v2.GetAttribute()); + } + + [Test] + public void TestGreaterThanFilter() { + GreaterThanFilter v1 = new GreaterThanFilter(ConnectorAttributeBuilder.Build("foo", "bar")); + GreaterThanFilter v2 = (GreaterThanFilter)CloneObject(v1); + Assert.AreEqual(v1.GetAttribute(), v2.GetAttribute()); + } + + [Test] + public void TestGreaterThanOrEqualFilter() { + GreaterThanOrEqualFilter v1 = new GreaterThanOrEqualFilter(ConnectorAttributeBuilder.Build("foo", "bar")); + GreaterThanOrEqualFilter v2 = (GreaterThanOrEqualFilter)CloneObject(v1); + Assert.AreEqual(v1.GetAttribute(), v2.GetAttribute()); + } + + [Test] + public void TestLessThanFilter() { + LessThanFilter v1 = new LessThanFilter(ConnectorAttributeBuilder.Build("foo", "bar")); + LessThanFilter v2 = (LessThanFilter)CloneObject(v1); + Assert.AreEqual(v1.GetAttribute(), v2.GetAttribute()); + } + + [Test] + public void TestLessThanOrEqualFilter() { + LessThanOrEqualFilter v1 = new LessThanOrEqualFilter(ConnectorAttributeBuilder.Build("foo", "bar")); + LessThanOrEqualFilter v2 = (LessThanOrEqualFilter)CloneObject(v1); + Assert.AreEqual(v1.GetAttribute(), v2.GetAttribute()); + } + + [Test] + public void TestNotFilter() { + ContainsFilter left1 = new ContainsFilter(ConnectorAttributeBuilder.Build("foo", "bar")); + NotFilter v1 = new NotFilter(left1); + NotFilter v2 = (NotFilter)CloneObject(v1); + ContainsFilter left2 = (ContainsFilter)v2.Filter; + Assert.AreEqual(left1.GetAttribute(),left2.GetAttribute()); + } + + [Test] + public void TestOrFilter() { + ContainsFilter left1 = new ContainsFilter(ConnectorAttributeBuilder.Build("foo", "bar")); + ContainsFilter right1 = new ContainsFilter(ConnectorAttributeBuilder.Build("foo2", "bar2")); + OrFilter v1 = new OrFilter(left1,right1); + OrFilter v2 = (OrFilter)CloneObject(v1); + ContainsFilter left2 = (ContainsFilter)v2.Left; + ContainsFilter right2 = (ContainsFilter)v2.Right; + Assert.AreEqual(left1.GetAttribute(),left2.GetAttribute()); + Assert.AreEqual(right1.GetAttribute(),right2.GetAttribute()); + } + + [Test] + public void TestStartsWithFilter() { + StartsWithFilter v1 = new StartsWithFilter(ConnectorAttributeBuilder.Build("foo", "bar")); + StartsWithFilter v2 = (StartsWithFilter)CloneObject(v1); + Assert.AreEqual(v1.GetAttribute(), v2.GetAttribute()); + } + + [Test] + public void TestContainsAllValuesFilter() { + ContainsAllValuesFilter v1 = new ContainsAllValuesFilter(ConnectorAttributeBuilder.Build("foo", "bar", "foo")); + ContainsAllValuesFilter v2 = (ContainsAllValuesFilter)CloneObject(v1); + Assert.AreEqual(v1.GetAttribute(), v2.GetAttribute()); + } + + [Test] + public void TestExceptions() { + { + AlreadyExistsException v1 = new AlreadyExistsException("ex"); + AlreadyExistsException v2 = (AlreadyExistsException)CloneObject(v1); + Assert.AreEqual("ex",v2.Message); + } + + { + ConfigurationException v1 = new ConfigurationException("ex"); + ConfigurationException v2 = (ConfigurationException)CloneObject(v1); + Assert.AreEqual("ex",v2.Message); + } + + { + ConnectionBrokenException v1 = new ConnectionBrokenException("ex"); + ConnectionBrokenException v2 = (ConnectionBrokenException)CloneObject(v1); + Assert.AreEqual("ex",v2.Message); + } + + { + ConnectionFailedException v1 = new ConnectionFailedException("ex"); + ConnectionFailedException v2 = (ConnectionFailedException)CloneObject(v1); + Assert.AreEqual("ex",v2.Message); + } + + { + ConnectorException v1 = new ConnectorException("ex"); + ConnectorException v2 = (ConnectorException)CloneObject(v1); + Assert.AreEqual("ex",v2.Message); + } + { + ConnectorIOException v1 = new ConnectorIOException("ex"); + ConnectorIOException v2 = (ConnectorIOException)CloneObject(v1); + Assert.AreEqual("ex",v2.Message); + } + { + ConnectorSecurityException v1 = new ConnectorSecurityException("ex"); + ConnectorSecurityException v2 = (ConnectorSecurityException)CloneObject(v1); + Assert.AreEqual("ex",v2.Message); + } + + { + InvalidCredentialException v1 = new InvalidCredentialException("ex"); + InvalidCredentialException v2 = (InvalidCredentialException)CloneObject(v1); + Assert.AreEqual("ex",v2.Message); + } + + { + InvalidPasswordException v1 = new InvalidPasswordException("ex"); + InvalidPasswordException v2 = (InvalidPasswordException)CloneObject(v1); + Assert.AreEqual("ex",v2.Message); + } + + { + PasswordExpiredException v1 = new PasswordExpiredException("ex"); + v1.Uid=(new Uid("myuid")); + PasswordExpiredException v2 = (PasswordExpiredException)CloneObject(v1); + Assert.AreEqual("ex",v2.Message); + Assert.AreEqual("myuid", v2.Uid.GetUidValue()); + } + + { + OperationTimeoutException v1 = new OperationTimeoutException("ex"); + OperationTimeoutException v2 = (OperationTimeoutException)CloneObject(v1); + Assert.AreEqual("ex",v2.Message); + } + + { + PermissionDeniedException v1 = new PermissionDeniedException("ex"); + PermissionDeniedException v2 = (PermissionDeniedException)CloneObject(v1); + Assert.AreEqual("ex",v2.Message); + } + + { + UnknownUidException v1 = new UnknownUidException("ex"); + UnknownUidException v2 = (UnknownUidException)CloneObject(v1); + Assert.AreEqual("ex",v2.Message); + } + + { + ArgumentException v1 = new ArgumentException("my msg"); + Exception v2 = (Exception)CloneObject(v1); + Assert.AreEqual("my msg", v2.Message); + } + + { + Exception v1 = new Exception("my msg2"); + Exception v2 = (Exception)CloneObject(v1); + Assert.AreEqual("my msg2", v2.Message); + } + + } + + [Test] + public void TestHelloRequest() { + HelloRequest v1 = new HelloRequest(); + HelloRequest v2 = (HelloRequest)CloneObject(v1); + Assert.IsNotNull(v2); + } + + [Test] + public void TestHelloResponse() { + RemoteConnectorInfoImpl info = new RemoteConnectorInfoImpl(); + info.Messages=(new ConnectorMessagesImpl()); + info.ConnectorKey=(new ConnectorKey("my bundle", + "my version", + "my connector")); + ConfigurationPropertiesImpl configProperties = new ConfigurationPropertiesImpl(); + configProperties.Properties=(new List()); + APIConfigurationImpl apiImpl = new APIConfigurationImpl(); + apiImpl.ConfigurationProperties=(configProperties); + info.DefaultAPIConfiguration=(apiImpl); + info.ConnectorDisplayNameKey=("mykey"); + + Exception ex = new Exception("foo"); + HelloResponse v1 = new HelloResponse(ex,CollectionUtil.NewReadOnlyList(info)); + HelloResponse v2 = (HelloResponse)CloneObject(v1); + Assert.IsNotNull(v2.Exception); + Assert.IsNotNull(v2.ConnectorInfos.First()); + } + + [Test] + public void TestOperationRequest() { + ConfigurationPropertiesImpl configProperties = new ConfigurationPropertiesImpl(); + configProperties.Properties=(new List()); + APIConfigurationImpl apiImpl = new APIConfigurationImpl(); + apiImpl.ConfigurationProperties=(configProperties); + + IList args = new List(); + args.Add("my arg"); + OperationRequest v1 = new + OperationRequest(new ConnectorKey("my bundle", + "my version", + "my connector"), + apiImpl, + SafeType.Get(), + "mymethodName", + args); + OperationRequest v2 = (OperationRequest)CloneObject(v1); + Assert.AreEqual("my bundle", v2.ConnectorKey.BundleName); + Assert.AreEqual("my version", v2.ConnectorKey.BundleVersion); + Assert.AreEqual("my connector", v2.ConnectorKey.ConnectorName); + Assert.IsNotNull(v2.Configuration); + Assert.AreEqual(SafeType.Get(), v2.Operation); + Assert.AreEqual("mymethodName",v2.OperationMethodName); + Assert.IsTrue( + CollectionUtil.Equals( + args, v2.Arguments)); + } + + [Test] + public void TestOperationResponseEnd() { + OperationResponseEnd v1 = new OperationResponseEnd(); + OperationResponseEnd v2 = (OperationResponseEnd)CloneObject(v1); + Assert.IsNotNull(v2); + } + [Test] + public void TestOperationResponsePart() { + Exception ex = new Exception("foo"); + OperationResponsePart v1 = new OperationResponsePart(ex,"bar"); + OperationResponsePart v2 = (OperationResponsePart)CloneObject(v1); + Assert.IsNotNull(v2.Exception); + Assert.AreEqual("bar", v2.Result); + } + + [Test] + public void TestOperationResponsePause() { + OperationResponsePause v1 = new OperationResponsePause(); + OperationResponsePause v2 = (OperationResponsePause)CloneObject(v1); + Assert.IsNotNull(v2); + } + + [Test] + public void TestOperationRequestMoreData() { + OperationRequestMoreData v1 = new OperationRequestMoreData(); + OperationRequestMoreData v2 = (OperationRequestMoreData)CloneObject(v1); + Assert.IsNotNull(v2); + } + + [Test] + public void TestOperationRequestStopData() { + OperationRequestStopData v1 = new OperationRequestStopData(); + OperationRequestStopData v2 = (OperationRequestStopData)CloneObject(v1); + Assert.IsNotNull(v2); + } + + [Test] + public void TestEchoMessage() { + EchoMessage v1 = new EchoMessage("test","xml"); + EchoMessage v2 = (EchoMessage)CloneObject(v1); + Assert.AreEqual("test", v2.Object); + Assert.AreEqual("xml", v2.ObjectXml); + } + + [Test] + public void TestOperationOptions() { + OperationOptionsBuilder builder = new OperationOptionsBuilder(); + builder.SetOption("foo", "bar"); + builder.SetOption("foo2", "bar2"); + OperationOptions v1 = builder.Build(); + OperationOptions v2 = (OperationOptions)CloneObject(v1); + Assert.AreEqual(2,v2.Options.Count); + Assert.AreEqual("bar",v2.Options["foo"]); + Assert.AreEqual("bar2",v2.Options["foo2"]); + } + + [Test] + public void TestScriptContext() { + ScriptContextBuilder builder = new ScriptContextBuilder(); + builder.ScriptLanguage=("language"); + builder.ScriptText=("text"); + builder.AddScriptArgument("foo", "bar"); + builder.AddScriptArgument("foo2", "bar2"); + ScriptContext v1 = builder.Build(); + ScriptContext v2 = (ScriptContext)CloneObject(v1); + Assert.AreEqual(2,v2.ScriptArguments.Count); + Assert.AreEqual("bar",v2.ScriptArguments["foo"]); + Assert.AreEqual("bar2",v2.ScriptArguments["foo2"]); + Assert.AreEqual("language",v2.ScriptLanguage); + Assert.AreEqual("text",v2.ScriptText); + } + [Test] + public void TestSyncDeltaType() { + SyncDeltaType v1 = SyncDeltaType.DELETE; + SyncDeltaType v2 = (SyncDeltaType)CloneObject(v1); + Assert.AreEqual(v1, v2); + } + + [Test] + public void TestSyncToken() { + SyncToken v1 = new SyncToken("mytoken"); + SyncToken v2 = (SyncToken)CloneObject(v1); + Assert.AreEqual(v1.Value,v2.Value); + Assert.AreEqual(v1,v2); + } + + [Test] + public void TestSyncDelta() { + ConnectorObjectBuilder bld = new ConnectorObjectBuilder(); + bld.SetUid("foo"); + bld.SetName("name"); + SyncDeltaBuilder builder = new SyncDeltaBuilder(); + builder.Uid=(new Uid("myuid")); + builder.DeltaType=(SyncDeltaType.CREATE_OR_UPDATE); + builder.Token=(new SyncToken("mytoken")); + builder.Object=(bld.Build()); + SyncDelta v1 = builder.Build(); + SyncDelta v2 = (SyncDelta)CloneObject(v1); + Assert.AreEqual(new Uid("foo"),v2.Uid); + Assert.AreEqual(new SyncToken("mytoken"),v2.Token); + Assert.AreEqual(SyncDeltaType.CREATE_OR_UPDATE,v2.DeltaType); + Assert.AreEqual(v1,v2); + } + + [Test] + public void TestNull() { + Object v1 = null; + Object v2 = CloneObject(v1); + Assert.IsNull(v2); + } + + [Test] + public void TestGuardedString() { + GuardedString v1 = new GuardedString(); + v1.AppendChar('f'); + v1.AppendChar('o'); + v1.AppendChar('o'); + v1.AppendChar('b'); + v1.AppendChar('a'); + v1.AppendChar('r'); + GuardedString v2 = (GuardedString)CloneObject(v1); + Assert.AreEqual("foobar",DecryptToString(v2)); + } + + [Test] + public void TestQualifiedUid() { + QualifiedUid v1 = new QualifiedUid(new ObjectClass("myclass"), + new Uid("myuid")); + QualifiedUid v2 = (QualifiedUid)CloneObject(v1); + Assert.AreEqual(v1, v2); + Assert.AreEqual("myclass", v2.ObjectClass.GetObjectClassValue()); + Assert.AreEqual("myuid", v2.Uid.GetUidValue()); + } + + /** + * Highly insecure method! Do not do this in production + * code. This is only for test purposes + */ + private String DecryptToString(GuardedString str) { + StringBuilder buf = new StringBuilder(); + str.Access( + array=> + { + for ( int i = 0 ; i < array.Length; i++) + { + buf.Append(array[i]); + } + }); + return buf.ToString(); + } + + protected virtual Object CloneObject(Object o) { + return SerializerUtil.CloneObject(o); + } + } + [TestFixture] + public class XmlSerializationTests : ObjectSerializationTests + { + protected override Object CloneObject(Object o) + { + String xml = SerializerUtil.SerializeXmlObject(o, true); + Console.WriteLine(xml); + o = SerializerUtil.DeserializeXmlObject(xml, true); + + //pass through a list to make sure dtd correctly defines all xml objects + List list = new List(); + list.Add(o); + xml = SerializerUtil.SerializeXmlObject(list, true); + Console.WriteLine(xml); + IList rv = (IList)SerializerUtil.DeserializeXmlObject(xml, true); + return rv[0]; + } + + [Test] + public void TestMultiObject() { + ObjectSerializerFactory factory = ObjectSerializerFactory.GetInstance(); + StringWriter sw = new StringWriter(); + XmlObjectSerializer ser = factory.NewXmlSerializer(sw,true,true); + ser.WriteObject("foo"); + ser.WriteObject("bar"); + ser.Close(true); + String xml = sw.ToString(); + Console.WriteLine(xml); + IList results = new List(); + factory.DeserializeXmlStream(new StringReader(xml), + o=>{ + results.Add(o); + return true; + }, + true); + Assert.AreEqual(2,results.Count); + Assert.AreEqual("foo",results[0]); + Assert.AreEqual("bar",results[1]); + } + + [Test] + public void TestWriter() + { + XmlWriterSettings settings = new XmlWriterSettings(); + settings.Indent = true; + settings.OmitXmlDeclaration = true; + XmlWriter writer = XmlWriter.Create(Console.Out, settings); + + // Write the book element. + writer.WriteStartElement("book"); + writer.WriteEndElement(); + writer.Close(); + // Write the title element. + //writer.WriteStartElement("title"); + //writer.WriteString("Pride And Prejudice<<"); + //writer.WriteEndElement(); + + // Write the close tag for the root element. + //writer.WriteEndElement(); + + // Write the XML and close the writer. + //writer.Close(); + + } + } +} diff --git a/DotNetConnectors.sln/FrameworkTests/ProxyTests.cs b/DotNetConnectors.sln/FrameworkTests/ProxyTests.cs new file mode 100644 index 00000000..863ba446 --- /dev/null +++ b/DotNetConnectors.sln/FrameworkTests/ProxyTests.cs @@ -0,0 +1,100 @@ +/* + * ==================== + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER. + * + * Copyright 2007-2008 Sun Microsystems, Inc. All rights reserved. + * + * The contents of this file are subject to the terms of the Common Development + * and Distribution License("CDDL") (the "License"). You may not use this file + * except in compliance with the License. + * + * You can obtain a copy of the License at + * http://IdentityConnectors.dev.java.net/legal/license.txt + * See the License for the specific language governing permissions and limitations + * under the License. + * + * When distributing the Covered Code, include this CDDL Header Notice in each file + * and include the License file at identityconnectors/legal/license.txt. + * If applicable, add the following below this CDDL Header, with the fields + * enclosed by brackets [] replaced by your own identifying information: + * "Portions Copyrighted [year] [name of copyright owner]" + * ==================== + */ +using System; +using NUnit.Framework; +using NUnit.Framework.SyntaxHelpers; +using System.Reflection; +using System.Reflection.Emit; +using Org.IdentityConnectors.Common.Proxy; +using System.Collections.Generic; +using Org.IdentityConnectors.Common; +namespace FrameworkTests +{ + public interface MyTestInterface + { + string TestProperty {get;set;} + + String TestTwoArgs(string val1, string val2); + void TestVoid(IList list); + int TestPrimitive(int arg); + DateTime TestStruct(DateTime arg); + } + + [TestFixture] + public class ProxyTests + { + public class MyHandler : InvocationHandler + { + private string _testProperty; + + public Object Invoke(Object proxy, MethodInfo method, Object [] args) + { + if (method.Name.Equals("TestTwoArgs")) + { + return ""+method.Name+" "+args[0]+" "+args[1]; + } + else if ( method.Name.Equals("TestVoid") ) + { + IList arg = (IList)args[0]; + arg.Add("my void result"); + return null; + } + else if ( method.Name.Equals("get_TestProperty") ) + { + return _testProperty; + } + else if ( method.Name.Equals("set_TestProperty") ) + { + _testProperty = (string)args[0]; + return null; + } + else + { + return args[0]; + } + } + } + + [Test] + public void TestProxy() + { + InvocationHandler handler = new MyHandler(); + + MyTestInterface inter = + (MyTestInterface)Proxy.NewProxyInstance(typeof(MyTestInterface), + handler); + Assert.AreEqual("TestTwoArgs foo bar", + inter.TestTwoArgs("foo","bar")); + IList temp = new List(); + inter.TestVoid(temp); + Assert.AreEqual("my void result",temp[0]); + Assert.AreEqual(10,inter.TestPrimitive(10)); + Assert.AreEqual(1000L,inter.TestStruct(new DateTime(1000)).Ticks); + inter.TestProperty = "my property"; + Assert.AreEqual("my property",inter.TestProperty); + } + + } + + +} diff --git a/DotNetConnectors.sln/FrameworkTests/SafeTypeTest.cs b/DotNetConnectors.sln/FrameworkTests/SafeTypeTest.cs new file mode 100644 index 00000000..49567e30 --- /dev/null +++ b/DotNetConnectors.sln/FrameworkTests/SafeTypeTest.cs @@ -0,0 +1,46 @@ +/* + * ==================== + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER. + * + * Copyright 2007-2008 Sun Microsystems, Inc. All rights reserved. + * + * The contents of this file are subject to the terms of the Common Development + * and Distribution License("CDDL") (the "License"). You may not use this file + * except in compliance with the License. + * + * You can obtain a copy of the License at + * http://IdentityConnectors.dev.java.net/legal/license.txt + * See the License for the specific language governing permissions and limitations + * under the License. + * + * When distributing the Covered Code, include this CDDL Header Notice in each file + * and include the License file at identityconnectors/legal/license.txt. + * If applicable, add the following below this CDDL Header, with the fields + * enclosed by brackets [] replaced by your own identifying information: + * "Portions Copyrighted [year] [name of copyright owner]" + * ==================== + */ +using System; +using NUnit.Framework; +using NUnit.Framework.SyntaxHelpers; +using Org.IdentityConnectors.Common; +using Org.IdentityConnectors.Framework.Api.Operations; +namespace FrameworkTests +{ + [TestFixture] + public class SafeTypeTest + { + [Test] + public void TestSafeType() + { + //compile-time type safe + SafeType op = + SafeType.Get(); + Assert.AreEqual(typeof(ScriptOnResourceApiOp),op.RawType); + //runtime type safe. needed for marshalling code, etc + op = + SafeType.ForRawType(typeof(SchemaApiOp)); + Assert.AreEqual(typeof(SchemaApiOp),op.RawType); + } + } +} diff --git a/DotNetConnectors.sln/FrameworkTests/ScriptTests.cs b/DotNetConnectors.sln/FrameworkTests/ScriptTests.cs new file mode 100644 index 00000000..a22d85b9 --- /dev/null +++ b/DotNetConnectors.sln/FrameworkTests/ScriptTests.cs @@ -0,0 +1,62 @@ +/* + * ==================== + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER. + * + * Copyright 2007-2008 Sun Microsystems, Inc. All rights reserved. + * + * The contents of this file are subject to the terms of the Common Development + * and Distribution License("CDDL") (the "License"). You may not use this file + * except in compliance with the License. + * + * You can obtain a copy of the License at + * http://IdentityConnectors.dev.java.net/legal/license.txt + * See the License for the specific language governing permissions and limitations + * under the License. + * + * When distributing the Covered Code, include this CDDL Header Notice in each file + * and include the License file at identityconnectors/legal/license.txt. + * If applicable, add the following below this CDDL Header, with the fields + * enclosed by brackets [] replaced by your own identifying information: + * "Portions Copyrighted [year] [name of copyright owner]" + * ==================== + */ +using System; +using System.Collections.Generic; +using System.Reflection; +using Org.IdentityConnectors.Common; +using Org.IdentityConnectors.Common.Script; +using NUnit.Framework; +using NUnit.Framework.SyntaxHelpers; + +namespace FrameworkTests +{ + /// + /// Description of ScriptTests. + /// + [TestFixture] + public class ScriptTests + { + [Test] + public void testBooScripting() { + ScriptExecutorFactory factory = ScriptExecutorFactory.NewInstance("BOO"); + ScriptExecutor exe = factory.NewScriptExecutor(new Assembly[0],"x", false); + IDictionary vals = new Dictionary(); + vals["x"] = 1; + Assert.AreEqual(1, exe.Execute(vals)); + vals["x"] = 2; + Assert.AreEqual(2, exe.Execute(vals)); + } + [Test] + public void testShellScripting() { + ScriptExecutorFactory factory = ScriptExecutorFactory.NewInstance("Shell"); + ScriptExecutor exe = factory.NewScriptExecutor(new Assembly[0],"echo bob", false); + IDictionary vals = new Dictionary(); + Assert.AreEqual(0, exe.Execute(vals)); + } + [Test] + [ExpectedException(typeof(ArgumentException))] + public void testUnsupported() { + ScriptExecutorFactory.NewInstance("fadsflkj"); + } + } +} diff --git a/DotNetConnectors.sln/FrameworkTests/TestHelperTests.cs b/DotNetConnectors.sln/FrameworkTests/TestHelperTests.cs new file mode 100644 index 00000000..5d79c29d --- /dev/null +++ b/DotNetConnectors.sln/FrameworkTests/TestHelperTests.cs @@ -0,0 +1,72 @@ +/* + * ==================== + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER. + * + * Copyright 2007-2008 Sun Microsystems, Inc. All rights reserved. + * + * The contents of this file are subject to the terms of the Common Development + * and Distribution License("CDDL") (the "License"). You may not use this file + * except in compliance with the License. + * + * You can obtain a copy of the License at + * http://IdentityConnectors.dev.java.net/legal/license.txt + * See the License for the specific language governing permissions and limitations + * under the License. + * + * When distributing the Covered Code, include this CDDL Header Notice in each file + * and include the License file at identityconnectors/legal/license.txt. + * If applicable, add the following below this CDDL Header, with the fields + * enclosed by brackets [] replaced by your own identifying information: + * "Portions Copyrighted [year] [name of copyright owner]" + * ==================== + */ +using System; +using System.IO; +using System.Xml; +using System.Collections.Generic; + +using NUnit.Framework; +using NUnit.Framework.SyntaxHelpers; + +using Org.IdentityConnectors.Framework.Test; + +namespace FrameworkTests +{ + /// + /// Description of TestHelperTests. + /// + [TestFixture] + public class TestHelperTests + { + [Test] + public void testLoadProperties() + { + string tmpFn = Path.GetTempFileName(); + try { + // create some xml text + TextWriter stringWriter = new StringWriter(); + XmlTextWriter w = new XmlTextWriter(stringWriter); + w.WriteStartElement("configuration"); + w.WriteStartElement("property"); + w.WriteAttributeString("name", "bob"); + w.WriteAttributeString("value", "bobsValue"); + w.WriteEndElement(); + w.WriteStartElement("property"); + w.WriteAttributeString("name", "bob2"); + w.WriteAttributeString("value", "bob2sValue"); + w.WriteEndElement(); + w.Close(); + File.WriteAllText(tmpFn, stringWriter.ToString()); + // load the properties files + IDictionary dict =TestHelpers.LoadPropertiesFile(tmpFn); + Assert.AreEqual(dict["bob"], "bobsValue"); + } finally { + File.Delete(tmpFn); + } + } + [Test] + public void testGetProperties() { + Assert.IsTrue(TestHelpers.GetProperty("Help", null).Equals("Me")); + } + } +} diff --git a/DotNetConnectors.sln/FrameworkTests/TestUtil.cs b/DotNetConnectors.sln/FrameworkTests/TestUtil.cs new file mode 100644 index 00000000..309f8b91 --- /dev/null +++ b/DotNetConnectors.sln/FrameworkTests/TestUtil.cs @@ -0,0 +1,53 @@ +/* + * ==================== + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER. + * + * Copyright 2007-2008 Sun Microsystems, Inc. All rights reserved. + * + * The contents of this file are subject to the terms of the Common Development + * and Distribution License("CDDL") (the "License"). You may not use this file + * except in compliance with the License. + * + * You can obtain a copy of the License at + * http://IdentityConnectors.dev.java.net/legal/license.txt + * See the License for the specific language governing permissions and limitations + * under the License. + * + * When distributing the Covered Code, include this CDDL Header Notice in each file + * and include the License file at identityconnectors/legal/license.txt. + * If applicable, add the following below this CDDL Header, with the fields + * enclosed by brackets [] replaced by your own identifying information: + * "Portions Copyrighted [year] [name of copyright owner]" + * ==================== + */ +using System; +using System.Diagnostics; + +namespace FrameworkTests +{ + /// + /// Description of TestUtil. + /// + public static class TestUtil + { + private static readonly object LOCK = new Object(); + private static bool _initialized; + + + public static void InitializeLogging() + { + lock(LOCK) + { + if (!_initialized) + { + ConsoleTraceListener listener = + new ConsoleTraceListener(); + listener.TraceOutputOptions = + TraceOptions.ThreadId|TraceOptions.Timestamp; + Trace.Listeners.Add(listener); + _initialized = true; + } + } + } + } +} diff --git a/DotNetConnectors.sln/FrameworkTests/UpdateImplTests.cs b/DotNetConnectors.sln/FrameworkTests/UpdateImplTests.cs new file mode 100644 index 00000000..f71f7c52 --- /dev/null +++ b/DotNetConnectors.sln/FrameworkTests/UpdateImplTests.cs @@ -0,0 +1,209 @@ +/* + * ==================== + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER. + * + * Copyright 2007-2008 Sun Microsystems, Inc. All rights reserved. + * + * The contents of this file are subject to the terms of the Common Development + * and Distribution License("CDDL") (the "License"). You may not use this file + * except in compliance with the License. + * + * You can obtain a copy of the License at + * http://IdentityConnectors.dev.java.net/legal/license.txt + * See the License for the specific language governing permissions and limitations + * under the License. + * + * When distributing the Covered Code, include this CDDL Header Notice in each file + * and include the License file at identityconnectors/legal/license.txt. + * If applicable, add the following below this CDDL Header, with the fields + * enclosed by brackets [] replaced by your own identifying information: + * "Portions Copyrighted [year] [name of copyright owner]" + * ==================== + */ +using System; +using System.Security; +using NUnit.Framework; +using NUnit.Framework.SyntaxHelpers; +using System.Collections.Generic; +using Org.IdentityConnectors.Common; +using Org.IdentityConnectors.Framework.Api; +using Org.IdentityConnectors.Framework.Api.Operations; +using Org.IdentityConnectors.Framework.Impl.Api.Local.Operations; +using Org.IdentityConnectors.Framework.Common.Objects; +namespace FrameworkTests +{ + /// + /// Description of UpdateImplTests. + /// + [TestFixture] + public class UpdateImplTests + { + [Test] + [ExpectedException(typeof(ArgumentNullException ))] + public void ValidateUidArg() { + UpdateImpl.ValidateInput(ObjectClass.ACCOUNT, null, new HashSet(),true); + } + [Test] + [ExpectedException(typeof(ArgumentNullException ))] + public void ValidateObjectClassArg() { + UpdateImpl.ValidateInput(null, new Uid("foo"), new HashSet(),true); + } + + [Test] + [ExpectedException(typeof(ArgumentNullException ))] + public void ValidateAttrsArg() { + UpdateImpl.ValidateInput(ObjectClass.ACCOUNT, new Uid("foo"),null,true); + } + + [Test] + [ExpectedException(typeof(ArgumentException ))] + public void ValidateUidAttribute() { + HashSet attrs=new HashSet(); + attrs.Add(new Uid("foo")); + UpdateImpl.ValidateInput(ObjectClass.ACCOUNT, new Uid("foo"),attrs,true); + } + + [Test] + [ExpectedException(typeof(ArgumentException ))] + public void ValidateAddWithNullAttribute() { + ICollection attrs = new HashSet(); + attrs.Add(ConnectorAttributeBuilder.Build("something")); + UpdateImpl.ValidateInput(ObjectClass.ACCOUNT, new Uid("foo"), attrs, true); + } + + [Test] + [ExpectedException(typeof(ArgumentException))] + public void ValidateAttemptToAddName() { + ICollection attrs = new HashSet(); + attrs.Add(new Name("fadf")); + UpdateImpl.ValidateInput(ObjectClass.ACCOUNT, new Uid("foo"), attrs, true); + } + + [Test] + public void ValidateAttemptToAddDeleteOperationalAttribute() { + // list of all the operational attributes.. + ICollection list = new List(); + list.Add(ConnectorAttributeBuilder.BuildEnabled(false)); + list.Add(ConnectorAttributeBuilder.BuildLockOut(true)); + list.Add(ConnectorAttributeBuilder.BuildCurrentPassword(newSecureString("fadsf"))); + list.Add(ConnectorAttributeBuilder.BuildPasswordExpirationDate(DateTime.Now)); + list.Add(ConnectorAttributeBuilder.BuildPassword(newSecureString("fadsf"))); + foreach (ConnectorAttribute attr in list) { + ICollection attrs = new HashSet(); + attrs.Add(attr); + try { + UpdateImpl.ValidateInput(ObjectClass.ACCOUNT, new Uid("1"), attrs, true); + Assert.Fail("Failed: " + attr.Name); + } catch (ArgumentException) { + // this is a good thing.. + } + } + } + + private static SecureString newSecureString(string password) { + SecureString rv = new SecureString(); + foreach (char c in password.ToCharArray()) { + rv.AppendChar(c); + } + return rv; + } + + /// + /// Validate two collections are equal. (Not fast but effective) + /// + public static bool AreEqual(ICollection arg1, + ICollection arg2) { + if (arg1.Count != arg2.Count) { + return false; + } + foreach (ConnectorAttribute attr in arg1) { + if (!arg2.Contains(attr)) { + return false; + } + } + return true; + } + [Test] + public void MergeAddAttribute() { + UpdateImpl up = new UpdateImpl(null, null); + ICollection actual; + ICollection baseAttrs = CollectionUtil.NewSet(); + ICollection expected = CollectionUtil.NewSet(); + ICollection changeset = CollectionUtil.NewSet(); + // attempt to add a value to an attribute.. + ConnectorAttribute cattr = ConnectorAttributeBuilder.Build("abc", 2); + changeset.Add(cattr); + expected.Add(ConnectorAttributeBuilder.Build("abc", 2)); + actual = up.Merge(changeset, baseAttrs,true); + Assert.IsTrue(AreEqual(expected, actual)); + } + + [Test] + public void MergeAddToExistingAttribute() { + UpdateImpl up = new UpdateImpl(null, null); + ICollection actual; + ICollection baseAttrs = CollectionUtil.NewSet(); + ICollection expected = CollectionUtil.NewSet(); + ICollection changeset = CollectionUtil.NewSet(); + // attempt to add a value to an attribute.. + ConnectorAttribute battr = ConnectorAttributeBuilder.Build("abc", 1); + ConnectorAttribute cattr = ConnectorAttributeBuilder.Build("abc", 2); + baseAttrs.Add(battr); + changeset.Add(cattr); + expected.Add(ConnectorAttributeBuilder.Build("abc", 1, 2)); + actual = up.Merge(changeset, baseAttrs, true); + Assert.IsTrue(AreEqual(expected, actual)); + } + + [Test] + public void MergeDeleteNonExistentAttribute() { + UpdateImpl up = new UpdateImpl(null, null); + ICollection actual; + ICollection baseAttrs = CollectionUtil.NewSet(); + ICollection expected = CollectionUtil.NewSet(); + ICollection changeset = CollectionUtil.NewSet(); + // attempt to add a value to an attribute.. + ConnectorAttribute cattr = ConnectorAttributeBuilder.Build("abc", 2); + changeset.Add(cattr); + actual = up.Merge(changeset, baseAttrs, false); + Assert.IsTrue(AreEqual(expected, actual)); + } + + [Test] + public void MergeDeleteToExistingAttribute() { + UpdateImpl up = new UpdateImpl(null, null); + ICollection actual; + ICollection baseAttrs = CollectionUtil.NewSet(); + ICollection expected = CollectionUtil.NewSet(); + ICollection changeset = CollectionUtil.NewSet(); + // attempt to add a value to an attribute.. + ConnectorAttribute battr = ConnectorAttributeBuilder.Build("abc", 1, 2); + ConnectorAttribute cattr = ConnectorAttributeBuilder.Build("abc", 2); + baseAttrs.Add(battr); + changeset.Add(cattr); + expected.Add(ConnectorAttributeBuilder.Build("abc", 1)); + actual = up.Merge(changeset, baseAttrs, false); + Assert.IsTrue(AreEqual(expected, actual)); + } + + [Test] + public void MergeDeleteToExistingAttributeCompletely() { + UpdateImpl up = new UpdateImpl(null, null); + ICollection actual; + ICollection baseAttrs = CollectionUtil.NewSet(); + ICollection expected = CollectionUtil.NewSet(); + ICollection changeset = CollectionUtil.NewSet(); + // attempt to add a value to an attribute.. + ConnectorAttribute battr = ConnectorAttributeBuilder.Build("abc", 1, 2); + ConnectorAttribute cattr = ConnectorAttributeBuilder.Build("abc", 1, 2); + baseAttrs.Add(battr); + changeset.Add(cattr); + expected.Add(ConnectorAttributeBuilder.Build("abc")); + actual = up.Merge(changeset, baseAttrs, false); + Assert.IsTrue(AreEqual(expected, actual)); + } + + + + } +} diff --git a/DotNetConnectors.sln/FrameworkTests/app.config b/DotNetConnectors.sln/FrameworkTests/app.config new file mode 100644 index 00000000..e228e5fa --- /dev/null +++ b/DotNetConnectors.sln/FrameworkTests/app.config @@ -0,0 +1,24 @@ + + + + + + + + + + + + + + + + + + + + + diff --git a/DotNetConnectors.sln/FrameworkTests/project.xml b/DotNetConnectors.sln/FrameworkTests/project.xml new file mode 100644 index 00000000..6f40613f --- /dev/null +++ b/DotNetConnectors.sln/FrameworkTests/project.xml @@ -0,0 +1,5 @@ + + + + + \ No newline at end of file diff --git a/DotNetConnectors.sln/Service/AssemblyInfo.cs b/DotNetConnectors.sln/Service/AssemblyInfo.cs new file mode 100644 index 00000000..b5a7e910 --- /dev/null +++ b/DotNetConnectors.sln/Service/AssemblyInfo.cs @@ -0,0 +1,53 @@ +/* + * ==================== + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER. + * + * Copyright 2007-2008 Sun Microsystems, Inc. All rights reserved. + * + * The contents of this file are subject to the terms of the Common Development + * and Distribution License("CDDL") (the "License"). You may not use this file + * except in compliance with the License. + * + * You can obtain a copy of the License at + * http://IdentityConnectors.dev.java.net/legal/license.txt + * See the License for the specific language governing permissions and limitations + * under the License. + * + * When distributing the Covered Code, include this CDDL Header Notice in each file + * and include the License file at identityconnectors/legal/license.txt. + * If applicable, add the following below this CDDL Header, with the fields + * enclosed by brackets [] replaced by your own identifying information: + * "Portions Copyrighted [year] [name of copyright owner]" + * ==================== + */ +#region Using directives + +using System.Reflection; +using System.Runtime.CompilerServices; +using System.Runtime.InteropServices; + +#endregion + +// General Information about an assembly is controlled through the following +// set of attributes. Change these attribute values to modify the information +// associated with an assembly. +[assembly: AssemblyTitle("Service")] +[assembly: AssemblyDescription("")] +[assembly: AssemblyConfiguration("")] +[assembly: AssemblyCompany("")] +[assembly: AssemblyProduct("Service")] +[assembly: AssemblyCopyright("")] +[assembly: AssemblyTrademark("")] +[assembly: AssemblyCulture("")] + +// This sets the default COM visibility of types in the assembly to invisible. +// If you need to expose a type to COM, use [ComVisible(true)] on that type. +[assembly: ComVisible(false)] + +// The assembly version has following format : +// +// Major.Minor.Build.Revision +// +// You can specify all the values or you can use the default the Revision and +// Build Numbers by using the '*' as shown below: +[assembly: AssemblyVersion("1.0.0.0")] diff --git a/DotNetConnectors.sln/Service/Program.cs b/DotNetConnectors.sln/Service/Program.cs new file mode 100644 index 00000000..aff6237a --- /dev/null +++ b/DotNetConnectors.sln/Service/Program.cs @@ -0,0 +1,230 @@ +/* + * ==================== + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER. + * + * Copyright 2007-2008 Sun Microsystems, Inc. All rights reserved. + * + * The contents of this file are subject to the terms of the Common Development + * and Distribution License("CDDL") (the "License"). You may not use this file + * except in compliance with the License. + * + * You can obtain a copy of the License at + * http://IdentityConnectors.dev.java.net/legal/license.txt + * See the License for the specific language governing permissions and limitations + * under the License. + * + * When distributing the Covered Code, include this CDDL Header Notice in each file + * and include the License file at identityconnectors/legal/license.txt. + * If applicable, add the following below this CDDL Header, with the fields + * enclosed by brackets [] replaced by your own identifying information: + * "Portions Copyrighted [year] [name of copyright owner]" + * ==================== + */ +using System; +using System.ComponentModel; +using System.Configuration; +using System.Configuration.Install; +using System.Collections.Generic; +using System.Security; +using System.Reflection; +using System.ServiceProcess; +using System.Text; +using Org.IdentityConnectors.Common.Security; + +namespace Org.IdentityConnectors.Framework.Service +{ + + static class Program + { + private const string OPT_SERVICE_NAME="/serviceName"; + + private static void Usage() + { + Console.WriteLine("Usage: ConnectorServer.exe [option], where command is one of the following: "); + Console.WriteLine(" /install [/serviceName ] - Installs the service."); + Console.WriteLine(" /uninstall [/serviceName ] - Uninstalls the service."); + Console.WriteLine(" /run - Runs the service from the console."); + Console.WriteLine(" /setKey [] - Sets the connector server key."); + } + + private static IDictionary ParseOptions(string [] args) + { + IDictionary rv = new Dictionary(); + String serviceName = null; + for ( int i = 1; i < args.Length; i++) { + String opt = args[i].ToLower(); + if (OPT_SERVICE_NAME.ToLower().Equals(opt)) { + i++; + if (i < args.Length) { + serviceName = args[i]; + } + else { + Usage(); + return null; + } + } + else { + Usage(); + return null; + } + } + rv["/serviceName"] = serviceName; + return rv; + } + + /// + /// This method starts the service. + /// + static void Main(string [] args) + { + if ( args.Length == 0 ) + { + //no args - start the service + ServiceBase.Run(new ServiceBase[] { new Service() }); + } + else + { + String cmd = args[0].ToLower(); + if ( cmd.Equals("/setkey") ) { + if ( args.Length > 2 ) { + Usage(); + return; + } + DoSetKey(args.Length>1?args[1]:null); + return; + } + IDictionary options = + ParseOptions(args); + if ( options == null ) { + //there's a parse error in the options, return + return; + } + if ( "/install".Equals(cmd) ) { + DoInstall(options); + } + else if ("/uninstall".Equals(cmd)) { + DoUninstall(options); + } + else if ("/run".Equals(cmd)) { + DoRun(options); + } + else { + Usage(); + return; + } + } + } + + private static void DoInstall(IDictionary options) + { + String serviceName = options[OPT_SERVICE_NAME]; + if ( serviceName != null ) { + ProjectInstaller.ServiceName = serviceName; + } + TransactedInstaller ti = new TransactedInstaller(); + string [] cmdline = + { + Assembly.GetExecutingAssembly ().Location + }; + AssemblyInstaller ai = new AssemblyInstaller( + cmdline[0], + new string[0]); + ti.Installers.Add (ai); + InstallContext ctx = new InstallContext ("install.log", + cmdline ); + ti.Context = ctx ; + ti.Install ( new System.Collections.Hashtable()); + } + + private static void DoUninstall(IDictionary options) + { + String serviceName = options[OPT_SERVICE_NAME]; + if ( serviceName != null ) { + ProjectInstaller.ServiceName = serviceName; + } + TransactedInstaller ti = new TransactedInstaller(); + string [] cmdline = + { + Assembly.GetExecutingAssembly ().Location + }; + AssemblyInstaller ai = new AssemblyInstaller( + cmdline[0], + new string[0]); + ti.Installers.Add (ai); + InstallContext ctx = new InstallContext ("uninstall.log", + cmdline ); + ti.Context = ctx ; + ti.Uninstall ( null); + } + + private static void DoRun(IDictionary options) + { + Service svc = new Service(); + + svc.StartService(new String[0]); + + Console.WriteLine("Press q to shutdown."); + Console.WriteLine("Press t for a thread dump."); + + while ( true ) { + ConsoleKeyInfo info = Console.ReadKey(); + if ( info.KeyChar == 'q' ) { + break; + } + else if ( info.KeyChar == 't' ) { + svc.DumpRequests(); + } + } + + svc.StopService(); + } + + private static GuardedString ReadPassword() + { + GuardedString rv = new GuardedString(); + while (true) { + ConsoleKeyInfo info = Console.ReadKey(true); + if ( info.Key == ConsoleKey.Enter ) { + Console.WriteLine(); + rv.MakeReadOnly(); + return rv; + } + else { + Console.Write("*"); + rv.AppendChar(info.KeyChar); + } + } + } + + private static void DoSetKey(string key) + { + GuardedString str; + if ( key == null ) { + Console.Write("Enter Key: "); + GuardedString v1 = ReadPassword(); + Console.Write("Confirm Key: "); + GuardedString v2 = ReadPassword(); + if (!v1.Equals(v2)) { + Console.WriteLine("Error: Key mismatch."); + return; + } + str = v2; + } + else { + str = new GuardedString(); + foreach (char c in key.ToCharArray()) { + str.AppendChar(c); + } + } + Configuration config = + ConfigurationManager.OpenExeConfiguration(ConfigurationUserLevel.None); + config.AppSettings.Settings.Remove(Service.PROP_KEY); + config.AppSettings.Settings.Add(Service.PROP_KEY,str.GetBase64SHA1Hash()); + config.Save(ConfigurationSaveMode.Modified); + Console.WriteLine("Key Updated."); + } + } + + + +} diff --git a/DotNetConnectors.sln/Service/ProjectInstaller.cs b/DotNetConnectors.sln/Service/ProjectInstaller.cs new file mode 100644 index 00000000..c119abc6 --- /dev/null +++ b/DotNetConnectors.sln/Service/ProjectInstaller.cs @@ -0,0 +1,56 @@ +/* + * ==================== + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER. + * + * Copyright 2007-2008 Sun Microsystems, Inc. All rights reserved. + * + * The contents of this file are subject to the terms of the Common Development + * and Distribution License("CDDL") (the "License"). You may not use this file + * except in compliance with the License. + * + * You can obtain a copy of the License at + * http://IdentityConnectors.dev.java.net/legal/license.txt + * See the License for the specific language governing permissions and limitations + * under the License. + * + * When distributing the Covered Code, include this CDDL Header Notice in each file + * and include the License file at identityconnectors/legal/license.txt. + * If applicable, add the following below this CDDL Header, with the fields + * enclosed by brackets [] replaced by your own identifying information: + * "Portions Copyrighted [year] [name of copyright owner]" + * ==================== + */ +using System; +using System.Collections.Generic; +using System.ComponentModel; +using System.Configuration.Install; +using System.ServiceProcess; + +namespace Org.IdentityConnectors.Framework.Service +{ + [RunInstaller(true)] + public class ProjectInstaller : Installer + { + public static string ServiceName { get; set; } + + static ProjectInstaller() + { + ServiceName = "Connector Server"; + } + + private ServiceProcessInstaller serviceProcessInstaller; + private ServiceInstaller serviceInstaller; + + public ProjectInstaller() + { + serviceProcessInstaller = new ServiceProcessInstaller(); + serviceInstaller = new ServiceInstaller(); + + // Here you can set properties on serviceProcessInstaller or register event handlers + serviceProcessInstaller.Account = ServiceAccount.LocalSystem; + + serviceInstaller.ServiceName = ServiceName; + this.Installers.AddRange(new Installer[] { serviceProcessInstaller, serviceInstaller }); + } + } +} diff --git a/DotNetConnectors.sln/Service/Service.cs b/DotNetConnectors.sln/Service/Service.cs new file mode 100644 index 00000000..2855a94e --- /dev/null +++ b/DotNetConnectors.sln/Service/Service.cs @@ -0,0 +1,182 @@ +/* + * ==================== + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER. + * + * Copyright 2007-2008 Sun Microsystems, Inc. All rights reserved. + * + * The contents of this file are subject to the terms of the Common Development + * and Distribution License("CDDL") (the "License"). You may not use this file + * except in compliance with the License. + * + * You can obtain a copy of the License at + * http://IdentityConnectors.dev.java.net/legal/license.txt + * See the License for the specific language governing permissions and limitations + * under the License. + * + * When distributing the Covered Code, include this CDDL Header Notice in each file + * and include the License file at identityconnectors/legal/license.txt. + * If applicable, add the following below this CDDL Header, with the fields + * enclosed by brackets [] replaced by your own identifying information: + * "Portions Copyrighted [year] [name of copyright owner]" + * ==================== + */ +using System; +using System.Collections.Generic; +using System.Collections.Specialized; +using System.ComponentModel; +using System.Configuration; +using System.Data; +using System.Diagnostics; +using System.IO; +using System.Reflection; +using System.Security.Cryptography.X509Certificates; +using System.ServiceProcess; +using System.Text; +using Org.IdentityConnectors.Common; +using Org.IdentityConnectors.Framework.Server; + +namespace Org.IdentityConnectors.Framework.Service +{ + public class Service : ServiceBase + { + private const string PROP_PORT = "connectorserver.port"; + private const string PROP_SSL = "connectorserver.usessl"; + private const string PROP_CERTSTORE = "connectorserver.certificatestorename"; + private const string PROP_IFADDRESS = "connectorserver.ifaddress"; + public const string PROP_KEY = "connectorserver.key"; + + private ConnectorServer _server; + + public Service() + { + } + + public void DumpRequests() + { + _server.DumpRequests(); + } + + + /// + /// Clean up any resources being used. + /// + protected override void Dispose(bool disposing) + { + base.Dispose(disposing); + } + + private void initializeCurrentDirectory() + { + Assembly assembly = + Assembly.GetExecutingAssembly(); + FileInfo thisAssemblyFile = + new FileInfo(assembly.Location); + DirectoryInfo directory = + thisAssemblyFile.Directory; + Environment.CurrentDirectory = + directory.FullName; + + } + + private NameValueCollection GetApplicationSettings() + { + return ConfigurationManager.AppSettings; + } + + private X509Certificate GetCertificate() + { + NameValueCollection settings = + GetApplicationSettings(); + String storeName = settings.Get(PROP_CERTSTORE); + if (storeName == null) { + throw new Org.IdentityConnectors.Framework.Common.Exceptions.ConfigurationException("Missing required configuration setting: "+PROP_CERTSTORE); + } + + X509Store store = new X509Store(storeName, + StoreLocation.LocalMachine); + + store.Open(OpenFlags.ReadOnly|OpenFlags.OpenExistingOnly); + X509CertificateCollection certificates = store.Certificates; + if ( certificates.Count != 1 ) { + throw new Org.IdentityConnectors.Framework.Common.Exceptions.ConfigurationException("There is supported to be exactly one certificate in the store: "+storeName); + } + X509Certificate certificate = store.Certificates[0]; + store.Close(); + return certificate; + } + + public void StartService(string [] args) + { + OnStart(args); + } + + /// + /// Start this service. + /// + protected override void OnStart(string[] args) + { + try { + initializeCurrentDirectory(); + Trace.TraceInformation("Starting connector server: "+Environment.CurrentDirectory); + NameValueCollection settings = + GetApplicationSettings(); + String portStr = + settings.Get(PROP_PORT); + if ( portStr == null ) { + throw new Org.IdentityConnectors.Framework.Common.Exceptions.ConfigurationException("Missing required configuration property: "+PROP_PORT); + } + String keyHash = settings.Get(PROP_KEY); + if ( keyHash == null ) { + throw new Org.IdentityConnectors.Framework.Common.Exceptions.ConfigurationException("Missing required configuration property: "+PROP_KEY); + } + + int port = Int32.Parse(portStr); + bool useSSL = Boolean.Parse(settings.Get(PROP_SSL)??"false"); + _server = ConnectorServer.NewInstance(); + _server.Port = port; + _server.UseSSL = useSSL; + _server.KeyHash = keyHash; + if (useSSL) { + _server.ServerCertificate = + GetCertificate(); + } + String ifaddress = settings.Get(PROP_IFADDRESS); + if ( ifaddress != null ) { + _server.IfAddress = + IOUtil.GetIPAddress(ifaddress); + } + _server.Start(); + Trace.TraceInformation("Started connector server"); + } + catch (Exception e) { + TraceUtil.TraceException("Exception occured starting connector server", + e); + throw; + } + } + + public void StopService() + { + OnStop(); + } + + + /// + /// Stop this service. + /// + protected override void OnStop() + { + try { + Trace.TraceInformation("Stopping connector server"); + if (_server != null) { + _server.Stop(); + } + Trace.TraceInformation("Stopped connector server"); + } + catch (Exception e) { + TraceUtil.TraceException("Exception occured stopping connector server", + e); + } + } + } +} diff --git a/DotNetConnectors.sln/Service/Service.csproj b/DotNetConnectors.sln/Service/Service.csproj new file mode 100644 index 00000000..ea668380 --- /dev/null +++ b/DotNetConnectors.sln/Service/Service.csproj @@ -0,0 +1,119 @@ + + + + {A9D6374A-D51F-4FA3-8C02-5B1D23FAA82E} + Debug + AnyCPU + Exe + Sun.OpenConnectors.Framework.Service + ConnectorServer + v3.5 + False + false + + + prompt + ./bin/Debug/ + true + Full + False + True + DEBUG;TRACE + Exe + ConnectorServer + False + 4 + + + ./bin/Release/ + False + pdbonly + True + False + TRACE + prompt + Exe + ConnectorServer + False + 4 + + + False + Auto + 4194304 + AnyCPU + 4096 + + + + + + + + + 3.5 + + + + + + + + + + Component + + + Component + + + + + + + + + + + + + + + + + + + {F140E8DA-52B4-4159-992A-9DA10EA8EEFB} + Common + + + {5B011775-B121-4EEE-A410-BA2D2F5BFB8B} + FrameworkInternal + + + {8B24461B-456A-4032-89A1-CD418F7B5B62} + Framework + + + diff --git a/DotNetConnectors.sln/Service/app.config b/DotNetConnectors.sln/Service/app.config new file mode 100644 index 00000000..342a7974 --- /dev/null +++ b/DotNetConnectors.sln/Service/app.config @@ -0,0 +1,37 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/DotNetConnectors.sln/ServiceInstall/ExtBuild.proj b/DotNetConnectors.sln/ServiceInstall/ExtBuild.proj new file mode 100644 index 00000000..df976a43 --- /dev/null +++ b/DotNetConnectors.sln/ServiceInstall/ExtBuild.proj @@ -0,0 +1,63 @@ + + + + Debug + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/DotNetConnectors.sln/ServiceInstall/File.bottom b/DotNetConnectors.sln/ServiceInstall/File.bottom new file mode 100644 index 00000000..8de6ef93 --- /dev/null +++ b/DotNetConnectors.sln/ServiceInstall/File.bottom @@ -0,0 +1,6 @@ + + + + + + \ No newline at end of file diff --git a/DotNetConnectors.sln/ServiceInstall/File.top b/DotNetConnectors.sln/ServiceInstall/File.top new file mode 100644 index 00000000..84086191 --- /dev/null +++ b/DotNetConnectors.sln/ServiceInstall/File.top @@ -0,0 +1,21 @@ + + + + + + + + + + + + diff --git a/DotNetConnectors.sln/ServiceInstall/ServiceInstall.wixproj b/DotNetConnectors.sln/ServiceInstall/ServiceInstall.wixproj new file mode 100644 index 00000000..4af8a460 --- /dev/null +++ b/DotNetConnectors.sln/ServiceInstall/ServiceInstall.wixproj @@ -0,0 +1,69 @@ + + + + {1CBA8F74-050C-432B-8437-08BD13BDC684} + Debug + AnyCPU + Package + ServiceInstall + ServiceInstall + $(WIX_HOME) + $(WixToolPath)\wix.targets + $(WixToolPath)\WixTasks.dll + ICE45 + WixUILicenseRtf=license.rtf + + + prompt + 4 + AnyCPU + bin\Debug\ + True + Full + False + True + DEBUG;TRACE + + + pdbonly + bin\Release\ + TRACE + prompt + 4 + AnyCPU + False + True + False + + + + + c:\Program Files\Windows Installer XML v3\bin\WixUIExtension.dll + + + + + + + \ No newline at end of file diff --git a/DotNetConnectors.sln/ServiceInstall/Setup.wxs b/DotNetConnectors.sln/ServiceInstall/Setup.wxs new file mode 100644 index 00000000..54724a64 --- /dev/null +++ b/DotNetConnectors.sln/ServiceInstall/Setup.wxs @@ -0,0 +1,50 @@ + + + + + + + + + + + + + + + + + + diff --git a/DotNetConnectors.sln/ServiceInstall/license.rtf b/DotNetConnectors.sln/ServiceInstall/license.rtf new file mode 100644 index 00000000..5ecc7cf2 --- /dev/null +++ b/DotNetConnectors.sln/ServiceInstall/license.rtf @@ -0,0 +1,30 @@ +{\rtf1\ansi\deff0\adeflang1025 +{\fonttbl{\f0\froman\fprq2\fcharset0 Thorndale{\*\falt Times New Roman};}{\f1\froman\fprq2\fcharset0 Thorndale{\*\falt Times New Roman};}{\f2\fswiss\fprq2\fcharset0 Albany{\*\falt Arial};}{\f3\fnil\fprq2\fcharset0 Andale Sans UI{\*\falt Arial Unicode MS};}{\f4\fnil\fprq2\fcharset0 Tahoma;}{\f5\fnil\fprq0\fcharset0 Tahoma;}} +{\colortbl;\red0\green0\blue0;\red128\green128\blue128;} +{\stylesheet{\s1\cf0{\*\hyphen2\hyphlead2\hyphtrail2\hyphmax0}\rtlch\af4\afs24\lang255\ltrch\dbch\af3\langfe255\hich\f0\fs24\lang1033\loch\f0\fs24\lang1033\snext1 Normal;} +{\s2\sb240\sa120\keepn\cf0{\*\hyphen2\hyphlead2\hyphtrail2\hyphmax0}\rtlch\afs28\lang255\ltrch\dbch\langfe255\hich\f2\fs28\lang1033\loch\f2\fs28\lang1033\sbasedon1\snext3 Heading;} +{\s3\sa120\cf0{\*\hyphen2\hyphlead2\hyphtrail2\hyphmax0}\rtlch\af4\afs24\lang255\ltrch\dbch\af3\langfe255\hich\f0\fs24\lang1033\loch\f0\fs24\lang1033\sbasedon1\snext3 Body Text;} +{\s4\sa120\cf0{\*\hyphen2\hyphlead2\hyphtrail2\hyphmax0}\rtlch\af5\afs24\lang255\ltrch\dbch\af3\langfe255\hich\f0\fs24\lang1033\loch\f0\fs24\lang1033\sbasedon3\snext4 List;} +{\s5\sb120\sa120\cf0{\*\hyphen2\hyphlead2\hyphtrail2\hyphmax0}\rtlch\af5\afs24\lang255\ai\ltrch\dbch\af3\langfe255\hich\f0\fs24\lang1033\i\loch\f0\fs24\lang1033\i\sbasedon1\snext5 caption;} +{\s6\cf0{\*\hyphen2\hyphlead2\hyphtrail2\hyphmax0}\rtlch\af5\afs24\lang255\ltrch\dbch\af3\langfe255\hich\f0\fs24\lang1033\loch\f0\fs24\lang1033\sbasedon1\snext6 Index;} +} +{\info{\author William Droste}{\creatim\yr2008\mo8\dy7\hr10\min50}{\revtim\yr0\mo0\dy0\hr0\min0}{\printim\yr0\mo0\dy0\hr0\min0}{\comment StarWriter}{\vern6800}}\deftab709 +{\*\pgdsctbl +{\pgdsc0\pgdscuse195\pgwsxn12240\pghsxn15840\marglsxn1134\margrsxn1134\margtsxn1134\margbsxn1134\pgdscnxt0 Standard;}} +\paperh15840\paperw12240\margl1134\margr1134\margt1134\margb1134\sectd\sbknone\pgwsxn12240\pghsxn15840\marglsxn1134\margrsxn1134\margtsxn1134\margbsxn1134\ftnbj\ftnstart1\ftnrstcont\ftnnar\aenddoc\aftnrstcont\aftnstart1\aftnnrlc +\pard\plain \ltrpar\s1\cf0{\*\hyphen2\hyphlead2\hyphtrail2\hyphmax0}\rtlch\af4\afs24\lang255\ltrch\dbch\af3\langfe255\hich\f0\fs24\lang1033\loch\f0\fs24\lang1033 {\rtlch \ltrch\loch\f0\fs24\lang1033\i0\b0 Copyright \'a9 2008 Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, California 95054, U.S.A. All rights reserved.} +\par \pard\plain \ltrpar\s1\cf0{\*\hyphen2\hyphlead2\hyphtrail2\hyphmax0}\rtlch\af4\afs24\lang255\ltrch\dbch\af3\langfe255\hich\f0\fs24\lang1033\loch\f0\fs24\lang1033 +\par \pard\plain \ltrpar\s1\cf0{\*\hyphen2\hyphlead2\hyphtrail2\hyphmax0}\rtlch\af4\afs24\lang255\ltrch\dbch\af3\langfe255\hich\f0\fs24\lang1033\loch\f0\fs24\lang1033 {\rtlch \ltrch\loch }{\rtlch \ltrch\loch\f0\fs24\lang1033\i0\b0 U.S. Government Rights - Commercial software. Government users are subject to the Sun Microsystems, Inc. standard license agreement and applicable provisions of the FAR and its supplements.} +\par \pard\plain \ltrpar\s1\cf0{\*\hyphen2\hyphlead2\hyphtrail2\hyphmax0}\rtlch\af4\afs24\lang255\ltrch\dbch\af3\langfe255\hich\f0\fs24\lang1033\loch\f0\fs24\lang1033 +\par \pard\plain \ltrpar\s1\cf0{\*\hyphen2\hyphlead2\hyphtrail2\hyphmax0}\rtlch\af4\afs24\lang255\ltrch\dbch\af3\langfe255\hich\f0\fs24\lang1033\loch\f0\fs24\lang1033 {\rtlch \ltrch\loch }{\rtlch \ltrch\loch\f0\fs24\lang1033\i0\b0 Use is subject to license terms.} +\par \pard\plain \ltrpar\s1\cf0{\*\hyphen2\hyphlead2\hyphtrail2\hyphmax0}\rtlch\af4\afs24\lang255\ltrch\dbch\af3\langfe255\hich\f0\fs24\lang1033\loch\f0\fs24\lang1033 +\par \pard\plain \ltrpar\s1\cf0{\*\hyphen2\hyphlead2\hyphtrail2\hyphmax0}\rtlch\af4\afs24\lang255\ltrch\dbch\af3\langfe255\hich\f0\fs24\lang1033\loch\f0\fs24\lang1033 {\rtlch \ltrch\loch }{\rtlch \ltrch\loch\f0\fs24\lang1033\i0\b0 This distribution may include materials developed by third parties.} +\par \pard\plain \ltrpar\s1\cf0{\*\hyphen2\hyphlead2\hyphtrail2\hyphmax0}\rtlch\af4\afs24\lang255\ltrch\dbch\af3\langfe255\hich\f0\fs24\lang1033\loch\f0\fs24\lang1033 +\par \pard\plain \ltrpar\s1\cf0{\*\hyphen2\hyphlead2\hyphtrail2\hyphmax0}\rtlch\af4\afs24\lang255\ltrch\dbch\af3\langfe255\hich\f0\fs24\lang1033\loch\f0\fs24\lang1033 {\rtlch \ltrch\loch }{\rtlch \ltrch\loch\f0\fs24\lang1033\i0\b0 Sun, Sun Microsystems, the Sun logo, Java and Project Identity Connectors are trademarks or registered trademarks of Sun Microsystems, Inc. in the U.S. and other countries.} +\par \pard\plain \ltrpar\s1\cf0{\*\hyphen2\hyphlead2\hyphtrail2\hyphmax0}\rtlch\af4\afs24\lang255\ltrch\dbch\af3\langfe255\hich\f0\fs24\lang1033\loch\f0\fs24\lang1033 +\par \pard\plain \ltrpar\s1\cf0{\*\hyphen2\hyphlead2\hyphtrail2\hyphmax0}\rtlch\af4\afs24\lang255\ltrch\dbch\af3\langfe255\hich\f0\fs24\lang1033\loch\f0\fs24\lang1033 {\rtlch \ltrch\loch }{\rtlch \ltrch\loch\f0\fs24\lang1033\i0\b0 UNIX is a registered trademark in the U.S. and other countries, exclusively licensed through X/Open Company, Ltd.} +\par \pard\plain \ltrpar\s1\cf0{\*\hyphen2\hyphlead2\hyphtrail2\hyphmax0}\rtlch\af4\afs24\lang255\ltrch\dbch\af3\langfe255\hich\f0\fs24\lang1033\loch\f0\fs24\lang1033 +\par \pard\plain \ltrpar\s1\cf0{\*\hyphen2\hyphlead2\hyphtrail2\hyphmax0}\rtlch\af4\afs24\lang255\ltrch\dbch\af3\langfe255\hich\f0\fs24\lang1033\loch\f0\fs24\lang1033 {\rtlch \ltrch\loch }{\rtlch \ltrch\loch\f0\fs24\lang1033\i0\b0 This product is covered and controlled by U.S. Export Control laws and may be subject to the export or import laws in other countries. Nuclear, missile, chemical biological weapons or nuclear maritime end uses or end users, whether direct or indirec +t, are strictly prohibited. Export or reexport to countries subject to U.S. embargo or to entities identified on U.S. export exclusion lists, including, but not limited to, the denied persons and specially designated nationals lists is strictly prohibited. + } +\par } \ No newline at end of file diff --git a/DotNetConnectors.sln/ShellScriptExecutorFactory/AssemblyInfo.cs b/DotNetConnectors.sln/ShellScriptExecutorFactory/AssemblyInfo.cs new file mode 100644 index 00000000..4e907090 --- /dev/null +++ b/DotNetConnectors.sln/ShellScriptExecutorFactory/AssemblyInfo.cs @@ -0,0 +1,53 @@ +/* + * ==================== + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER. + * + * Copyright 2007-2008 Sun Microsystems, Inc. All rights reserved. + * + * The contents of this file are subject to the terms of the Common Development + * and Distribution License("CDDL") (the "License"). You may not use this file + * except in compliance with the License. + * + * You can obtain a copy of the License at + * http://IdentityConnectors.dev.java.net/legal/license.txt + * See the License for the specific language governing permissions and limitations + * under the License. + * + * When distributing the Covered Code, include this CDDL Header Notice in each file + * and include the License file at identityconnectors/legal/license.txt. + * If applicable, add the following below this CDDL Header, with the fields + * enclosed by brackets [] replaced by your own identifying information: + * "Portions Copyrighted [year] [name of copyright owner]" + * ==================== + */ +#region Using directives + +using System; +using System.Reflection; +using System.Runtime.InteropServices; + +#endregion + +// General Information about an assembly is controlled through the following +// set of attributes. Change these attribute values to modify the information +// associated with an assembly. +[assembly: AssemblyTitle("ShellScriptExecutorFactory")] +[assembly: AssemblyDescription("")] +[assembly: AssemblyConfiguration("")] +[assembly: AssemblyCompany("")] +[assembly: AssemblyProduct("ShellScriptExecutorFactory")] +[assembly: AssemblyCopyright("")] +[assembly: AssemblyTrademark("")] +[assembly: AssemblyCulture("")] + +// This sets the default COM visibility of types in the assembly to invisible. +// If you need to expose a type to COM, use [ComVisible(true)] on that type. +[assembly: ComVisible(false)] + +// The assembly version has following format : +// +// Major.Minor.Build.Revision +// +// You can specify all the values or you can use the default the Revision and +// Build Numbers by using the '*' as shown below: +[assembly: AssemblyVersion("1.0.*")] diff --git a/DotNetConnectors.sln/ShellScriptExecutorFactory/ShellScriptExecutorFactory.cs b/DotNetConnectors.sln/ShellScriptExecutorFactory/ShellScriptExecutorFactory.cs new file mode 100644 index 00000000..60be5ff6 --- /dev/null +++ b/DotNetConnectors.sln/ShellScriptExecutorFactory/ShellScriptExecutorFactory.cs @@ -0,0 +1,128 @@ +/* + * ==================== + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER. + * + * Copyright 2007-2008 Sun Microsystems, Inc. All rights reserved. + * + * The contents of this file are subject to the terms of the Common Development + * and Distribution License("CDDL") (the "License"). You may not use this file + * except in compliance with the License. + * + * You can obtain a copy of the License at + * http://IdentityConnectors.dev.java.net/legal/license.txt + * See the License for the specific language governing permissions and limitations + * under the License. + * + * When distributing the Covered Code, include this CDDL Header Notice in each file + * and include the License file at identityconnectors/legal/license.txt. + * If applicable, add the following below this CDDL Header, with the fields + * enclosed by brackets [] replaced by your own identifying information: + * "Portions Copyrighted [year] [name of copyright owner]" + * ==================== + */ +using System; +using System.IO; +using System.Security; +using System.Diagnostics; +using System.Reflection; +using System.Collections.Generic; +using System.Text.RegularExpressions; +using Org.IdentityConnectors.Common.Security; + +namespace Org.IdentityConnectors.Common.Script.Shell +{ + + /// + /// Process shell scripts. Valid arguments are below, the rest will be + /// used as environment variables. The return is the exit code of the + /// process. + ///
    + ///
  • USERNAME - name of the user to run this script as..
  • + ///
  • PASSWORD - (GuardedString or SecureString) password for the user to run this script as..
  • + ///
  • WORKINGDIR - working directory run this script in..
  • + ///
  • TIMEOUT - timeout waiting for script to finish in ms (default: 30 secs)
  • + ///
+ ///
+ [ScriptExecutorFactoryClass("Shell")] + public class ShellScriptExecutorFactory : ScriptExecutorFactory + { + + /// + /// Creates a script executor give the Shell script. + /// + override + public ScriptExecutor NewScriptExecutor(Assembly [] refs, string script, bool compile) { + return new ShellScriptExecutor(script); + } + + /// + /// Processes the script. + /// + class ShellScriptExecutor : ScriptExecutor { + private readonly string _script; + + public ShellScriptExecutor(string script) { + _script = script; + } + public object Execute(IDictionary arguments) { + // create the process info.. + Process process = new Process(); + // set the defaults.. + process.StartInfo.CreateNoWindow = true; + process.StartInfo.UseShellExecute = true; + // set the default timeout.. + int timeout = 1000 * 30; // 30 secss + // if there are any environment varibles set to false.. + process.StartInfo.UseShellExecute = arguments.Count == 0; + // take out username and password if they're in the options. + foreach (KeyValuePair kv in arguments) { + if (kv.Key.ToUpper().Equals("USERNAME")) { + string domainUser = kv.Value.ToString(); + string[] split = domainUser.Split(new char[] {'\\'}); + if (split.Length == 1) { + process.StartInfo.UserName = split[0]; + } else { + process.StartInfo.Domain = split[0]; + process.StartInfo.UserName = split[1]; + } + } else if (kv.Key.ToUpper().Equals("PASSWORD")) { + if (kv.Value is SecureString) { + process.StartInfo.Password = (SecureString)kv.Value; + } + else if (kv.Value is GuardedString) { + process.StartInfo.Password = ((GuardedString)kv.Value).ToSecureString(); + } else { + throw new ArgumentException("Invalid type for password."); + } + } else if (kv.Key.ToUpper().Equals("WORKINGDIR")) { + process.StartInfo.WorkingDirectory = kv.Value.ToString(); + } else if (kv.Key.ToUpper().Equals("TIMEOUT")) { + timeout = Int32.Parse(kv.Value.ToString()); + } else { + process.StartInfo.EnvironmentVariables[kv.Key] = kv.Value.ToString(); + } + } + // write out the script.. + string fn = Path.GetTempFileName() + ".cmd"; + using (StreamWriter sw = new StreamWriter(fn)) { + sw.Write(_script); + } + // set temp file.. + process.StartInfo.FileName = fn; + // execute script.. + process.Start(); + // wait for the process to exit.. + if (!process.WaitForExit(timeout)) { + process.Close(); + throw new TimeoutException("Script failed to exit in time!"); + } + int exitCode = process.ExitCode; + // close up the process and return the exit code.. + process.Close(); + // clean up temp file.. + File.Delete(fn); + return exitCode; + } + } + } +} diff --git a/DotNetConnectors.sln/ShellScriptExecutorFactory/ShellScriptExecutorFactory.csproj b/DotNetConnectors.sln/ShellScriptExecutorFactory/ShellScriptExecutorFactory.csproj new file mode 100644 index 00000000..0779540f --- /dev/null +++ b/DotNetConnectors.sln/ShellScriptExecutorFactory/ShellScriptExecutorFactory.csproj @@ -0,0 +1,81 @@ + + + + {4700690A-2D29-40A0-86AC-E5A9F71A479A} + Debug + AnyCPU + Library + Sun.OpenConnectors.Common.Script.Shell + Shell.ScriptExecutorFactory + v3.5 + + + prompt + 4 + AnyCPU + bin\Debug\ + True + Full + False + True + DEBUG;TRACE + + + pdbonly + bin\Release\ + prompt + False + 4 + AnyCPU + None + True + False + TRACE + + + + + + 3.5 + + + + 3.5 + + + + + + + + + {F140E8DA-52B4-4159-992A-9DA10EA8EEFB} + Common + + + + + + + diff --git a/DotNetConnectors.sln/TestBundleV1/AssemblyInfo.cs b/DotNetConnectors.sln/TestBundleV1/AssemblyInfo.cs new file mode 100644 index 00000000..f1b27d89 --- /dev/null +++ b/DotNetConnectors.sln/TestBundleV1/AssemblyInfo.cs @@ -0,0 +1,57 @@ +/* + * ==================== + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER. + * + * Copyright 2007-2008 Sun Microsystems, Inc. All rights reserved. + * + * The contents of this file are subject to the terms of the Common Development + * and Distribution License("CDDL") (the "License"). You may not use this file + * except in compliance with the License. + * + * You can obtain a copy of the License at + * http://IdentityConnectors.dev.java.net/legal/license.txt + * See the License for the specific language governing permissions and limitations + * under the License. + * + * When distributing the Covered Code, include this CDDL Header Notice in each file + * and include the License file at identityconnectors/legal/license.txt. + * If applicable, add the following below this CDDL Header, with the fields + * enclosed by brackets [] replaced by your own identifying information: + * "Portions Copyrighted [year] [name of copyright owner]" + * ==================== + */ +#region Using directives + +using System; +using System.Reflection; +using System.Runtime.InteropServices; +using Org.IdentityConnectors.Framework.Spi; +using System.Resources; + +#endregion + +// General Information about an assembly is controlled through the following +// set of attributes. Change these attribute values to modify the information +// associated with an assembly. +[assembly: AssemblyTitle("TestBundleV1")] +[assembly: AssemblyDescription("")] +[assembly: AssemblyConfiguration("")] +[assembly: AssemblyCompany("")] +[assembly: AssemblyProduct("TestBundleV1")] +[assembly: AssemblyCopyright("")] +[assembly: AssemblyTrademark("")] +[assembly: AssemblyCulture("")] + +// This sets the default COM visibility of types in the assembly to invisible. +// If you need to expose a type to COM, use [ComVisible(true)] on that type. +[assembly: ComVisible(false)] + +// The assembly version has following format : +// +// Major.Minor.Build.Revision +// +// You can specify all the values or you can use the default the Revision and +// Build Numbers by using the '*' as shown below: +[assembly: AssemblyVersion("1.0.0.0")] +[assembly: NeutralResourcesLanguage("en-US")] + diff --git a/DotNetConnectors.sln/TestBundleV1/Messages.es-ES.resx b/DotNetConnectors.sln/TestBundleV1/Messages.es-ES.resx new file mode 100644 index 00000000..ec894ca7 --- /dev/null +++ b/DotNetConnectors.sln/TestBundleV1/Messages.es-ES.resx @@ -0,0 +1,126 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + text/microsoft-resx + + + 2.0 + + + System.Resources.ResXResourceReader, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + System.Resources.ResXResourceWriter, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + tstField.display_es-ES + + + tstField.help_es-ES + + \ No newline at end of file diff --git a/DotNetConnectors.sln/TestBundleV1/Messages.es.resx b/DotNetConnectors.sln/TestBundleV1/Messages.es.resx new file mode 100644 index 00000000..523473e4 --- /dev/null +++ b/DotNetConnectors.sln/TestBundleV1/Messages.es.resx @@ -0,0 +1,126 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + text/microsoft-resx + + + 2.0 + + + System.Resources.ResXResourceReader, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + System.Resources.ResXResourceWriter, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + tstField.help_es + + + tstField.display_es + + \ No newline at end of file diff --git a/DotNetConnectors.sln/TestBundleV1/Messages.resx b/DotNetConnectors.sln/TestBundleV1/Messages.resx new file mode 100644 index 00000000..a988e1e7 --- /dev/null +++ b/DotNetConnectors.sln/TestBundleV1/Messages.resx @@ -0,0 +1,126 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + text/microsoft-resx + + + 2.0 + + + System.Resources.ResXResourceReader, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + System.Resources.ResXResourceWriter, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + Help for test field. + + + Display for test field. + + \ No newline at end of file diff --git a/DotNetConnectors.sln/TestBundleV1/TestBundleV1.csproj b/DotNetConnectors.sln/TestBundleV1/TestBundleV1.csproj new file mode 100644 index 00000000..0c67a441 --- /dev/null +++ b/DotNetConnectors.sln/TestBundleV1/TestBundleV1.csproj @@ -0,0 +1,91 @@ + + + + {0BC2A013-56FE-46DD-90FC-2D2D57748FB6} + Debug + AnyCPU + Library + TestBundleV1 + TestBundleV1.Connector + v3.5 + OnBuildSuccess + + + prompt + 4 + AnyCPU + bin\Debug\ + True + Full + False + True + DEBUG;TRACE + + + pdbonly + bin\Release\ + TRACE + prompt + 4 + AnyCPU + False + True + False + + + + + + + + + + + + + + Designer + + + Designer + + + Designer + + + + + {F140E8DA-52B4-4159-992A-9DA10EA8EEFB} + Common + + + {8B24461B-456A-4032-89A1-CD418F7B5B62} + Framework + + + + + + + diff --git a/DotNetConnectors.sln/TestBundleV1/TestConnector.cs b/DotNetConnectors.sln/TestBundleV1/TestConnector.cs new file mode 100644 index 00000000..abc8f446 --- /dev/null +++ b/DotNetConnectors.sln/TestBundleV1/TestConnector.cs @@ -0,0 +1,216 @@ +/* + * ==================== + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER. + * + * Copyright 2007-2008 Sun Microsystems, Inc. All rights reserved. + * + * The contents of this file are subject to the terms of the Common Development + * and Distribution License("CDDL") (the "License"). You may not use this file + * except in compliance with the License. + * + * You can obtain a copy of the License at + * http://IdentityConnectors.dev.java.net/legal/license.txt + * See the License for the specific language governing permissions and limitations + * under the License. + * + * When distributing the Covered Code, include this CDDL Header Notice in each file + * and include the License file at identityconnectors/legal/license.txt. + * If applicable, add the following below this CDDL Header, with the fields + * enclosed by brackets [] replaced by your own identifying information: + * "Portions Copyrighted [year] [name of copyright owner]" + * ==================== + */ +using System; +using System.Collections.Generic; +using System.Globalization; +using System.Threading; +using Org.IdentityConnectors.Common; +using Org.IdentityConnectors.Common.Pooling; +using Org.IdentityConnectors.Framework.Common.Objects; +using Org.IdentityConnectors.Framework.Common.Objects.Filters; +using Org.IdentityConnectors.Framework.Common.Exceptions; +using Org.IdentityConnectors.Framework.Spi; +using Org.IdentityConnectors.Framework.Spi.Operations; +namespace org.identityconnectors.testconnector +{ + public class TstConnectorConfig : AbstractConfiguration + { + /// + /// keep lower case for consistent unit tests + /// + [ConfigurationProperty(OperationTypes=new Type[]{typeof(SyncOp)})] + public string tstField{get;set;} + + /// + /// keep lower case for consistent unit tests + /// + public int numResults{get;set;} + + /// + /// keep lower case for consistent unit tests + /// + public bool failValidation{get;set;} + + /// + /// keep lower case for consistent unit tests + /// + public bool resetConnectionCount{get;set;} + + public override void Validate() { + if (failValidation) { + throw new ConnectorException("validation failed "+CultureInfo.CurrentUICulture.TwoLetterISOLanguageName); + } + } + + } + + public class MyTstConnection { + private readonly int _connectionNumber; + private bool _isGood = true; + + public MyTstConnection(int connectionNumber) { + _connectionNumber = connectionNumber; + } + + public void Test() { + if (!_isGood) { + throw new ConnectorException("Connection is bad"); + } + } + + public void Dispose() { + _isGood = false; + } + + public bool IsGood() { + return _isGood; + } + + public int GetConnectionNumber() { + return _connectionNumber; + } + } + + [ConnectorClass("TestConnector", + typeof(TstConnectorConfig), + MessageCatalogPaths= new String[]{"TestBundleV1.Messages"} + )] + public class TstConnector : CreateOp, PoolableConnector, SchemaOp, SearchOp, SyncOp + { + private static int _connectionCount = 0; + private MyTstConnection _myConnection; + private TstConnectorConfig _config; + + public Uid Create(ObjectClass oclass, ICollection attrs, OperationOptions options) { + int? delay = (int?)CollectionUtil.GetValue(options.Options,"delay",null); + if ( delay != null ) { + Thread.Sleep((int)delay); + } + + if ( options.Options.ContainsKey("testPooling")) { + return new Uid(_myConnection.GetConnectionNumber().ToString()); + } + else { + String version = "1.0"; + return new Uid(version); + } + } + public void Init(Configuration cfg) { + _config = (TstConnectorConfig)cfg; + if (_config.resetConnectionCount) { + _connectionCount = 0; + } + _myConnection = new MyTstConnection(_connectionCount++); + } + + public static String getVersion() + { + return "1.0"; + } + + public void Dispose() { + if (_myConnection != null) { + _myConnection.Dispose(); + _myConnection = null; + } + } + /** + * Used by the script tests + */ + public String concat(String s1, String s2) { + return s1+s2; + } + + public void CheckAlive() { + _myConnection.Test(); + } + + private class MyTranslator : AbstractFilterTranslator { + + } + public FilterTranslator CreateFilterTranslator(ObjectClass oclass, OperationOptions options) { + return new MyTranslator(); + } + public void ExecuteQuery(ObjectClass oclass, String query, ResultsHandler handler, OperationOptions options) { + + for ( int i = 0; i < _config.numResults; i++ ) { + int? delay = (int?)CollectionUtil.GetValue(options.Options,"delay",null); + if ( delay != null ) { + Thread.Sleep((int)delay); + } + ConnectorObjectBuilder builder = + new ConnectorObjectBuilder(); + builder.SetUid(""+i); + builder.SetName(i.ToString()); + builder.ObjectClass = oclass; + for ( int j = 0; j < 50; j++ ) { + builder.AddAttribute("myattribute"+j,"myvaluevaluevalue"+j); + } + ConnectorObject rv = builder.Build(); + if (!handler(rv)) { + break; + } + } + } + public void Sync(ObjectClass objClass, SyncToken token, + SyncResultsHandler handler, + OperationOptions options) { + for (int i = 0; i < _config.numResults; i++ ) { + ConnectorObjectBuilder obuilder = + new ConnectorObjectBuilder(); + obuilder.SetUid(i.ToString()); + obuilder.SetName(i.ToString()); + obuilder.ObjectClass=(objClass); + + SyncDeltaBuilder builder = + new SyncDeltaBuilder(); + builder.Object=(obuilder.Build()); + builder.DeltaType=(SyncDeltaType.CREATE_OR_UPDATE); + builder.Token=(new SyncToken("mytoken")); + SyncDelta rv = builder.Build(); + if (!handler(rv)) { + break; + } + } + } + + public SyncToken GetLatestSyncToken(ObjectClass objectClass) + { + return new SyncToken("mylatest"); + } + + public Schema Schema() { + SchemaBuilder builder = new SchemaBuilder(SafeType.Get()); + for ( int i = 0 ; i < 2; i++ ) { + ObjectClassInfoBuilder classBuilder = new ObjectClassInfoBuilder(); + classBuilder.ObjectType=("class"+i); + for ( int j = 0; j < 200; j++) { + classBuilder.AddAttributeInfo(ConnectorAttributeInfoBuilder.Build("attributename"+j, typeof(String))); + } + builder.DefineObjectClass(classBuilder.Build()); + } + return builder.Build(); + } + + } +} diff --git a/DotNetConnectors.sln/TestBundleV2/AssemblyInfo.cs b/DotNetConnectors.sln/TestBundleV2/AssemblyInfo.cs new file mode 100644 index 00000000..9c8de8e4 --- /dev/null +++ b/DotNetConnectors.sln/TestBundleV2/AssemblyInfo.cs @@ -0,0 +1,55 @@ +/* + * ==================== + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER. + * + * Copyright 2007-2008 Sun Microsystems, Inc. All rights reserved. + * + * The contents of this file are subject to the terms of the Common Development + * and Distribution License("CDDL") (the "License"). You may not use this file + * except in compliance with the License. + * + * You can obtain a copy of the License at + * http://IdentityConnectors.dev.java.net/legal/license.txt + * See the License for the specific language governing permissions and limitations + * under the License. + * + * When distributing the Covered Code, include this CDDL Header Notice in each file + * and include the License file at identityconnectors/legal/license.txt. + * If applicable, add the following below this CDDL Header, with the fields + * enclosed by brackets [] replaced by your own identifying information: + * "Portions Copyrighted [year] [name of copyright owner]" + * ==================== + */ +#region Using directives + +using System; +using System.Reflection; +using System.Runtime.InteropServices; +using Org.IdentityConnectors.Framework.Spi; + +#endregion + +// General Information about an assembly is controlled through the following +// set of attributes. Change these attribute values to modify the information +// associated with an assembly. +[assembly: AssemblyTitle("TestBundleV2")] +[assembly: AssemblyDescription("")] +[assembly: AssemblyConfiguration("")] +[assembly: AssemblyCompany("")] +[assembly: AssemblyProduct("TestBundleV2")] +[assembly: AssemblyCopyright("")] +[assembly: AssemblyTrademark("")] +[assembly: AssemblyCulture("")] + +// This sets the default COM visibility of types in the assembly to invisible. +// If you need to expose a type to COM, use [ComVisible(true)] on that type. +[assembly: ComVisible(false)] + +// The assembly version has following format : +// +// Major.Minor.Build.Revision +// +// You can specify all the values or you can use the default the Revision and +// Build Numbers by using the '*' as shown below: +[assembly: AssemblyVersion("2.0.0.0")] + diff --git a/DotNetConnectors.sln/TestBundleV2/TestBundleV2.csproj b/DotNetConnectors.sln/TestBundleV2/TestBundleV2.csproj new file mode 100644 index 00000000..2b6e3cfe --- /dev/null +++ b/DotNetConnectors.sln/TestBundleV2/TestBundleV2.csproj @@ -0,0 +1,82 @@ + + + + {3E737796-3A83-4924-9FF1-DC542F21F59E} + Debug + AnyCPU + Library + TestBundleV2 + TestBundleV2.Connector + v3.5 + OnBuildSuccess + + + prompt + 4 + AnyCPU + bin\Debug\ + True + Full + False + True + DEBUG;TRACE + + + pdbonly + bin\Release\ + TRACE + prompt + 4 + AnyCPU + False + True + False + + + + + + + + + + + + + + + + {F140E8DA-52B4-4159-992A-9DA10EA8EEFB} + Common + + + {8B24461B-456A-4032-89A1-CD418F7B5B62} + Framework + + + + + + + diff --git a/DotNetConnectors.sln/TestBundleV2/TestConnector.cs b/DotNetConnectors.sln/TestBundleV2/TestConnector.cs new file mode 100644 index 00000000..cce29c4a --- /dev/null +++ b/DotNetConnectors.sln/TestBundleV2/TestConnector.cs @@ -0,0 +1,61 @@ +/* + * ==================== + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER. + * + * Copyright 2007-2008 Sun Microsystems, Inc. All rights reserved. + * + * The contents of this file are subject to the terms of the Common Development + * and Distribution License("CDDL") (the "License"). You may not use this file + * except in compliance with the License. + * + * You can obtain a copy of the License at + * http://IdentityConnectors.dev.java.net/legal/license.txt + * See the License for the specific language governing permissions and limitations + * under the License. + * + * When distributing the Covered Code, include this CDDL Header Notice in each file + * and include the License file at identityconnectors/legal/license.txt. + * If applicable, add the following below this CDDL Header, with the fields + * enclosed by brackets [] replaced by your own identifying information: + * "Portions Copyrighted [year] [name of copyright owner]" + * ==================== + */ +using System; +using System.Collections.Generic; +using System.Globalization; +using Org.IdentityConnectors.Common.Pooling; +using Org.IdentityConnectors.Framework.Common.Objects; +using Org.IdentityConnectors.Framework.Spi; +using Org.IdentityConnectors.Framework.Spi.Operations; +namespace org.identityconnectors.testconnector +{ + public class TstConnectorConfig : AbstractConfiguration + { + public override void Validate() { + } + } + + [ConnectorClass("TestConnector", + typeof(TstConnectorConfig), + MessageCatalogPaths=new String[]{"TestBundleV2.Messages"} + )] + public class TstConnector : CreateOp, Connector, SchemaOp + { + public Uid Create(ObjectClass oclass, ICollection attrs, OperationOptions options) { + String version = "2.0"; + return new Uid(version); + } + public void Init(Configuration cfg) { + } + + public void Dispose() { + + } + + public Schema Schema() { + return null; + } + + + } +} diff --git a/DotNetConnectors.sln/build.xml b/DotNetConnectors.sln/build.xml new file mode 100644 index 00000000..3a8cc532 --- /dev/null +++ b/DotNetConnectors.sln/build.xml @@ -0,0 +1,82 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/DotNetConnectors.sln/server.pfx b/DotNetConnectors.sln/server.pfx new file mode 100644 index 0000000000000000000000000000000000000000..63f632a287bcbab06114bdde513c6b95c616be6d GIT binary patch literal 1704 zcmY+Dc|6p47{`Aze*K1Q>@-RyjO5G?zsZ=HZN$hmMvf#`VMnfF4I$Tr#^#7D8sr|i z!V*e!IEzZDSSqvFvTg2;DNWL>{pr>2zV?sr^L?K8b9}#lJRg_`RiaT+Fc10xlG%~W zOcrlIVNe1dv=8J#b_lkCc~UL^OG#CLJgG7S7a=DflKtBC&3Y7Cz(ZewdFU2c8Cw4j zMuy9Q(yVc-fI8yT5E_kn1oF^Xbc#*1_s~A#@Ol4_{1b&gn@69;7rhl%cs$Nj91|Kl z&TeB+#~Ww+*jjI791dETOOBQ%BpL`0izlv+uW}8P0`+5(%UaPj6tfZ!?ScG&=PTJt z4ilYGH@7wP@sv}U^eg%ANA)I!qjVtOKy;3(`;vGab2$=t;J<@V?%Q9`ROFi_(#8t>&7 zoL3VNq~t7X0p}s>Y*Tg6`r|KBrdh+Fa@}bY{)0lp)s=Ow5(8HwGYok~CQoUfkF1DzRsa3I~Ea1Ek110bF;u_mz`&iFgr;+$nIZBc^HY!jcj5?7v@uP$E zjz^!n3+}a>-mSurz(Gsq$vUrm7K3s~?Q|BV*i4R_y4$<<$rNL>xfsAph$YLFg3Y5n z$@_E%bmSDaEo~g@Y3@@_YWAR>CYHJNvQgN##9`{Rk%f|Fr@Yk%*>F1`tfyY z!0~4o8lJ>{nVvC^{`6dxN*bg6YD|Z~Tf-@+YT3fPG2u?f;koDT{7v>@Cx*LC$t9U` zHkZ0ru*7OWqGd&7RqdZJxxrEZ|a{ zmsjQ$D~B3S+FG^lnay2Th%8qis4&?n+gtTdY9Ig5mD>Bw2+d|=g|7@=Y4LOZe!eE@ z__Yx z2>}9-$Q^;Vyp)})%qLo>_lTW-sup11OqYu+0e@2O3XPH)_YcN*zs%jTl> z&SmIaT!|Y#dfR%Yf=)5>n#b6_FRAC+Wd~^xo@IoncI?%0$=UsW^ATG{6mRk}&G)sy zygKvck-{ldNA=6t%T`hBqLQvMpIv)z4i`18f*)_y=iX-ztb{S%Q54|raojntLp6!f z2H@SzwIf$s-M94;tSTdadCoE5+!PwZLOvz%%#Ju|@}TI$L^R=YeG{Sy{P zx3_QMrT=z4HP}&iB&5=b0?XMgblcM&Hk}RBoQ{8bQJB<@7 zNRPa>_?}nf-Vc4X_uaB{et5==kTHuCL`+8Kk$}ig@8(@`62?G&@%0OpF@4rdQNEF_ zmXV`*-{kxFok=R+`c3^lm&CM`xMmomKI|6I$lN7cZQ8oNcu8O&`IF_43J! zrvv1`;DGt|ycVC7Snc|IH4Y%vHB{QHZ6?TWIjf_>9URCMZK)H@&HQyWY#yr>ACFeg zm~UJ-l!?_|6uC8I75=r Date: Mon, 22 Dec 2008 19:31:05 +0000 Subject: [PATCH 128/342] Resurection error --- .../ActiveDirectoryConfiguration.cs | 139 - .../ActiveDirectoryConnector.cs | 1193 ----- .../ActiveDirectoryConnector.csproj | 120 - .../ActiveDirectoryFilterTranslator.cs | 479 -- .../ActiveDirectorySyncToken.cs | 89 - .../ActiveDirectoryUtils.cs | 669 --- .../ActiveDirectoryConnector/AssemblyInfo.cs | 54 - .../ActiveDirectoryConnector/CommonUtils.cs | 73 - .../CustomAttributeHandlers.cs | 1167 ----- .../Messages.en.Designer.cs | 0 .../ActiveDirectoryConnector/Messages.en.resx | 123 - .../Messages.es-ES.resx | 123 - .../ActiveDirectoryConnector/Messages.es.resx | 123 - .../ActiveDirectoryConnector/Messages.resx | 264 -- .../ObjectClasses.xml | 262 -- .../PasswordChangeHandler.cs | 237 - .../TerminalServicesUtils.cs | 304 -- .../UserAccountControl.cs | 110 - .../ActiveDirectoryConnector/build.xml | 28 - .../ActiveDirectoryConnectorTest.cs | 2750 ------------ .../ActiveDirectoryConnectorTests.csproj | 140 - .../Properties/AssemblyInfo.cs | 58 - .../TestParams.resx | 147 - .../ActiveDirectoryConnectorTests/project.xml | 14 - .../BooScriptExecutorFactory/AssemblyInfo.cs | 53 - .../BooScriptExecutorFactory.cs | 81 - .../BooScriptExecutorFactory.csproj | 95 - .../lib/Boo.Lang.Compiler.dll | Bin 729088 -> 0 bytes .../lib/Boo.Lang.Interpreter.dll | Bin 86016 -> 0 bytes .../lib/Boo.Lang.Parser.dll | Bin 421888 -> 0 bytes .../lib/Boo.Lang.Useful.dll | Bin 81920 -> 0 bytes .../BooScriptExecutorFactory/lib/Boo.Lang.dll | Bin 110592 -> 0 bytes DotNetConnectors.sln/Common/AssemblyInfo.cs | 53 - DotNetConnectors.sln/Common/Assertions.cs | 51 - DotNetConnectors.sln/Common/CollectionUtil.cs | 861 ---- DotNetConnectors.sln/Common/Common.csproj | 94 - DotNetConnectors.sln/Common/DateTimeUtil.cs | 44 - .../Common/FrameworkInternalBridge.cs | 54 - DotNetConnectors.sln/Common/IOUtil.cs | 47 - DotNetConnectors.sln/Common/Locale.cs | 210 - DotNetConnectors.sln/Common/Pair.cs | 73 - DotNetConnectors.sln/Common/Pooling.cs | 189 - DotNetConnectors.sln/Common/Proxy.cs | 267 -- DotNetConnectors.sln/Common/ReflectionUtil.cs | 149 - DotNetConnectors.sln/Common/SafeType.cs | 142 - DotNetConnectors.sln/Common/Script.cs | 167 - DotNetConnectors.sln/Common/Security.cs | 542 --- DotNetConnectors.sln/Common/StringUtil.cs | 92 - DotNetConnectors.sln/Common/TraceUtil.cs | 51 - DotNetConnectors.sln/Common/XmlUtil.cs | 192 - DotNetConnectors.sln/Console/AssemblyInfo.cs | 53 - DotNetConnectors.sln/Console/Console.csproj | 90 - .../Console/MainForm.Designer.cs | 47 - DotNetConnectors.sln/Console/MainForm.cs | 47 - DotNetConnectors.sln/Console/Program.cs | 45 - DotNetConnectors.sln/DotNetConnectors.sln | 90 - .../ExchangeConnector/CommandInfos.xml | 257 -- .../ExchangeConfiguration.cs | 37 - .../ExchangeConnector/ExchangeConnector.cs | 302 -- .../ExchangeConnector.csproj | 108 - .../ExchangeConnector/ExchangeUtils.cs | 375 -- .../LegacyExchangeConnector.cs | 462 -- .../ExchangeConnector/Messages.resx | 123 - .../ExchangeConnector/ObjectClasses.xml | 114 - .../Properties/AssemblyInfo.cs | 58 - .../ExchangeConnector/RunSpaceInstance.cs | 335 -- .../ExchangeConnector/build.xml | 28 - DotNetConnectors.sln/Framework/Api.cs | 535 --- .../Framework/ApiOperations.cs | 404 -- .../Framework/AssemblyInfo.cs | 53 - DotNetConnectors.sln/Framework/Common.cs | 287 -- .../Framework/CommonExceptions.cs | 291 -- .../Framework/CommonObjects.cs | 3893 ----------------- .../Framework/CommonObjectsFilter.cs | 1314 ------ .../Framework/CommonSerializer.cs | 289 -- .../Framework/Framework.csproj | 99 - DotNetConnectors.sln/Framework/Spi.cs | 233 - .../Framework/SpiOperations.cs | 412 -- DotNetConnectors.sln/Framework/Test.cs | 296 -- DotNetConnectors.sln/FrameworkInternal/Api.cs | 889 ---- .../FrameworkInternal/ApiLocal.cs | 1034 ----- .../FrameworkInternal/ApiLocalOperations.cs | 1416 ------ .../FrameworkInternal/ApiRemote.cs | 383 -- .../FrameworkInternal/ApiRemoteMessages.cs | 243 - .../FrameworkInternal/AssemblyInfo.cs | 53 - .../FrameworkInternal.csproj | 103 - .../FrameworkInternal/Resources.resx | 492 --- .../FrameworkInternal/Security.cs | 124 - .../FrameworkInternal/Serializer.cs | 2404 ---------- .../FrameworkInternal/SerializerBinary.cs | 775 ---- .../FrameworkInternal/SerializerXml.cs | 794 ---- .../FrameworkInternal/Server.cs | 900 ---- .../FrameworkInternal/Test.cs | 121 - .../FrameworkTests/AssemblyInfo.cs | 53 - .../FrameworkTests/CollectionUtilTests.cs | 160 - .../ConnectorInfoManagerTests.cs | 592 --- .../FrameworkTests/FilterTranslatorTests.cs | 609 --- .../FrameworkTests/FrameworkTests.csproj | 153 - .../FrameworkTests/GuardedStringTests.cs | 100 - .../FrameworkTests/LocaleTests.cs | 210 - .../ObjectNormalizerFacadeTests.cs | 246 -- .../FrameworkTests/ObjectPoolTests.cs | 252 -- .../ObjectSerializationTests.cs | 1128 ----- .../FrameworkTests/ProxyTests.cs | 100 - .../FrameworkTests/SafeTypeTest.cs | 46 - .../FrameworkTests/ScriptTests.cs | 62 - .../FrameworkTests/TestHelperTests.cs | 72 - .../FrameworkTests/TestUtil.cs | 53 - .../FrameworkTests/UpdateImplTests.cs | 209 - .../FrameworkTests/app.config | 24 - .../FrameworkTests/project.xml | 5 - DotNetConnectors.sln/Service/AssemblyInfo.cs | 53 - DotNetConnectors.sln/Service/Program.cs | 230 - .../Service/ProjectInstaller.cs | 56 - DotNetConnectors.sln/Service/Service.cs | 182 - DotNetConnectors.sln/Service/Service.csproj | 119 - DotNetConnectors.sln/Service/app.config | 37 - .../ServiceInstall/ExtBuild.proj | 63 - .../ServiceInstall/File.bottom | 6 - DotNetConnectors.sln/ServiceInstall/File.top | 21 - .../ServiceInstall/ServiceInstall.wixproj | 69 - DotNetConnectors.sln/ServiceInstall/Setup.wxs | 50 - .../ServiceInstall/license.rtf | 30 - .../AssemblyInfo.cs | 53 - .../ShellScriptExecutorFactory.cs | 128 - .../ShellScriptExecutorFactory.csproj | 81 - .../TestBundleV1/AssemblyInfo.cs | 57 - .../TestBundleV1/Messages.es-ES.resx | 126 - .../TestBundleV1/Messages.es.resx | 126 - .../TestBundleV1/Messages.resx | 126 - .../TestBundleV1/TestBundleV1.csproj | 91 - .../TestBundleV1/TestConnector.cs | 216 - .../TestBundleV2/AssemblyInfo.cs | 55 - .../TestBundleV2/TestBundleV2.csproj | 82 - .../TestBundleV2/TestConnector.cs | 61 - DotNetConnectors.sln/build.xml | 82 - DotNetConnectors.sln/server.pfx | Bin 1704 -> 0 bytes 137 files changed, 38855 deletions(-) delete mode 100644 DotNetConnectors.sln/ActiveDirectoryConnector/ActiveDirectoryConfiguration.cs delete mode 100644 DotNetConnectors.sln/ActiveDirectoryConnector/ActiveDirectoryConnector.cs delete mode 100644 DotNetConnectors.sln/ActiveDirectoryConnector/ActiveDirectoryConnector.csproj delete mode 100644 DotNetConnectors.sln/ActiveDirectoryConnector/ActiveDirectoryFilterTranslator.cs delete mode 100644 DotNetConnectors.sln/ActiveDirectoryConnector/ActiveDirectorySyncToken.cs delete mode 100644 DotNetConnectors.sln/ActiveDirectoryConnector/ActiveDirectoryUtils.cs delete mode 100644 DotNetConnectors.sln/ActiveDirectoryConnector/AssemblyInfo.cs delete mode 100644 DotNetConnectors.sln/ActiveDirectoryConnector/CommonUtils.cs delete mode 100644 DotNetConnectors.sln/ActiveDirectoryConnector/CustomAttributeHandlers.cs delete mode 100644 DotNetConnectors.sln/ActiveDirectoryConnector/Messages.en.Designer.cs delete mode 100644 DotNetConnectors.sln/ActiveDirectoryConnector/Messages.en.resx delete mode 100644 DotNetConnectors.sln/ActiveDirectoryConnector/Messages.es-ES.resx delete mode 100644 DotNetConnectors.sln/ActiveDirectoryConnector/Messages.es.resx delete mode 100644 DotNetConnectors.sln/ActiveDirectoryConnector/Messages.resx delete mode 100644 DotNetConnectors.sln/ActiveDirectoryConnector/ObjectClasses.xml delete mode 100644 DotNetConnectors.sln/ActiveDirectoryConnector/PasswordChangeHandler.cs delete mode 100644 DotNetConnectors.sln/ActiveDirectoryConnector/TerminalServicesUtils.cs delete mode 100644 DotNetConnectors.sln/ActiveDirectoryConnector/UserAccountControl.cs delete mode 100644 DotNetConnectors.sln/ActiveDirectoryConnector/build.xml delete mode 100644 DotNetConnectors.sln/ActiveDirectoryConnectorTests/ActiveDirectoryConnectorTest.cs delete mode 100644 DotNetConnectors.sln/ActiveDirectoryConnectorTests/ActiveDirectoryConnectorTests.csproj delete mode 100644 DotNetConnectors.sln/ActiveDirectoryConnectorTests/Properties/AssemblyInfo.cs delete mode 100644 DotNetConnectors.sln/ActiveDirectoryConnectorTests/TestParams.resx delete mode 100644 DotNetConnectors.sln/ActiveDirectoryConnectorTests/project.xml delete mode 100644 DotNetConnectors.sln/BooScriptExecutorFactory/AssemblyInfo.cs delete mode 100644 DotNetConnectors.sln/BooScriptExecutorFactory/BooScriptExecutorFactory.cs delete mode 100644 DotNetConnectors.sln/BooScriptExecutorFactory/BooScriptExecutorFactory.csproj delete mode 100644 DotNetConnectors.sln/BooScriptExecutorFactory/lib/Boo.Lang.Compiler.dll delete mode 100644 DotNetConnectors.sln/BooScriptExecutorFactory/lib/Boo.Lang.Interpreter.dll delete mode 100644 DotNetConnectors.sln/BooScriptExecutorFactory/lib/Boo.Lang.Parser.dll delete mode 100644 DotNetConnectors.sln/BooScriptExecutorFactory/lib/Boo.Lang.Useful.dll delete mode 100644 DotNetConnectors.sln/BooScriptExecutorFactory/lib/Boo.Lang.dll delete mode 100644 DotNetConnectors.sln/Common/AssemblyInfo.cs delete mode 100644 DotNetConnectors.sln/Common/Assertions.cs delete mode 100644 DotNetConnectors.sln/Common/CollectionUtil.cs delete mode 100644 DotNetConnectors.sln/Common/Common.csproj delete mode 100644 DotNetConnectors.sln/Common/DateTimeUtil.cs delete mode 100644 DotNetConnectors.sln/Common/FrameworkInternalBridge.cs delete mode 100644 DotNetConnectors.sln/Common/IOUtil.cs delete mode 100644 DotNetConnectors.sln/Common/Locale.cs delete mode 100644 DotNetConnectors.sln/Common/Pair.cs delete mode 100644 DotNetConnectors.sln/Common/Pooling.cs delete mode 100644 DotNetConnectors.sln/Common/Proxy.cs delete mode 100644 DotNetConnectors.sln/Common/ReflectionUtil.cs delete mode 100644 DotNetConnectors.sln/Common/SafeType.cs delete mode 100644 DotNetConnectors.sln/Common/Script.cs delete mode 100644 DotNetConnectors.sln/Common/Security.cs delete mode 100644 DotNetConnectors.sln/Common/StringUtil.cs delete mode 100644 DotNetConnectors.sln/Common/TraceUtil.cs delete mode 100644 DotNetConnectors.sln/Common/XmlUtil.cs delete mode 100644 DotNetConnectors.sln/Console/AssemblyInfo.cs delete mode 100644 DotNetConnectors.sln/Console/Console.csproj delete mode 100644 DotNetConnectors.sln/Console/MainForm.Designer.cs delete mode 100644 DotNetConnectors.sln/Console/MainForm.cs delete mode 100644 DotNetConnectors.sln/Console/Program.cs delete mode 100644 DotNetConnectors.sln/DotNetConnectors.sln delete mode 100644 DotNetConnectors.sln/ExchangeConnector/CommandInfos.xml delete mode 100644 DotNetConnectors.sln/ExchangeConnector/ExchangeConfiguration.cs delete mode 100644 DotNetConnectors.sln/ExchangeConnector/ExchangeConnector.cs delete mode 100644 DotNetConnectors.sln/ExchangeConnector/ExchangeConnector.csproj delete mode 100644 DotNetConnectors.sln/ExchangeConnector/ExchangeUtils.cs delete mode 100644 DotNetConnectors.sln/ExchangeConnector/LegacyExchangeConnector.cs delete mode 100644 DotNetConnectors.sln/ExchangeConnector/Messages.resx delete mode 100644 DotNetConnectors.sln/ExchangeConnector/ObjectClasses.xml delete mode 100644 DotNetConnectors.sln/ExchangeConnector/Properties/AssemblyInfo.cs delete mode 100644 DotNetConnectors.sln/ExchangeConnector/RunSpaceInstance.cs delete mode 100644 DotNetConnectors.sln/ExchangeConnector/build.xml delete mode 100644 DotNetConnectors.sln/Framework/Api.cs delete mode 100644 DotNetConnectors.sln/Framework/ApiOperations.cs delete mode 100644 DotNetConnectors.sln/Framework/AssemblyInfo.cs delete mode 100644 DotNetConnectors.sln/Framework/Common.cs delete mode 100644 DotNetConnectors.sln/Framework/CommonExceptions.cs delete mode 100644 DotNetConnectors.sln/Framework/CommonObjects.cs delete mode 100644 DotNetConnectors.sln/Framework/CommonObjectsFilter.cs delete mode 100644 DotNetConnectors.sln/Framework/CommonSerializer.cs delete mode 100644 DotNetConnectors.sln/Framework/Framework.csproj delete mode 100644 DotNetConnectors.sln/Framework/Spi.cs delete mode 100644 DotNetConnectors.sln/Framework/SpiOperations.cs delete mode 100644 DotNetConnectors.sln/Framework/Test.cs delete mode 100644 DotNetConnectors.sln/FrameworkInternal/Api.cs delete mode 100644 DotNetConnectors.sln/FrameworkInternal/ApiLocal.cs delete mode 100644 DotNetConnectors.sln/FrameworkInternal/ApiLocalOperations.cs delete mode 100644 DotNetConnectors.sln/FrameworkInternal/ApiRemote.cs delete mode 100644 DotNetConnectors.sln/FrameworkInternal/ApiRemoteMessages.cs delete mode 100644 DotNetConnectors.sln/FrameworkInternal/AssemblyInfo.cs delete mode 100644 DotNetConnectors.sln/FrameworkInternal/FrameworkInternal.csproj delete mode 100644 DotNetConnectors.sln/FrameworkInternal/Resources.resx delete mode 100644 DotNetConnectors.sln/FrameworkInternal/Security.cs delete mode 100644 DotNetConnectors.sln/FrameworkInternal/Serializer.cs delete mode 100644 DotNetConnectors.sln/FrameworkInternal/SerializerBinary.cs delete mode 100644 DotNetConnectors.sln/FrameworkInternal/SerializerXml.cs delete mode 100644 DotNetConnectors.sln/FrameworkInternal/Server.cs delete mode 100644 DotNetConnectors.sln/FrameworkInternal/Test.cs delete mode 100644 DotNetConnectors.sln/FrameworkTests/AssemblyInfo.cs delete mode 100644 DotNetConnectors.sln/FrameworkTests/CollectionUtilTests.cs delete mode 100644 DotNetConnectors.sln/FrameworkTests/ConnectorInfoManagerTests.cs delete mode 100644 DotNetConnectors.sln/FrameworkTests/FilterTranslatorTests.cs delete mode 100644 DotNetConnectors.sln/FrameworkTests/FrameworkTests.csproj delete mode 100644 DotNetConnectors.sln/FrameworkTests/GuardedStringTests.cs delete mode 100644 DotNetConnectors.sln/FrameworkTests/LocaleTests.cs delete mode 100644 DotNetConnectors.sln/FrameworkTests/ObjectNormalizerFacadeTests.cs delete mode 100644 DotNetConnectors.sln/FrameworkTests/ObjectPoolTests.cs delete mode 100644 DotNetConnectors.sln/FrameworkTests/ObjectSerializationTests.cs delete mode 100644 DotNetConnectors.sln/FrameworkTests/ProxyTests.cs delete mode 100644 DotNetConnectors.sln/FrameworkTests/SafeTypeTest.cs delete mode 100644 DotNetConnectors.sln/FrameworkTests/ScriptTests.cs delete mode 100644 DotNetConnectors.sln/FrameworkTests/TestHelperTests.cs delete mode 100644 DotNetConnectors.sln/FrameworkTests/TestUtil.cs delete mode 100644 DotNetConnectors.sln/FrameworkTests/UpdateImplTests.cs delete mode 100644 DotNetConnectors.sln/FrameworkTests/app.config delete mode 100644 DotNetConnectors.sln/FrameworkTests/project.xml delete mode 100644 DotNetConnectors.sln/Service/AssemblyInfo.cs delete mode 100644 DotNetConnectors.sln/Service/Program.cs delete mode 100644 DotNetConnectors.sln/Service/ProjectInstaller.cs delete mode 100644 DotNetConnectors.sln/Service/Service.cs delete mode 100644 DotNetConnectors.sln/Service/Service.csproj delete mode 100644 DotNetConnectors.sln/Service/app.config delete mode 100644 DotNetConnectors.sln/ServiceInstall/ExtBuild.proj delete mode 100644 DotNetConnectors.sln/ServiceInstall/File.bottom delete mode 100644 DotNetConnectors.sln/ServiceInstall/File.top delete mode 100644 DotNetConnectors.sln/ServiceInstall/ServiceInstall.wixproj delete mode 100644 DotNetConnectors.sln/ServiceInstall/Setup.wxs delete mode 100644 DotNetConnectors.sln/ServiceInstall/license.rtf delete mode 100644 DotNetConnectors.sln/ShellScriptExecutorFactory/AssemblyInfo.cs delete mode 100644 DotNetConnectors.sln/ShellScriptExecutorFactory/ShellScriptExecutorFactory.cs delete mode 100644 DotNetConnectors.sln/ShellScriptExecutorFactory/ShellScriptExecutorFactory.csproj delete mode 100644 DotNetConnectors.sln/TestBundleV1/AssemblyInfo.cs delete mode 100644 DotNetConnectors.sln/TestBundleV1/Messages.es-ES.resx delete mode 100644 DotNetConnectors.sln/TestBundleV1/Messages.es.resx delete mode 100644 DotNetConnectors.sln/TestBundleV1/Messages.resx delete mode 100644 DotNetConnectors.sln/TestBundleV1/TestBundleV1.csproj delete mode 100644 DotNetConnectors.sln/TestBundleV1/TestConnector.cs delete mode 100644 DotNetConnectors.sln/TestBundleV2/AssemblyInfo.cs delete mode 100644 DotNetConnectors.sln/TestBundleV2/TestBundleV2.csproj delete mode 100644 DotNetConnectors.sln/TestBundleV2/TestConnector.cs delete mode 100644 DotNetConnectors.sln/build.xml delete mode 100644 DotNetConnectors.sln/server.pfx diff --git a/DotNetConnectors.sln/ActiveDirectoryConnector/ActiveDirectoryConfiguration.cs b/DotNetConnectors.sln/ActiveDirectoryConnector/ActiveDirectoryConfiguration.cs deleted file mode 100644 index 20d662b5..00000000 --- a/DotNetConnectors.sln/ActiveDirectoryConnector/ActiveDirectoryConfiguration.cs +++ /dev/null @@ -1,139 +0,0 @@ -/* - * ==================== - * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER. - * - * Copyright 2007-2008 Sun Microsystems, Inc. All rights reserved. - * - * The contents of this file are subject to the terms of the Common Development - * and Distribution License("CDDL") (the "License"). You may not use this file - * except in compliance with the License. - * - * You can obtain a copy of the License at - * http://IdentityConnectors.dev.java.net/legal/license.txt - * See the License for the specific language governing permissions and limitations - * under the License. - * - * When distributing the Covered Code, include this CDDL Header Notice in each file - * and include the License file at identityconnectors/legal/license.txt. - * If applicable, add the following below this CDDL Header, with the fields - * enclosed by brackets [] replaced by your own identifying information: - * "Portions Copyrighted [year] [name of copyright owner]" - * ==================== - */ -using System; -using System.Collections.Generic; -using System.Text; -using System.Diagnostics; -using Org.IdentityConnectors.Framework.Spi; -using Org.IdentityConnectors.Framework.Common.Exceptions; -using Org.IdentityConnectors.Framework.Spi.Operations; - -namespace Org.IdentityConnectors.ActiveDirectory -{ - public class ActiveDirectoryConfiguration : Org.IdentityConnectors.Framework.Spi.AbstractConfiguration - { - [ConfigurationProperty(Confidential = false, DisplayMessageKey = "display_DirectoryAdminName", - Required = true, HelpMessageKey = "help_DirectoryAdminName", Order = 1)] - public String DirectoryAdminName - { get; set; } - - [ConfigurationProperty(Confidential = true, DisplayMessageKey = "display_DirectoryAdminPassword", - Required = true, HelpMessageKey = "help_DirectoryAdminPassword", Order = 2)] - public String DirectoryAdminPassword - { get; set; } - - [ConfigurationProperty(Confidential = false, DisplayMessageKey = "display_ObjectClass", HelpMessageKey = "help_ObjectClass", Order = 3)] - public String ObjectClass - { get; set; } - - [ConfigurationProperty(Confidential = false, DisplayMessageKey = "display_SearchContainer", - Required=true, HelpMessageKey = "help_SearchContainer", Order = 4)] - public String SearchContainer - { get; set; } - - [ConfigurationProperty(Confidential = false, DisplayMessageKey = "display_CreateHomeDirectory", HelpMessageKey = "help_CreateHomeDirectory", Order = 5)] - public bool CreateHomeDirectory { get; set; } - - [ConfigurationProperty(Confidential = false, DisplayMessageKey = "display_LDAPHostName", HelpMessageKey = "help_LDAPHostName", Order = 6)] - public String LDAPHostName - { get; set; } - - [ConfigurationProperty(Confidential = false, DisplayMessageKey = "display_SearchChildDomains", HelpMessageKey = "help_SearchChildDomains", Order = 7)] - public bool SearchChildDomains { get; set; } - - [ConfigurationProperty(Confidential = false, DisplayMessageKey = "display_domainName", - Required = true, HelpMessageKey = "help_domainName", Order = 8)] - public String DomainName - { get; set; } - - [ConfigurationProperty(OperationTypes = new Type[] { typeof(SyncOp) }, Confidential = false, DisplayMessageKey = "display_SyncGlobalCatalogServer", HelpMessageKey = "help_SyncGlobalCatalogServer", Order = 9)] - public String SyncGlobalCatalogServer - { get; set; } - - [ConfigurationProperty(OperationTypes = new Type[] { typeof(SyncOp) }, Confidential = false, DisplayMessageKey = "display_SyncDomainController", HelpMessageKey = "help_SyncDomainController", Order=10)] - public String SyncDomainController - { get; set; } - - [ConfigurationProperty(OperationTypes = new Type[] { typeof(SyncOp) }, Confidential = false, DisplayMessageKey = "display_SyncSearchContext", HelpMessageKey = "help_SyncSearchContext", Order=11)] - public String SyncSearchContext - { get; set; } - - public ActiveDirectoryConfiguration() - { - DomainName = ""; - SearchContainer = ""; - DirectoryAdminName = "administrator"; - ObjectClass = "User"; - CreateHomeDirectory = true; - SearchChildDomains = false; - LDAPHostName = ""; - } - - public override void Validate() - { - String message = "Configuration errors: "; - Boolean foundError = false; - - // can't lookup the schema without the domain name - if ((DomainName == null) || (DomainName.Length == 0)) - { - message += ConnectorMessages.Format( - "confReqParam_domainName", "Domain name not supplied "); - foundError = true; - } - - if ((DirectoryAdminName == null) || (DirectoryAdminName.Length == 0)) - { - message += ConnectorMessages.Format( - "confReqParam_adminName", "Directory administrator name not supplied "); - foundError = true; - } - - if ((DirectoryAdminPassword == null) || (DirectoryAdminPassword.Length == 0)) - { - message += ConnectorMessages.Format( - "confReqParam_adminPass", "Directory administrator password not supplied "); - foundError = true; - } - - if ((ObjectClass == null) || (ObjectClass.Length == 0)) - { - message += ConnectorMessages.Format( - "confReqParam_objClass", "ObjectClass was not supplied "); - foundError = true; - } - - if ((SearchContainer == null) || (SearchContainer.Length == 0)) - { - message += ConnectorMessages.Format( - "confReqParam_searchContainer", "Search Container was not supplied "); - foundError = true; - } - - if (foundError) - { - throw new ConfigurationException(message); - } - } - } -} diff --git a/DotNetConnectors.sln/ActiveDirectoryConnector/ActiveDirectoryConnector.cs b/DotNetConnectors.sln/ActiveDirectoryConnector/ActiveDirectoryConnector.cs deleted file mode 100644 index c685ee84..00000000 --- a/DotNetConnectors.sln/ActiveDirectoryConnector/ActiveDirectoryConnector.cs +++ /dev/null @@ -1,1193 +0,0 @@ -/* - * ==================== - * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER. - * - * Copyright 2007-2008 Sun Microsystems, Inc. All rights reserved. - * - * The contents of this file are subject to the terms of the Common Development - * and Distribution License("CDDL") (the "License"). You may not use this file - * except in compliance with the License. - * - * You can obtain a copy of the License at - * http://IdentityConnectors.dev.java.net/legal/license.txt - * See the License for the specific language governing permissions and limitations - * under the License. - * - * When distributing the Covered Code, include this CDDL Header Notice in each file - * and include the License file at identityconnectors/legal/license.txt. - * If applicable, add the following below this CDDL Header, with the fields - * enclosed by brackets [] replaced by your own identifying information: - * "Portions Copyrighted [year] [name of copyright owner]" - * ==================== - */ -using System; -using System.Reflection; -using ActiveDs; -using System.Collections.Generic; -using System.Collections.ObjectModel; -using Org.IdentityConnectors.Common; -using Org.IdentityConnectors.Framework.Api.Operations; -using Org.IdentityConnectors.Framework.Spi; -using Org.IdentityConnectors.Framework.Spi.Operations; -using System.Diagnostics; -using Org.IdentityConnectors.Framework.Common.Objects; -using Org.IdentityConnectors.Framework.Common.Exceptions; -using System.DirectoryServices; -using System.DirectoryServices.ActiveDirectory; -using Org.IdentityConnectors.Framework.Common; -using System.Text; -using Org.IdentityConnectors.Common.Script; - -namespace Org.IdentityConnectors.ActiveDirectory -{ - public enum UpdateType { - ADD, - DELETE, - REPLACE - } - - - /// - /// The Active Directory Connector - /// - [ConnectorClass("connector_displayName", - typeof(ActiveDirectoryConfiguration), - MessageCatalogPaths = new String[]{"Org.IdentityConnectors.ActiveDirectory.Messages"} - )] - public class ActiveDirectoryConnector : CreateOp, Connector, SchemaOp, DeleteOp, - SearchOp, TestOp, UpdateAttributeValuesOp, ScriptOnResourceOp, SyncOp, - AuthenticateOp, AttributeNormalizer, PoolableConnector - { - public static IDictionary> AttributesReturnedByDefault = null; - - // special attribute names - public static readonly string ATT_CONTAINER = "ad_container"; - public static readonly string ATT_USER_PASSWORD = "userPassword"; - public static readonly string ATT_CN = "cn"; - public static readonly string ATT_OU = "ou"; - public static readonly string ATT_OBJECT_GUID = "objectGuid"; - public static readonly string ATT_IS_DELETED = "isDeleted"; - public static readonly string ATT_USN_CHANGED = "uSNChanged"; - public static readonly string ATT_DISTINGUISHED_NAME = "distinguishedName"; - public static readonly string ATT_SAMACCOUNT_NAME = "sAMAccountName"; - public static readonly string ATT_MEMBER = "member"; - public static readonly string ATT_MEMBEROF = "memberOf"; - public static readonly string ATT_HOME_DIRECTORY = "homeDirectory"; - public static readonly string ATT_OBJECT_SID = "objectSid"; - public static readonly string ATT_PWD_LAST_SET = "pwdLastSet"; - public static readonly string ATT_ACCOUNT_EXPIRES = "accountExpires"; - public static readonly string ATT_LOCKOUT_TIME = "lockoutTime"; - public static readonly string ATT_GROUP_TYPE = "groupType"; - public static readonly string ATT_DESCRIPTION = "description"; - public static readonly string ATT_SHORT_NAME = "name"; - public static readonly string ATT_DISPLAY_NAME = "displayName"; - public static readonly string ATT_USER_ACOUNT_CONTROL = "userAccountControl"; - public static readonly string OBJECTCLASS_OU = "Organizational Unit"; - - public static readonly ObjectClass ouObjectClass = new ObjectClass(OBJECTCLASS_OU); - private static readonly string OLD_SEARCH_FILTER_STRING = "Search Filter String"; - private static readonly string OLD_SEARCH_FILTER = "searchFilter"; - - ActiveDirectoryConfiguration _configuration = null; - ActiveDirectoryUtils _utils = null; - private static Schema _schema = null; - public ActiveDirectoryConnector() - { - // populate default attributes and Schema - Schema(); - } - - #region CreateOp Members - // implementation of CreateSpiOp - public virtual Uid Create(ObjectClass oclass, - ICollection attributes, OperationOptions options) - { - Uid uid = null; - - // I had lots of problems here. Here are the things - // that seemed to make everything work: - // - Create the object with the minimum data and commit it, - // then update the object with the rest of the info. - // - After updating an object and committing, be sure to - // do a refresh cache before continuing to use it. If - // not, it seems like multi-value attributes get hosed. - // - Group membership cannot be change by memberOf, but must - // be changed by changing the members property of the group - - Trace.TraceInformation("Create method"); - if (_configuration == null) - { - throw new ConfigurationException(_configuration.ConnectorMessages.Format( - "ex_ConnectorNotConfigured", "Connector has not been configured")); - } - Name nameAttribute = ConnectorAttributeUtil.GetNameFromAttributes(attributes); - if (nameAttribute == null) - { - throw new ConnectorException( - _configuration.ConnectorMessages.Format("ex_OperationalAttributeNull", - "The name operational attribute cannot be null")); - } - - String ldapContainerPath = ActiveDirectoryUtils.GetLDAPPath(_configuration.LDAPHostName, - ActiveDirectoryUtils.GetParentDn(nameAttribute.GetNameValue())); - String ldapEntryPath = ActiveDirectoryUtils.GetLDAPPath(_configuration.LDAPHostName, - nameAttribute.GetNameValue()); - - try - { - if (!DirectoryEntry.Exists(ldapContainerPath)) - { - throw new ConnectorException("Container does not exist"); - } - - // Get the correct container, and put the new user in it - DirectoryEntry containerDe = new DirectoryEntry(ldapContainerPath, - _configuration.DirectoryAdminName, _configuration.DirectoryAdminPassword); - DirectoryEntry newDe = containerDe.Children.Add( - ActiveDirectoryUtils.GetRelativeName(nameAttribute), - _utils.GetADObjectClass(oclass)); - - if (oclass.Equals(ObjectClass.GROUP)) - { - ConnectorAttribute groupAttribute = - ConnectorAttributeUtil.Find(ActiveDirectoryConnector.ATT_GROUP_TYPE, attributes); - if (groupAttribute != null) - { - int? groupType = ConnectorAttributeUtil.GetIntegerValue(groupAttribute); - if (groupType.HasValue) - { - newDe.Properties[ActiveDirectoryConnector.ATT_GROUP_TYPE].Value = groupType; - } - } - } - - newDe.CommitChanges(); - - // default to creating users enabled - if ((ObjectClass.ACCOUNT.Equals(oclass)) && - (ConnectorAttributeUtil.Find(OperationalAttributes.ENABLE_NAME, attributes) == null)) - { - attributes.Add(ConnectorAttributeBuilder.Build(OperationalAttributes.ENABLE_NAME, true)); - } - _utils.UpdateADObject(oclass, newDe, attributes, - UpdateType.REPLACE, _configuration); - Object guidValue = newDe.Properties["objectGUID"].Value; - if (guidValue != null) - { - // format the uid in the special way required for searching - String guidString = - ActiveDirectoryUtils.ConvertUIDBytesToGUIDString( - (Byte[])guidValue); - - Trace.TraceInformation("Created object with uid {0}", guidString); - uid = new Uid(guidString); - } - else - { - Trace.TraceError("Unable to find uid attribute for newly created object"); - } - - - } - catch (DirectoryServicesCOMException exception) - { - // have to make sure the new thing gets deleted in - // the case of error - Console.WriteLine("caught exception:" + exception); - Trace.TraceError(exception.Message); - throw; - } - - if (!oclass.Equals(ObjectClass.ACCOUNT)) - { - // uid will be the dn for non account objects - return new Uid(nameAttribute.GetNameValue()); - } - return uid; - } - - #endregion - - #region Connector Members - - // implementation of Connector - public virtual void Init(Configuration configuration) - { - Trace.TraceInformation("Active Directory Init method"); - _configuration = (ActiveDirectoryConfiguration)configuration; - _utils = new ActiveDirectoryUtils(_configuration); - } - - #endregion - - #region IDisposable Members - - public virtual void Dispose() - { - } - - #endregion - - protected ICollection GetDefaultAttributeListForObjectClass( - ObjectClass oclass, ObjectClassInfo oclassInfo) - { - ICollection defaultAttributeList = new List(); - - foreach (ConnectorAttributeInfo attInfo in oclassInfo.ConnectorAttributeInfos) - { - if (attInfo.IsReturnedByDefault) - { - defaultAttributeList.Add(attInfo.Name); - } - } - - return defaultAttributeList; - } - - #region SchemaOp Members - // implementation of SchemaSpiOp - public Schema Schema() - { - Trace.TraceInformation("Schema method"); - if (_schema != null) - { - Trace.TraceInformation("Returning cached schema"); - return _schema; - } - - SchemaBuilder schemaBuilder = - new SchemaBuilder(SafeType.Get(this)); - AttributesReturnedByDefault = new Dictionary>(); - - //iterate through supported object classes - foreach(ObjectClass oc in GetSupportedObjectClasses()) - { - ObjectClassInfo ocInfo = GetObjectClassInfo(oc); - Assertions.NullCheck(ocInfo, "ocInfo"); - - //populate the list of default attributes to get - AttributesReturnedByDefault.Add(oc, new HashSet()); - foreach (ConnectorAttributeInfo caInfo in ocInfo.ConnectorAttributeInfos) - { - if( caInfo.IsReturnedByDefault ) { - AttributesReturnedByDefault[oc].Add(caInfo.Name); - } - } - - //add object class to schema - schemaBuilder.DefineObjectClass(ocInfo); - - //add supported operations - IList> supportedOps = GetSupportedOperations(oc); - if (supportedOps != null) - { - foreach (SafeType op in supportedOps) - { - schemaBuilder.AddSupportedObjectClass(op, ocInfo); - } - } - - //remove unsupported operatons - IList> unSupportedOps = GetUnSupportedOperations(oc); - if (unSupportedOps != null) - { - foreach (SafeType op in unSupportedOps) - { - schemaBuilder.RemoveSupportedObjectClass(op, ocInfo); - } - } - } - Trace.TraceInformation("Finished retrieving schema"); - _schema = schemaBuilder.Build(); - Trace.TraceInformation("Returning schema"); - - return _schema; - } - - /// - /// Defines the supported object classes by the connector, used for schema building - /// - /// List of supported object classes - protected virtual ICollection GetSupportedObjectClasses() - { - IDictionary objectClassInfos = - CommonUtils.GetOCInfo("Org.IdentityConnectors.ActiveDirectory.ObjectClasses.xml"); - - return objectClassInfos.Keys; - } - - /// - /// Gets the object class info for specified object class, used for schema building - /// - /// ObjectClass to get info for - /// ObjectClass' ObjectClassInfo - protected virtual ObjectClassInfo GetObjectClassInfo(ObjectClass oc) - { - IDictionary objectClassInfos = - CommonUtils.GetOCInfo("Org.IdentityConnectors.ActiveDirectory.ObjectClasses.xml"); - - return objectClassInfos[oc]; - } - - /// - /// Gets the list of supported operations by the object class, used for schema building - /// - /// - /// - protected virtual IList> GetSupportedOperations(ObjectClass oc) - { - return null; - } - - /// - /// Gets the list of UNsupported operations by the object class, used for schema building - /// - /// - /// - protected virtual IList> GetUnSupportedOperations(ObjectClass oc) - { - if (oc.Equals(ObjectClass.GROUP) || oc.Equals(ouObjectClass)) - { - return new List> { - SafeType.Get(), - SafeType.Get()}; - } - - return null; - } - - - private ActiveDirectorySchema GetADSchema() - { - String serverName = _configuration.LDAPHostName; - Forest forest = null; - - if ((serverName == null) || (serverName.Length == 0)) - { - // get the active directory schema - DirectoryContext context = new DirectoryContext( - DirectoryContextType.Domain, - _configuration.DomainName, - _configuration.DirectoryAdminName, - _configuration.DirectoryAdminPassword); - DomainController dc = DomainController.FindOne(context); - forest = dc.Forest; - } - else - { - DirectoryContext context = new DirectoryContext( - DirectoryContextType.DirectoryServer, - _configuration.LDAPHostName, - _configuration.DirectoryAdminName, - _configuration.DirectoryAdminPassword); - forest = Forest.GetForest(context); - } - - ActiveDirectorySchema ADSchema = forest.Schema; - return ADSchema; - } - - #endregion - - #region SearchOp Members - - // implementation of SearchSpiOp - public virtual Org.IdentityConnectors.Framework.Common.Objects.Filters.FilterTranslator CreateFilterTranslator(ObjectClass oclass, OperationOptions options) - { - return new ActiveDirectoryFilterTranslator(); - } - - // implementation of SearchSpiOp - public virtual void ExecuteQuery(ObjectClass oclass, string query, - ResultsHandler handler, OperationOptions options) - { - try - { - bool useGC = false; - if (_configuration.SearchChildDomains) - { - useGC = true; - } - - IDictionarysearchOptions = options.Options; - - SearchScope searchScope = GetADSearchScopeFromOptions(options); - string searchContext = GetADSearchContextFromOptions(options); - - // for backward compatibility, support old query style from resource adapters - // but log a warning - if((query == null) || (query.Length == 0)) { - if ((options != null) && (options.Options != null)) - { - Object oldStyleQuery = null; - if (options.Options.Keys.Contains(OLD_SEARCH_FILTER_STRING)) - { - oldStyleQuery = options.Options[OLD_SEARCH_FILTER_STRING]; - } - else if (options.Options.Keys.Contains(OLD_SEARCH_FILTER)) - { - oldStyleQuery = options.Options[OLD_SEARCH_FILTER]; - } - if ((oldStyleQuery != null) && (oldStyleQuery is string)) - { - query = (string)oldStyleQuery; - Trace.TraceWarning(_configuration.ConnectorMessages.Format( - "warn_CompatibilityModeQuery", - "Using Identity Manger Resource Adapter style query ''{0}''. This should be updated to use the new connector query syntax.", - ((query != null) && (query.Length > 0)) ? query : "")); - } - } - } - - ExecuteQuery(oclass, query, handler, options, - false, null, _configuration.LDAPHostName, useGC, searchContext, searchScope); - } - catch (Exception e) - { - Trace.TraceError(String.Format("Caught Exception: {0}", e)); - throw; - } - } - - public string GetADSearchContextFromOptions(OperationOptions options) - { - if (options != null) - { - QualifiedUid qUid = options.getContainer; - if (qUid != null) - { - return ConnectorAttributeUtil.GetStringValue(qUid.Uid); - } - } - - return _configuration.SearchContainer; - } - - public SearchScope GetADSearchScopeFromOptions(OperationOptions options) - { - if (options != null) - { - string scope = options.Scope; - if (scope != null) - { - if (scope.Equals(OperationOptions.SCOPE_ONE_LEVEL)) - { - return SearchScope.OneLevel; - } - else if (scope.Equals(OperationOptions.SCOPE_SUBTREE)) - { - return SearchScope.Subtree; - } - else if (scope.Equals(OperationOptions.SCOPE_OBJECT)) - { - return SearchScope.Base; - } - else - { - throw new ConnectorException(_configuration.ConnectorMessages.Format( - "ex_invalidSearchScope", "An invalid search scope was specified: {0}", scope)); - } - } - } - - // default value is subtree; - return SearchScope.Subtree; - } - - // this is used by the ExecuteQuery method of SearchSpiOp, and - // by the SyncSpiOp - private void ExecuteQuery(ObjectClass oclass, string query, - ResultsHandler handler, OperationOptions options, bool includeDeleted, - SortOption sortOption, string serverName, bool useGlobalCatalog, - string searchRoot, SearchScope searchScope) - { - Trace.TraceInformation("Search: modifying query"); - StringBuilder fullQueryBuilder = new StringBuilder(); - if (query == null) - { - fullQueryBuilder.Append("(objectclass="); - fullQueryBuilder.Append(_utils.GetADObjectClass(oclass)); - fullQueryBuilder.Append(")"); - } - else - { - fullQueryBuilder.Append("(&(objectclass="); - fullQueryBuilder.Append(_utils.GetADObjectClass(oclass)); - fullQueryBuilder.Append(")"); - fullQueryBuilder.Append(query); - fullQueryBuilder.Append(")"); - } - query = fullQueryBuilder.ToString(); - - if (query == null) - { - Trace.TraceInformation("query is null"); - } - else - { - Trace.TraceInformation("Setting search string to \'{0}\'", query); - } - - string path; - path = GetSearchContainerPath(useGlobalCatalog, serverName, searchRoot); - - Trace.TraceInformation("Search: Getting root node for search"); - DirectoryEntry searchRootEntry = new DirectoryEntry(path, - _configuration.DirectoryAdminName, _configuration.DirectoryAdminPassword); - DirectorySearcher searcher = new DirectorySearcher(searchRootEntry, query); - searcher.PageSize = 1000; - searcher.SearchScope = searchScope; - - if (includeDeleted) - { - searcher.Tombstone = true; - } - - if (sortOption != null) - { - searcher.Sort = sortOption; - } - - Trace.TraceInformation("Search: Performing query"); - SearchResultCollection resultSet = searcher.FindAll(); - Trace.TraceInformation("Search: found {0} results", resultSet.Count); - ICollection attributesToReturn = null; - if (resultSet.Count > 0) - { - attributesToReturn = GetAttributesToReturn(oclass, options); - } - - Trace.TraceInformation("Building connectorObjects"); - foreach (SearchResult result in resultSet) - { - try - { - Trace.TraceInformation("Found object {0}", result.Path); - ConnectorObjectBuilder builder = new ConnectorObjectBuilder(); - builder.ObjectClass = oclass; - - bool isDeleted = false; - if (result.Properties.Contains(ATT_IS_DELETED)) - { - ResultPropertyValueCollection pvc = result.Properties[ATT_IS_DELETED]; - if (pvc.Count > 0) - { - isDeleted = (bool)pvc[0]; - } - } - - if (isDeleted.Equals(false)) - { - // if we were using the global catalog (gc), we have to - // now retrieve the object from a domain controller (dc) - // because the gc may not have have all of the attributes, - // depending on which attributes are replicated to the gc. - SearchResult savedGcResult = null; - SearchResult savedDcResult = result; - if (useGlobalCatalog) - { - savedGcResult = result; - - String dcSearchRootPath = ActiveDirectoryUtils.GetLDAPPath( - _configuration.LDAPHostName, searchRoot); - - DirectoryEntry dcSearchRoot = new DirectoryEntry(dcSearchRootPath, - _configuration.DirectoryAdminName, _configuration.DirectoryAdminPassword); - - string dcSearchQuery = String.Format("(" + ATT_DISTINGUISHED_NAME + "={0})", - ActiveDirectoryUtils.GetDnFromPath(savedGcResult.Path)); - DirectorySearcher dcSearcher = - new DirectorySearcher(dcSearchRoot, dcSearchQuery); - savedDcResult = dcSearcher.FindOne(); - if (savedDcResult == null) - { - // in this case, we found it in the gc, but not in the - // domain controller. We cant return a result. The could - // be a case where the account was deleted, but the global - // catalog doesn't know it yet - Trace.TraceWarning(String.Format("Found result in global catalog " + - "for ''{0}'', but could not retrieve the entry from the domain " + - "controller", savedGcResult.Path)); - continue; - } - } - - foreach (string attributeName in attributesToReturn) - { - SearchResult savedResults = savedDcResult; - // if we are using the global catalog, we had to get the - // dc's version of the directory entry, but for usnchanged, - // we need the gc version of it - if (useGlobalCatalog && attributeName.Equals(ATT_USN_CHANGED, - StringComparison.CurrentCultureIgnoreCase)) - { - savedResults = savedGcResult; - } - - AddAttributeIfNotNull(builder, - _utils.GetConnectorAttributeFromADEntry( - oclass, attributeName, savedResults)); - } - } - else - { - // get uid - AddAttributeIfNotNull(builder, - _utils.GetConnectorAttributeFromADEntry( - oclass, Uid.NAME, result)); - - // get uid - AddAttributeIfNotNull(builder, - _utils.GetConnectorAttributeFromADEntry( - oclass, Name.NAME, result)); - - // get usnchanged - AddAttributeIfNotNull(builder, - _utils.GetConnectorAttributeFromADEntry( - oclass, ATT_USN_CHANGED, result)); - - // add isDeleted - builder.AddAttribute(ATT_IS_DELETED, true); - } - - String msg = String.Format("Returning ''{0}''", - (result.Path != null) ? result.Path : ""); - Trace.TraceInformation(msg); - handler(builder.Build()); - } - catch (DirectoryServicesCOMException e) - { - // there is a chance that we found the result, but - // in the mean time, it was deleted. In that case, - // log an error and continue - Trace.TraceWarning("Error in creating ConnectorObject from DirectoryEntry. It may have been deleted during search."); - Trace.TraceWarning(e.Message); - } - } - } - - private string GetSearchContainerPath() - { - return GetSearchContainerPath(UseGlobalCatalog(), _configuration.LDAPHostName, _configuration.SearchContainer); - } - - private string GetSearchContainerPath(bool useGC, string hostname, string searchContainer) - { - String path; - - if (useGC) - { - path = ActiveDirectoryUtils.GetGCPath(hostname, searchContainer); - } - else - { - path = ActiveDirectoryUtils.GetLDAPPath(hostname, searchContainer); - } - - return path; - } - - private ICollection GetAttributesToReturn(ObjectClass oclass, OperationOptions options) - { - ICollection attributeNames = null; - - if ((options.AttributesToGet != null) && (options.AttributesToGet.Length > 0)) - { - attributeNames = new HashSet(options.AttributesToGet); - } - else - { - attributeNames = AttributesReturnedByDefault[oclass]; - } - - // Uid and name are always returned - attributeNames.Add(Uid.NAME); - attributeNames.Add(Name.NAME); - return attributeNames; - } - - private void AddAttributeIfNotNull(ConnectorObjectBuilder builder, - ConnectorAttribute attribute) - { - if (attribute != null) - { - builder.AddAttribute(attribute); - } - } - - #endregion - - #region TestOp Members - - public virtual void Test() - { - _configuration.Validate(); - - bool objectFound = true; - // now make sure they specified a valid value for the User Object Class - ActiveDirectorySchema ADSchema = GetADSchema(); - ActiveDirectorySchemaClass ADSchemaClass = null; - try - { - ADSchemaClass = ADSchema.FindClass(_configuration.ObjectClass); - - } - catch (ActiveDirectoryObjectNotFoundException exception) - { - objectFound = false; - } - if ((!objectFound) || (ADSchemaClass == null)) - { - throw new ConnectorException( - _configuration.ConnectorMessages.Format( - "ex_InvalidObjectClassInConfiguration", - "Invalid Object Class was specified in the connector configuration. Object Class \'{0}\' was not found in Active Directory", - _configuration.ObjectClass)); - } - - // see if search container is valid - if (!DirectoryEntry.Exists(GetSearchContainerPath())) - { - throw new ConnectorException( - _configuration.ConnectorMessages.Format( - "ex_InvalidSearchContainerInConfiguration", - "An invalid search container was supplied: {0}", - _configuration.SearchContainer)); - } - } - - #endregion - - #region AdvancedUpdateOp Members - public Uid Update(ObjectClass objclass, Uid uid, ICollection attrs, OperationOptions options) { - return Update(UpdateType.REPLACE,objclass,ConnectorAttributeUtil.AddUid(attrs,uid),options); - } - - public Uid AddAttributeValues(ObjectClass objclass, - Uid uid, - ICollection valuesToAdd, - OperationOptions options) { - return Update(UpdateType.ADD,objclass,ConnectorAttributeUtil.AddUid(valuesToAdd, uid),options); - } - - public Uid RemoveAttributeValues(ObjectClass objclass, - Uid uid, - ICollection valuesToRemove, - OperationOptions options) { - return Update(UpdateType.DELETE,objclass,ConnectorAttributeUtil.AddUid(valuesToRemove, uid),options); - } - - // implementation of AdvancedUpdateSpiOp - public virtual Uid Update(UpdateType type, ObjectClass oclass, - ICollection attributes, OperationOptions options) - { - Uid updatedUid = null; - - Trace.TraceInformation("Update method"); - if (_configuration == null) - { - throw new ConfigurationException(_configuration.ConnectorMessages.Format( - "ex_ConnectorNotConfigured", "Connector has not been configured")); - } - - updatedUid = ConnectorAttributeUtil.GetUidAttribute(attributes); - if (updatedUid == null) - { - throw new ConnectorException(_configuration.ConnectorMessages.Format( - "ex_UIDNotPresent", "Uid was not present")); - } - - DirectoryEntry updateEntry = - ActiveDirectoryUtils.GetDirectoryEntryFromUid(_configuration.LDAPHostName, updatedUid, - _configuration.DirectoryAdminName, _configuration.DirectoryAdminPassword); - - _utils.UpdateADObject(oclass, updateEntry, - attributes, type, _configuration); - - if(!ObjectClass.ACCOUNT.Equals(oclass)) { - // other objects use dn as guid for idm backward compatibility - updatedUid = new Uid((string)updateEntry.Properties["distinguishedName"][0]); - } - return updatedUid; - } - - #endregion - - #region DeleteOp Members - - // implementation of DeleteSpiOp - public virtual void Delete(ObjectClass objClass, Uid uid, OperationOptions options) - { - DirectoryEntry de = null; - try - { - de = ActiveDirectoryUtils.GetDirectoryEntryFromUid(_configuration.LDAPHostName, uid, - _configuration.DirectoryAdminName, _configuration.DirectoryAdminPassword); - } - catch (System.DirectoryServices.DirectoryServicesCOMException e) - { - // if it's not found, throw that, else just rethrow - if (e.ErrorCode == -2147016656) - { - throw new UnknownUidException(); - } - throw; - } - - if (objClass.Equals(ObjectClass.ACCOUNT)) - { - // if it's a user account, get the parent's child list - // and remove this entry - DirectoryEntry parent = de.Parent; - parent.Children.Remove(de); - } - else if (objClass.Equals(ObjectClass.GROUP) || objClass.Equals(ouObjectClass)) - { - // if it's a group or ou (container), delete this - // entry and all it's children - de.DeleteTree(); - } - else - { - throw new ConnectorException(_configuration.ConnectorMessages.Format( - "ex_DeleteNotSupported", "Delete is not supported for ObjectClass {0}", - objClass.GetObjectClassValue())); - } - } - - #endregion - - - #region ScriptOnResourceOp Members - - public object RunScriptOnResource(ScriptContext request, OperationOptions options) - { - IDictionary arguments = new Dictionary(request.ScriptArguments); - // per Will D. batch scripts need special parameters set, but other scripts - // don't. He doesn't feel that this can be changed at present, so setting - // the parameters here. - - // Cant find a constant for the string to represent the shell script executor, - // replace embedded string constant if one turns up. - if (request.ScriptLanguage.Equals("Shell", StringComparison.CurrentCultureIgnoreCase)) - { - if (options.RunAsUser != null) - { - arguments.Add("USERNAME", options.RunAsUser); - arguments.Add("PASSWORD", - options.RunWithPassword.ToSecureString()); - } - } - - - ScriptExecutorFactory factory = ScriptExecutorFactory.NewInstance(request.ScriptLanguage); - ScriptExecutor executor = factory.NewScriptExecutor(new Assembly[0],request.ScriptText, true); - return executor.Execute(arguments); - } - - #endregion - - #region SyncOp Members - - // implementation of SyncSpiOp - public class SyncResults - { - SyncResultsHandler _syncResultsHandler; - ActiveDirectorySyncToken _adSyncToken; - ActiveDirectoryConfiguration _configuration; - - internal SyncResults(SyncResultsHandler syncResultsHandler, - ActiveDirectorySyncToken adSyncToken, ActiveDirectoryConfiguration configuration) { - _syncResultsHandler = syncResultsHandler; - _adSyncToken = adSyncToken; - _configuration = configuration; - } - - public bool SyncHandler(ConnectorObject obj) - { - SyncDeltaBuilder builder = new SyncDeltaBuilder(); - ICollection attrs = new HashSet(); - foreach(ConnectorAttribute attribute in obj.GetAttributes()) { - // add all attributes to the object except the - // one used to flag deletes. - if (!attribute.Name.Equals(ATT_IS_DELETED)) - { - attrs.Add(attribute); - } - } - - ConnectorObjectBuilder coBuilder = new ConnectorObjectBuilder(); - coBuilder.SetName(obj.Name); - coBuilder.SetUid(obj.Uid); - coBuilder.AddAttributes(attrs); - builder.Object = coBuilder.Build(); - - ConnectorAttribute tokenAttr = - ConnectorAttributeUtil.Find(ATT_USN_CHANGED, obj.GetAttributes()); - if(tokenAttr == null) { - string msg = _configuration.ConnectorMessages.Format("ex_missingSyncAttribute", - "Attribute {0} is not present in connector object. Cannot proceed with Synchronization", - ATT_USN_CHANGED); - Trace.TraceError(msg); - throw new ConnectorException(msg); - } - long tokenUsnValue = (long)ConnectorAttributeUtil.GetSingleValue(tokenAttr); - - bool? isDeleted = false; - ConnectorAttribute isDeletedAttr = - ConnectorAttributeUtil.Find(ATT_IS_DELETED, obj.GetAttributes()); - if (isDeletedAttr != null) - { - isDeleted = (bool?)ConnectorAttributeUtil.GetSingleValue(isDeletedAttr); - _adSyncToken.LastDeleteUsn = tokenUsnValue; - } - else - { - _adSyncToken.LastModifiedUsn = tokenUsnValue; - } - - builder.Token = _adSyncToken.GetSyncToken(); - - if ((isDeleted != null) && (isDeleted.Equals(true))) - { - builder.DeltaType = SyncDeltaType.DELETE; - } - else - { - builder.DeltaType = SyncDeltaType.CREATE_OR_UPDATE; - } - - builder.Uid = obj.Uid; - _syncResultsHandler(builder.Build()); - return true; - } - } - - public virtual void Sync(ObjectClass objClass, SyncToken token, - SyncResultsHandler handler, OperationOptions options) - { - if (!ObjectClass.ACCOUNT.Equals(objClass)) - { - throw new ConnectorException(_configuration.ConnectorMessages.Format( - "ex_SyncNotAvailable", - "Sync operation is not available for ObjectClass {0}", - objClass.GetObjectClassValue())); - } - - String serverName = GetSyncServerName(); - - ActiveDirectorySyncToken adSyncToken = - new ActiveDirectorySyncToken(token, serverName, UseGlobalCatalog()); - - string modifiedQuery = GetSyncUpdateQuery(adSyncToken); - string deletedQuery = GetSyncDeleteQuery(adSyncToken); - - OperationOptionsBuilder builder = new OperationOptionsBuilder(); - SyncResults syncResults = new SyncResults(handler, adSyncToken, _configuration); - - // find modified usn's - ExecuteQuery(objClass, modifiedQuery, syncResults.SyncHandler, builder.Build(), - false, new SortOption(ATT_USN_CHANGED, SortDirection.Ascending), - serverName, UseGlobalCatalog(), _configuration.SyncSearchContext, SearchScope.Subtree); - - // find deleted usn's - DirectoryContext domainContext = new DirectoryContext(DirectoryContextType.DirectoryServer, - serverName, - _configuration.DirectoryAdminName, - _configuration.DirectoryAdminPassword); - Domain domain = Domain.GetDomain(domainContext); - String deleteObjectsSearchRoot = null; - if (domain != null) - { - DirectoryEntry domainDe = domain.GetDirectoryEntry(); - deleteObjectsSearchRoot = ActiveDirectoryUtils.GetDnFromPath(domainDe.Path); - } - ExecuteQuery(objClass, deletedQuery, syncResults.SyncHandler, builder.Build(), - true, new SortOption(ATT_USN_CHANGED, SortDirection.Ascending), - serverName, UseGlobalCatalog(), deleteObjectsSearchRoot, SearchScope.Subtree); - - } - - public virtual SyncToken GetLatestSyncToken(ObjectClass objectClass) - { - string serverName = GetSyncServerName(); - long highestCommittedUsn = 0; - bool useGlobalCatalog = UseGlobalCatalog(); - if (useGlobalCatalog) - { - DirectoryContext context = new DirectoryContext(DirectoryContextType.DirectoryServer, - serverName, _configuration.DirectoryAdminName, _configuration.DirectoryAdminPassword); - GlobalCatalog gc = GlobalCatalog.GetGlobalCatalog(context); - highestCommittedUsn = gc.HighestCommittedUsn; - } - else - { - DirectoryContext context = new DirectoryContext(DirectoryContextType.DirectoryServer, - serverName, _configuration.DirectoryAdminName, _configuration.DirectoryAdminPassword); - DomainController dc = DomainController.GetDomainController(context); - highestCommittedUsn = dc.HighestCommittedUsn; - } - - ActiveDirectorySyncToken token = - new ActiveDirectorySyncToken("", serverName, useGlobalCatalog); - token.LastDeleteUsn = highestCommittedUsn; - token.LastModifiedUsn = highestCommittedUsn; - return token.GetSyncToken(); - } - - string GetSyncServerName() - { - string serverName = null; - - if (UseGlobalCatalog()) - { - serverName = _configuration.SyncGlobalCatalogServer; - } - else - { - serverName = _configuration.SyncDomainController; - } - - if ((serverName == null) || (serverName.Length == 0)) - { - Trace.TraceWarning("No server was configured for synchronization, so picking one. You should configure a server for best performance."); - // we have to know which server we are working against, - // so find one. - if (UseGlobalCatalog()) - { - DirectoryContext context = new DirectoryContext( - DirectoryContextType.Forest, _configuration.DomainName, - _configuration.DirectoryAdminName, - _configuration.DirectoryAdminPassword); - GlobalCatalog gc = GlobalCatalog.FindOne(context); - _configuration.SyncGlobalCatalogServer = gc.ToString(); - serverName = _configuration.SyncGlobalCatalogServer; - } - else - { - DirectoryContext context = new DirectoryContext( - DirectoryContextType.Domain, _configuration.DomainName, - _configuration.DirectoryAdminName, - _configuration.DirectoryAdminPassword); - DomainController controller = DomainController.FindOne(context); - _configuration.SyncDomainController = controller.ToString(); - serverName = _configuration.SyncDomainController; - } - } - return serverName; - } - - bool UseGlobalCatalog() - { - return (_configuration.SearchChildDomains); - } - - String GetSyncUpdateQuery(ActiveDirectorySyncToken adSyncToken) - { - string modifiedQuery = null; - - // if the token is not null, we may be able to start from - // the usn contained there - if (adSyncToken != null) - { - modifiedQuery = string.Format("(!({0}<={1}))", ATT_USN_CHANGED, adSyncToken.LastModifiedUsn); - } - - return modifiedQuery; - } - - String GetSyncDeleteQuery(ActiveDirectorySyncToken adSyncToken) - { - string deletedQuery = null; - - // if the token is not null, we may be able to start from - // the usn contained there - if (adSyncToken != null) - { - deletedQuery = string.Format("(&(!({0}<={1}))(isDeleted=TRUE))", ATT_USN_CHANGED, adSyncToken.LastDeleteUsn); - } - else - { - deletedQuery = string.Format("(isDeleted=TRUE)"); - } - - return deletedQuery; - } - - #endregion - - #region AuthenticateOp Members - - public Uid Authenticate(ObjectClass objectClass, string username, - Org.IdentityConnectors.Common.Security.GuardedString password, - OperationOptions options) - { - PasswordChangeHandler handler = new PasswordChangeHandler(_configuration); - return handler.Authenticate(username, password); - } - - #endregion - - #region AttributeNormalizer Members - - public virtual ConnectorAttribute NormalizeAttribute(ObjectClass oclass, ConnectorAttribute attribute) - { - // if this gets big, use delegates, but for now, just - // handle individual attributes; - if (attribute is Uid) - { - String uidValue = ((Uid)attribute).GetUidValue(); - // convert to upper case - if (uidValue != null) - { - StringBuilder normalizedUidValue = new StringBuilder(); - - if (oclass.Equals(ObjectClass.ACCOUNT)) - { - // convert to upper case - uidValue = uidValue.ToUpper(); - - // now remove spaces - foreach (Char nextChar in uidValue) - { - if (!nextChar.Equals(" ")) - { - normalizedUidValue.Append(nextChar); - } - } - - return new Uid(normalizedUidValue.ToString()); - } - else - { - // the uid is a dn - return new Uid(ActiveDirectoryUtils.NormalizeLdapString(uidValue)); - } - } - else - { - return attribute; - } - } - else if (attribute is Name) - { - String nameValue = ((Name)attribute).GetNameValue(); - return ConnectorAttributeBuilder.Build(attribute.Name, - ActiveDirectoryUtils.NormalizeLdapString(nameValue)); - } - - return attribute; - } - - #endregion - - #region PoolableConnector Members - - public void CheckAlive() - { - return; - } - - #endregion - } -} diff --git a/DotNetConnectors.sln/ActiveDirectoryConnector/ActiveDirectoryConnector.csproj b/DotNetConnectors.sln/ActiveDirectoryConnector/ActiveDirectoryConnector.csproj deleted file mode 100644 index 2ffc1c15..00000000 --- a/DotNetConnectors.sln/ActiveDirectoryConnector/ActiveDirectoryConnector.csproj +++ /dev/null @@ -1,120 +0,0 @@ - - - - {BDF495CA-0FCD-4E51-A871-D467CDE3B43E} - Debug - AnyCPU - Library - Org.IdentityConnectors.ActiveDirectory - ActiveDirectory.Connector - v3.5 - - - prompt - 4 - AnyCPU - bin\Debug\ - True - Full - False - True - DEBUG;TRACE - - - pdbonly - bin\Release\ - TRACE - prompt - 4 - AnyCPU - False - True - False - - - - - - 3.5 - - - - - 3.5 - - - - 3.5 - - - - - - Code - - - - - - - - - - - - - - {F140E8DA-52B4-4159-992A-9DA10EA8EEFB} - Common - - - {8B24461B-456A-4032-89A1-CD418F7B5B62} - Framework - - - {97d25db0-0363-11cf-abc4-02608c9e7553} - 1 - 0 - 0 - tlbimp - False - - - {97d25db0-0363-11cf-abc4-02608c9e7553} - 1 - 0 - 0 - tlbimp - False - - - - - Designer - - - - - - diff --git a/DotNetConnectors.sln/ActiveDirectoryConnector/ActiveDirectoryFilterTranslator.cs b/DotNetConnectors.sln/ActiveDirectoryConnector/ActiveDirectoryFilterTranslator.cs deleted file mode 100644 index 2b0978d7..00000000 --- a/DotNetConnectors.sln/ActiveDirectoryConnector/ActiveDirectoryFilterTranslator.cs +++ /dev/null @@ -1,479 +0,0 @@ -/* - * ==================== - * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER. - * - * Copyright 2007-2008 Sun Microsystems, Inc. All rights reserved. - * - * The contents of this file are subject to the terms of the Common Development - * and Distribution License("CDDL") (the "License"). You may not use this file - * except in compliance with the License. - * - * You can obtain a copy of the License at - * http://IdentityConnectors.dev.java.net/legal/license.txt - * See the License for the specific language governing permissions and limitations - * under the License. - * - * When distributing the Covered Code, include this CDDL Header Notice in each file - * and include the License file at identityconnectors/legal/license.txt. - * If applicable, add the following below this CDDL Header, with the fields - * enclosed by brackets [] replaced by your own identifying information: - * "Portions Copyrighted [year] [name of copyright owner]" - * ==================== - */ -using System; -using System.Collections.Generic; -using System.Linq; -using System.Text; -using Org.IdentityConnectors.Framework.Common.Objects.Filters; -using Org.IdentityConnectors.Framework.Common.Objects; -using Org.IdentityConnectors.Common; -using System.Diagnostics; -using Org.IdentityConnectors.Framework.Common.Exceptions; - -namespace Org.IdentityConnectors.ActiveDirectory -{ - /// - /// This was taken from the LDAP filter translator (java) and ported to - /// C#. There are a few changes, but not many ... that will change over - /// time of course. - /// - public class ActiveDirectoryFilterTranslator : AbstractFilterTranslator - { - protected override String CreateAndExpression(String leftExpression, - String rightExpression) { - StringBuilder builder = new StringBuilder(); - builder.Append("(&"); - builder.Append(leftExpression); - builder.Append(rightExpression); - builder.Append(')'); - return builder.ToString(); - } - - protected override String CreateOrExpression(String leftExpression, - String rightExpression) { - StringBuilder builder = new StringBuilder(); - builder.Append("(|"); - builder.Append(leftExpression); - builder.Append(rightExpression); - builder.Append(')'); - return builder.ToString(); - } - - protected override String CreateContainsExpression(ContainsFilter filter, - Boolean not) { - String[] attrNames = GetLdapNamesForAttribute(filter.GetAttribute()); - if (attrNames == null) { - return null; - } - - StringBuilder builder = new StringBuilder(); - if (not) { - builder.Append("!("); - } - if (attrNames.Length == 1) { - builder.Append('('); - builder.Append(attrNames[0]); - builder.Append("=*"); - int len = builder.Length; - GetLdapFilterValue(builder, attrNames[0], filter.GetValue()); - // Build (attr=*) rather than (attr=**) for zero-length values. - if (builder.Length != len) { - builder.Append('*'); - } - builder.Append(')'); - } else { - builder.Append("(|"); - foreach (String attrName in attrNames) { - builder.Append('('); - builder.Append(attrName); - builder.Append("=*"); - int len = builder.Length; - GetLdapFilterValue(builder, attrName, filter.GetValue()); - // Build (attr=*) rather than (attr=**) for zero-length values. - if (builder.Length != len) { - builder.Append('*'); - } - builder.Append(')'); - } - builder.Append(')'); - } - if (not) { - builder.Append(')'); - } - return builder.ToString(); - } - - protected override String CreateStartsWithExpression(StartsWithFilter filter, - Boolean not) { - String[] attrNames = GetLdapNamesForAttribute(filter.GetAttribute()); - if (attrNames == null) { - return null; - } - - StringBuilder builder = new StringBuilder(); - if (not) { - builder.Append("!("); - } - if (attrNames.Length == 1) { - builder.Append('('); - builder.Append(attrNames[0]); - builder.Append('='); - GetLdapFilterValue(builder, attrNames[0], filter.GetValue()); - builder.Append("*)"); - } else { - builder.Append("(|"); - foreach (String attrName in attrNames) { - builder.Append('('); - builder.Append(attrName); - builder.Append('='); - GetLdapFilterValue(builder, attrName, filter.GetValue()); - builder.Append("*)"); - } - builder.Append(')'); - } - if (not) { - builder.Append(')'); - } - return builder.ToString(); - } - - protected override String CreateEndsWithExpression(EndsWithFilter filter, - Boolean not) { - String[] attrNames = GetLdapNamesForAttribute(filter.GetAttribute()); - if (attrNames == null) { - return null; - } - - StringBuilder builder = new StringBuilder(); - if (not) { - builder.Append("!("); - } - if (attrNames.Length == 1) { - builder.Append('('); - builder.Append(attrNames[0]); - builder.Append("=*"); - GetLdapFilterValue(builder, attrNames[0], filter.GetValue()); - builder.Append(')'); - } else { - builder.Append("(|"); - foreach (String attrName in attrNames) { - builder.Append('('); - builder.Append(attrName); - builder.Append("=*"); - GetLdapFilterValue(builder, attrName, filter.GetValue()); - builder.Append(')'); - } - builder.Append(')'); - } - if (not) { - builder.Append(')'); - } - return builder.ToString(); - } - - protected override String CreateEqualsExpression(EqualsFilter filter, Boolean not) { - // The LDAP equality filter matches any one attribute value, - // whereas the connector EqualsFilter matches an attribute and - // its values exactly. - if (not) { - return null; - } - - ConnectorAttribute attr = filter.GetAttribute(); - // if there is only one thing to search on, and it's - // a uid we need to convert the uid to something we - // can search on. NOTE: only handling the case where - // we are doing an equality search, and only one item - // is in the equality search ... It's all that makes - // sense for uid. - if (attr is Uid) - { - String attrValue = ((Uid)attr).GetUidValue(); - if (LooksLikeGUID(attrValue)) - { - String searchGuid = GetUidSearchString(((Uid)attr).GetUidValue()); - attr = new Uid(searchGuid); - } else { - attr = new Name(attrValue); - } - - } - - - String[] attrNames = GetLdapNamesForAttribute(attr); - if (attrNames == null) { - return null; - } - - StringBuilder builder = new StringBuilder(); - - if (attr.Value == null) { - return null; - } - if (attr.Value.Count == 1) { - BuildEqualityFilter(builder, attrNames, - attr.Value[0]); - } else { - builder.Append("(&"); - foreach (Object value in attr.Value) { - BuildEqualityFilter(builder, attrNames, value); - } - builder.Append(')'); - } - - return builder.ToString(); - } - - protected override String CreateGreaterThanExpression(GreaterThanFilter filter, - Boolean not) { - // Note that (!(a > X)) is only the same as (a <= X) if every object - // has a value of a. - if (not) { - return null; - } - - String[] attrNames = GetLdapNamesForAttribute(filter.GetAttribute()); - if (attrNames == null) { - return null; - } - - StringBuilder builder = new StringBuilder(); - BuildGreaterOrEqualFilter(builder, attrNames, filter.GetValue()); - return builder.ToString(); - } - - protected override String CreateGreaterThanOrEqualExpression( - GreaterThanOrEqualFilter filter, Boolean not) { - String[] attrNames = GetLdapNamesForAttribute(filter.GetAttribute()); - if (attrNames == null) { - return null; - } - - StringBuilder builder = new StringBuilder(); - if (not) { - builder.Append("!("); - } - BuildGreaterOrEqualFilter(builder, attrNames, filter.GetValue()); - if (not) { - builder.Append(')'); - } - return builder.ToString(); - } - - protected override String CreateLessThanExpression(LessThanFilter filter, - Boolean not) { - // Note that (!(a < X)) is only the same as (a >= X) if every object - // has a value of a. - if (not) { - return null; - } - - String[] attrNames = GetLdapNamesForAttribute(filter.GetAttribute()); - if (attrNames == null) { - return null; - } - - StringBuilder builder = new StringBuilder(); - BuildLessOrEqualFilter(builder, attrNames, filter.GetValue()); - return builder.ToString(); - } - - protected override String CreateLessThanOrEqualExpression( - LessThanOrEqualFilter filter, Boolean not) { - String[] attrNames = GetLdapNamesForAttribute(filter.GetAttribute()); - if (attrNames == null) { - return null; - } - - StringBuilder builder = new StringBuilder(); - if (not) { - builder.Append("!("); - } - BuildLessOrEqualFilter(builder, attrNames, filter.GetValue()); - if (not) { - builder.Append(')'); - } - return builder.ToString(); - } - - /** - * Get the string representation of an attribute value suitable for - * embedding in an LDAP search filter (RFC 2254 / RFC 4515). - * - * @param builder A string builder on to which a suitably escaped attribute - * value will be appended. - * - * @param value The attribute value to be embedded. - */ - static void GetLdapFilterValue(StringBuilder builder, - String AttributeName, Object value) { - // at this point, this can probably go away - // it was here to properyly escape queries, but - // it doesn't seem that they need escaping. - if (value == null) - { - return; - } - else - { - builder.Append(value); - } - } - - /** - * Get the LDAP name or names for a given connector attribute used in a - * search filter. - * - * @param attr The connector attribute used in a search filter. - * - * @return The name or names of the corresponding LDAP attribute. - * Returns null if the attribute cannot be specified in an LDAP - * filter. - */ - - protected virtual String[] GetLdapNamesForAttribute(ConnectorAttribute attr) { - // Special processing for certain connector attributes. - String[] attrNames = null; - if (attr is Uid) { - /* - attrNames = new String[] { - configCache.getConfiguration().getUuidAttribute() }; - */ - attrNames = new String[] { "objectGUID" }; - } else if (attr is Name) { - /* - attrNames = configCache.getNamingAttributes(); - */ - attrNames = new String [] { "distinguishedName" }; - } else if (attr.Is(OperationalAttributes.PASSWORD_NAME)) { - /* - attrNames = new String[] { - configCache.getConfiguration().getPasswordAttribute() - }; - */ - attrNames = new String[] { "userPassword" }; - } else if (ConnectorAttributeUtil.IsSpecial(attr)) { - return null; - } else { - attrNames = new String[] { attr.Name }; - } - - return attrNames; - } - - static void BuildEqualityFilter(StringBuilder builder, - String[] attrNames, - Object attrValue) { - if (attrNames.Length == 1) { - builder.Append('('); - builder.Append(attrNames[0]); - builder.Append('='); - GetLdapFilterValue(builder, attrNames[0], attrValue); - builder.Append(')'); - } else { - builder.Append("(|"); - foreach (String attrName in attrNames) { - builder.Append('('); - builder.Append(attrName); - builder.Append('='); - GetLdapFilterValue(builder, attrName, attrValue); - builder.Append(')'); - } - builder.Append(')'); - } - } - - static void BuildGreaterOrEqualFilter(StringBuilder builder, - String[] attrNames, - Object attrValue) { - if (attrNames.Length == 1) { - builder.Append('('); - builder.Append(attrNames[0]); - builder.Append(">="); - GetLdapFilterValue(builder, attrNames[0], attrValue); - builder.Append(')'); - } else { - builder.Append("(|"); - foreach (String attrName in attrNames) { - builder.Append('('); - builder.Append(attrName); - builder.Append(">="); - GetLdapFilterValue(builder, attrName, attrValue); - builder.Append(')'); - } - builder.Append(')'); - } - } - - static void BuildLessOrEqualFilter(StringBuilder builder, - String[] attrNames, - Object attrValue) { - if (attrNames.Length == 1) { - builder.Append('('); - builder.Append(attrNames[0]); - builder.Append("<="); - GetLdapFilterValue(builder, attrNames[0], attrValue); - builder.Append(')'); - } else { - builder.Append("(|"); - foreach (String attrName in attrNames) { - builder.Append('('); - builder.Append(attrName); - builder.Append("<="); - GetLdapFilterValue(builder, attrName, attrValue); - builder.Append(')'); - } - builder.Append(')'); - } - } - - // This is a special case for IDM backward compatibility for - // non account objects. If it doesn't look like a UID, just - // assume it's a dn - static internal bool LooksLikeGUID(string value) - { - string[] uidStringParts = value.Split('='); - if ((uidStringParts.Length != 2) || (uidStringParts[1] == null)) - { - // This is a special case for IDM backward compatibility for - // non account objects. If it doesn't look like a UID, just - // assume it's a dn - return false; - } - - return true; - } - - // This is called to fix up UID values which are in the - // format , but need to be in the - // format \\xx\\xx\\xx... - static internal string GetUidSearchString(string uidString) - { - // be tolerant of whitespace between '<' and "GUID", - // and between "GUID" and '=' and between '=' and - // start of guidstring, and between start of guidstring - // and '>' - string uidSearchString = ""; - string[] uidStringParts = uidString.Split('='); - if ((uidStringParts.Length != 2) || (uidStringParts[1] == null)) - { - throw new ConnectorException("Uid is not in the expected format"); - } - uidSearchString = uidStringParts[1].Trim(); - - // take off the final '>' - uidSearchString = uidSearchString.Substring(0, uidSearchString.IndexOf('>')); - - // now put the '\' characters in - string escapedSearchString = ""; - for(int position = 0;position < uidSearchString.Length;position++) { - if(position % 2 == 0) { - escapedSearchString += "\\"; - } - escapedSearchString += uidSearchString[position]; - } - - return escapedSearchString; - } - } -} diff --git a/DotNetConnectors.sln/ActiveDirectoryConnector/ActiveDirectorySyncToken.cs b/DotNetConnectors.sln/ActiveDirectoryConnector/ActiveDirectorySyncToken.cs deleted file mode 100644 index 9449ba52..00000000 --- a/DotNetConnectors.sln/ActiveDirectoryConnector/ActiveDirectorySyncToken.cs +++ /dev/null @@ -1,89 +0,0 @@ -/* - * ==================== - * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER. - * - * Copyright 2007-2008 Sun Microsystems, Inc. All rights reserved. - * - * The contents of this file are subject to the terms of the Common Development - * and Distribution License("CDDL") (the "License"). You may not use this file - * except in compliance with the License. - * - * You can obtain a copy of the License at - * http://IdentityConnectors.dev.java.net/legal/license.txt - * See the License for the specific language governing permissions and limitations - * under the License. - * - * When distributing the Covered Code, include this CDDL Header Notice in each file - * and include the License file at identityconnectors/legal/license.txt. - * If applicable, add the following below this CDDL Header, with the fields - * enclosed by brackets [] replaced by your own identifying information: - * "Portions Copyrighted [year] [name of copyright owner]" - * ==================== - */ -using System; -using System.Collections.Generic; -using System.Linq; -using System.Text; -using Org.IdentityConnectors.Framework.Common.Objects; -using Org.IdentityConnectors.Framework.Common.Exceptions; - -namespace Org.IdentityConnectors.ActiveDirectory -{ - public class ActiveDirectorySyncToken - { - internal long LastModifiedUsn { get; set; } - internal long LastDeleteUsn { get; set; } - internal bool UseGlobalCatalog { get; set; } - internal string SyncServer { get; set; } - - public ActiveDirectorySyncToken(SyncToken token, string serverName, bool useGlobalCatalog) - : this(token == null ? null : (string)token.Value, serverName, useGlobalCatalog) - { - } - - public ActiveDirectorySyncToken(String tokenValue, string configServerName, bool configUseGlobalCatalog) - { - UseGlobalCatalog = configUseGlobalCatalog; - SyncServer = configServerName; - - if ((tokenValue == null) || (tokenValue.Length == 0)) - { - LastDeleteUsn = 0; - LastModifiedUsn = 0; - return; - } - - string[] tokenParts = (tokenValue).Split('|'); - if (tokenParts.Length != 4) - { - throw new ConnectorException("Unable to parse sync token"); - } - - string tokenSyncServer = tokenParts[3]; - bool tokenUseGlobalCatalog = bool.Parse(tokenParts[2]); - - // If the token server is the same as the configured server, - // use the token value (usn) to limit the query. The token is - // server specific though, so we cant use the usn if it didn't come - // from this server. - // If no server is configured, just try to use what we used last time. - if ((SyncServer != null) && (SyncServer.Equals(configServerName)) && - (UseGlobalCatalog.Equals(tokenUseGlobalCatalog))) - { - LastModifiedUsn = long.Parse(tokenParts[0]); - LastDeleteUsn = long.Parse(tokenParts[1]); - } - else - { - LastModifiedUsn = 0; - LastDeleteUsn = 0; - } - } - - public SyncToken GetSyncToken() - { - return new SyncToken(String.Format("{0}|{1}|{2}|{3}", - LastModifiedUsn, LastDeleteUsn, UseGlobalCatalog, SyncServer)); - } - } -} diff --git a/DotNetConnectors.sln/ActiveDirectoryConnector/ActiveDirectoryUtils.cs b/DotNetConnectors.sln/ActiveDirectoryConnector/ActiveDirectoryUtils.cs deleted file mode 100644 index 814b4366..00000000 --- a/DotNetConnectors.sln/ActiveDirectoryConnector/ActiveDirectoryUtils.cs +++ /dev/null @@ -1,669 +0,0 @@ -/* - * ==================== - * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER. - * - * Copyright 2007-2008 Sun Microsystems, Inc. All rights reserved. - * - * The contents of this file are subject to the terms of the Common Development - * and Distribution License("CDDL") (the "License"). You may not use this file - * except in compliance with the License. - * - * You can obtain a copy of the License at - * http://IdentityConnectors.dev.java.net/legal/license.txt - * See the License for the specific language governing permissions and limitations - * under the License. - * - * When distributing the Covered Code, include this CDDL Header Notice in each file - * and include the License file at identityconnectors/legal/license.txt. - * If applicable, add the following below this CDDL Header, with the fields - * enclosed by brackets [] replaced by your own identifying information: - * "Portions Copyrighted [year] [name of copyright owner]" - * ==================== - */ -using System; -using System.Collections.Generic; -using System.Linq; -using System.Text; -using Org.IdentityConnectors.Framework.Common.Objects; -using System.DirectoryServices; -using Org.IdentityConnectors.Framework.Common.Exceptions; -using System.Diagnostics; -using Org.IdentityConnectors.Framework.Common; -using Org.IdentityConnectors.Framework.Spi.Operations; -using System.Security; -using ActiveDs; -using Org.IdentityConnectors.Common.Security; -using System.DirectoryServices.ActiveDirectory; - -namespace Org.IdentityConnectors.ActiveDirectory -{ - /// - /// Collection of Active directory utilities. Some are static methods, - /// other require configuration, so they are instance methods. - /// - public class ActiveDirectoryUtils - { - ActiveDirectoryConfiguration _configuration = null; - private CustomAttributeHandlers _customHandlers = null; - - /// - /// Constructor - /// - /// - /// Configuration object for the connector. - /// - public ActiveDirectoryUtils(ActiveDirectoryConfiguration configuration) - { - _configuration = configuration; - _customHandlers = new CustomAttributeHandlers(_configuration); - } - - /// - /// Converts a guid in byte array form to a string suitable - /// for ldap search. - /// - /// - /// - internal static String ConvertUIDBytesToSearchString(Byte[] guidBytes) - { - String searchGuid = ""; - - for (int i = 0; i < guidBytes.Length; i++) - { - searchGuid += String.Format("\\{0:X2}", guidBytes[i]); - } - - return searchGuid; - } - - /// - /// Converts a guid in byte array form to a string with the format - /// >GUID = xxxxxxxxxxxxxxxxxxxxxxxxxxxxx< where the x's represent - /// uppercase hexadecimal digits - /// - /// - /// - internal static String ConvertUIDBytesToGUIDString(Byte[] guidBytes) - { - return ConvertBytesToADSpecialString("GUID", guidBytes); - } - - internal static String ConvertSIDBytesToGUIDString(Byte[] guidBytes) - { - return ConvertBytesToADSpecialString("SID", guidBytes); - } - - internal static String ConvertBytesToADSpecialString(string attribute, Byte[] bytes) - { - String guidString = "<" + attribute + "="; - - for (int i = 0; i < bytes.Length; i++) - { - guidString += String.Format("{0:X2}", bytes[i]); - } - guidString += ">"; - - return guidString; - } - - /// - /// Returns an ldap path in the form of: - /// LDAP://servernameIfSpecified/path - /// - /// Servername can be null - /// Path should not be null - /// - internal static String GetLDAPPath(string serverName, string path) - { - return GetFullPath("LDAP", serverName, path); - } - - /// - /// Returns a path string in the format: - /// GC://servernameIfSpecified/path - /// - /// Servername is optional - /// Path should be specified - /// - internal static String GetGCPath(string serverName, string path) - { - return GetFullPath("GC", serverName, path); - } - - /// - /// Returns a path string in the format: - /// provider://servernameIfSpecified/path - /// - /// provider (such as ldap or gc) - /// servername - optional - /// path to resource - /// - internal static String GetFullPath(string provider, string serverName, string path) - { - IADsPathname pathName = getADSPathname(provider, serverName, path); - return pathName.Retrieve((int)ADS_FORMAT_ENUM.ADS_FORMAT_X500); - } - - /// - /// uses iadspathname to create paths in a standard way - /// - /// - /// - /// - /// - internal static IADsPathname getADSPathname(string provider, string serverName, string path) - { - IADsPathname pathName = new PathnameClass(); - if ((provider != null) && (provider.Length != 0)) - { - pathName.Set(provider, (int)ADS_SETTYPE_ENUM.ADS_SETTYPE_PROVIDER); - } - - if ((serverName != null) && (serverName.Length != 0)) - { - pathName.Set(serverName, (int)ADS_SETTYPE_ENUM.ADS_SETTYPE_SERVER); - } - - if ((path != null) && (path.Length != 0)) - { - // must supply a path - pathName.Set(path, (int)ADS_SETTYPE_ENUM.ADS_SETTYPE_DN); - } - return pathName; - } - - /// - /// Gets the dn of the parent object of the object specified by childDn - /// - /// distinguished name of an object to retrieve the parent of - /// distinguished name of the parent of 'childDn' or null - internal static string GetParentDn(string childDn) - { - IADsPathname pathName = getADSPathname(null, null, childDn); - return pathName.Retrieve((int)ADS_FORMAT_ENUM.ADS_FORMAT_X500_PARENT); - } - - /// - /// Updates an AD object (also called by create after object is created) - /// - /// - /// - /// - /// - /// - internal void UpdateADObject(ObjectClass oclass, - DirectoryEntry directoryEntry, ICollection attributes, - UpdateType type, ActiveDirectoryConfiguration config) - { - if(oclass.Equals(ObjectClass.ACCOUNT)) - { - // translate attribute passed in - foreach (ConnectorAttribute attribute in attributes) - { - // encountered problems when processing change password at the same time - // as setting expired. It would be set to expired, but the change would - // clear that. So we must ensure that expired comes last. - if (OperationalAttributes.PASSWORD_EXPIRED_NAME.Equals(attribute.Name)) - { - continue; - } - - AddConnectorAttributeToADProperties(oclass, - directoryEntry, attribute, type); - - // Uncommenting the next line is very helpful in - // finding mysterious errors. - // directoryEntry.CommitChanges(); - } - - directoryEntry.CommitChanges(); - - // now do the password change. This is handled separately, because - // it might be a user changing his own password, or it might be an - // administrative change. - - GuardedString gsNewPassword = ConnectorAttributeUtil.GetPasswordValue(attributes); - if (gsNewPassword != null) - { - GuardedString gsCurrentPassword = ConnectorAttributeUtil.GetCurrentPasswordValue(attributes); - PasswordChangeHandler changeHandler = new PasswordChangeHandler(_configuration); - if (gsCurrentPassword == null) - { - // just a normal password change - changeHandler.changePassword(directoryEntry, gsNewPassword); - } - else - { - changeHandler.changePassword(directoryEntry, - gsCurrentPassword, gsNewPassword); - } - - - UserAccountControl.Set(directoryEntry.Properties[ActiveDirectoryConnector.ATT_USER_ACOUNT_CONTROL], - UserAccountControl.PASSWD_NOTREQD, false); - directoryEntry.CommitChanges(); - } - - // see note in loop above for explaination of this - ConnectorAttribute expirePasswordAttribute = ConnectorAttributeUtil.Find( - OperationalAttributes.PASSWORD_EXPIRED_NAME, attributes); - - if (expirePasswordAttribute != null) - { - AddConnectorAttributeToADProperties(oclass, - directoryEntry, expirePasswordAttribute, type); - directoryEntry.CommitChanges(); - } - - UserAccountControl.Set(directoryEntry.Properties[ActiveDirectoryConnector.ATT_USER_ACOUNT_CONTROL], - UserAccountControl.PASSWD_NOTREQD, false); - - directoryEntry.CommitChanges(); - - HandleNameChange(type, directoryEntry, attributes); - HandleContainerChange(type, directoryEntry, attributes, config); - } - else if (oclass.Equals(ObjectClass.GROUP)) - { - // translate attribute passed in - foreach (ConnectorAttribute attribute in attributes) - { - // Temporary - // Trace.TraceInformation(String.Format("Setting attribute {0} to {1}", - // attribute.Name, attribute.Value)); - AddConnectorAttributeToADProperties(oclass, - directoryEntry, attribute, type); - // Uncommenting the next line is very helpful in - // finding mysterious errors. - directoryEntry.CommitChanges(); - } - - directoryEntry.CommitChanges(); - HandleNameChange(type, directoryEntry, attributes); - HandleContainerChange(type, directoryEntry, attributes, config); - } - else if (oclass.Equals(ActiveDirectoryConnector.ouObjectClass)) - { - // translate attribute passed in - foreach (ConnectorAttribute attribute in attributes) - { - // Temporary - // Trace.TraceInformation(String.Format("Setting attribute {0} to {1}", - // attribute.Name, attribute.Value)); - AddConnectorAttributeToADProperties(oclass, - directoryEntry, attribute, type); - // Uncommenting the next line is very helpful in - // finding mysterious errors. - directoryEntry.CommitChanges(); - } - - directoryEntry.CommitChanges(); - HandleNameChange(type, directoryEntry, attributes); - HandleContainerChange(type, directoryEntry, attributes, config); - } - else - { - throw new ConnectorException( - _configuration.ConnectorMessages.Format("ex_InvalidObjectClass", - "Invalid object class: {0}", oclass.GetObjectClassValue())); - } - } - - internal ConnectorAttribute GetConnectorAttributeFromADEntry(ObjectClass oclass, - String attributeName, SearchResult searchResult) - { - // Boolean translated = false; - if (searchResult == null) - { - throw new ConnectorException(_configuration.ConnectorMessages.Format( - "ex_AttributeNull", - "Could not add connector attribute to search result")); - } - - return _customHandlers.GetCaFromDe(oclass, - attributeName, searchResult); - - } - - internal void AddConnectorAttributeToADProperties(ObjectClass oclass, - DirectoryEntry directoryEntry, ConnectorAttribute attribute, - UpdateType type) - { - // Boolean translated = false; - if (directoryEntry == null) - { - throw new ConnectorException(_configuration.ConnectorMessages.Format( - "ex_CouldNotAddNullAttributeToDe", - "Could not add connector attribute to directory entry")); - } - - _customHandlers.UpdateDeFromCa(oclass, type, - directoryEntry, attribute); - - } - - /* - /// - /// creates and returns a connector attribute or null. the attribute - /// has the name 'name' and the values associated with 'name' in the - /// directory entry - /// - /// - /// - /// - private static ConnectorAttribute CreateConnectorAttribute(String name, - PropertyValueCollection pvc) - { - ConnectorAttributeBuilder attributeBuilder = new ConnectorAttributeBuilder(); - - if (name == null) - { - return null; - } - - attributeBuilder.Name = name; - - if (pvc == null) - { - attributeBuilder.AddValue(null); - } - else - { - for (int i = 0; i < pvc.Count; i++) - { - Object valueObject = pvc[i]; - if ((pvc[i] == null) || - (FrameworkUtil.IsSupportedAttributeType(valueObject.GetType()))) - { - attributeBuilder.AddValue(pvc[i]); - } - else - { - Trace.TraceWarning( - "Unsupported attribute type ... calling ToString (Name: \'{0}\'({1}) Type: \'{2}\' String Value: \'{3}\'", - name, i, pvc[i].GetType(), pvc[i].ToString()); - attributeBuilder.AddValue(pvc[i].ToString()); - } - } - } - - return attributeBuilder.Build(); - } - - private static void AddConnectorAttributeToADProperties_general( - PropertyCollection properties, - ConnectorAttribute attribute, UpdateType type) - { - // null out the values if we are deleting - // or replacing attributes. - if (type.Equals(UpdateType.DELETE) || - type.Equals(UpdateType.REPLACE)) - { - properties[attribute.Name].Value = null; - } - - // if we are updating or adding, put the - // new values in. - if (type.Equals(UpdateType.ADD) || - type.Equals(UpdateType.REPLACE)) - { - foreach (Object valueObject in attribute.Value) - { - properties[attribute.Name].Add(valueObject); - } - } - } - */ - - /// - /// Gets a single value from a propertyvaluecollection - /// for a particular property name. Its an error if the - /// property contains multiple values. - /// - /// - /// - internal Object GetSingleValue(PropertyValueCollection pvc) - { - if((pvc == null) || (pvc.Count == 0)) - { - return null; - } - - if (pvc.Count > 1) - { - String msg = _configuration.ConnectorMessages.Format( - "ex_ExpectingSingleValue", - "Expecting single value, but found multiple values for attribute {0}", - pvc.PropertyName); - throw new ConnectorException(msg); - } - - return pvc[0]; - } - - /// - /// Finds a DirectoryEntry by it's uid - /// - /// - /// - /// - /// - /// - internal static DirectoryEntry GetDirectoryEntryFromUid(String serverName, - Uid uid, string adminUserName, string adminPassword) - { - DirectoryEntry foundDirectoryEntry = new DirectoryEntry( - ActiveDirectoryUtils.GetLDAPPath(serverName, uid.GetUidValue()), - adminUserName, adminPassword); - string dn = (string)foundDirectoryEntry.Properties["distinguishedName"][0]; - foundDirectoryEntry = new DirectoryEntry( - ActiveDirectoryUtils.GetLDAPPath(serverName, dn), - adminUserName, adminPassword); - return foundDirectoryEntry; - } - - /// - /// Returns the AD ObjectClass associated with a particular - /// Connector ObjectClass - /// - /// - /// - internal String GetADObjectClass(ObjectClass oclass) - { - - if (oclass.Equals(ObjectClass.ACCOUNT)) - { - return "User"; - } - else if (oclass.Equals(ObjectClass.GROUP)) - { - return "Group"; - } - else if ("ORGANIZATIONAL UNIT".Equals(oclass.GetObjectClassValue(), StringComparison.CurrentCultureIgnoreCase)) - { - return "organizationalUnit"; - } - else - { - String msg = _configuration.ConnectorMessages.Format( - "ex_ObjectClassInvalidForConnector", - "ObjectClass \'{0}\' is not valid for this connector", - oclass.GetObjectClassValue()); - throw new ConnectorException(msg); - } - } - - /// - /// Puts an ldap string into a normalilzed format - /// - /// - /// - public static String NormalizeLdapString(String ldapString) - { - StringBuilder normalPath = new StringBuilder(); - String[] parts = ldapString.Split(','); - for (int i = 0; i < parts.Length; i++) - { - normalPath.Append(parts[i].Trim().ToUpper()); - // append a comma after each part (except the last one) - if (i < (parts.Length - 1)) - { - normalPath.Append(","); - } - } - return normalPath.ToString(); - } - - public static String GetRelativeName(Name name) - { - return GetNameAsCN(name.GetNameValue()); - } - - /// - /// Returns the leaf value of a distinguished name - /// - /// - /// - internal static String GetNameAsCN(String nameValue) - { - IADsPathname pathName = getADSPathname(null, null, nameValue); - return pathName.Retrieve((int)ADS_FORMAT_ENUM.ADS_FORMAT_LEAF); - } - - /// - /// This does not work ... for now, don't handle container changes - /// - /// - /// - /// - /// - private static void HandleContainerChange(UpdateType type, - DirectoryEntry directoryEntry, ICollection attributes, - ActiveDirectoryConfiguration config) - { - // this return means that te connector attribute is ignored for - // the purpose of moving an object to a different container - return; - - // this code seems right, but doesnt work. The DirectoryEntry.Move() - // method always throws an Exception with the text 'unspecified error' - - ConnectorAttribute containerAttribute = - ConnectorAttributeUtil.Find(ActiveDirectoryConnector.ATT_CONTAINER, attributes); - if (containerAttribute != null) - { - // this only make sense for replace. you can't - // add a name or delete a name - if (type.Equals(UpdateType.REPLACE)) - { - DirectoryEntry parent = directoryEntry.Parent; - String oldContainer = null; - if (parent != null) - { - PropertyValueCollection parentDNValues = - parent.Properties[ActiveDirectoryConnector.ATT_DISTINGUISHED_NAME]; - if ((parentDNValues.Count == 1) && (parentDNValues[0] is String)) - { - oldContainer = (String)parentDNValues[0]; - } - else - { - String msg = String.Format("Unable to retrieve the distinguished name for {0}.", - parent.Path); - throw new ConnectorException(msg); - } - } - - String newContainer = ConnectorAttributeUtil.GetStringValue(containerAttribute); - - if (newContainer != null) - { - try - { - if (!NormalizeLdapString(oldContainer).Equals( - NormalizeLdapString(newContainer))) - { - DirectoryEntry newContainerDe = new DirectoryEntry(newContainer, - config.DirectoryAdminName, config.DirectoryAdminPassword); - directoryEntry.MoveTo(newContainerDe); - } - } - catch (Exception e) - { - throw e; - } - } - } - } - } - - private static void HandleNameChange(UpdateType type, - DirectoryEntry directoryEntry, - ICollection attributes) - { - Name nameAttribute = ConnectorAttributeUtil.GetNameFromAttributes(attributes); - if (nameAttribute != null) - { - // this only make sense for replace. you can't - // add a name or delete a name - if (type.Equals(UpdateType.REPLACE)) - { - String oldName = directoryEntry.Name; - String newName = GetRelativeName(nameAttribute); - if (!NormalizeLdapString(oldName).Equals(NormalizeLdapString(newName))) - { - directoryEntry.Rename(newName); - } - } - } - } - - public static SecureString GetSecureString(String stringToSecure) - { - SecureString secure = new SecureString(); - - foreach (char nextChar in stringToSecure) - { - secure.AppendChar(nextChar); - } - - return secure; - } - - internal static string GetDnFromPath(string fullPath) - { - IADsPathname pathName = new PathnameClass(); - pathName.Set(fullPath, (int)ADS_SETTYPE_ENUM.ADS_SETTYPE_FULL); - return pathName.Retrieve((int)ADS_FORMAT_ENUM.ADS_FORMAT_X500_DN); - } - - internal static DomainController GetDomainController(ActiveDirectoryConfiguration configuration) - { - String serverName = configuration.LDAPHostName; - DomainController controller = null; - - if ((serverName == null) || (serverName.Length == 0)) - { - // get the active directory schema - DirectoryContext context = new DirectoryContext( - DirectoryContextType.Domain, - configuration.DomainName, - configuration.DirectoryAdminName, - configuration.DirectoryAdminPassword); - controller = DomainController.FindOne(context); - } - else - { - DirectoryContext context = new DirectoryContext( - DirectoryContextType.DirectoryServer, - configuration.LDAPHostName, - configuration.DirectoryAdminName, - configuration.DirectoryAdminPassword); - controller = DomainController.GetDomainController(context); - } - - return controller; - } - } - -} diff --git a/DotNetConnectors.sln/ActiveDirectoryConnector/AssemblyInfo.cs b/DotNetConnectors.sln/ActiveDirectoryConnector/AssemblyInfo.cs deleted file mode 100644 index 65843b5d..00000000 --- a/DotNetConnectors.sln/ActiveDirectoryConnector/AssemblyInfo.cs +++ /dev/null @@ -1,54 +0,0 @@ -/* - * ==================== - * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER. - * - * Copyright 2007-2008 Sun Microsystems, Inc. All rights reserved. - * - * The contents of this file are subject to the terms of the Common Development - * and Distribution License("CDDL") (the "License"). You may not use this file - * except in compliance with the License. - * - * You can obtain a copy of the License at - * http://IdentityConnectors.dev.java.net/legal/license.txt - * See the License for the specific language governing permissions and limitations - * under the License. - * - * When distributing the Covered Code, include this CDDL Header Notice in each file - * and include the License file at identityconnectors/legal/license.txt. - * If applicable, add the following below this CDDL Header, with the fields - * enclosed by brackets [] replaced by your own identifying information: - * "Portions Copyrighted [year] [name of copyright owner]" - * ==================== - */ -#region Using directives - -using System; -using System.Reflection; -using System.Resources; -using System.Runtime.InteropServices; - -#endregion - -// General Information about an assembly is controlled through the following -// set of attributes. Change these attribute values to modify the information -// associated with an assembly. -[assembly: AssemblyTitle("ActiveDirectoryConnector")] -[assembly: AssemblyDescription("")] -[assembly: AssemblyConfiguration("")] -[assembly: AssemblyCompany("")] -[assembly: AssemblyProduct("ActiveDirectoryConnector")] -[assembly: AssemblyCopyright("")] -[assembly: AssemblyTrademark("")] -[assembly: AssemblyCulture("")] - -// This sets the default COM visibility of types in the assembly to invisible. -// If you need to expose a type to COM, use [ComVisible(true)] on that type. -[assembly: ComVisible(false)] - -// The assembly version has following format : -// -// Major.Minor.Build.Revision -// -// You can specify all the values or you can use the default the Revision and -// Build Numbers by using the '*' as shown below: -[assembly: AssemblyVersion("1.0.0.0")] diff --git a/DotNetConnectors.sln/ActiveDirectoryConnector/CommonUtils.cs b/DotNetConnectors.sln/ActiveDirectoryConnector/CommonUtils.cs deleted file mode 100644 index d42bb8cd..00000000 --- a/DotNetConnectors.sln/ActiveDirectoryConnector/CommonUtils.cs +++ /dev/null @@ -1,73 +0,0 @@ -/* - * ==================== - * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER. - * - * Copyright 2007-2008 Sun Microsystems, Inc. All rights reserved. - * - * The contents of this file are subject to the terms of the Common Development - * and Distribution License("CDDL") (the "License"). You may not use this file - * except in compliance with the License. - * - * You can obtain a copy of the License at - * http://IdentityConnectors.dev.java.net/legal/license.txt - * See the License for the specific language governing permissions and limitations - * under the License. - * - * When distributing the Covered Code, include this CDDL Header Notice in each file - * and include the License file at identityconnectors/legal/license.txt. - * If applicable, add the following below this CDDL Header, with the fields - * enclosed by brackets [] replaced by your own identifying information: - * "Portions Copyrighted [year] [name of copyright owner]" - * ==================== - */ -using System; -using System.Collections.Generic; -using System.Reflection; -using System.IO; -using Org.IdentityConnectors.Common; -using Org.IdentityConnectors.Framework.Common.Objects; -using Org.IdentityConnectors.Framework.Common.Serializer; - -namespace Org.IdentityConnectors.ActiveDirectory -{ - public class CommonUtils - { - /// - /// reads the object class info definitions from xml - /// - ///Dictionary of object classes - protected internal static IDictionary GetOCInfo(string name) - { - Assembly assembly = Assembly.GetExecutingAssembly(); - Stream stream = assembly.GetManifestResourceStream(name); - - Assertions.NullCheck(stream, "stream"); - - //we just read - TextReader streamReader = new StreamReader(stream); - String xml; - try - { - xml = streamReader.ReadToEnd(); - } - finally - { - streamReader.Close(); - } - - //read from xml - var ret = (ICollection)SerializerUtil.DeserializeXmlObject(xml, true); - - Assertions.NullCheck(ret, "ret"); - - //create map of object infos - var map = new Dictionary(ret.Count); - foreach (ObjectClassInfo o in ret) - { - map.Add(new ObjectClass(o.ObjectType.ToString()), o); - } - - return map; - } - } -} diff --git a/DotNetConnectors.sln/ActiveDirectoryConnector/CustomAttributeHandlers.cs b/DotNetConnectors.sln/ActiveDirectoryConnector/CustomAttributeHandlers.cs deleted file mode 100644 index 50c175a8..00000000 --- a/DotNetConnectors.sln/ActiveDirectoryConnector/CustomAttributeHandlers.cs +++ /dev/null @@ -1,1167 +0,0 @@ -/* - * ==================== - * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER. - * - * Copyright 2007-2008 Sun Microsystems, Inc. All rights reserved. - * - * The contents of this file are subject to the terms of the Common Development - * and Distribution License("CDDL") (the "License"). You may not use this file - * except in compliance with the License. - * - * You can obtain a copy of the License at - * http://IdentityConnectors.dev.java.net/legal/license.txt - * See the License for the specific language governing permissions and limitations - * under the License. - * - * When distributing the Covered Code, include this CDDL Header Notice in each file - * and include the License file at identityconnectors/legal/license.txt. - * If applicable, add the following below this CDDL Header, with the fields - * enclosed by brackets [] replaced by your own identifying information: - * "Portions Copyrighted [year] [name of copyright owner]" - * ==================== - */ -using System; -using System.Collections.Generic; -using System.Linq; -using System.Text; -using System.Security; -using Org.IdentityConnectors.Framework.Common.Objects; -using Org.IdentityConnectors.Framework.Common; -using Org.IdentityConnectors.Framework.Spi.Operations; -using System.DirectoryServices; -using Org.IdentityConnectors.Framework.Common.Exceptions; -using Org.IdentityConnectors.Common.Security; -using System.Diagnostics; -using ActiveDs; -using System.IO; -using System.Security.AccessControl; -using System.Security.Principal; -using Org.IdentityConnectors.Common; - -namespace Org.IdentityConnectors.ActiveDirectory -{ - /// - /// This class will encapsulate all changes from AD attributes - /// to Connector attributes and from Connector attributes to - /// AD attributes. If attributes are more complex and can't be - /// handled here, add them to the appropriate ignore list, and handle - /// them in the AD Connector (or elsewhere). - /// - /// If it is a connector attribute that has the same name as the - /// ad attribute, and the value requires no translation, it will - /// be handled by the generic handler. If not, add a delegate for - /// either AD->Connector or for Connector->AD (or both). - /// - internal class CustomAttributeHandlers - { - // names from active directory attributes to ignore during - // generic translation - IList IgnoreADAttributeNames_account = new List(); - IList IgnoreADAttributeNames_group = new List(); - - // names from connector attributes to ignore during - // generic translation - IList IgnoreConnectorAttributeNames_account = new List(); - IList IgnoreConnectorAttributeNames_group = new List(); - IList IgnoreConnectorAttributeNames_ou = new List(); - - // method to update a directory entry from a connector attribute - Dictionary - UpdateDeFromCaDelegates = new Dictionary(StringComparer.CurrentCultureIgnoreCase); - - // method to get a connector attribute from a directory entry - Dictionary - GetCaFromDeDelegates = new Dictionary(StringComparer.CurrentCultureIgnoreCase); - - ActiveDirectoryConfiguration _configuration = null; - - internal CustomAttributeHandlers(ActiveDirectoryConfiguration configuration) { - // save the configuration - _configuration = configuration; - - // Connector attributes names to ignore for accounts - IgnoreConnectorAttributeNames_account.Add(Name.NAME); - IgnoreConnectorAttributeNames_account.Add(ActiveDirectoryConnector.ATT_CONTAINER); - IgnoreConnectorAttributeNames_account.Add(Uid.NAME); - IgnoreConnectorAttributeNames_account.Add(OperationalAttributes.PASSWORD_NAME); - IgnoreConnectorAttributeNames_account.Add(OperationalAttributes.CURRENT_PASSWORD_NAME); - - // Connector attributes names to ignore for groups - IgnoreConnectorAttributeNames_group.Add(Name.NAME); - IgnoreConnectorAttributeNames_group.Add(ActiveDirectoryConnector.ATT_CONTAINER); - IgnoreConnectorAttributeNames_group.Add(Uid.NAME); - IgnoreConnectorAttributeNames_group.Add("authOrig"); - IgnoreConnectorAttributeNames_group.Add("unauthOrig"); - IgnoreConnectorAttributeNames_group.Add("groupTypes"); - - // Connector attributes names to ignore for ous - IgnoreConnectorAttributeNames_ou.Add(Name.NAME); - IgnoreConnectorAttributeNames_ou.Add(Uid.NAME); - - // methods to update a directory entry from a connectorattribute - UpdateDeFromCaDelegates.Add(PredefinedAttributes.ACCOUNTS_NAME, - UpdateDeFromCa_OpAtt_Accounts); - UpdateDeFromCaDelegates.Add(PredefinedAttributes.GROUPS_NAME, - UpdateDeFromCa_OpAtt_Groups); - UpdateDeFromCaDelegates.Add(ActiveDirectoryConnector.ATT_HOME_DIRECTORY, - UpdateDeFromCa_Att_HomeDirectory); - UpdateDeFromCaDelegates.Add(OperationalAttributes.ENABLE_NAME, - UpdateDeFromCa_OpAtt_Enable); - UpdateDeFromCaDelegates.Add(OperationalAttributes.PASSWORD_EXPIRED_NAME, - UpdateDeFromCa_OpAtt_PasswordExpired); - UpdateDeFromCaDelegates.Add(OperationalAttributes.PASSWORD_EXPIRATION_DATE_NAME, - UpdateDeFromCa_OpAtt_PasswordExpireDate); - UpdateDeFromCaDelegates.Add(OperationalAttributes.LOCK_OUT_NAME, - UpdateDeFromCa_OpAtt_Lockout); - // supporting class not implemented in the framework - /* - UpdateDeFromCaDelegates.Add(OperationalAttributes.ENABLE_DATE_NAME, - UpdateDeFromCa_OpAtt_EnableDate); - UpdateDeFromCaDelegates.Add(OperationalAttributes.DISABLE_DATE_NAME, - UpdateDeFromCa_OpAtt_DisableDate); - */ - UpdateDeFromCaDelegates.Add(TerminalServicesUtils.TS_ALLOW_LOGON, - UpdateDeFromCa_Att_TSAllowLogon); - UpdateDeFromCaDelegates.Add(TerminalServicesUtils.TS_INITIAL_PROGRAM, - UpdateDeFromCa_Att_TSInitialProgram); - UpdateDeFromCaDelegates.Add(TerminalServicesUtils.TS_INITIAL_PROGRAM_DIR, - UpdateDeFromCa_Att_TSInitialProgramDir); - UpdateDeFromCaDelegates.Add(TerminalServicesUtils.TS_MAX_CONNECTION_TIME, - UpdateDeFromCa_Att_TSMaxConnectionTime); - UpdateDeFromCaDelegates.Add(TerminalServicesUtils.TS_MAX_DISCONNECTION_TIME, - UpdateDeFromCa_Att_TSMaxDisconnectionTime); - UpdateDeFromCaDelegates.Add(TerminalServicesUtils.TS_MAX_IDLE_TIME, - UpdateDeFromCa_Att_TSMaxIdleTime); - UpdateDeFromCaDelegates.Add(TerminalServicesUtils.TS_CONNECT_CLIENT_DRIVES_AT_LOGON, - UpdateDeFromCa_Att_TSConnectClientDrivesAtLogon); - UpdateDeFromCaDelegates.Add(TerminalServicesUtils.TS_CONNECT_CLIENT_PRINTERS_AT_LOGON, - UpdateDeFromCa_Att_TSConnectClientPrintersAtLogon); - UpdateDeFromCaDelegates.Add(TerminalServicesUtils.TS_DEFAULT_TO_MAIN_PRINTER, - UpdateDeFromCa_Att_TSDefaultToMainPrinter); - UpdateDeFromCaDelegates.Add(TerminalServicesUtils.TS_BROKEN_CONNECTION_ACTION, - UpdateDeFromCa_Att_TSBrokenConnectionAction); - UpdateDeFromCaDelegates.Add(TerminalServicesUtils.TS_RECONNECTION_ACTION, - UpdateDeFromCa_Att_TSReconnectionAction); - UpdateDeFromCaDelegates.Add(TerminalServicesUtils.TS_ENABLE_REMOTE_CONTROL, - UpdateDeFromCa_Att_TSEnableRemoteControl); - UpdateDeFromCaDelegates.Add(TerminalServicesUtils.TS_PROFILE_PATH, - UpdateDeFromCa_Att_TSProfilePath); - UpdateDeFromCaDelegates.Add(TerminalServicesUtils.TS_HOME_DIRECTORY, - UpdateDeFromCa_Att_TSHomeDirectory); - UpdateDeFromCaDelegates.Add(TerminalServicesUtils.TS_HOME_DRIVE, - UpdateDeFromCa_Att_TSHomeDrive); - - // methods to create a connector attribute from a directory entry - GetCaFromDeDelegates.Add(Name.NAME, GetCaFromDe_OpAtt_Name); - GetCaFromDeDelegates.Add(Uid.NAME, GetCaFromDe_OpAtt_Uid); - GetCaFromDeDelegates.Add(ActiveDirectoryConnector.ATT_CONTAINER, - GetCaFromDe_Att_Container); - GetCaFromDeDelegates.Add(PredefinedAttributes.ACCOUNTS_NAME, - GetCaFromDe_OpAtt_Accounts); - GetCaFromDeDelegates.Add(PredefinedAttributes.GROUPS_NAME, - GetCaFromDe_OpAtt_Groups); - GetCaFromDeDelegates.Add(OperationalAttributes.ENABLE_NAME, - GetCaFromDe_OpAtt_Enabled); - GetCaFromDeDelegates.Add(OperationalAttributes.PASSWORD_EXPIRED_NAME, - GetCaFromDe_OpAtt_PasswordExpired); - GetCaFromDeDelegates.Add(PredefinedAttributes.DESCRIPTION, - GetCaFromDe_OpAtt_Description); - GetCaFromDeDelegates.Add(PredefinedAttributes.SHORT_NAME, - GetCaFromDe_OpAtt_ShortName); - GetCaFromDeDelegates.Add(OperationalAttributes.LOCK_OUT_NAME, - GetCaFromDe_OpAtt_Lockout); - GetCaFromDeDelegates.Add(OperationalAttributes.PASSWORD_EXPIRATION_DATE_NAME, - GetCaFromDe_OpAtt_PasswordExpireDate); - // supporting class not implemented in the framework - /* - GetCaFromDeDelegates.Add(OperationalAttributes.ENABLE_DATE_NAME, - GetCaFromDe_OpAtt_EnableDate); - GetCaFromDeDelegates.Add(OperationalAttributes.DISABLE_DATE_NAME, - GetCaFromDe_OpAtt_DisableDate); - */ - GetCaFromDeDelegates.Add(TerminalServicesUtils.TS_INITIAL_PROGRAM, - GetCaFromDe_Att_TSInitialProgram); - GetCaFromDeDelegates.Add(TerminalServicesUtils.TS_INITIAL_PROGRAM_DIR, - GetCaFromDe_Att_TSInitalProgramDir); - GetCaFromDeDelegates.Add(TerminalServicesUtils.TS_ALLOW_LOGON, - GetCaFromDe_Att_TSAllowLogon); - GetCaFromDeDelegates.Add(TerminalServicesUtils.TS_MAX_CONNECTION_TIME, - GetCaFromDe_Att_TSMaxConnectionTime); - GetCaFromDeDelegates.Add(TerminalServicesUtils.TS_MAX_DISCONNECTION_TIME, - GetCaFromDe_Att_TSMaxDisconnectionTime); - GetCaFromDeDelegates.Add(TerminalServicesUtils.TS_MAX_IDLE_TIME, - GetCaFromDe_Att_TSMaxIdleTime); - GetCaFromDeDelegates.Add(TerminalServicesUtils.TS_CONNECT_CLIENT_DRIVES_AT_LOGON, - GetCaFromDe_Att_TSConnectClientDrivesAtLogon); - GetCaFromDeDelegates.Add(TerminalServicesUtils.TS_CONNECT_CLIENT_PRINTERS_AT_LOGON, - GetCaFromDe_Att_TSConnectClientPrintersAtLogon); - GetCaFromDeDelegates.Add(TerminalServicesUtils.TS_DEFAULT_TO_MAIN_PRINTER, - GetCaFromDe_Att_TSDefaultToMainPrinter); - GetCaFromDeDelegates.Add(TerminalServicesUtils.TS_BROKEN_CONNECTION_ACTION, - GetCaFromDe_Att_TSBrokenConnectionAction); - GetCaFromDeDelegates.Add(TerminalServicesUtils.TS_RECONNECTION_ACTION, - GetCaFromDe_Att_TSReconnectionAction); - GetCaFromDeDelegates.Add(TerminalServicesUtils.TS_ENABLE_REMOTE_CONTROL, - GetCaFromDe_Att_TSEnableRemoteControl); - GetCaFromDeDelegates.Add(TerminalServicesUtils.TS_PROFILE_PATH, - GetCaFromDe_Att_TSProfilePath); - GetCaFromDeDelegates.Add(TerminalServicesUtils.TS_HOME_DIRECTORY, - GetCaFromDe_Att_TSHomeDirectory); - GetCaFromDeDelegates.Add(TerminalServicesUtils.TS_HOME_DRIVE, - GetCaFromDe_Att_TSHomeDrive); - } - - internal void UpdateDeFromCa(ObjectClass oclass, - UpdateType type, DirectoryEntry directoryEntry, - ConnectorAttribute attribute) { - - // if this gets big, replace with dictionary key by object class - IList ignoreList = null; - if(oclass.Equals(ObjectClass.ACCOUNT)) { - ignoreList = IgnoreConnectorAttributeNames_account; - } - else if (oclass.Equals(ObjectClass.GROUP)) - { - ignoreList = IgnoreConnectorAttributeNames_group; - } - else if (oclass.Equals(ActiveDirectoryConnector.ouObjectClass)) - { - ignoreList = IgnoreConnectorAttributeNames_ou; - } - - // if it's an ignored attribute, we're done - if ((ignoreList != null) && - (ignoreList.Contains(attribute.Name, - StringComparer.CurrentCultureIgnoreCase))) { - return; - } - - if (UpdateDeFromCaDelegates.ContainsKey(attribute.Name)) - { - // if it's an attribute with a special handler, - // call the handler - UpdateDeFromCa_delegate handler = - UpdateDeFromCaDelegates[attribute.Name]; - handler(oclass, type, directoryEntry, attribute); - } - else - { - // if none of the above, call the generic handler - UpdateDeFromCa_Att_Generic(oclass, type, directoryEntry, attribute); - } - } - - - internal ConnectorAttribute GetCaFromDe(ObjectClass oclass, - string attributeName, SearchResult searchResult) - { - ConnectorAttribute attribute = null; - - if (GetCaFromDeDelegates.ContainsKey(attributeName)) - { - // if it's an attribute with a special handler, - // call the handler - GetCaFromDe_delegate handler = GetCaFromDeDelegates[attributeName]; - attribute = handler(oclass, attributeName, searchResult); - } - else - { - // if none of the above, call the generic handler - attribute = GetCaFromDe_Att_Generic(oclass, attributeName, searchResult); - } - - return attribute; - } - - internal delegate void UpdateDeFromCa_delegate(ObjectClass oclass, - UpdateType type, DirectoryEntry directoryEntry, - ConnectorAttribute attribute); - - internal delegate ConnectorAttribute GetCaFromDe_delegate(ObjectClass oclass, - string attributeName, SearchResult searchResult); - - public void GetAddsAndDeletes(ICollectionvaluesToAdd, ICollectionvaluesToRemove, - PropertyValueCollection oldValues, ICollectionnewValues, UpdateType type) { - if (UpdateType.ADD.Equals(type)) - { - // add all groups - foreach (Object value in newValues) - { - valuesToAdd.Add(value); - } - } - else if (UpdateType.REPLACE.Equals(type)) - { - // look through existing values, and remove them - // if they are not in the newValues - if (oldValues != null) - { - foreach (Object value in oldValues) - { - if (!newValues.Contains(value)) - { - valuesToRemove.Add(value); - } - } - } - - // look through the values passed in and - // add them if they are not existing values - foreach (Object value in newValues) - { - if ((oldValues == null) || (!oldValues.Contains(value))) - { - valuesToAdd.Add(value); - } - } - } - else if (UpdateType.DELETE.Equals(type)) - { - foreach (Object value in newValues) - { - valuesToRemove.Add(value); - } - } - } - - #region UpdateDeFromCa handlers - - - - internal void UpdateDeFromCa_OpAtt_Groups(ObjectClass oclass, - UpdateType type, DirectoryEntry directoryEntry, - ConnectorAttribute attribute) - { - if (oclass.Equals(ObjectClass.ACCOUNT)) - { - // in this case, AD will not allow groups to be added - // to a user. To simulate this, lookup each added group - // and add this user to the group - ICollection newValues = attribute.Value; - PropertyValueCollection oldValues = null; - if(directoryEntry.Properties.Contains(ActiveDirectoryConnector.ATT_MEMBEROF)) { - oldValues = directoryEntry.Properties[ActiveDirectoryConnector.ATT_MEMBEROF]; - } - - ICollection groupsToAdd = new HashSet(); - ICollection groupsToRemove = new HashSet(); - - GetAddsAndDeletes(groupsToAdd, groupsToRemove, oldValues, newValues, type); - - foreach (Object obj in groupsToRemove) - { - // lookup the group and remove this user from group if it's a - // valid group. - String groupPath = ActiveDirectoryUtils.GetLDAPPath( - _configuration.LDAPHostName, (String)obj); - DirectoryEntry groupDe = new DirectoryEntry(groupPath, - _configuration.DirectoryAdminName, _configuration.DirectoryAdminPassword); - String distinguishedName = ActiveDirectoryUtils.GetDnFromPath(directoryEntry.Path); - groupDe.Properties[ActiveDirectoryConnector.ATT_MEMBER].Remove(distinguishedName); - groupDe.CommitChanges(); - } - - foreach (Object obj in groupsToAdd) - { - // lookup the group and add this user to group if it's a - // valid group. - String groupPath = ActiveDirectoryUtils.GetLDAPPath( - _configuration.LDAPHostName, (String)obj); - DirectoryEntry groupDe = new DirectoryEntry(groupPath, - _configuration.DirectoryAdminName, _configuration.DirectoryAdminPassword); - String distinguishedName = ActiveDirectoryUtils.GetDnFromPath(directoryEntry.Path); - groupDe.Properties[ActiveDirectoryConnector.ATT_MEMBER].Add(distinguishedName); - groupDe.CommitChanges(); - } - } - else - { - throw new ConnectorException( - String.Format("''{0}'' is an invalid attribute for object class ''{1}''", - PredefinedAttributeInfos.GROUPS, oclass.GetObjectClassValue())); - } - } - - internal void UpdateDeFromCa_OpAtt_Accounts(ObjectClass oclass, - UpdateType type, DirectoryEntry directoryEntry, - ConnectorAttribute attribute) - { - if (ObjectClass.GROUP.Equals(oclass)) - { - // create an 'attribute' with the real name, and then call the - // generic version - ConnectorAttribute newAttribute = ConnectorAttributeBuilder.Build( - ActiveDirectoryConnector.ATT_MEMBER, attribute.Value); - UpdateDeFromCa_Att_Generic(oclass, type, directoryEntry, newAttribute); - } - else - { - throw new ConnectorException( - String.Format("'{0}' is an invalid attribute for object class '{1}'", - PredefinedAttributeInfos.ACCOUNTS, oclass.GetObjectClassValue())); - } - } - - internal void UpdateDeFromCa_Att_HomeDirectory(ObjectClass oclass, - UpdateType type, DirectoryEntry directoryEntry, - ConnectorAttribute attribute) - { - String homeDir = ConnectorAttributeUtil.GetStringValue(attribute); - - if (homeDir != null) - { - if (type == UpdateType.REPLACE) - { - // first set the attribute - UpdateDeFromCa_Att_Generic(oclass, type, directoryEntry, attribute); - - // now create attribute if needed/possible - if (_configuration.CreateHomeDirectory) - { - // from old code ... should start with '\\' and have at least one - // '\' later that's not the end of the string - // i.e - // \\somemachine\someshare\somedirectory - // \\somemachine\someshare\somedirectory\someotherdirectory - // but not - // \\somemachine\someshare\ - // \\somemachine\someshare - // just ignore if it's not correct - String directoryName = ConnectorAttributeUtil.GetStringValue(attribute); - if (directoryName.StartsWith("\\\\")) - { - int secondPathSepIndex = directoryName.IndexOf('\\', 2); - if ((secondPathSepIndex > 2) && (directoryName.Length > secondPathSepIndex + 1)) - { - // name passes, so create directory - - // create security object - DirectorySecurity dirSecurity = new DirectorySecurity(); - PropertyValueCollection pvc = - directoryEntry.Properties[ActiveDirectoryConnector.ATT_OBJECT_SID]; - // there should always be exactly one sid - SecurityIdentifier sid = new SecurityIdentifier((byte[])pvc[0], 0); - // dirSecurity.SetOwner(sid); - InheritanceFlags iFlags = InheritanceFlags.ContainerInherit; - dirSecurity.AddAccessRule( - new FileSystemAccessRule(sid, FileSystemRights.FullControl, - InheritanceFlags.ContainerInherit | InheritanceFlags.ObjectInherit, - PropagationFlags.None, AccessControlType.Allow) - ); - Directory.CreateDirectory(directoryName, dirSecurity); - } - } - } - } - else - { - throw new ConnectorException("Only updatetype of replace is supported for home directory"); - } - } - } - - internal void UpdateDeFromCa_OpAtt_Enable(ObjectClass oclass, - UpdateType type, DirectoryEntry directoryEntry, - ConnectorAttribute attribute) { - - // set the proper flag in the userAccountControl bitfield - PropertyValueCollection uacPvc = - directoryEntry.Properties[UserAccountControl.UAC_ATTRIBUTE_NAME]; - - UserAccountControl.Set(uacPvc, - UserAccountControl.ACCOUNTDISABLE, - // attribute is enable, but the flag is for - // disable, so send the opposite - !ConnectorAttributeUtil.GetBooleanValue(attribute)); - } - - internal void UpdateDeFromCa_OpAtt_PasswordExpired(ObjectClass oclass, - UpdateType type, DirectoryEntry directoryEntry, - ConnectorAttribute attribute) - { - bool? passwordExpired = ConnectorAttributeUtil.GetBooleanValue(attribute); - if ((passwordExpired.HasValue) && (passwordExpired.Value == true)) - { - directoryEntry.Properties[ActiveDirectoryConnector.ATT_PWD_LAST_SET].Clear(); - directoryEntry.Properties[ActiveDirectoryConnector.ATT_PWD_LAST_SET].Value = GetLargeIntegerFromLong(0); - } - else - { - // this value can't be set (other than to zero) I'm throwing my own exception - // here, because if not, AD thows this (at least on my machine): - // System.DirectoryServices.DirectoryServicesCOMException : A device attached to the system is not functioning. (Exception from HRESULT: 0x8007001F) - throw new ConnectorException(_configuration.ConnectorMessages.Format( - "ex_PasswordMustBeReset", - "Password expiration can only be reset by reseting the password")); - } - } - - internal void UpdateDeFromCa_OpAtt_PasswordExpireDate(ObjectClass oclass, - UpdateType type, DirectoryEntry directoryEntry, - ConnectorAttribute attribute) - { - DateTime? expireDate = ConnectorAttributeUtil.GetDateTimeValue(attribute); - if(expireDate.HasValue) { - directoryEntry.Properties[ActiveDirectoryConnector.ATT_ACCOUNT_EXPIRES].Value = - GetLargeIntegerFromLong((ulong)expireDate.Value.ToFileTime()); - } - } - - internal void UpdateDeFromCa_OpAtt_Lockout(ObjectClass oclass, - UpdateType type, DirectoryEntry directoryEntry, - ConnectorAttribute attribute) - { - bool? lockout = ConnectorAttributeUtil.GetBooleanValue(attribute); - if (lockout.HasValue) - { - long lockoutTime = lockout.Value ? DateTimeUtil.GetCurrentUtcTimeMillis() : 0; - - if(lockoutTime != 0) { - throw new ConnectorException(_configuration.ConnectorMessages.Format( - "ex_LockAccountNotAllowed", "Active Directory does not support locking users. User may be unlocked only")); - } - directoryEntry.Properties[ActiveDirectoryConnector.ATT_LOCKOUT_TIME].Value = - GetLargeIntegerFromLong((ulong)lockoutTime); - } - } - - // supporting class not implemented in the framework -/* - internal void UpdateDeFromCa_OpAtt_EnableDate(ObjectClass oclass, - UpdateType type, DirectoryEntry directoryEntry, - ConnectorAttribute attribute) - { - } - - internal void UpdateDeFromCa_OpAtt_DisableDate(ObjectClass oclass, - UpdateType type, DirectoryEntry directoryEntry, - ConnectorAttribute attribute) - { - / * - long? utcMilliDate = ConnectorAttributeUtil.GetLongValue(attribute); - if(utcMilliDate == null) { - return; - } - - DateTime disableDate = DateTime.FromFileTimeUtc((long)utcMilliDate); - - LargeInteger disableTicks = new LargeIntegerClass(); - disableTicks.HighPart = (int)((disableDate.Ticks >> 32) & 0xFFFFFFFF); - disableTicks.LowPart = (int)(disableDate.Ticks & 0xFFFFFFFF); - - PropertyValueCollection pvc = directoryEntry.Properties["accountExpires"]; - if ((pvc == null) || (pvc.Count == 0)) - { - // if nothing there, add the value - pvc.Add(disableTicks); - } - else - { - // set the value - pvc[0] = disableTicks; - } - * / - } -*/ - internal void UpdateDeFromCa_Att_TSAllowLogon(ObjectClass oclass, - UpdateType type, DirectoryEntry directoryEntry, - ConnectorAttribute attribute) - { - TerminalServicesUtils.SetAllowLogon(type, directoryEntry, - ConnectorAttributeUtil.GetIntegerValue(attribute)); - } - - internal void UpdateDeFromCa_Att_TSInitialProgram(ObjectClass oclass, - UpdateType type, DirectoryEntry directoryEntry, - ConnectorAttribute attribute) - { - TerminalServicesUtils.SetInitialProgram(type, directoryEntry, - ConnectorAttributeUtil.GetStringValue(attribute)); - } - - internal void UpdateDeFromCa_Att_TSInitialProgramDir(ObjectClass oclass, - UpdateType type, DirectoryEntry directoryEntry, - ConnectorAttribute attribute) - { - TerminalServicesUtils.SetInitialProgramDir(type, directoryEntry, - ConnectorAttributeUtil.GetStringValue(attribute)); - } - - internal void UpdateDeFromCa_Att_TSMaxConnectionTime(ObjectClass oclass, - UpdateType type, DirectoryEntry directoryEntry, - ConnectorAttribute attribute) - { - TerminalServicesUtils.SetMaxConnectionTime(type, directoryEntry, - ConnectorAttributeUtil.GetIntegerValue(attribute)); - } - - internal void UpdateDeFromCa_Att_TSMaxDisconnectionTime(ObjectClass oclass, - UpdateType type, DirectoryEntry directoryEntry, - ConnectorAttribute attribute) - { - TerminalServicesUtils.SetMaxDisconnectionTime(type, directoryEntry, - ConnectorAttributeUtil.GetIntegerValue(attribute)); - } - - internal void UpdateDeFromCa_Att_TSMaxIdleTime(ObjectClass oclass, - UpdateType type, DirectoryEntry directoryEntry, - ConnectorAttribute attribute) - { - TerminalServicesUtils.SetMaxIdleTime(type, directoryEntry, - ConnectorAttributeUtil.GetIntegerValue(attribute)); - } - - internal void UpdateDeFromCa_Att_TSConnectClientDrivesAtLogon(ObjectClass oclass, - UpdateType type, DirectoryEntry directoryEntry, - ConnectorAttribute attribute) - { - TerminalServicesUtils.SetConnectClientDrivesAtLogon(type, directoryEntry, - ConnectorAttributeUtil.GetIntegerValue(attribute)); - } - - internal void UpdateDeFromCa_Att_TSConnectClientPrintersAtLogon(ObjectClass oclass, - UpdateType type, DirectoryEntry directoryEntry, - ConnectorAttribute attribute) - { - TerminalServicesUtils.SetConnectClientPrintersAtLogon(type, directoryEntry, - ConnectorAttributeUtil.GetIntegerValue(attribute)); - } - - internal void UpdateDeFromCa_Att_TSDefaultToMainPrinter(ObjectClass oclass, - UpdateType type, DirectoryEntry directoryEntry, - ConnectorAttribute attribute) - { - TerminalServicesUtils.SetDefaultToMainPrinter(type, directoryEntry, - ConnectorAttributeUtil.GetIntegerValue(attribute)); - } - - internal void UpdateDeFromCa_Att_TSBrokenConnectionAction(ObjectClass oclass, - UpdateType type, DirectoryEntry directoryEntry, - ConnectorAttribute attribute) - { - TerminalServicesUtils.SetBrokenConnectionAction(type, directoryEntry, - ConnectorAttributeUtil.GetIntegerValue(attribute)); - } - - internal void UpdateDeFromCa_Att_TSReconnectionAction(ObjectClass oclass, - UpdateType type, DirectoryEntry directoryEntry, - ConnectorAttribute attribute) - { - TerminalServicesUtils.SetReconnectionAction(type, directoryEntry, - ConnectorAttributeUtil.GetIntegerValue(attribute)); - } - - internal void UpdateDeFromCa_Att_TSEnableRemoteControl(ObjectClass oclass, - UpdateType type, DirectoryEntry directoryEntry, - ConnectorAttribute attribute) - { - TerminalServicesUtils.SetEnableRemoteControl(type, directoryEntry, - ConnectorAttributeUtil.GetIntegerValue(attribute)); - } - - internal void UpdateDeFromCa_Att_TSProfilePath(ObjectClass oclass, - UpdateType type, DirectoryEntry directoryEntry, - ConnectorAttribute attribute) - { - TerminalServicesUtils.SetProfilePath(type, directoryEntry, - ConnectorAttributeUtil.GetStringValue(attribute)); - } - - internal void UpdateDeFromCa_Att_TSHomeDirectory(ObjectClass oclass, - UpdateType type, DirectoryEntry directoryEntry, - ConnectorAttribute attribute) - { - TerminalServicesUtils.SetHomeDirectory(type, directoryEntry, - ConnectorAttributeUtil.GetStringValue(attribute)); - } - - internal void UpdateDeFromCa_Att_TSHomeDrive(ObjectClass oclass, - UpdateType type, DirectoryEntry directoryEntry, - ConnectorAttribute attribute) - { - TerminalServicesUtils.SetHomeDrive(type, directoryEntry, - ConnectorAttributeUtil.GetStringValue(attribute)); - } - - internal void UpdateDeFromCa_Att_Generic(ObjectClass oclass, - UpdateType type, DirectoryEntry directoryEntry, - ConnectorAttribute attribute) - { - - // null out the values if we are replacing attributes. - if (type.Equals(UpdateType.REPLACE)) - { - directoryEntry.Properties[attribute.Name].Value = null; - } - - if (attribute.Value == null) - { - return; - } - // if we are updating or adding, put the - // new values in. - if (type.Equals(UpdateType.ADD) || - type.Equals(UpdateType.REPLACE)) - { - foreach (Object valueObject in attribute.Value) - { - directoryEntry.Properties[attribute.Name].Add(valueObject); - } - } - else if (type.Equals(UpdateType.DELETE)) - { - // if deleting, find the values, - // and remove them if they exist - if (directoryEntry.Properties.Contains(attribute.Name)) - { - PropertyValueCollection pvc = directoryEntry.Properties[attribute.Name]; - - foreach (Object valueObject in attribute.Value) - { - if (pvc.Contains(valueObject)) - { - pvc.Remove(valueObject); - } - } - } - - } - } - - #endregion - - #region GetCaFromDe Handlers - private ConnectorAttribute GetCaFromDe_Att_Generic( - ObjectClass oclass, string attributeName, SearchResult searchResult) - { - ConnectorAttributeBuilder attributeBuilder = new ConnectorAttributeBuilder(); - - if (attributeName == null) - { - return null; - } - - attributeBuilder.Name = attributeName; - - ResultPropertyValueCollection pvc = null; - if (searchResult.Properties.Contains(attributeName)) - { - pvc = searchResult.Properties[attributeName]; - } - - if (pvc == null) - { - return null; - } - else - { - for (int i = 0; i < pvc.Count; i++) - { - Object valueObject = pvc[i]; - if ((pvc[i] == null) || - (FrameworkUtil.IsSupportedAttributeType(valueObject.GetType()))) - { - attributeBuilder.AddValue(pvc[i]); - } - else - { - Trace.TraceWarning( - "Unsupported attribute type ... calling ToString (Name: \'{0}\'({1}) Type: \'{2}\' String Value: \'{3}\'", - attributeName, i, pvc[i].GetType(), pvc[i].ToString()); - attributeBuilder.AddValue(pvc[i].ToString()); - } - } - } - - return attributeBuilder.Build(); - } - - private ConnectorAttribute GetCaFromDe_OpAtt_Name( - ObjectClass oclass, string attributeName, SearchResult searchResult) - { - String value = null; - ResultPropertyValueCollection pvc = null; - - pvc = searchResult.Properties[ActiveDirectoryConnector.ATT_DISTINGUISHED_NAME]; - if ((pvc != null) && (pvc.Count == 1) && (pvc[0] is String)) - { - value = (String)pvc[0]; - } - else - { - throw new ConnectorException("There should be exactly one value for the name attribute"); - } - - return ConnectorAttributeBuilder.Build(Name.NAME, ActiveDirectoryUtils.NormalizeLdapString(value)); - } - - private ConnectorAttribute GetCaFromDe_OpAtt_Uid( - ObjectClass oclass, string attributeName, SearchResult searchResult) - { - ICollection value = new List(); - - if (ObjectClass.ACCOUNT.Equals(oclass)) - { - // uid is objectGuid - ResultPropertyValueCollection pvc = - searchResult.Properties[ActiveDirectoryConnector.ATT_OBJECT_GUID]; - - if ((pvc.Count == 1) && (pvc[0] is Byte[])) - { - value.Add(ActiveDirectoryUtils.ConvertUIDBytesToGUIDString((Byte[])pvc[0])); - } - else if (pvc.Count > 1) - { - throw new ConnectorException("There should be only one UID, but multiple values were specified"); - } - } - else - { - ConnectorAttribute name = GetCaFromDe_OpAtt_Name(oclass, attributeName, searchResult); - if ((name.Value != null) && (name.Value.Count != 0)) - { - value.Add(ActiveDirectoryUtils.NormalizeLdapString((String)name.Value[0])); - } - } - - return ConnectorAttributeBuilder.Build(Uid.NAME, value); - } - - private ConnectorAttribute GetCaFromDe_Att_Container( - ObjectClass oclass, string attributeName, SearchResult searchResult) - { - if (searchResult == null) - { - return null; - } - - DirectoryEntry parentDe = searchResult.GetDirectoryEntry().Parent; - String container = ""; - if (parentDe != null) - { - container = ActiveDirectoryUtils.GetDnFromPath(parentDe.Path); - } - - return ConnectorAttributeBuilder.Build( - ActiveDirectoryConnector.ATT_CONTAINER, container); - } - - private ConnectorAttribute GetCaFromDe_OpAtt_Groups( - ObjectClass oclass, string attributeName, SearchResult searchResult) - { - ConnectorAttribute realAttribute = GetCaFromDe_Att_Generic( - oclass, ActiveDirectoryConnector.ATT_MEMBEROF, searchResult); - if (realAttribute == null) - { - return null; - } - else - { - return ConnectorAttributeBuilder.Build(PredefinedAttributes.GROUPS_NAME, - realAttribute.Value); - } - } - - private ConnectorAttribute GetCaFromDe_OpAtt_Accounts( - ObjectClass oclass, string attributeName, SearchResult searchResult) - { - ConnectorAttribute realAttribute = GetCaFromDe_Att_Generic( - oclass, ActiveDirectoryConnector.ATT_MEMBER, searchResult); - if (realAttribute == null) - { - return null; - } - else - { - return ConnectorAttributeBuilder.Build(PredefinedAttributes.ACCOUNTS_NAME, - realAttribute.Value); - } - } - - private ConnectorAttribute GetCaFromDe_OpAtt_Enabled( - ObjectClass oclass, string attributeName, SearchResult searchResult) - { - if (searchResult == null) - { - return null; - } - - bool disabled = UserAccountControl.IsSet( - searchResult.GetDirectoryEntry().Properties[UserAccountControl.UAC_ATTRIBUTE_NAME], - UserAccountControl.ACCOUNTDISABLE); - - return ConnectorAttributeBuilder.BuildEnabled(!disabled); - } - - private ConnectorAttribute GetCaFromDe_OpAtt_PasswordExpired( - ObjectClass oclass, string attributeName, SearchResult searchResult) - { - ConnectorAttribute realAttribute = GetCaFromDe_Att_Generic( - oclass, ActiveDirectoryConnector.ATT_PWD_LAST_SET, searchResult); - long? lastSetDate = ConnectorAttributeUtil.GetLongValue(realAttribute); - if ((lastSetDate.HasValue) && (lastSetDate.Value != 0)) - { - return ConnectorAttributeBuilder.BuildPasswordExpired(false); - } - - return ConnectorAttributeBuilder.BuildPasswordExpired(true); - } - - private ConnectorAttribute GetCaFromDe_OpAtt_Description( - ObjectClass oclass, string attributeName, SearchResult searchResult) - { - ConnectorAttribute realDescription = GetCaFromDe_Att_Generic( - oclass, ActiveDirectoryConnector.ATT_DESCRIPTION, searchResult); - - if (realDescription != null) - { - string description = ConnectorAttributeUtil.GetStringValue(realDescription); - - if (description != null) - { - return ConnectorAttributeBuilder.Build(PredefinedAttributes.DESCRIPTION, description); - } - } - return null; - } - - private ConnectorAttribute GetCaFromDe_OpAtt_ShortName( - ObjectClass oclass, string attributeName, SearchResult searchResult) - { - ConnectorAttribute realShortName = GetCaFromDe_Att_Generic( - oclass, ActiveDirectoryConnector.ATT_SHORT_NAME, searchResult); - - if (realShortName != null) - { - string shortName = ConnectorAttributeUtil.GetStringValue(realShortName); - - if (shortName != null) - { - return ConnectorAttributeBuilder.Build(PredefinedAttributes.SHORT_NAME, shortName); - } - } - return null; - } - - private ConnectorAttribute GetCaFromDe_OpAtt_PasswordExpireDate( - ObjectClass oclass, string attributeName, SearchResult searchResult) - { - // get the value from ad - ConnectorAttribute accountExpireAttribute = GetCaFromDe_Att_Generic( - oclass, ActiveDirectoryConnector.ATT_ACCOUNT_EXPIRES, searchResult); - - // now change name - if (accountExpireAttribute != null) - { - long? expireValue = ConnectorAttributeUtil.GetLongValue(accountExpireAttribute); - if (expireValue != null) - { - return ConnectorAttributeBuilder.BuildPasswordExpirationDate(expireValue.Value); - } - else - { - return null; - } - } - return null; - } - - private ConnectorAttribute GetCaFromDe_OpAtt_Lockout( - ObjectClass oclass, string attributeName, SearchResult searchResult) - { - bool locked = false; - - ConnectorAttribute realAttribute = GetCaFromDe_Att_Generic( - oclass, ActiveDirectoryConnector.ATT_LOCKOUT_TIME, searchResult); - if (realAttribute != null) - { - long? lockoutDate = ConnectorAttributeUtil.GetLongValue(realAttribute); - if ((lockoutDate.HasValue) && (lockoutDate.Value != 0)) - { - // if there is a date (non zero), then the account - // is locked - locked = true; - } - } - return ConnectorAttributeBuilder.BuildLockOut(locked); - } - - // supporting class not implemented in the framework -/* - private ConnectorAttribute GetCaFromDe_OpAtt_EnableDate( - ObjectClass oclass, string attributeName, SearchResult searchResult) - { - return null; - } - - private ConnectorAttribute GetCaFromDe_OpAtt_DisableDate( - ObjectClass oclass, string attributeName, SearchResult searchResult) - { - / * - if (searchResult == null) - { - return null; - } - - ResultPropertyValueCollection rpvc = - searchResult.Properties["accountExpires"]; - if(rpvc.Count == 0) { - return null; - } - - long ticks = (long)rpvc[0]; - if ((ticks < DateTime.MinValue.Ticks) || (ticks > DateTime.MaxValue.Ticks)) - { - return null; - } - - DateTime disableDate = new DateTime(ticks); - - return ConnectorAttributeBuilder.BuildDisableDate(disableDate); - * / - return null; - } - */ - - private ConnectorAttribute GetCaFromDe_Att_TSInitialProgram( - ObjectClass oclass, string attributeName, SearchResult searchResult) - { - return ReturnConnectorAttribute(TerminalServicesUtils.TS_INITIAL_PROGRAM, - TerminalServicesUtils.GetInitialProgram(searchResult)); - } - - private ConnectorAttribute GetCaFromDe_Att_TSInitalProgramDir( - ObjectClass oclass, string attributeName, SearchResult searchResult) - { - return ReturnConnectorAttribute(TerminalServicesUtils.TS_INITIAL_PROGRAM_DIR, - TerminalServicesUtils.GetInitialProgramDir(searchResult)); - } - - - private ConnectorAttribute GetCaFromDe_Att_TSAllowLogon( - ObjectClass oclass, string attributeName, SearchResult searchResult) - { - return ReturnConnectorAttribute(TerminalServicesUtils.TS_ALLOW_LOGON, - TerminalServicesUtils.GetAllowLogon(searchResult)); - } - - private ConnectorAttribute GetCaFromDe_Att_TSMaxConnectionTime( - ObjectClass oclass, string attributeName, SearchResult searchResult) - { - return ReturnConnectorAttribute(TerminalServicesUtils.TS_MAX_CONNECTION_TIME, - TerminalServicesUtils.GetMaxConnectionTime(searchResult)); - } - - private ConnectorAttribute GetCaFromDe_Att_TSMaxDisconnectionTime( - ObjectClass oclass, string attributeName, SearchResult searchResult) - { - return ReturnConnectorAttribute(TerminalServicesUtils.TS_MAX_DISCONNECTION_TIME, - TerminalServicesUtils.GetMaxDisconnectionTime(searchResult)); - } - - private ConnectorAttribute GetCaFromDe_Att_TSMaxIdleTime( - ObjectClass oclass, string attributeName, SearchResult searchResult) - { - return ReturnConnectorAttribute(TerminalServicesUtils.TS_MAX_IDLE_TIME, - TerminalServicesUtils.GetMaxIdleTime(searchResult)); - } - - private ConnectorAttribute GetCaFromDe_Att_TSConnectClientDrivesAtLogon( - ObjectClass oclass, string attributeName, SearchResult searchResult) - { - return ReturnConnectorAttribute(TerminalServicesUtils.TS_CONNECT_CLIENT_DRIVES_AT_LOGON, - TerminalServicesUtils.GetConnectClientDrivesAtLogon(searchResult)); - } - - private ConnectorAttribute GetCaFromDe_Att_TSConnectClientPrintersAtLogon( - ObjectClass oclass, string attributeName, SearchResult searchResult) - { - return ReturnConnectorAttribute( - TerminalServicesUtils.TS_CONNECT_CLIENT_PRINTERS_AT_LOGON, - TerminalServicesUtils.GetConnectClientPrintersAtLogon(searchResult)); - } - - private ConnectorAttribute GetCaFromDe_Att_TSDefaultToMainPrinter( - ObjectClass oclass, string attributeName, SearchResult searchResult) - { - return ReturnConnectorAttribute( - TerminalServicesUtils.TS_DEFAULT_TO_MAIN_PRINTER, - TerminalServicesUtils.GetDefaultToMainPrinter(searchResult)); - } - - private ConnectorAttribute GetCaFromDe_Att_TSBrokenConnectionAction( - ObjectClass oclass, string attributeName, SearchResult searchResult) - { - return ReturnConnectorAttribute( - TerminalServicesUtils.TS_BROKEN_CONNECTION_ACTION, - TerminalServicesUtils.GetBrokenConnectionAction(searchResult)); - } - - private ConnectorAttribute GetCaFromDe_Att_TSReconnectionAction( - ObjectClass oclass, string attributeName, SearchResult searchResult) - { - return ReturnConnectorAttribute(TerminalServicesUtils.TS_RECONNECTION_ACTION, - TerminalServicesUtils.GetReconnectionAction(searchResult)); - } - - private ConnectorAttribute GetCaFromDe_Att_TSEnableRemoteControl( - ObjectClass oclass, string attributeName, SearchResult searchResult) - { - return ReturnConnectorAttribute(TerminalServicesUtils.TS_ENABLE_REMOTE_CONTROL, - TerminalServicesUtils.GetEnableRemoteControl(searchResult)); - } - - private ConnectorAttribute GetCaFromDe_Att_TSProfilePath( - ObjectClass oclass, string attributeName, SearchResult searchResult) - { - return ReturnConnectorAttribute(TerminalServicesUtils.TS_PROFILE_PATH, - TerminalServicesUtils.GetProfilePath(searchResult)); - } - private ConnectorAttribute GetCaFromDe_Att_TSHomeDirectory( - ObjectClass oclass, string attributeName, SearchResult searchResult) - { - return ReturnConnectorAttribute(TerminalServicesUtils.TS_HOME_DIRECTORY, - TerminalServicesUtils.GetHomeDirectory(searchResult)); - } - private ConnectorAttribute GetCaFromDe_Att_TSHomeDrive( - ObjectClass oclass, string attributeName, SearchResult searchResult) - { - return ReturnConnectorAttribute(TerminalServicesUtils.TS_HOME_DRIVE, - TerminalServicesUtils.GetHomeDrive(searchResult)); - } - - #endregion - - internal ConnectorAttribute ReturnConnectorAttribute - (string name, T value) { - ConnectorAttribute newAttribute = null; - - if (value != null) - { - newAttribute = ConnectorAttributeBuilder.Build( - name, value); - } - return newAttribute; - } - - // gets a long from a LargeInteger (COM object) - ulong GetLongFromLargeInteger(LargeInteger largeInteger) - { - ulong longValue = ((ulong)largeInteger.HighPart) << 32; - longValue += (ulong)largeInteger.LowPart; - return longValue; - } - - // sets a LargeInteger (COM object) from a long - LargeInteger GetLargeIntegerFromLong(ulong longValue) - { - LargeInteger largeInteger = new LargeIntegerClass(); - largeInteger.HighPart = (int)((longValue & 0xFFFFFFFF00000000) >> 32); - largeInteger.LowPart = (int)(longValue & 0x00000000FFFFFFFF); - return largeInteger; - } - } - -} diff --git a/DotNetConnectors.sln/ActiveDirectoryConnector/Messages.en.Designer.cs b/DotNetConnectors.sln/ActiveDirectoryConnector/Messages.en.Designer.cs deleted file mode 100644 index e69de29b..00000000 diff --git a/DotNetConnectors.sln/ActiveDirectoryConnector/Messages.en.resx b/DotNetConnectors.sln/ActiveDirectoryConnector/Messages.en.resx deleted file mode 100644 index aacdfbf3..00000000 --- a/DotNetConnectors.sln/ActiveDirectoryConnector/Messages.en.resx +++ /dev/null @@ -1,123 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - text/microsoft-resx - - - 2.0 - - - System.Resources.ResXResourceReader, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 - - - System.Resources.ResXResourceWriter, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 - - - ActiveDirectory Connector - - \ No newline at end of file diff --git a/DotNetConnectors.sln/ActiveDirectoryConnector/Messages.es-ES.resx b/DotNetConnectors.sln/ActiveDirectoryConnector/Messages.es-ES.resx deleted file mode 100644 index 03886a03..00000000 --- a/DotNetConnectors.sln/ActiveDirectoryConnector/Messages.es-ES.resx +++ /dev/null @@ -1,123 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - text/microsoft-resx - - - 2.0 - - - System.Resources.ResXResourceReader, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 - - - System.Resources.ResXResourceWriter, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 - - - Active Direcory Connector - - \ No newline at end of file diff --git a/DotNetConnectors.sln/ActiveDirectoryConnector/Messages.es.resx b/DotNetConnectors.sln/ActiveDirectoryConnector/Messages.es.resx deleted file mode 100644 index aacdfbf3..00000000 --- a/DotNetConnectors.sln/ActiveDirectoryConnector/Messages.es.resx +++ /dev/null @@ -1,123 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - text/microsoft-resx - - - 2.0 - - - System.Resources.ResXResourceReader, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 - - - System.Resources.ResXResourceWriter, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 - - - ActiveDirectory Connector - - \ No newline at end of file diff --git a/DotNetConnectors.sln/ActiveDirectoryConnector/Messages.resx b/DotNetConnectors.sln/ActiveDirectoryConnector/Messages.resx deleted file mode 100644 index d685fb80..00000000 --- a/DotNetConnectors.sln/ActiveDirectoryConnector/Messages.resx +++ /dev/null @@ -1,264 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - text/microsoft-resx - - - 2.0 - - - System.Resources.ResXResourceReader, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 - - - System.Resources.ResXResourceWriter, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 - - - Windows Active Directory Connector - - - Create Home Directory - - - Directory Adminstrator''s Account - - - Directory Administrator''s Password - - - Domain Name - - - Active Directory Domain Controller Hostname - - - Object Class for User Objects - - - Search Child Domains - - - Search Container - - - Sync Domain Controller - - - Sync Global Catalog Server - - - Sync Search Context - - - Specify whether or not the home directory for the user will be created. - - - Enter the administrator user name with which the system should authenticate. The setting can be either be a username or 'domainname'\'username'. - - - Enter the password that should be used when authenticating. - - - Name of the windows domain (e.g. windowsdomain.mycompany.com) - - - For cross-domain administration, enter the hostname, IP address, or domain name of the LDAP server. - - - Specify the Active Directory object class for user objects that will be managed on this resource. The default is User, and for most situations, this should be fine. - - - Select if you want searches of Active Directory to include child domains. In addition, the Search Container and Sync Search Context (see sync settings) attributes must be set to the top of the parent domain, e.g. DC=mydomain,DC=com. - - - Specify a container object which will be the default root of all searches. Unless a search explicitly passes in other criteria, only objects under this container will be searched. For example, if you want to retrieve users from the Users container, enter CN=Users,DC=MYDOMAIN,DC=COM. - - - Domain controller to use during active sync. Only used if not searching child domains. - - - Name of the global catalog server. This is needed only if searching child domains. - - - Distinguished name of the object under which to search for changes during sync operation. - - - Connector has not been configured - - - Delete is not supported for ObjectClass {0} - - - Invalid object class: {0} - - - Attribute {0} is not present in connector object. Cannot proceed with synchronization - - - The name operational attribute cannot be null - - - Sync operation is not available for ObjectClass {0} - - - Uid was not present - - - Invalid Object Class was specified in the connector configuration. Object Class \'{0}\' was not found in Active Directory - - - Could not add connector attribute to <null> search result - - - Could not add connector attribute to <null> directory entry - - - Expecting single value, but found multiple values for attribute {0} - - - ObjectClass \'{0}\' is not valid for this connector - - - Invalid credentials supplied for user {0} - - - Password expiration can only be reset by reseting the password - - - Active Directory does not support locking users. User may be unlocked only - - - An invalid searchscope was supplied: {0} - - - An execption occurred during validation of user {0}. The user was successfully authenticated, but the user's guid could not be determined. - - - An execption occurred during validation of user {0}. The user was successfully authenticated, but the user's sid could not be determined. - - - Directory administrator name not supplied. - - - Directory administrator password not supplied. - - - Domain name not supplied. - - - ObjectClass was not supplied. - - - Search Container was not supplied. - - - Using Identity Manger Resource Adapter style query '{0}'. This should be updated to use the new connector query syntax. - - - An invalid search container was supplied: {0} - - \ No newline at end of file diff --git a/DotNetConnectors.sln/ActiveDirectoryConnector/ObjectClasses.xml b/DotNetConnectors.sln/ActiveDirectoryConnector/ObjectClasses.xml deleted file mode 100644 index 7dd21635..00000000 --- a/DotNetConnectors.sln/ActiveDirectoryConnector/ObjectClasses.xml +++ /dev/null @@ -1,262 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/DotNetConnectors.sln/ActiveDirectoryConnector/PasswordChangeHandler.cs b/DotNetConnectors.sln/ActiveDirectoryConnector/PasswordChangeHandler.cs deleted file mode 100644 index 98b3efbc..00000000 --- a/DotNetConnectors.sln/ActiveDirectoryConnector/PasswordChangeHandler.cs +++ /dev/null @@ -1,237 +0,0 @@ -/* - * ==================== - * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER. - * - * Copyright 2007-2008 Sun Microsystems, Inc. All rights reserved. - * - * The contents of this file are subject to the terms of the Common Development - * and Distribution License("CDDL") (the "License"). You may not use this file - * except in compliance with the License. - * - * You can obtain a copy of the License at - * http://IdentityConnectors.dev.java.net/legal/license.txt - * See the License for the specific language governing permissions and limitations - * under the License. - * - * When distributing the Covered Code, include this CDDL Header Notice in each file - * and include the License file at identityconnectors/legal/license.txt. - * If applicable, add the following below this CDDL Header, with the fields - * enclosed by brackets [] replaced by your own identifying information: - * "Portions Copyrighted [year] [name of copyright owner]" - * ==================== - */ -using System; -using System.Collections.Generic; -using System.Linq; -using System.Text; -using System.DirectoryServices; -using Org.IdentityConnectors.Common.Security; -using ActiveDs; -using Org.IdentityConnectors.Framework.Common.Exceptions; -using System.DirectoryServices.AccountManagement; -using System.DirectoryServices.ActiveDirectory; -using System.Threading; -using Org.IdentityConnectors.Framework.Common.Objects; -using System.Security.Principal; - -namespace Org.IdentityConnectors.ActiveDirectory -{ - - /** - * This class will decrypt passwords, and handle - * authentication and password changes (both - * administrative and user) - */ - internal class PasswordChangeHandler - { - String _currentPassword; - String _newPassword; - ActiveDirectoryConfiguration _configuration = null; - static Semaphore authenticationSem = new Semaphore(1, 1, "ActiveDirectoryConnectorAuthSem"); - static readonly int ERR_PASSWORD_MUST_BE_CHANGED = -2147022989; - static readonly int ERR_PASSWORD_EXPIRED = -2147023688; - - - internal PasswordChangeHandler(ActiveDirectoryConfiguration configuration) - { - _configuration = configuration; - } - - /// - /// sets the _currentPassword variable - /// - /// - internal void setCurrentPassword(UnmanagedArray clearChars) - { - _currentPassword = ""; - - // build up the string from the unmanaged array - for (int i = 0; i < clearChars.Length; i++) - { - _currentPassword += clearChars[i]; - } - } - - /// - /// Sets the _newPassword variable - /// - /// - internal void setNewPassword(UnmanagedArray clearChars) - { - _newPassword = ""; - - // build up the string from the unmanaged array - for (int i = 0; i < clearChars.Length; i++) - { - _newPassword += clearChars[i]; - } - } - - /// - /// Does an administrative password change. The Directory - /// entry must be created with username and password of - /// a user with permission to change the password - /// - /// - /// - internal void changePassword(DirectoryEntry directoryEntry, - GuardedString gsNewPassword) - { - // decrypt and save the new password - gsNewPassword.Access(setNewPassword); - - // get the native com object as an IADsUser, and set the - // password - IADsUser user = (IADsUser)directoryEntry.NativeObject; - user.SetPassword(_newPassword); - } - - /// - /// Does a user password change. Must supply the currentpassword - /// and the new password - /// - /// - /// - /// - internal void changePassword(DirectoryEntry directoryEntry, - GuardedString gsCurrentPassword, GuardedString gsNewPassword) - { - // decrypt and save the old nad new passwords - gsNewPassword.Access(setNewPassword); - gsCurrentPassword.Access(setCurrentPassword); - - // get the native com object as an IADsUser, and change the - // password - IADsUser user = (IADsUser)directoryEntry.NativeObject; - user.ChangePassword(_currentPassword, _newPassword); - } - - /// - /// Authenticates the user - /// - /// - /// - /// - internal Uid Authenticate(/*DirectoryEntry directoryEntry,*/ string username, - Org.IdentityConnectors.Common.Security.GuardedString password) - { - password.Access(setCurrentPassword); - - // create principle context for authentication - string serverName = _configuration.LDAPHostName; - PrincipalContext context = null; - UserPrincipal userPrincipal = null; - try - { - // according to microsoft docs: - // Wait on return - true if the current instance receives a signal. If the current instance is never signaled, WaitOne never returns. - // no need to check return, since it will not return false; - authenticationSem.WaitOne(); - if ((serverName == null) || (serverName.Length == 0)) - { - // if they haven't specified an ldap host, use the domain that is - // in the connector configuration - DomainController domainController = ActiveDirectoryUtils.GetDomainController(_configuration); - context = new PrincipalContext(ContextType.Domain, - domainController.Domain.Name, _configuration.DirectoryAdminName, - _configuration.DirectoryAdminPassword); - } - else - { - // if the specified an ldap host, use it. - context = new PrincipalContext(ContextType.Machine, - _configuration.LDAPHostName, _configuration.DirectoryAdminName, - _configuration.DirectoryAdminPassword); - } - - if (context == null) - { - throw new ConnectorException("Unable to get PrincipalContext"); - } - - if (!context.ValidateCredentials(username, _currentPassword)) - { - throw new InvalidCredentialException(_configuration.ConnectorMessages.Format( - "ex_InvalidCredentials", "Invalid credentials supplied for user {0}", - username)); - } - return GetUidFromSamAccountName(context, username); - } - catch (PrincipalOperationException e) - { - if ((e.ErrorCode.Equals(ERR_PASSWORD_MUST_BE_CHANGED)) || - (e.ErrorCode.Equals(ERR_PASSWORD_EXPIRED))) - { - Uid uid = GetUidFromSamAccountName(context, username); - PasswordExpiredException exception = new PasswordExpiredException(e.Message); - exception.Uid = uid; - throw exception; - } - - throw; - } - finally - { - if (context != null) - { - context.Dispose(); - context = null; - } - authenticationSem.Release(); - } - } - - public Uid GetUidFromSamAccountName(PrincipalContext context, String sAMAccountName) - { - UserPrincipal userPrincipal = null; - - try - { - userPrincipal = UserPrincipal.FindByIdentity(context, - IdentityType.SamAccountName, sAMAccountName); - - if (userPrincipal.Sid == null) - { - throw new ConnectorException(_configuration.ConnectorMessages.Format( - "ex_SIDLookup", "An execption occurred during validation of user {0}. The user was successfully authenticated, but the user's sid could not be determined.", - sAMAccountName)); - } - - string sidString = ""; - DirectoryEntry userDe = new DirectoryEntry( - ActiveDirectoryUtils.GetLDAPPath(_configuration.LDAPHostName, sidString), - _configuration.DirectoryAdminName, _configuration.DirectoryAdminPassword); - - return new Uid(ActiveDirectoryUtils.ConvertUIDBytesToGUIDString(userDe.Guid.ToByteArray())); - } - finally - { - if (userPrincipal != null) - { - userPrincipal.Dispose(); - userPrincipal = null; - } - } - } - } -} diff --git a/DotNetConnectors.sln/ActiveDirectoryConnector/TerminalServicesUtils.cs b/DotNetConnectors.sln/ActiveDirectoryConnector/TerminalServicesUtils.cs deleted file mode 100644 index 599506d2..00000000 --- a/DotNetConnectors.sln/ActiveDirectoryConnector/TerminalServicesUtils.cs +++ /dev/null @@ -1,304 +0,0 @@ -/* - * ==================== - * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER. - * - * Copyright 2007-2008 Sun Microsystems, Inc. All rights reserved. - * - * The contents of this file are subject to the terms of the Common Development - * and Distribution License("CDDL") (the "License"). You may not use this file - * except in compliance with the License. - * - * You can obtain a copy of the License at - * http://IdentityConnectors.dev.java.net/legal/license.txt - * See the License for the specific language governing permissions and limitations - * under the License. - * - * When distributing the Covered Code, include this CDDL Header Notice in each file - * and include the License file at identityconnectors/legal/license.txt. - * If applicable, add the following below this CDDL Header, with the fields - * enclosed by brackets [] replaced by your own identifying information: - * "Portions Copyrighted [year] [name of copyright owner]" - * ==================== - */ -using System; -using System.Collections.Generic; -using System.Linq; -using System.Text; -using System.DirectoryServices; -using Org.IdentityConnectors.Framework.Spi.Operations; -using Org.IdentityConnectors.Framework.Common.Exceptions; - -namespace Org.IdentityConnectors.ActiveDirectory -{ - /** - * This class will handle setting all of the terminal services attributes - */ - public class TerminalServicesUtils - { - // used to be 'Terminal Services Initial Program' - public static string TS_INITIAL_PROGRAM = "TerminalServicesInitialProgram"; - - // used to be 'Terminal Services Initial Program Directory' - public static string TS_INITIAL_PROGRAM_DIR = "TerminalServicesWorkDirectory"; - - // used to be 'Terminal Services Inherit Initial Program' - - // used to be 'Terminal Services Allow Logon' - defaults to false, so testing true - public static string TS_ALLOW_LOGON = "AllowLogon"; - - // used to be 'Terminal Services Active Session Timeout' - public static string TS_MAX_CONNECTION_TIME = "MaxConnectionTime"; - - // used to be 'Terminal Services Disconnected Session Timeout' - public static string TS_MAX_DISCONNECTION_TIME = "MaxDisconnectionTime"; - - // used to be 'Terminal Services Idle Timeout' - public static string TS_MAX_IDLE_TIME = "MaxIdleTime"; - - // used to be 'Terminal Services Connect Client Drives At Logon' - public static string TS_CONNECT_CLIENT_DRIVES_AT_LOGON = "ConnectClientDrivesAtLogon"; - - // used to be 'Terminal Services Connect Client Printers At Logon' - public static string TS_CONNECT_CLIENT_PRINTERS_AT_LOGON = "ConnectClientPrintersAtLogon"; - - // used to be 'Terminal Services Default To Main Client Printer' - public static string TS_DEFAULT_TO_MAIN_PRINTER = "DefaultToMainPrinter"; - - // used to be 'Terminal Services End Session On Timeout Or Broken Connection' - public static string TS_BROKEN_CONNECTION_ACTION = "BrokenConnectionAction"; - - // used to be 'Terminal Services Allow Reconnect From Originating Client Only' - public static string TS_RECONNECTION_ACTION = "ReconnectionAction"; - - // used to be 'Terminal Services Callback Settings' - - // used to be 'Terminal Services Callback Phone Number' - - // used to be 'Terminal Services Remote Control Settings' - public static string TS_ENABLE_REMOTE_CONTROL = "EnableRemoteControl"; - - // used to be 'Terminal Services User Profile' - public static string TS_PROFILE_PATH = "TerminalServicesProfilePath"; - - // used to be 'Terminal Services Local Home Directory' - public static string TS_HOME_DIRECTORY = "TerminalServicesHomeDirectory"; - - // used to be 'Terminal Services Home Directory Drive' - public static string TS_HOME_DRIVE = "TerminalServicesHomeDrive"; - - private static T GetValue(SearchResult searchResult, string name, T defaultValue) - { - // get the directory entry - DirectoryEntry directoryEntry = searchResult.GetDirectoryEntry(); - - // get 'name' from the directory entry, and return it if it exists - object result = directoryEntry.InvokeGet(name); - if (result != null) - { - T value = (T)result; - return value; - } - - // if the name didn't exist, return 'defaultValue' - return defaultValue; - } - - internal static void SetValue(UpdateType type, - DirectoryEntry directoryEntry, string name, T value) - { - if (!type.Equals(UpdateType.REPLACE)) - { - // Only allow replace on single value attributes, - // and for now, all terminal services are single value - ThrowInvalidUpdateType(name); - } - - if (value == null) - { - // just ignore a null - return; - } - - // invoke set on 'name' with 'value' - directoryEntry.InvokeSet(name, value); - } - - private static void ThrowInvalidUpdateType(string attributeName) - { - // throws an exception that says invalid update type - string msg = string.Format("The update type specified is invalid for the terminal services attribute ''{0}''", - attributeName); - throw new ConnectorException(msg); - } - - internal static string GetInitialProgram(SearchResult searchResult) - { - return GetValue(searchResult, TS_INITIAL_PROGRAM, null); - } - - internal static void SetInitialProgram(UpdateType type, DirectoryEntry directoryEntry, - string initialProgram) - { - SetValue(type, directoryEntry, TS_INITIAL_PROGRAM, initialProgram); - } - - internal static string GetInitialProgramDir(SearchResult searchResult) - { - return GetValue(searchResult, TS_INITIAL_PROGRAM_DIR, null); - } - - internal static void SetInitialProgramDir(UpdateType type, - DirectoryEntry directoryEntry, string initialProgramDir) - { - SetValue(type, directoryEntry, TS_INITIAL_PROGRAM_DIR, initialProgramDir); - } - - internal static int? GetAllowLogon(SearchResult searchResult) - { - return GetValue(searchResult, TS_ALLOW_LOGON, null); - } - - internal static void SetAllowLogon(UpdateType type, DirectoryEntry directoryEntry, - int? isAllowed) - { - SetValue(type, directoryEntry, TS_ALLOW_LOGON, isAllowed); - } - - internal static int? GetMaxConnectionTime(SearchResult searchResult) - { - return GetValue(searchResult, TS_MAX_CONNECTION_TIME, null); - } - - internal static void SetMaxConnectionTime(UpdateType type, DirectoryEntry directoryEntry, - int? maxConnectionTime) - { - SetValue(type, directoryEntry, TS_MAX_CONNECTION_TIME, maxConnectionTime); - } - - internal static int? GetMaxDisconnectionTime(SearchResult searchResult) - { - return GetValue(searchResult, TS_MAX_DISCONNECTION_TIME, null); - } - - internal static void SetMaxDisconnectionTime(UpdateType type, - DirectoryEntry directoryEntry, int? maxDisconnectionTime) - { - SetValue(type, directoryEntry, TS_MAX_DISCONNECTION_TIME, maxDisconnectionTime); - } - - internal static int? GetMaxIdleTime(SearchResult searchResult) - { - return GetValue(searchResult, TS_MAX_IDLE_TIME, null); - } - - internal static void SetMaxIdleTime(UpdateType type, DirectoryEntry directoryEntry, - int? maxIdleTime) - { - SetValue(type, directoryEntry, TS_MAX_IDLE_TIME, maxIdleTime); - } - - internal static int? GetConnectClientDrivesAtLogon(SearchResult searchResult) - { - return GetValue(searchResult, TS_CONNECT_CLIENT_DRIVES_AT_LOGON, null); - } - - internal static void SetConnectClientDrivesAtLogon(UpdateType type, - DirectoryEntry directoryEntry, int? connectClientDrivesAtLogon) - { - SetValue(type, directoryEntry, TS_CONNECT_CLIENT_DRIVES_AT_LOGON, - connectClientDrivesAtLogon); - } - - internal static int? GetConnectClientPrintersAtLogon(SearchResult searchResult) - { - return GetValue(searchResult, TS_CONNECT_CLIENT_PRINTERS_AT_LOGON, null); - } - - internal static void SetConnectClientPrintersAtLogon(UpdateType type, - DirectoryEntry directoryEntry, int? connectClientPrintersAtLogon) - { - SetValue(type, directoryEntry, TS_CONNECT_CLIENT_PRINTERS_AT_LOGON, - connectClientPrintersAtLogon); - } - - internal static int? GetDefaultToMainPrinter(SearchResult searchResult) - { - return GetValue(searchResult, TS_DEFAULT_TO_MAIN_PRINTER, null); - } - - internal static void SetDefaultToMainPrinter(UpdateType type, - DirectoryEntry directoryEntry, int? defaultToMainPrinter) - { - SetValue(type, directoryEntry, TS_DEFAULT_TO_MAIN_PRINTER, - defaultToMainPrinter); - } - - internal static int? GetBrokenConnectionAction(SearchResult searchResult) - { - return GetValue(searchResult, TS_BROKEN_CONNECTION_ACTION, null); - } - - internal static void SetBrokenConnectionAction(UpdateType type, - DirectoryEntry directoryEntry, int? brokenConnectionAction) - { - SetValue(type, directoryEntry, TS_BROKEN_CONNECTION_ACTION, - brokenConnectionAction); - } - - internal static int? GetReconnectionAction(SearchResult searchResult) - { - return GetValue(searchResult, TS_RECONNECTION_ACTION, null); - } - - internal static void SetReconnectionAction(UpdateType type, - DirectoryEntry directoryEntry, int? reconnectionAction) - { - SetValue(type, directoryEntry, TS_RECONNECTION_ACTION, reconnectionAction); - } - - internal static int? GetEnableRemoteControl(SearchResult searchResult) - { - return GetValue(searchResult, TS_ENABLE_REMOTE_CONTROL, null); - } - - internal static void SetEnableRemoteControl(UpdateType type, - DirectoryEntry directoryEntry, int? enableRemoteControl) - { - SetValue(type, directoryEntry, TS_ENABLE_REMOTE_CONTROL, enableRemoteControl); - } - - internal static string GetProfilePath(SearchResult searchResult) - { - return GetValue(searchResult, TS_PROFILE_PATH, null); - } - - internal static void SetProfilePath(UpdateType type, - DirectoryEntry directoryEntry, string profilePath) - { - SetValue(type, directoryEntry, TS_PROFILE_PATH, profilePath); - } - - internal static string GetHomeDirectory(SearchResult searchResult) - { - return GetValue(searchResult, TS_HOME_DIRECTORY, null); - } - - internal static void SetHomeDirectory(UpdateType type, - DirectoryEntry directoryEntry, string homeDirectory) - { - SetValue(type, directoryEntry, TS_HOME_DIRECTORY, homeDirectory); - } - - internal static string GetHomeDrive(SearchResult searchResult) - { - return GetValue(searchResult, TS_HOME_DRIVE, null); - } - - internal static void SetHomeDrive(UpdateType type, - DirectoryEntry directoryEntry, string homeDrive) - { - SetValue(type, directoryEntry, TS_HOME_DRIVE, homeDrive); - } - - } -} diff --git a/DotNetConnectors.sln/ActiveDirectoryConnector/UserAccountControl.cs b/DotNetConnectors.sln/ActiveDirectoryConnector/UserAccountControl.cs deleted file mode 100644 index 074de031..00000000 --- a/DotNetConnectors.sln/ActiveDirectoryConnector/UserAccountControl.cs +++ /dev/null @@ -1,110 +0,0 @@ -/* - * ==================== - * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER. - * - * Copyright 2007-2008 Sun Microsystems, Inc. All rights reserved. - * - * The contents of this file are subject to the terms of the Common Development - * and Distribution License("CDDL") (the "License"). You may not use this file - * except in compliance with the License. - * - * You can obtain a copy of the License at - * http://IdentityConnectors.dev.java.net/legal/license.txt - * See the License for the specific language governing permissions and limitations - * under the License. - * - * When distributing the Covered Code, include this CDDL Header Notice in each file - * and include the License file at identityconnectors/legal/license.txt. - * If applicable, add the following below this CDDL Header, with the fields - * enclosed by brackets [] replaced by your own identifying information: - * "Portions Copyrighted [year] [name of copyright owner]" - * ==================== - */ -using System; -using System.Collections.Generic; -using System.Linq; -using System.Text; -using System.DirectoryServices; -using Org.IdentityConnectors.Framework.Common.Exceptions; - -namespace Org.IdentityConnectors.ActiveDirectory -{ - internal class UserAccountControl - { - public static string UAC_ATTRIBUTE_NAME = "userAccountControl"; - - // values taken from - http://support.microsoft.com/kb/305144 - static public int SCRIPT = 0x0001; - static public int ACCOUNTDISABLE = 0x0002; - static public int HOMEDIR_REQUIRED = 0x0008; - static public int LOCKOUT = 0x0010; - static public int PASSWD_NOTREQD = 0x0020; - - // Note You cannot assign this permission by directly modifying - // the UserAccountControl attribute. For information about how - // to set the permission programmatically, see the "Property - // flag descriptions" section. - static public int PASSWD_CANT_CHANGE = 0x0040; - - static public int ENCRYPTED_TEXT_PWD_ALLOWED = 0x0080; - static public int TEMP_DUPLICATE_ACCOUNT = 0x0100; - static public int NORMAL_ACCOUNT = 0x0200; - static public int INTERDOMAIN_TRUST_ACCOUNT = 0x0800; - static public int WORKSTATION_TRUST_ACCOUNT = 0x1000; - static public int SERVER_TRUST_ACCOUNT = 0x2000; - static public int DONT_EXPIRE_PASSWORD = 0x10000; - static public int MNS_LOGON_ACCOUNT = 0x20000; - static public int SMARTCARD_REQUIRED = 0x40000; - static public int TRUSTED_FOR_DELEGATION = 0x80000; - static public int NOT_DELEGATED = 0x100000; - static public int USE_DES_KEY_ONLY = 0x200000; - static public int DONT_REQ_PREAUTH = 0x400000; - public static int PASSWORD_EXPIRED = 0x800000; - public static int TRUSTED_TO_AUTH_FOR_DELEGATION = 0x1000000; - - // get the uac value from the property value collection - private static int GetUAC(PropertyValueCollection pvc) - { - // default schema says it's an integer, so it better be one - if ((pvc != null) && (pvc.Count == 1) && (pvc[0] is int)) - { - return (int)pvc[0]; - } - else - { - return 0; - } - } - - // sets the uac value in a propertyvaluecollection - private static void SetUAC(PropertyValueCollection pvc, int value) - { - // set the value - pvc[0] = value; - } - - // generically set a value in the uac to the value of 'isSet' - internal static void Set(PropertyValueCollection pvc, int flag, bool? isSet) - { - int uac = GetUAC(pvc); - // boolean false (null is same as false) - if ((isSet == null) || (isSet.Value.Equals(false))) - { - int clearMask = 0xFFFF ^ flag; - uac &= clearMask; - } - else - { - uac |= flag; - } - SetUAC(pvc, uac); - } - - // chec to see if a particular value of the uac is set - internal static bool IsSet(PropertyValueCollection pvc, int flag) - { - int uac = GetUAC(pvc); - return ((uac & flag) != 0); - } - } -} diff --git a/DotNetConnectors.sln/ActiveDirectoryConnector/build.xml b/DotNetConnectors.sln/ActiveDirectoryConnector/build.xml deleted file mode 100644 index 5972f10c..00000000 --- a/DotNetConnectors.sln/ActiveDirectoryConnector/build.xml +++ /dev/null @@ -1,28 +0,0 @@ - - - - - - - diff --git a/DotNetConnectors.sln/ActiveDirectoryConnectorTests/ActiveDirectoryConnectorTest.cs b/DotNetConnectors.sln/ActiveDirectoryConnectorTests/ActiveDirectoryConnectorTest.cs deleted file mode 100644 index 8b9d562a..00000000 --- a/DotNetConnectors.sln/ActiveDirectoryConnectorTests/ActiveDirectoryConnectorTest.cs +++ /dev/null @@ -1,2750 +0,0 @@ -/* - * ==================== - * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER. - * - * Copyright 2007-2008 Sun Microsystems, Inc. All rights reserved. - * - * The contents of this file are subject to the terms of the Common Development - * and Distribution License("CDDL") (the "License"). You may not use this file - * except in compliance with the License. - * - * You can obtain a copy of the License at - * http://IdentityConnectors.dev.java.net/legal/license.txt - * See the License for the specific language governing permissions and limitations - * under the License. - * - * When distributing the Covered Code, include this CDDL Header Notice in each file - * and include the License file at identityconnectors/legal/license.txt. - * If applicable, add the following below this CDDL Header, with the fields - * enclosed by brackets [] replaced by your own identifying information: - * "Portions Copyrighted [year] [name of copyright owner]" - * ==================== - */ -using System; -using System.Collections.Generic; -using System.Linq; -using System.Text; -using NUnit.Framework; -using Org.IdentityConnectors.Framework; -using Org.IdentityConnectors.Framework.Spi; -using Org.IdentityConnectors.Framework.Spi.Operations; -using Org.IdentityConnectors.Framework.Common.Objects; -using Org.IdentityConnectors.Framework.Common.Objects.Filters; -using Org.IdentityConnectors.Framework.Test; -using Org.IdentityConnectors.Common.Security; -using System.Diagnostics; -using System.Resources; -using System.IO; -using System.Security.AccessControl; -using System.Security.Principal; -using Org.IdentityConnectors.Framework.Common.Exceptions; -using Org.IdentityConnectors.Common; -using System.Threading; - -namespace Org.IdentityConnectors.ActiveDirectory -{ - [TestFixture] - public class ActiveDirectoryConnectorTest - { - Random _rand = new Random(); - public static readonly string CONFIG_PROPERTY_USER = "config_user"; - public static readonly string CONFIG_PROPERTY_PASSWORD = "config_password"; - public static readonly string CONFIG_PROPERTY_HOST = "config_host"; - public static readonly string CONFIG_PROPERTY_PORT = "config_port"; - public static readonly string CONFIG_PROPERTY_CONTAINER = "config_container"; - public static readonly string CONFIG_PROPERTY_SCRIPT_USER_LOCAL = "config_script_user_local"; - public static readonly string CONFIG_PROPERTY_SCRIPT_PASSWORD_LOCAL = "config_script_password_local"; - public static readonly string CONFIG_PROPERTY_SCRIPT_USER_DOMAIN = "config_script_user_domain"; - public static readonly string CONFIG_PROPERTY_SCRIPT_PASSWORD_DOMAIN = "config_script_password_domain"; - public static readonly string CONFIG_PROPERTY_LDAPHOSTNAME = "config_ldap_hostname"; - public static readonly string CONFIG_PROPERTY_SEARCH_CONTEXT = "config_search_context"; - public static readonly string CONFIG_PROPERTY_SYNC_SEARCH_CONTEXT_ROOT = "config_sync_search_context_root"; - public static readonly string CONFIG_PROPERTY_SYNC_SEARCH_CONTEXT_CHILD = "config_sync_search_context_child"; - public static readonly string CONFIG_PROPERTY_DOMAIN_NAME = "config_domain_name"; - public static readonly string CONFIG_PROPERTY_SYNC_DOMAIN_CONTROLLER = "config_sync_domain_controller"; - public static readonly string CONFIG_PROPERTY_GC_DOMAIN_CONTROLLER = "config_sync_gc_domain_controller"; - public static readonly string TEST_PARAM_SHARED_HOME_FOLDER = "test_param_shared_home_folder"; - - // having troubles with duplicate random numbers - public static List randomList = new List(); - - [Test] - public void TestConfiguration() { - ActiveDirectoryConfiguration config = new ActiveDirectoryConfiguration(); - config.ConnectorMessages = TestHelpers.CreateDummyMessages(); - String directoryAdminName = GetProperty(CONFIG_PROPERTY_USER); - config.DirectoryAdminName = directoryAdminName; - Assert.AreEqual(directoryAdminName, config.DirectoryAdminName); - } - - [Test] - public void TestTest() - { - ActiveDirectoryConnector connectorGood = new ActiveDirectoryConnector(); - ActiveDirectoryConfiguration config = (ActiveDirectoryConfiguration)GetConfiguration(); - connectorGood.Init(config); - connectorGood.Test(); - - bool threwException = false; - try - { - config.ObjectClass = "BadObjectClass"; - ActiveDirectoryConnector connectorBad = new ActiveDirectoryConnector(); - connectorBad.Init(config); - connectorBad.Test(); - } - catch (ConnectorException e) - { - threwException = true; - } - - Assert.IsTrue(threwException, "Bad configuration should have caused an exception"); - } - - [Test] - public void TestSchema() - { - ActiveDirectoryConnector connector = new ActiveDirectoryConnector(); - connector.Init(GetConfiguration()); - Schema schema = connector.Schema(); - Boolean foundOptionalAttributes = false; - Boolean foundOperationalAttributes = false; - - // just a very high level check of things. Should have 3 ObjectClassInfos, - // (group, account, and organizationalUnit) and nothing contained in them - // should be null. Group and account should have some operational - // attributes - Assert.AreEqual(3, schema.ObjectClassInfo.Count); - foreach(ObjectClassInfo ocInfo in schema.ObjectClassInfo) { - Assert.IsNotNull(ocInfo); - Assert.That((ocInfo.ObjectType == ObjectClass.ACCOUNT.GetObjectClassValue()) - || (ocInfo.ObjectType == ObjectClass.GROUP.GetObjectClassValue()) - || (ocInfo.ObjectType == ActiveDirectoryConnector.OBJECTCLASS_OU)); - Trace.WriteLine("****** " + ocInfo.ObjectType); - - // skip this for organizational unit ... it doesnt really have this - if (ocInfo.ObjectType.Equals(ActiveDirectoryConnector.ouObjectClass)) - { - continue; - } - - foreach (ConnectorAttributeInfo caInfo in ocInfo.ConnectorAttributeInfos) - { - Assert.IsNotNull(caInfo); - Trace.WriteLine(String.Format("{0} {1} {2} {3}", caInfo.Name, - caInfo.IsCreatable ? "createable" : "", - caInfo.IsUpdateable ? "updateable" : "", - caInfo.IsRequired ? "required" : "", - caInfo.IsMultiValued ? "multivalue" : "")); - if(ConnectorAttributeUtil.IsSpecial(caInfo)) { - foundOperationalAttributes = true; - } else { - if (!caInfo.IsRequired) - { - foundOptionalAttributes = true; - } - } - } - Assert.That(foundOperationalAttributes && foundOptionalAttributes); - } - } - - // test proper behavior of each supported operation - // and test proper reporting of unsuppoorted operations - [Test] - public void TestBasics_Account() - { - //Initialize Connector - ActiveDirectoryConnector connector = new ActiveDirectoryConnector(); - connector.Init(GetConfiguration()); - - // create user - ICollection createAttributes = GetNormalAttributes_Account(); - Uid createUid = CreateAndVerifyObject(connector, - ObjectClass.ACCOUNT, createAttributes); - - // update the user - replace - ICollection updateReplaceAttrs = - new List(); - Name oldName = ConnectorAttributeUtil.GetNameFromAttributes(createAttributes); - String newName = ActiveDirectoryUtils.GetRelativeName(oldName); - newName = newName.Trim() + "_new, " + GetProperty(CONFIG_PROPERTY_CONTAINER); - updateReplaceAttrs.Add(ConnectorAttributeBuilder.Build( - Name.NAME, newName)); - updateReplaceAttrs.Add(ConnectorAttributeBuilder.Build( - "sn", "newsn")); - Uid updateReplaceUid = UpdateReplaceAndVerifyObject(connector, - ObjectClass.ACCOUNT, createUid, updateReplaceAttrs); - - // update the user - add - ICollection updateAddAttrs = - new List(); - updateAddAttrs.Add(ConnectorAttributeBuilder.Build("otherHomePhone", "123.456.7890", "098.765.4321")); - Uid updateAddUid = UpdateAddAndVerifyUser(connector, - ObjectClass.ACCOUNT, createUid, updateAddAttrs, null); - - // update the user - delete - - // delete user - DeleteAndVerifyObject(connector, ObjectClass.ACCOUNT, createUid, true, true); - } - - // test proper behaviour of each supported operation - // and test proper reporting of unsuppoorted operations - [Test] - public void TestBasics_Group() - { - //Initialize Connector - ActiveDirectoryConnector connector = new ActiveDirectoryConnector(); - connector.Init(GetConfiguration()); - - Uid uidToDelete = null; - try - { - // create group - ICollection createAttributes = GetNormalAttributes_Group(); - createAttributes.Add(ConnectorAttributeBuilder.Build(PredefinedAttributes.ACCOUNTS_NAME, - CreateGroupMember(connector))); - - // create object - uidToDelete = connector.Create(ObjectClass.GROUP, createAttributes, null); - Uid createUid = uidToDelete; - Assert.IsNotNull(createUid); - - // find new object ... have to add groups to list of things to return - OperationOptionsBuilder optionsBuilder = new OperationOptionsBuilder(); - ICollection attributesToGet = GetDefaultAttributesToGet(ObjectClass.GROUP); - attributesToGet.Add(PredefinedAttributes.ACCOUNTS_NAME); - optionsBuilder.AttributesToGet = attributesToGet.ToArray(); - - ConnectorObject newObject = GetConnectorObjectFromUid(connector, - ObjectClass.GROUP, createUid, optionsBuilder.Build()); - VerifyObject(createAttributes, newObject); - // update the group - replace - ICollection updateReplaceAttrs = - new List(); - Name oldName = ConnectorAttributeUtil.GetNameFromAttributes(createAttributes); - String newName = ActiveDirectoryUtils.GetRelativeName(oldName); - newName = newName.Trim() + "_new, " + GetProperty(CONFIG_PROPERTY_CONTAINER); - - updateReplaceAttrs.Add(createUid); - updateReplaceAttrs.Add(ConnectorAttributeBuilder.Build( - Name.NAME, newName)); - updateReplaceAttrs.Add(ConnectorAttributeBuilder.Build( - "description", "New description")); - uidToDelete = UpdateReplaceAndVerifyObject(connector, - ObjectClass.GROUP, createUid, updateReplaceAttrs); - Uid updateReplaceUid = uidToDelete; - - // update the group - add - ICollection updateAddAttrs = - new List(); - updateAddAttrs.Add(ConnectorAttributeBuilder.Build(PredefinedAttributes.ACCOUNTS_NAME, - CreateGroupMember(connector), CreateGroupMember(connector))); - - uidToDelete = UpdateAddAndVerifyUser(connector, - ObjectClass.GROUP, updateReplaceUid, updateAddAttrs, optionsBuilder.Build()); - } - finally - { - if (uidToDelete != null) - { - // delete user - DeleteAndVerifyObject(connector, ObjectClass.GROUP, uidToDelete, true, true); - } - } - } - - [Test] - public void TestCreate_Account() - { - //Initialize Connector - ActiveDirectoryConnector connector = new ActiveDirectoryConnector(); - connector.Init(GetConfiguration()); - Uid createUid = null; - try - { - ICollection createAttributes = GetNormalAttributes_Account(); - createUid = CreateAndVerifyObject(connector, ObjectClass.ACCOUNT, createAttributes); - } - finally - { - if (createUid != null) - { - //remove the one we created - DeleteAndVerifyObject(connector, ObjectClass.ACCOUNT, - createUid, false, true); - } - } - } - - [Test] - public void TestCreate_Group() - { - //Initialize Connector - ActiveDirectoryConnector connector = new ActiveDirectoryConnector(); - connector.Init(GetConfiguration()); - Uid createUid = null; - try - { - ICollection createAttributes = GetNormalAttributes_Group(); - createUid = CreateAndVerifyObject(connector, ObjectClass.GROUP, createAttributes); - } - finally - { - if (createUid != null) - { - //remove the one we created - DeleteAndVerifyObject(connector, ObjectClass.GROUP, - createUid, false, true); - } - } - } - - [Test] - public void TestCreate_OrganizationalUnit() - { - //Initialize Connector - ActiveDirectoryConnector connector = new ActiveDirectoryConnector(); - connector.Init(GetConfiguration()); - Uid createUid = null; - try - { - ICollection createAttributes = GetNormalAttributes_OrganizationalUnit(); - createUid = CreateAndVerifyObject(connector, - ActiveDirectoryConnector.ouObjectClass, createAttributes); - } - finally - { - if (createUid != null) - { - //remove the one we created - DeleteAndVerifyObject(connector, - ActiveDirectoryConnector.ouObjectClass, - createUid, false, true); - } - } - } - - [Test] - public void TestCreateWithHomeDirectory_Account() - { - //Initialize Connector - ActiveDirectoryConnector connector = new ActiveDirectoryConnector(); - ActiveDirectoryConfiguration config = (ActiveDirectoryConfiguration)GetConfiguration(); - config.CreateHomeDirectory = true; - connector.Init(config); - Uid userUid = null; - try - { - // get the normal attributes - ICollection createAttributes = GetNormalAttributes_Account(); - - // read the homedir path, and append the samaccountname - StringBuilder homeDirPathBuilder = new StringBuilder(GetProperty(TEST_PARAM_SHARED_HOME_FOLDER)); - if (!homeDirPathBuilder.ToString().EndsWith("\\")) - { - homeDirPathBuilder.Append('\\'); - } - ConnectorAttribute samAccountNameAttr = ConnectorAttributeUtil.Find( - ActiveDirectoryConnector.ATT_SAMACCOUNT_NAME, createAttributes); - homeDirPathBuilder.Append(ConnectorAttributeUtil.GetStringValue(samAccountNameAttr)); - - // if it exists, delete it - String homeDir = homeDirPathBuilder.ToString(); - if (Directory.Exists(homeDir)) - { - Directory.Delete(homeDir); - } - Assert.IsFalse(Directory.Exists(homeDir)); - - // add homeDirectory to the attributes, and create user - createAttributes.Add(ConnectorAttributeBuilder.Build("homeDirectory", homeDir)); - userUid = CreateAndVerifyObject(connector, - ObjectClass.ACCOUNT, createAttributes); - - // now directory should exist - Assert.IsTrue(Directory.Exists(homeDir)); - - // get sid to check permissions - OperationOptionsBuilder optionsBuilder = new OperationOptionsBuilder(); - ICollection attributesToGet = GetDefaultAttributesToGet(ObjectClass.ACCOUNT); - attributesToGet.Add(ActiveDirectoryConnector.ATT_OBJECT_SID); - optionsBuilder.AttributesToGet = attributesToGet.ToArray(); - - ConnectorObject newUser = GetConnectorObjectFromUid(connector, - ObjectClass.ACCOUNT, userUid, optionsBuilder.Build()); - ConnectorAttribute sidAttr = - newUser.GetAttributeByName(ActiveDirectoryConnector.ATT_OBJECT_SID); - Byte[] sidBytes = (Byte[])ConnectorAttributeUtil.GetSingleValue(sidAttr); - SecurityIdentifier newUserSid = new SecurityIdentifier(sidBytes, 0); - - // check permissions - DirectoryInfo dirInfo = new DirectoryInfo(homeDir); - DirectorySecurity dirSec = dirInfo.GetAccessControl(); - AuthorizationRuleCollection rules = dirSec.GetAccessRules(true, true, typeof(SecurityIdentifier)); - bool foundCorrectRule = false; - foreach (AuthorizationRule rule in rules) - { - if (rule is FileSystemAccessRule) - { - FileSystemAccessRule fsaRule = (FileSystemAccessRule)rule; - if (fsaRule.IdentityReference.Equals(newUserSid)) - { - if ((fsaRule.AccessControlType.Equals(AccessControlType.Allow)) && - (fsaRule.FileSystemRights.Equals(FileSystemRights.FullControl)) && - (fsaRule.InheritanceFlags.Equals(InheritanceFlags.ContainerInherit | InheritanceFlags.ObjectInherit)) && - (fsaRule.IsInherited.Equals(false))) - { - foundCorrectRule = true; - } - - } - } - } - - // remove the directory (before assertion may fail) - Directory.Delete(homeDir); - - // check that we found the proper permission record - Assert.IsTrue(foundCorrectRule); - } - finally - { - if (userUid != null) - { - DeleteAndVerifyObject(connector, ObjectClass.ACCOUNT, userUid, false, false); - } - } - } - - [Test] // tests that if create home directory is set to false, no directory is created - public void TestCreateWithHomeDirectoryNoCreateConfig_Account() - { - //Initialize Connector - ActiveDirectoryConnector connector = new ActiveDirectoryConnector(); - ActiveDirectoryConfiguration config = (ActiveDirectoryConfiguration)GetConfiguration(); - config.CreateHomeDirectory = false; - connector.Init(config); - Uid userUid = null; - try - { - // get the normal attributes - ICollection createAttributes = GetNormalAttributes_Account(); - - // read the homedir path, and append the samaccountname - StringBuilder homeDirPathBuilder = new StringBuilder(GetProperty(TEST_PARAM_SHARED_HOME_FOLDER)); - if (!homeDirPathBuilder.ToString().EndsWith("\\")) - { - homeDirPathBuilder.Append('\\'); - } - ConnectorAttribute samAccountNameAttr = ConnectorAttributeUtil.Find( - ActiveDirectoryConnector.ATT_SAMACCOUNT_NAME, createAttributes); - homeDirPathBuilder.Append(ConnectorAttributeUtil.GetStringValue(samAccountNameAttr)); - - // if it exists, delete it - String homeDir = homeDirPathBuilder.ToString(); - if (Directory.Exists(homeDir)) - { - Directory.Delete(homeDir); - } - Assert.IsFalse(Directory.Exists(homeDir)); - - // add homeDirectory to the attributes, and create user - createAttributes.Add(ConnectorAttributeBuilder.Build("homeDirectory", homeDir)); - userUid = CreateAndVerifyObject(connector, - ObjectClass.ACCOUNT, createAttributes); - - // now directory should not exist - // (createhomedirectory was set to false in the configuration) - Assert.IsFalse(Directory.Exists(homeDir)); - } - finally - { - if (userUid != null) - { - DeleteAndVerifyObject(connector, ObjectClass.ACCOUNT, userUid, false, false); - } - } - } - - [Test] - public void TestSearchNoFilter_Account() - { - ActiveDirectoryConnector connector = new ActiveDirectoryConnector(); - Configuration config = GetConfiguration(); - config.ConnectorMessages = TestHelpers.CreateDummyMessages(); - connector.Init(config); - - ICollection createdUids = new HashSet(); - - try - { - int numCreated = 0; - for (numCreated = 0; numCreated < 5; numCreated++) - { - createdUids.Add(CreateAndVerifyObject(connector, - ObjectClass.ACCOUNT, GetNormalAttributes_Account())); - } - - ICollection results = TestHelpers.SearchToList(connector, - ObjectClass.ACCOUNT, null); - - // not sure how many should be found ... it should find everything - // it's hard to say how many that is, but it should at least find the - // number we created - Assert.GreaterOrEqual(results.Count, numCreated); - - // check that they are all of the proper objectclass - foreach (ConnectorObject co in results) - { - ConnectorAttribute objectClassAttr = - co.GetAttributeByName("objectClass"); - Boolean foundCorrectObjectClass = false; - foreach (Object o in objectClassAttr.Value) - { - if ((o is String) && (o != null)) - { - String stringValue = (String)o; - if (stringValue.ToUpper().Trim().Equals("USER")) - { - foundCorrectObjectClass = true; - } - } - } - Assert.IsTrue(foundCorrectObjectClass); - } - } - finally - { - foreach (Uid uid in createdUids) - { - if (uid != null) - { - DeleteAndVerifyObject(connector, ObjectClass.ACCOUNT, uid, false, true); - } - } - } - } - - [Test] - public void TestSearchNoFilter_Group() - { - ActiveDirectoryConnector connector = new ActiveDirectoryConnector(); - connector.Init(GetConfiguration()); - ICollection createdUids = new HashSet(); - - try - { - int numCreated = 0; - for (numCreated = 0; numCreated < 5; numCreated++) - { - createdUids.Add(CreateAndVerifyObject(connector, - ObjectClass.GROUP, GetNormalAttributes_Group())); - } - - ICollection results = TestHelpers.SearchToList(connector, - ObjectClass.GROUP, null); - - // not sure how many should be found ... it should find everything - // it's hard to say how many that is, but it should at least find the - // number we created - Assert.GreaterOrEqual(results.Count, numCreated); - - // check that they are all of the proper objectclass - foreach (ConnectorObject co in results) - { - ConnectorAttribute objectClassAttr = - co.GetAttributeByName("objectClass"); - Boolean foundCorrectObjectClass = false; - foreach (Object o in objectClassAttr.Value) - { - if ((o is String) && (o != null)) - { - String stringValue = (String)o; - if (stringValue.ToUpper().Trim().Equals("GROUP")) - { - foundCorrectObjectClass = true; - } - } - } - Assert.IsTrue(foundCorrectObjectClass); - } - } - finally - { - foreach (Uid uid in createdUids) - { - if (uid != null) - { - DeleteAndVerifyObject(connector, ObjectClass.GROUP, uid, false, true); - } - } - } - } - - [Test] - public void TestSearchByName_account() - { - ActiveDirectoryConnector connector = new ActiveDirectoryConnector(); - connector.Init(GetConfiguration()); - - Uid createUid = null; - - try - { - createUid = CreateAndVerifyObject(connector, ObjectClass.ACCOUNT, - GetNormalAttributes_Account()); - - // find out what the name was - ConnectorObject newObject = GetConnectorObjectFromUid(connector, - ObjectClass.ACCOUNT, createUid); - Name nameAttr = newObject.Name; - Assert.IsNotNull(nameAttr); - - //search normally - ICollection results = TestHelpers.SearchToList(connector, - ObjectClass.ACCOUNT, FilterBuilder.EqualTo(nameAttr)); - - // there really should only be one - Assert.AreEqual(results.Count, 1); - - // and it must have the value we were searching for - String createName = ActiveDirectoryUtils.NormalizeLdapString(nameAttr.GetNameValue()); - String foundName = ActiveDirectoryUtils.NormalizeLdapString(results.ElementAt(0).Name.GetNameValue()); - Assert.AreEqual(createName, foundName); - - //search in uppercase - ConnectorAttribute nameUpper = ConnectorAttributeBuilder.Build( - nameAttr.Name, nameAttr.GetNameValue().ToUpper()); - results = TestHelpers.SearchToList(connector, - ObjectClass.ACCOUNT, FilterBuilder.EqualTo(nameUpper)); - - // there really should only be one - Assert.AreEqual(results.Count, 1); - - // and it must have the value we were searching for - createName = ActiveDirectoryUtils.NormalizeLdapString(nameAttr.GetNameValue()); - foundName = ActiveDirectoryUtils.NormalizeLdapString(results.ElementAt(0).Name.GetNameValue()); - Assert.AreEqual(createName, foundName); - - //search in lowercase - ConnectorAttribute nameLower = ConnectorAttributeBuilder.Build( - nameAttr.Name, nameAttr.GetNameValue().ToLower()); - results = TestHelpers.SearchToList(connector, - ObjectClass.ACCOUNT, FilterBuilder.EqualTo(nameLower)); - - // there really should only be one - Assert.AreEqual(results.Count, 1); - - // and it must have the value we were searching for - createName = ActiveDirectoryUtils.NormalizeLdapString(nameAttr.GetNameValue()); - foundName = ActiveDirectoryUtils.NormalizeLdapString(results.ElementAt(0).Name.GetNameValue()); - Assert.AreEqual(createName, foundName); - - } - finally - { - if (createUid != null) - { - //remove the one we created - DeleteAndVerifyObject(connector, ObjectClass.ACCOUNT, - createUid, false, true); - } - } - } - - [Test] - public void TestSearchByName_group() - { - ActiveDirectoryConnector connector = new ActiveDirectoryConnector(); - connector.Init(GetConfiguration()); - - Uid createUid = null; - - try - { - createUid = CreateAndVerifyObject(connector, ObjectClass.GROUP, - GetNormalAttributes_Group()); - - // find out what the name was - ConnectorObject newObject = GetConnectorObjectFromUid(connector, - ObjectClass.GROUP, createUid); - Name nameAttr = newObject.Name; - Assert.IsNotNull(nameAttr); - - //search normally - ICollection results = TestHelpers.SearchToList(connector, - ObjectClass.GROUP, FilterBuilder.EqualTo(nameAttr)); - - // there really should only be one - Assert.AreEqual(results.Count, 1); - - // and it must have the value we were searching for - String createName = ActiveDirectoryUtils.NormalizeLdapString(nameAttr.GetNameValue()); - String foundName = ActiveDirectoryUtils.NormalizeLdapString(results.ElementAt(0).Name.GetNameValue()); - Assert.AreEqual(createName, foundName); - - //search in uppercase - ConnectorAttribute nameUpper = ConnectorAttributeBuilder.Build( - nameAttr.Name, nameAttr.GetNameValue().ToUpper()); - results = TestHelpers.SearchToList(connector, - ObjectClass.GROUP, FilterBuilder.EqualTo(nameUpper)); - - // there really should only be one - Assert.AreEqual(results.Count, 1); - - // and it must have the value we were searching for - createName = ActiveDirectoryUtils.NormalizeLdapString(nameAttr.GetNameValue()); - foundName = ActiveDirectoryUtils.NormalizeLdapString(results.ElementAt(0).Name.GetNameValue()); - Assert.AreEqual(createName, foundName); - - //search in lowercase - ConnectorAttribute nameLower = ConnectorAttributeBuilder.Build( - nameAttr.Name, nameAttr.GetNameValue().ToLower()); - results = TestHelpers.SearchToList(connector, - ObjectClass.GROUP, FilterBuilder.EqualTo(nameLower)); - - // there really should only be one - Assert.AreEqual(results.Count, 1); - - // and it must have the value we were searching for - createName = ActiveDirectoryUtils.NormalizeLdapString(nameAttr.GetNameValue()); - foundName = ActiveDirectoryUtils.NormalizeLdapString(results.ElementAt(0).Name.GetNameValue()); - Assert.AreEqual(createName, foundName); - - } - finally - { - if (createUid != null) - { - //remove the one we created - DeleteAndVerifyObject(connector, ObjectClass.GROUP, - createUid, false, true); - } - } - } - - [Test] - public void TestSearchByCNWithWildcard_account() - { - ActiveDirectoryConnector connector = new ActiveDirectoryConnector(); - connector.Init(GetConfiguration()); - - Uid uid1 = null; - Uid uid2 = null; - - try - { - // create a couple things to find - uid1 = CreateAndVerifyObject(connector, ObjectClass.ACCOUNT, - GetNormalAttributes_Account()); - uid2 = CreateAndVerifyObject(connector, ObjectClass.ACCOUNT, - GetNormalAttributes_Account()); - - ICollection results = TestHelpers.SearchToList(connector, - ObjectClass.ACCOUNT, FilterBuilder.EqualTo( - ConnectorAttributeBuilder.Build("CN", "nunit*"))); - - // there should be at least the two we just created - Assert.GreaterOrEqual(results.Count, 2); - foreach (ConnectorObject co in results) - { - // and it must have the value we were searching for - String foundName = ActiveDirectoryUtils.NormalizeLdapString( - co.Name.GetNameValue()); - Assert.That(foundName.ToUpper().StartsWith("CN=NUNIT")); - } - } - finally - { - if (uid1 != null) - { - //remove the one we created - DeleteAndVerifyObject(connector, ObjectClass.ACCOUNT, - uid1, false, true); - } - if (uid2 != null) - { - //remove the one we created - DeleteAndVerifyObject(connector, ObjectClass.ACCOUNT, - uid2, false, true); - } - } - } - - [Test] - public void TestSearchByRegularAttribute_account() - { - ActiveDirectoryConnector connector = new ActiveDirectoryConnector(); - connector.Init(GetConfiguration()); - - Uid createUid = null; - - try - { - ConnectorAttribute createSnAttr = - ConnectorAttributeBuilder.Build("sn", "nunitSearch"); - ICollection attributes = GetNormalAttributes_Account(); - attributes.Remove(ConnectorAttributeUtil.Find("sn", attributes)); - attributes.Add(createSnAttr); - createUid = CreateAndVerifyObject(connector, ObjectClass.ACCOUNT, - attributes); - - ICollection results = TestHelpers.SearchToList(connector, - ObjectClass.ACCOUNT, FilterBuilder.EqualTo(createSnAttr)); - - // there should be at least the newly created one - Assert.GreaterOrEqual(results.Count, 1); - - // and it must have the value we were searching for - Boolean foundCreated = false; - foreach (ConnectorObject resultObject in results) - { - ConnectorAttribute foundSnAttr = - resultObject.GetAttributeByName("sn"); - Assert.AreEqual(createSnAttr, foundSnAttr); - - // keep track of if we've found the one we created - if (createUid.Equals(resultObject.Uid)) - { - // cant have it twice - Assert.IsFalse(foundCreated); - foundCreated = true; - } - } - // be certain we saw the one we created - Assert.IsTrue(foundCreated); - } - finally - { - if (createUid != null) - { - //remove the one we created - DeleteAndVerifyObject(connector, ObjectClass.ACCOUNT, - createUid, false, true); - } - } - } - - [Test] - public void TestSearchByRegularAttributeWithWildcard_account() - { - ActiveDirectoryConnector connector = new ActiveDirectoryConnector(); - connector.Init(GetConfiguration()); - - ICollection uids = new HashSet(); - - try - { - int randomNumber = GetRandomNumber(); - - String snPrefix = "nunitWCTest"; - - for (int i = 0; i < 10; i++) - { - ICollection attributes = - GetNormalAttributes_Account(); - attributes.Remove(ConnectorAttributeUtil.Find("sn", attributes)); - attributes.Add(ConnectorAttributeBuilder.Build("sn", - snPrefix + GetRandomNumber())); - Uid tempUid = CreateAndVerifyObject(connector, ObjectClass.ACCOUNT, - attributes); - Assert.IsNotNull(tempUid); - uids.Add(tempUid); - } - - ICollection results = TestHelpers.SearchToList(connector, - ObjectClass.ACCOUNT, FilterBuilder.StartsWith( - ConnectorAttributeBuilder.Build("sn", snPrefix))); - - // there should be at least the newly created one - Assert.GreaterOrEqual(results.Count, 1); - - // make a duplicate list - ICollection uidsToValidate = new HashSet(uids); - - // and it must have the value we were searching for - foreach (ConnectorObject resultObject in results) - { - ConnectorAttribute foundSnAttr = - resultObject.GetAttributeByName("sn"); - String snValue = ConnectorAttributeUtil.GetStringValue(foundSnAttr); - Assert.That(snValue.StartsWith(snPrefix)); - uidsToValidate.Remove(resultObject.Uid); - if (uidsToValidate.Count == 0) - { - break; - } - } - Assert.AreEqual(0, uidsToValidate.Count); - } - finally - { - foreach (Uid createdUid in uids) - { - //remove the one we created - DeleteAndVerifyObject(connector, ObjectClass.ACCOUNT, - createdUid, false, true); - } - } - } - - // test proper behavior of create with ALL attributes specified - [Test] - public void TestCreateWithAllAttributes_Account() - { - //Initialize Connector - ActiveDirectoryConnector connector = new ActiveDirectoryConnector(); - connector.Init(GetConfiguration()); - Uid createUid = null; - - try - { - // create user - ICollection createAttributes = GetAllAttributes_Account(); - createUid = CreateAndVerifyObject(connector, - ObjectClass.ACCOUNT, createAttributes); - } - finally - { - if (createUid != null) - { - //remove the one we created - DeleteAndVerifyObject(connector, ObjectClass.ACCOUNT, - createUid, false, true); - } - } - } - - // test proper behavior of create with ALL attributes specified - [Test] - public void Test_OpAtt_Enabled_Account() - { - //Initialize Connector - ActiveDirectoryConnector connector = new ActiveDirectoryConnector(); - connector.Init(GetConfiguration()); - Uid createUid = null; - - try - { - // create user - ICollection createAttributes = GetNormalAttributes_Account(); - createAttributes.Add(ConnectorAttributeBuilder.BuildEnabled(true)); - createUid = CreateAndVerifyObject(connector, - ObjectClass.ACCOUNT, createAttributes); - - ICollection updateReplaceAttributes = - new HashSet(); - updateReplaceAttributes.Add(ConnectorAttributeBuilder.BuildEnabled(false)); - UpdateReplaceAndVerifyObject(connector, ObjectClass.ACCOUNT, - createUid, updateReplaceAttributes); - } - finally - { - if (createUid != null) - { - //remove the one we created - DeleteAndVerifyObject(connector, ObjectClass.ACCOUNT, - createUid, false, true); - } - } - } - - // Test scripting - [Test] - public void TestScriptOnResource() - { - //Initialize Connector - ActiveDirectoryConnector connector = new ActiveDirectoryConnector(); - connector.Init(GetConfiguration()); - - try - { - RunScript(connector, "", ""); - RunScript(connector, GetProperty(CONFIG_PROPERTY_SCRIPT_USER_LOCAL), - GetProperty(CONFIG_PROPERTY_SCRIPT_PASSWORD_LOCAL)); - RunScript(connector, GetProperty(CONFIG_PROPERTY_SCRIPT_USER_DOMAIN), - GetProperty(CONFIG_PROPERTY_SCRIPT_PASSWORD_DOMAIN)); - - - // try with invalid credentials - bool scriptFailed = false; - try - { - RunScript(connector, GetProperty(CONFIG_PROPERTY_USER), "bogus"); - } - catch (Exception e) - { - scriptFailed = true; - } - Assert.That(scriptFailed); - } - finally - { - } - } - - [Test] - public void TestAddGroup_Account() - { - //Initialize Connector - ActiveDirectoryConnector connector = new ActiveDirectoryConnector(); - connector.Init(GetConfiguration()); - Uid groupUid = null; - Uid userUid = null; - try - { - userUid = CreateAndVerifyObject(connector, - ObjectClass.ACCOUNT, GetNormalAttributes_Account()); - Filter userUidFilter = FilterBuilder.EqualTo(userUid); - IList foundUserObjects = - TestHelpers.SearchToList(connector, ObjectClass.ACCOUNT, userUidFilter); - Assert.AreEqual(1, foundUserObjects.Count); - - groupUid = CreateAndVerifyObject(connector, - ObjectClass.GROUP, GetNormalAttributes_Group()); - Filter groupUidFilter = FilterBuilder.EqualTo(groupUid); - IList foundGroupObjects = - TestHelpers.SearchToList(connector, ObjectClass.GROUP, groupUidFilter); - Assert.AreEqual(1, foundGroupObjects.Count); - String groupName = foundGroupObjects[0].Name.GetNameValue(); - - ICollection modifiedAttrs = new HashSet(); - modifiedAttrs.Add(ConnectorAttributeBuilder.Build(PredefinedAttributes.GROUPS_NAME, groupName)); - OperationOptionsBuilder optionsBuilder = new OperationOptionsBuilder(); - ICollection attributesToGet = GetDefaultAttributesToGet(ObjectClass.ACCOUNT); - attributesToGet.Add(PredefinedAttributes.GROUPS_NAME); - optionsBuilder.AttributesToGet = attributesToGet.ToArray(); - UpdateAddAndVerifyUser(connector, ObjectClass.ACCOUNT, - userUid, modifiedAttrs, optionsBuilder.Build()); - - } finally { - DeleteAndVerifyObject(connector, ObjectClass.ACCOUNT, userUid, false, false); - DeleteAndVerifyObject(connector, ObjectClass.GROUP, groupUid, false, false); - } - } - - [Test] - public void TestRemoveAttributeValue() - { - ActiveDirectoryConnector connector = new ActiveDirectoryConnector(); - connector.Init(GetConfiguration()); - - Uid createUid = null; - - try - { - int randomNumber = GetRandomNumber(); - ICollection attributes = new HashSet(); - - attributes.Add(ConnectorAttributeBuilder.Build( - "ad_container", GetProperty(CONFIG_PROPERTY_CONTAINER))); - attributes.Add(ConnectorAttributeBuilder.Build( - "userPassword", "secret")); - attributes.Add(ConnectorAttributeBuilder.Build( - "sAMAccountName", "nunit" + randomNumber)); - attributes.Add(ConnectorAttributeBuilder.Build( - "givenName", "nunit")); - attributes.Add(ConnectorAttributeBuilder.Build( - "sn", "TestUser" + randomNumber)); - attributes.Add(ConnectorAttributeBuilder.Build( - "displayName", "nunit test user " + randomNumber)); - attributes.Add(ConnectorAttributeBuilder.Build( - Name.NAME, "cn=nunit" + randomNumber + "," + - GetProperty(CONFIG_PROPERTY_CONTAINER))); - attributes.Add(ConnectorAttributeBuilder.Build( - "mail", "nunitUser" + randomNumber + "@some.com")); - attributes.Add(ConnectorAttributeBuilder.Build( - "otherHomePhone", "512.555.1212", "512.123.4567")); - - createUid = CreateAndVerifyObject(connector, ObjectClass.ACCOUNT, - attributes); - - ICollection modifyAttributes = new HashSet(); - modifyAttributes.Add(createUid); - modifyAttributes.Add(ConnectorAttributeBuilder.Build("otherHomePhone", "512.555.1212")); - - connector.Update(UpdateType.DELETE, ObjectClass.ACCOUNT, modifyAttributes, null); - - Filter uidFilter = FilterBuilder.EqualTo(createUid); - IList objects = TestHelpers.SearchToList(connector, ObjectClass.ACCOUNT, uidFilter); - Assert.AreEqual(1, objects.Count); - - ConnectorAttribute otherHomePhoneAttr = ConnectorAttributeUtil.Find( - "otherHomePhone", objects[0].GetAttributes()); - - Assert.AreEqual(1, otherHomePhoneAttr.Value.Count); - Assert.AreEqual("512.123.4567", ConnectorAttributeUtil.GetSingleValue(otherHomePhoneAttr)); - } - finally - { - if (createUid != null) - { - //remove the one we created - DeleteAndVerifyObject(connector, ObjectClass.ACCOUNT, - createUid, false, true); - } - } - } - - - [Ignore] - [Test] - public void TestContainerChange_account() - { - ActiveDirectoryConnector connector = new ActiveDirectoryConnector(); - connector.Init(GetConfiguration()); - - Uid createGroupUid = null; - Uid createUserUid = null; - - try { - // create container for this test - ICollection groupAttributes = GetNormalAttributes_Group(); - createGroupUid = CreateAndVerifyObject(connector, - ObjectClass.GROUP, groupAttributes); - ICollection groupResults = TestHelpers.SearchToList( - connector, ObjectClass.GROUP, FilterBuilder.EqualTo(createGroupUid)); - Assert.AreEqual(1, groupResults.Count); - Assert.AreEqual(createGroupUid, groupResults.ElementAt(0).Uid); - ConnectorAttribute groupDnAttr = - groupResults.ElementAt(0).GetAttributeByName("distinguishedName"); - Assert.IsNotNull(groupDnAttr); - String groupPath = ConnectorAttributeUtil.GetStringValue(groupDnAttr); - - // create user - createUserUid = CreateAndVerifyObject(connector, - ObjectClass.ACCOUNT, GetNormalAttributes_Account()); - - //now change user container to the newly created one - ICollection updateAttrs = new HashSet(); - updateAttrs.Add(ConnectorAttributeBuilder.Build("ad_container", groupPath)); - updateAttrs.Add(createUserUid); - - connector.Update(UpdateType.REPLACE, ObjectClass.ACCOUNT, updateAttrs, null); - - ICollection results = TestHelpers.SearchToList( - connector, ObjectClass.ACCOUNT, FilterBuilder.EqualTo(createUserUid)); - Assert.AreEqual(1, results.Count); - Assert.AreEqual(createUserUid, results.ElementAt(0).Uid); - ConnectorAttribute foundContainerAttr = results.ElementAt(0).GetAttributeByName("ad_container"); - Assert.IsNotNull(foundContainerAttr); - - String lhs = ActiveDirectoryUtils.NormalizeLdapString(groupPath); - String rhs = ActiveDirectoryUtils.NormalizeLdapString(ConnectorAttributeUtil.GetStringValue(foundContainerAttr)); - Assert.AreEqual(lhs, rhs); - } - finally - { - if (createUserUid != null) - { - //remove the one we created - DeleteAndVerifyObject(connector, ObjectClass.ACCOUNT, - createUserUid, false, true); - } - - if (createGroupUid != null) - { - //remove the one we created - DeleteAndVerifyObject(connector, ObjectClass.GROUP, - createGroupUid, false, true); - } - } - } - - [Ignore] - [Test] - public void TestContainerChange_group() - { - ActiveDirectoryConnector connector = new ActiveDirectoryConnector(); - connector.Init(GetConfiguration()); - - // create user - Uid createUid = CreateAndVerifyObject(connector, - ObjectClass.ACCOUNT, GetNormalAttributes_Account()); - - // create container for this test - - //now change user container to the newly created one - - throw new NotImplementedException(); - } - - [Ignore] - [Test] - public void TestEnableDate() - { - //Initialize Connector - ActiveDirectoryConnector connector = new ActiveDirectoryConnector(); - connector.Init(GetConfiguration()); - Uid createUid = null; - - try - { - // create user - ICollection createAttributes = GetNormalAttributes_Account(); - createAttributes.Add(ConnectorAttributeBuilder.BuildEnabled(true)); - createAttributes.Add(ConnectorAttributeBuilder.BuildEnableDate(new DateTime(2000, 01, 01))); - createUid = CreateAndVerifyObject(connector, - ObjectClass.ACCOUNT, createAttributes); - - ICollection updateReplaceAttributes = - new HashSet(); - updateReplaceAttributes.Add(ConnectorAttributeBuilder.BuildEnabled(false)); - UpdateReplaceAndVerifyObject(connector, ObjectClass.ACCOUNT, - createUid, updateReplaceAttributes); - } - finally - { - if (createUid != null) - { - //remove the one we created - DeleteAndVerifyObject(connector, ObjectClass.ACCOUNT, - createUid, false, true); - } - } - } - - [Ignore] - [Test] - public void TestDisableDate() - { - //Initialize Connector - ActiveDirectoryConnector connector = new ActiveDirectoryConnector(); - connector.Init(GetConfiguration()); - Uid createUid = null; - - try - { - // create user - ICollection createAttributes = GetNormalAttributes_Account(); - createAttributes.Add(ConnectorAttributeBuilder.BuildEnabled(true)); - - // disable tommorrow - DateTime disableDate = DateTime.Now.AddHours(24); - - createAttributes.Add(ConnectorAttributeBuilder.BuildDisableDate(disableDate)); - createUid = CreateAndVerifyObject(connector, - ObjectClass.ACCOUNT, createAttributes); - - ICollection updateReplaceAttributes = - new HashSet(); - updateReplaceAttributes.Add(ConnectorAttributeBuilder.BuildEnabled(false)); - UpdateReplaceAndVerifyObject(connector, ObjectClass.ACCOUNT, - createUid, updateReplaceAttributes); - } - finally - { - if (createUid != null) - { - //remove the one we created - DeleteAndVerifyObject(connector, ObjectClass.ACCOUNT, - createUid, false, true); - } - } - } - - // test sync - [Test] - public void TestSyncGC() - { - // test with searchChildDomain (uses GC) - TestSync(true, GetProperty(CONFIG_PROPERTY_SYNC_SEARCH_CONTEXT_ROOT)); - TestSync(true, GetProperty(CONFIG_PROPERTY_SYNC_SEARCH_CONTEXT_CHILD)); - } - - // test sync - [Test] - public void TestSyncDC() - { - // test withouth searchChildDomains (uses DC) - TestSync(false, GetProperty(CONFIG_PROPERTY_SYNC_SEARCH_CONTEXT_ROOT)); - TestSync(false, GetProperty(CONFIG_PROPERTY_SYNC_SEARCH_CONTEXT_CHILD)); - } - - [Test] - public void TestUserPasswordChange() - { - //Initialize Connector - ActiveDirectoryConnector connector = new ActiveDirectoryConnector(); - connector.Init(GetConfiguration()); - Uid createUid = null; - - try - { - // create user - ICollection createAttributes = GetNormalAttributes_Account(); - // remove password, and set to something memorable - createAttributes.Remove(ConnectorAttributeUtil.Find(OperationalAttributes.PASSWORD_NAME, createAttributes)); - GuardedString gsCurrentPassword = GetGuardedString("1Password"); - createAttributes.Add(ConnectorAttributeBuilder.BuildPassword(gsCurrentPassword)); - createAttributes.Add(ConnectorAttributeBuilder.BuildEnabled(true)); - createUid = CreateAndVerifyObject(connector, - ObjectClass.ACCOUNT, createAttributes); - - // make sure authenticate works here - connector.Authenticate(ObjectClass.ACCOUNT, - ConnectorAttributeUtil.GetAsStringValue(ConnectorAttributeUtil.Find("sAMAccountName", createAttributes)), - gsCurrentPassword, null); - - ICollection updateReplaceAttributes = - new HashSet(); - GuardedString gsNewPassword = GetGuardedString("LongPassword2MeetTheRequirements!"); - updateReplaceAttributes.Add(ConnectorAttributeBuilder.BuildCurrentPassword(gsCurrentPassword)); - updateReplaceAttributes.Add(ConnectorAttributeBuilder.BuildPassword(gsNewPassword)); - UpdateReplaceAndVerifyObject(connector, ObjectClass.ACCOUNT, - createUid, updateReplaceAttributes); - - // make sure authenticate works here - connector.Authenticate(ObjectClass.ACCOUNT, - ConnectorAttributeUtil.GetAsStringValue(ConnectorAttributeUtil.Find("sAMAccountName", createAttributes)), - gsNewPassword, null); - - bool caughtAuthenticateFailedException = false; - try - { - // make sure authenticate doesnt work with original password - connector.Authenticate(ObjectClass.ACCOUNT, - ConnectorAttributeUtil.GetAsStringValue(ConnectorAttributeUtil.Find("sAMAccountName", createAttributes)), - gsCurrentPassword, null); - } - catch (Exception e) - { - caughtAuthenticateFailedException = true; - } - - Assert.IsTrue(caughtAuthenticateFailedException, "Negative test case should throw an exception"); - - - // now a negative test case - GuardedString gsBogusPassword = GetGuardedString("BogusPassword"); - ICollection updateErrorReplaceAttributes = - new HashSet(); - updateErrorReplaceAttributes.Add(ConnectorAttributeBuilder.BuildCurrentPassword(gsBogusPassword)); - updateErrorReplaceAttributes.Add(ConnectorAttributeBuilder.BuildPassword(gsNewPassword)); - bool caughtWrongCurrentPasswordException = false; - try - { - // update should fail due to wrong current password - UpdateReplaceAndVerifyObject(connector, ObjectClass.ACCOUNT, - createUid, updateErrorReplaceAttributes); - } - catch (Exception e) - { - caughtWrongCurrentPasswordException = true; - } - - Assert.IsTrue(caughtWrongCurrentPasswordException, "Negative test case should throw an exception"); - - // make sure authenticate works here - - } - finally - { - if (createUid != null) - { - //remove the one we created - DeleteAndVerifyObject(connector, ObjectClass.ACCOUNT, - createUid, false, true); - } - } - } - - [Test] - public void TestAuthenticateUser() - { - //Initialize Connector - ActiveDirectoryConnector connector = new ActiveDirectoryConnector(); - connector.Init(GetConfiguration()); - Uid createUid = null; - - try - { - // create user - ICollection createAttributes = GetNormalAttributes_Account(); - // remove password, and set to something memorable - createAttributes.Remove(ConnectorAttributeUtil.Find(OperationalAttributes.PASSWORD_NAME, createAttributes)); - GuardedString gsCurrentPassword = GetGuardedString("1Password"); - createAttributes.Add(ConnectorAttributeBuilder.BuildPassword(gsCurrentPassword)); - createAttributes.Add(ConnectorAttributeBuilder.BuildEnabled(true)); - createUid = CreateAndVerifyObject(connector, - ObjectClass.ACCOUNT, createAttributes); - - // make sure authenticate works here - Uid authUid = connector.Authenticate(ObjectClass.ACCOUNT, - ConnectorAttributeUtil.GetAsStringValue(ConnectorAttributeUtil.Find("sAMAccountName", createAttributes)), - gsCurrentPassword, null); - - Assert.AreEqual(createUid, authUid); - - // make sure authenticate fails - wrong password - bool caughtException = false; - try - { - connector.Authenticate(ObjectClass.ACCOUNT, - ConnectorAttributeUtil.GetAsStringValue(ConnectorAttributeUtil.Find("sAMAccountName", createAttributes)), - GetGuardedString("boguspassword"), null); - - } - catch (InvalidCredentialException e) - { - caughtException = true; - } - Assert.IsTrue(caughtException, "Negative test case should throw InvalidCredentialsException"); - - // change password - ICollection updateReplaceAttributes = - new HashSet(); - GuardedString gsNewPassword = GetGuardedString("LongPassword2MeetTheRequirements!"); - updateReplaceAttributes.Add(ConnectorAttributeBuilder.BuildCurrentPassword(gsCurrentPassword)); - updateReplaceAttributes.Add(ConnectorAttributeBuilder.BuildPassword(gsNewPassword)); - UpdateReplaceAndVerifyObject(connector, ObjectClass.ACCOUNT, - createUid, updateReplaceAttributes); - - // make sure authenticate works here - new password - connector.Authenticate(ObjectClass.ACCOUNT, - ConnectorAttributeUtil.GetAsStringValue(ConnectorAttributeUtil.Find("sAMAccountName", createAttributes)), - gsNewPassword, null); - - // make sure it fails with the wrong password - caughtException = false; - try { - connector.Authenticate(ObjectClass.ACCOUNT, - ConnectorAttributeUtil.GetAsStringValue(ConnectorAttributeUtil.Find("sAMAccountName", createAttributes)), - GetGuardedString("bogusPassword"), null); - } - catch (Exception e) - { - caughtException = true; - } - Assert.IsTrue(caughtException, "Negative test case should throw an exception"); - - // now set user must change password attribute - ICollection expirePasswordAttrs = - new HashSet(); - expirePasswordAttrs.Add(ConnectorAttributeBuilder.BuildPasswordExpired(true)); - UpdateReplaceAndVerifyObject(connector, ObjectClass.ACCOUNT, - createUid, expirePasswordAttrs); - - // make sure authenticate fails - correct password, but expired - caughtException = false; - try - { - // make sure authenticate fails with correct password - connector.Authenticate(ObjectClass.ACCOUNT, - ConnectorAttributeUtil.GetAsStringValue(ConnectorAttributeUtil.Find("sAMAccountName", createAttributes)), - gsNewPassword, null); - } - catch (PasswordExpiredException e) - { - caughtException = true; - Assert.AreEqual(createUid, e.Uid); - } - Assert.IsTrue(caughtException, "Negative test case should throw an exception"); - - // make sure authenticate fails - incorrect password, and expired - caughtException = false; - try - { - // make sure authenticate fails with wrong password (invalid credentials exception) - connector.Authenticate(ObjectClass.ACCOUNT, - ConnectorAttributeUtil.GetAsStringValue(ConnectorAttributeUtil.Find("sAMAccountName", createAttributes)), - GetGuardedString("bogusPassword"), null); - } - catch (InvalidCredentialException e) - { - caughtException = true; - } - Assert.IsTrue(caughtException, "Negative test case should throw an exception"); - } - finally - { - if (createUid != null) - { - //remove the one we created - DeleteAndVerifyObject(connector, ObjectClass.ACCOUNT, - createUid, false, true); - } - } - } - - [Test] - public void TestShortnameAndDescription() - { - //Initialize Connector - ActiveDirectoryConnector connector = new ActiveDirectoryConnector(); - connector.Init(GetConfiguration()); - Uid uidAccount = null; - Uid uidGroup = null; - Uid uidOu = null; - string accountDescription = "nunit test account description"; - string groupDescription = "nunit test group description"; - string ouDescription = "nunit test ou description"; - try - { - ICollection accountAttributes = GetNormalAttributes_Account(); - RemoveAttributeByName(accountAttributes, "description"); - accountAttributes.Add(ConnectorAttributeBuilder.Build( - "description", accountDescription)); - ICollection groupAttributes = GetNormalAttributes_Group(); - RemoveAttributeByName(groupAttributes, "description"); - groupAttributes.Add(ConnectorAttributeBuilder.Build( - "description", groupDescription)); - ICollection ouAttributes = GetNormalAttributes_OrganizationalUnit(); - RemoveAttributeByName(ouAttributes, "description"); - ouAttributes.Add(ConnectorAttributeBuilder.Build( - "description", ouDescription)); - - uidAccount = CreateObject(connector, ObjectClass.ACCOUNT, accountAttributes); - - OperationOptionsBuilder accountOptionsBuilder = new OperationOptionsBuilder(); - ICollection accountAttributesToGet = GetDefaultAttributesToGet(ObjectClass.ACCOUNT); - accountAttributesToGet.Add(PredefinedAttributes.DESCRIPTION); - accountAttributesToGet.Add(PredefinedAttributes.SHORT_NAME); - accountAttributesToGet.Add("name"); - accountAttributesToGet.Add("description"); - accountOptionsBuilder.AttributesToGet = accountAttributesToGet.ToArray(); - - ConnectorObject accountObject = GetConnectorObjectFromUid(connector, - ObjectClass.ACCOUNT, uidAccount, accountOptionsBuilder.Build()); - - // compare description - string foundAccountDescription = ConnectorAttributeUtil.GetStringValue( - ConnectorAttributeUtil.Find(PredefinedAttributes.DESCRIPTION, accountObject.GetAttributes())); - Assert.AreEqual(accountDescription, foundAccountDescription); - - // compare shortname - string accountShortName = ConnectorAttributeUtil.GetStringValue( - ConnectorAttributeUtil.Find( - PredefinedAttributes.SHORT_NAME, accountObject.GetAttributes())); - string accountDisplayName = ConnectorAttributeUtil.GetStringValue( - ConnectorAttributeUtil.Find( - "name", accountObject.GetAttributes())); - Assert.AreEqual(accountShortName, accountDisplayName); - - uidGroup = CreateObject(connector, ObjectClass.GROUP, groupAttributes); - - OperationOptionsBuilder groupOptionsBuilder = new OperationOptionsBuilder(); - ICollection groupAttributesToGet = GetDefaultAttributesToGet(ObjectClass.GROUP); - groupAttributesToGet.Add(PredefinedAttributes.DESCRIPTION); - groupAttributesToGet.Add(PredefinedAttributes.SHORT_NAME); - groupAttributesToGet.Add("name"); - groupAttributesToGet.Add("description"); - groupOptionsBuilder.AttributesToGet = groupAttributesToGet.ToArray(); - - ConnectorObject groupObject = GetConnectorObjectFromUid(connector, - ObjectClass.GROUP, uidGroup, groupOptionsBuilder.Build()); - - // compare description - string foundGroupDescription = ConnectorAttributeUtil.GetStringValue( - ConnectorAttributeUtil.Find(PredefinedAttributes.DESCRIPTION, groupObject.GetAttributes())); - Assert.AreEqual(groupDescription, foundGroupDescription); - - // compare shortnameB - string groupShortName = ConnectorAttributeUtil.GetStringValue( - ConnectorAttributeUtil.Find( - PredefinedAttributes.SHORT_NAME, groupObject.GetAttributes())); - string groupDisplayName = ConnectorAttributeUtil.GetStringValue( - ConnectorAttributeUtil.Find( - "name", groupObject.GetAttributes())); - Assert.AreEqual(groupShortName, groupDisplayName); - - uidOu = CreateObject(connector, ActiveDirectoryConnector.ouObjectClass, ouAttributes); - OperationOptionsBuilder ouOptionsBuilder = new OperationOptionsBuilder(); - ICollection ouAttributesToGet = GetDefaultAttributesToGet(ActiveDirectoryConnector.ouObjectClass); - ouAttributesToGet.Add(PredefinedAttributes.DESCRIPTION); - ouAttributesToGet.Add(PredefinedAttributes.SHORT_NAME); - ouAttributesToGet.Add("name"); - ouAttributesToGet.Add("description"); - ouOptionsBuilder.AttributesToGet = ouAttributesToGet.ToArray(); - - ConnectorObject ouObject = GetConnectorObjectFromUid(connector, - ActiveDirectoryConnector.ouObjectClass, uidOu, ouOptionsBuilder.Build()); - - // compare description - string foundOuDescription = ConnectorAttributeUtil.GetStringValue( - ConnectorAttributeUtil.Find(PredefinedAttributes.DESCRIPTION, ouObject.GetAttributes())); - Assert.AreEqual(ouDescription, foundOuDescription); - - // compare shortname - string ouShortName = ConnectorAttributeUtil.GetStringValue( - ConnectorAttributeUtil.Find( - PredefinedAttributes.SHORT_NAME, ouObject.GetAttributes())); - string ouDisplayName = ConnectorAttributeUtil.GetStringValue( - ConnectorAttributeUtil.Find( - "name", ouObject.GetAttributes())); - Assert.AreEqual(ouShortName, ouDisplayName); - } - finally - { - if (uidAccount != null) - { - //remove the one we created - DeleteAndVerifyObject(connector, ObjectClass.ACCOUNT, - uidAccount, false, true); - } - if (uidGroup != null) - { - //remove the one we created - DeleteAndVerifyObject(connector, ObjectClass.GROUP, - uidGroup, false, true); - } - if (uidOu != null) - { - //remove the one we created - DeleteAndVerifyObject(connector, ActiveDirectoryConnector.ouObjectClass, - uidOu, false, true); - } - } - } - - [Test] - public void TestPasswordExpiration() - { - //Initialize Connector - ActiveDirectoryConnector connector = new ActiveDirectoryConnector(); - connector.Init(GetConfiguration()); - Uid createUid = null; - - try - { - // create user - ICollection createAttributes = GetNormalAttributes_Account(); - // remove password, and set to something memorable - createAttributes.Remove(ConnectorAttributeUtil.Find(OperationalAttributes.PASSWORD_NAME, createAttributes)); - GuardedString gsCurrentPassword = GetGuardedString("1Password"); - createAttributes.Add(ConnectorAttributeBuilder.BuildPassword(gsCurrentPassword)); - createAttributes.Add(ConnectorAttributeBuilder.BuildEnabled(true)); - createUid = CreateAndVerifyObject(connector, - ObjectClass.ACCOUNT, createAttributes); - - // make sure authenticate works here - connector.Authenticate(ObjectClass.ACCOUNT, - ConnectorAttributeUtil.GetAsStringValue(ConnectorAttributeUtil.Find("sAMAccountName", createAttributes)), - gsCurrentPassword, null); - - // now set expiration to right now - ICollection expirePasswordNowAttrs = - new HashSet(); - - expirePasswordNowAttrs.Add( - ConnectorAttributeBuilder.BuildPasswordExpirationDate(DateTime.UtcNow)); - UpdateReplaceAndVerifyObject(connector, ObjectClass.ACCOUNT, - createUid, expirePasswordNowAttrs); - - // sometimes expiring now, really means in a few milliseconds - // there is some rounding or something that happens. - Thread.Sleep(30000); - - // make sure authenticate fails - correct password, but expired - bool caughtException = false; - try - { - // make sure authenticate fails with correct password - connector.Authenticate(ObjectClass.ACCOUNT, - ConnectorAttributeUtil.GetAsStringValue(ConnectorAttributeUtil.Find("sAMAccountName", createAttributes)), - gsCurrentPassword, null); - } - catch (PasswordExpiredException e) - { - caughtException = true; - Assert.AreEqual(createUid, e.Uid); - } - Assert.IsTrue(caughtException, "Negative test case should throw an exception"); - - // set expiration to tommorrow - ICollection expirePasswordTomorrowAttrs = - new HashSet(); - expirePasswordTomorrowAttrs.Add( - ConnectorAttributeBuilder.BuildPasswordExpirationDate(DateTime.UtcNow.AddDays(1))); - UpdateReplaceAndVerifyObject(connector, ObjectClass.ACCOUNT, - createUid, expirePasswordTomorrowAttrs); - - // make sure succeeds - connector.Authenticate(ObjectClass.ACCOUNT, - ConnectorAttributeUtil.GetAsStringValue(ConnectorAttributeUtil.Find("sAMAccountName", createAttributes)), - gsCurrentPassword, null); - - } - finally - { - if (createUid != null) - { - //remove the one we created - DeleteAndVerifyObject(connector, ObjectClass.ACCOUNT, - createUid, false, true); - } - } - } - - [Test] - public void TestAccountLocked() - { - //Initialize Connector - ActiveDirectoryConnector connector = new ActiveDirectoryConnector(); - connector.Init(GetConfiguration()); - Uid createUid = null; - - try - { - // create user - ICollection createAttributes = GetNormalAttributes_Account(); - // remove password, and set to something memorable - createAttributes.Remove(ConnectorAttributeUtil.Find(OperationalAttributes.PASSWORD_NAME, createAttributes)); - GuardedString gsCurrentPassword = GetGuardedString("1Password"); - createAttributes.Add(ConnectorAttributeBuilder.BuildPassword(gsCurrentPassword)); - createAttributes.Add(ConnectorAttributeBuilder.BuildEnabled(true)); - createUid = CreateAndVerifyObject(connector, - ObjectClass.ACCOUNT, createAttributes); - - // make sure authenticate works here - connector.Authenticate(ObjectClass.ACCOUNT, - ConnectorAttributeUtil.GetAsStringValue(ConnectorAttributeUtil.Find("sAMAccountName", createAttributes)), - gsCurrentPassword, null); - - // not allowed to lock ... only unlock, so test unlock - // setting on machine must lockout user after 3 unsuccessful - // attempst for this to work. - // lock out by having unsucessful attempts. - try - { - connector.Authenticate(ObjectClass.ACCOUNT, - ConnectorAttributeUtil.GetAsStringValue(ConnectorAttributeUtil.Find("sAMAccountName", createAttributes)), - GetGuardedString("bogusPassword"), null); - } - catch(Exception e) - { - } - - try - { - connector.Authenticate(ObjectClass.ACCOUNT, - ConnectorAttributeUtil.GetAsStringValue(ConnectorAttributeUtil.Find("sAMAccountName", createAttributes)), - GetGuardedString("bogusPassword"), null); - } - catch (Exception e) - { - } - - try - { - connector.Authenticate(ObjectClass.ACCOUNT, - ConnectorAttributeUtil.GetAsStringValue(ConnectorAttributeUtil.Find("sAMAccountName", createAttributes)), - GetGuardedString("bogusPassword"), null); - } - catch (Exception e) - { - } - - bool exceptionCaught = false; - try - { - connector.Authenticate(ObjectClass.ACCOUNT, - ConnectorAttributeUtil.GetAsStringValue(ConnectorAttributeUtil.Find("sAMAccountName", createAttributes)), - gsCurrentPassword, null); - } - catch (Exception e) - { - exceptionCaught = true; - } - Assert.IsTrue(exceptionCaught, "Account not locked. Make sure that the server is setup for account lockout after 3 attempts"); - - ICollection unlockAttrs = - new HashSet(); - unlockAttrs.Add( - ConnectorAttributeBuilder.BuildLockOut(false)); - UpdateReplaceAndVerifyObject(connector, ObjectClass.ACCOUNT, - createUid, unlockAttrs); - - // make sure succeeds - connector.Authenticate(ObjectClass.ACCOUNT, - ConnectorAttributeUtil.GetAsStringValue(ConnectorAttributeUtil.Find("sAMAccountName", createAttributes)), - gsCurrentPassword, null); - - - // now try to write lockout. Should get connector exception - bool connectorExceptionCaught = false; - try - { - ICollection lockAttrs = - new HashSet(); - lockAttrs.Add( - ConnectorAttributeBuilder.BuildLockOut(true)); - UpdateReplaceAndVerifyObject(connector, ObjectClass.ACCOUNT, - createUid, lockAttrs); - - } - catch (ConnectorException e) - { - connectorExceptionCaught = true; - } - Assert.IsTrue(connectorExceptionCaught); - } - finally - { - if (createUid != null) - { - //remove the one we created - DeleteAndVerifyObject(connector, ObjectClass.ACCOUNT, - createUid, false, true); - } - } - } - - public void TestSync(bool searchChildDomains, String syncSearchContext) - { - //Initialize Connector - ActiveDirectoryConnector connector = new ActiveDirectoryConnector(); - ActiveDirectoryConfiguration configuration = - (ActiveDirectoryConfiguration)GetConfiguration(); - configuration.SyncSearchContext = syncSearchContext; - configuration.SearchChildDomains = searchChildDomains; - connector.Init(configuration); - - Uid createUid = null; - - ICollection createdUids = new List(); - try - { - SyncTestHelper syncHelper = new SyncTestHelper(); - - // do the first sync - //connector.Sync(ObjectClass.ACCOUNT, syncHelper.Token, syncHelper.SyncHandler_Initial, null); - - syncHelper.Init(connector.GetLatestSyncToken(ObjectClass.ACCOUNT)); - ICollection attributes = null; - - // create some users - for (int i = 0; i < 10; i++) - { - attributes = GetNormalAttributes_Account(); - createUid = CreateAndVerifyObject(connector, ObjectClass.ACCOUNT, - attributes); - syncHelper.AddModUid(createUid, attributes); - createdUids.Add(createUid); - } - - // sync, and verify - connector.Sync(ObjectClass.ACCOUNT, syncHelper._token, syncHelper.SyncHandler_ModifiedAccounts, null); - syncHelper.CheckAllSyncsProcessed(); - - // reset everything - syncHelper.Init(connector.GetLatestSyncToken(ObjectClass.ACCOUNT)); - - // modify a user, then add some users, then modify one of the added users - attributes = new List(); - attributes.Add(createdUids.First()); - attributes.Add(ConnectorAttributeBuilder.Build("sn", "replaced")); - connector.Update(UpdateType.REPLACE, ObjectClass.ACCOUNT, attributes, null); - syncHelper.AddModUid(createdUids.First(), attributes); - - for(int i = 0; i < 10; i++) - { - attributes = GetNormalAttributes_Account(); - createUid = CreateAndVerifyObject(connector, ObjectClass.ACCOUNT, - attributes); - syncHelper.AddModUid(createUid, attributes); - createdUids.Add(createUid); - } - - attributes = new List(); - attributes.Add(createdUids.Last()); - attributes.Add(ConnectorAttributeBuilder.Build("sn", "replaced")); - connector.Update(UpdateType.REPLACE, ObjectClass.ACCOUNT, attributes, null); - syncHelper.AddModUid(createdUids.Last(), attributes); - - // sync, and verify - connector.Sync(ObjectClass.ACCOUNT, syncHelper._token, syncHelper.SyncHandler_ModifiedAccounts, null); - syncHelper.CheckAllSyncsProcessed(); - - syncHelper.Init(connector.GetLatestSyncToken(ObjectClass.ACCOUNT)); - // delete the user - foreach (Uid uid in createdUids) - { - DeleteAndVerifyObject(connector, ObjectClass.ACCOUNT, uid, - false, true); - syncHelper.AddDelUid(uid); - } - // sync and verify - connector.Sync(ObjectClass.ACCOUNT, syncHelper._token, syncHelper.SyncHandler_DeletedAccounts, null); - syncHelper.CheckAllSyncsProcessed(); - - createUid = CreateAndVerifyObject(connector, ObjectClass.ACCOUNT, - GetNormalAttributes_Account()); - syncHelper.AddModUid(createUid, attributes); - createdUids.Add(createUid); - - // now get the latest sync token, and it - // should be greater or equal to the last one we saw - SyncToken latestToken = connector.GetLatestSyncToken(ObjectClass.ACCOUNT); - Assert.Greater(GetUpdateUsnFromToken(latestToken), GetUpdateUsnFromToken(syncHelper._token)); - Assert.GreaterOrEqual(GetDeleteUsnFromToken(latestToken), GetDeleteUsnFromToken(syncHelper._token)); - } - finally - { - foreach (Uid uid in createdUids) - { - DeleteAndVerifyObject(connector, ObjectClass.ACCOUNT, uid, - false, false); - } - } - } - - [Test] - public void TestGetLastSyncToken() - { - //Initialize Connector - ActiveDirectoryConnector connector = new ActiveDirectoryConnector(); - ActiveDirectoryConfiguration configuration = - (ActiveDirectoryConfiguration)GetConfiguration(); - configuration.SearchChildDomains = false; - connector.Init(configuration); - SyncToken noGCToken = connector.GetLatestSyncToken(ObjectClass.ACCOUNT); - - configuration.SearchChildDomains = true; - connector.Init(configuration); - SyncToken GCToken = connector.GetLatestSyncToken(ObjectClass.ACCOUNT); - } - - public long GetUpdateUsnFromToken(SyncToken token) - { - string[] tokenParts = ((string)token.Value).Split('|'); - return long.Parse(tokenParts[0]); - } - - public long GetDeleteUsnFromToken(SyncToken token) - { - string[] tokenParts = ((string)token.Value).Split('|'); - return long.Parse(tokenParts[1]); - } - - class SyncTestHelper - { - IDictionary> _mods = null; - IList _dels = null; - - public SyncToken _token { get; set; } - - public void Init(SyncToken token) - { - _mods = new Dictionary>(); - _dels = new List(); - _token = token; - } - - public void AddModUid(Uid uid, ICollection attributes) - { - _mods[uid]=attributes; - } - - public void AddDelUid(Uid uid) - { - _dels.Add(uid); - } - - public bool SyncHandler_Initial(SyncDelta delta) - { - // do nothing .. just establishing the baseline - _token = delta.Token; - return true; - } - - public bool SyncHandler_ModifiedAccounts(SyncDelta delta) - { - _token = delta.Token; - if(delta.DeltaType.Equals(SyncDeltaType.CREATE_OR_UPDATE)) { - // just ignore extra ones. they might have come in by other means - if (_mods.ContainsKey(delta.Uid)) - { - ICollection requestedAttrs = _mods[delta.Uid]; - - ActiveDirectoryConnectorTest.VerifyObject(requestedAttrs, - delta.Object); - _mods.Remove(delta.Uid); - } - } - return true; - } - - public bool SyncHandler_DeletedAccounts(SyncDelta delta) - { - _token = delta.Token; - - _dels.Remove(delta.Uid); - return true; - } - - public bool SyncHandler_Mixed(SyncDelta delta) - { - return true; - } - - public void CheckAllSyncsProcessed() - { - // since the handlers remove things from - // the list as found, this method is called - // at then end of a sync, and all arrays should - // be empty ... meaning everything is accounted - // for. - Assert.AreEqual(0, _dels.Count); - Assert.AreEqual(0, _mods.Count); - } - } - - public void RunScript(ActiveDirectoryConnector connector, String user, - string password) - { - string tempFileName = Path.GetTempFileName(); - string scriptText = String.Format( - "echo %ARG0%:%ARG1%:%USERNAME%:%PASSWORD% > \"{0}\"", tempFileName); - - IDictionary arguments = new Dictionary(); - string arg0 = "argument_zero"; - string arg1 = "argument one"; - arguments.Add("ARG0", arg0); - arguments.Add("ARG1", arg1); - - OperationOptionsBuilder builder = new OperationOptionsBuilder(); - if (user.Length > 0) - { - builder.RunAsUser = user; - } - if (password.Length > 0) - { - builder.RunWithPassword = GetGuardedString(password); - } - - ScriptContext context = new ScriptContext("Shell", scriptText, arguments); - object resultObject = connector.RunScriptOnResource(context, builder.Build()); - Assert.IsNotNull(resultObject); - Assert.That(resultObject is int); - Assert.AreEqual(0, resultObject); - FileStream outputFs = new FileStream(tempFileName, FileMode.Open, FileAccess.Read); - StreamReader outputReader = new StreamReader(outputFs); - // read the first line - string output = outputReader.ReadLine(); - string[] returnedArray = output.Split(':'); - Assert.AreEqual(4, returnedArray.Length); - Assert.AreEqual(arg0, returnedArray[0]); - Assert.AreEqual(arg1, returnedArray[1]); - } -/* - [Test] - public void testBooScript() - { - //Initialize Connector - ActiveDirectoryConnector connector = new ActiveDirectoryConnector(); - connector.Init(GetConfiguration()); - - try - { - string tempFileName = Path.GetTempFileName(); - StringBuilder scriptText = new StringBuilder(); - scriptText.Append("print(\"this is, \");"); - scriptText.Append("print(\"a test.\");"); - - IDictionary arguments = new Dictionary(); - string arg0 = "argument_zero"; - string arg1 = "argument one"; - arguments.Add("ARG0", arg0); - arguments.Add("ARG1", arg1); - - OperationOptionsBuilder builder = new OperationOptionsBuilder(); - - ScriptContext context = new ScriptContext("Boo", scriptText.ToString(), arguments); - object resultObject = connector.RunScriptOnResource(context, builder.Build()); - } - finally - { - } - } -*/ - - // does a create and verify, then looks up and returns - // the new user's dn (used for adding to a group) - public String CreateGroupMember(ActiveDirectoryConnector connector) - { - Uid uidMember = CreateAndVerifyObject(connector, ObjectClass.ACCOUNT, - GetNormalAttributes_Account()); - - Filter uidFilter = FilterBuilder.EqualTo(uidMember); - ICollection foundObjects = TestHelpers.SearchToList( - connector, ObjectClass.ACCOUNT, uidFilter); - Assert.IsTrue(foundObjects.Count == 1); - String dnMember = ConnectorAttributeUtil.GetAsStringValue( - foundObjects.ElementAt(0).GetAttributeByName("distinguishedName")); - return dnMember; - } - - public Uid UpdateReplaceAndVerifyObject(ActiveDirectoryConnector connector, - ObjectClass oclass, Uid uid, ICollection attributes) - { - attributes.Add(uid); - Filter uidFilter = FilterBuilder.EqualTo(uid); - - // find the object ... can't update if it doesn't exist - ICollection currentConnectorObjects = TestHelpers.SearchToList( - connector, oclass, uidFilter); - Assert.AreEqual(1, currentConnectorObjects.Count); - - Uid updatedUid = connector.Update(UpdateType.REPLACE, oclass, - attributes, null); - - Assert.IsNotNull(updatedUid); - - uidFilter = FilterBuilder.EqualTo(updatedUid); - ICollection updatedConnectorObjects = TestHelpers.SearchToList( - connector, oclass, uidFilter); - Assert.IsTrue(updatedConnectorObjects.Count == 1); - VerifyObject(attributes, updatedConnectorObjects.ElementAt(0)); - return updatedUid; - } - - public Uid UpdateAddAndVerifyUser(ActiveDirectoryConnector connector, - ObjectClass oclass, Uid uid, ICollection attributes, - OperationOptions searchOptions) - { - // find the existing one, and save off all attributes - Filter uidFilter = FilterBuilder.EqualTo(uid); - ICollection currentObjects = TestHelpers.SearchToList( - connector, oclass, uidFilter, searchOptions); - Assert.IsTrue(currentObjects.Count == 1); - ICollection currentAttributes = - currentObjects.ElementAt(0).GetAttributes(); - - // build a list that has the 'added' values added to the existing values - ICollection comparisonAttributes = new List(); - foreach (ConnectorAttribute updateAttribute in attributes) - { - ConnectorAttribute existingAttribute = ConnectorAttributeUtil.Find( - updateAttribute.Name, currentAttributes); - comparisonAttributes.Add(AttConcat(updateAttribute, existingAttribute)); - } - - // make sure the uid is present in the attributes - attributes.Add(uid); - // now update with ADD to add additional home phones - Uid updatedUid = connector.Update(UpdateType.ADD, oclass, - attributes, null); - - // find it back - ICollection updatedObjects = TestHelpers.SearchToList( - connector, oclass, uidFilter, searchOptions); - Assert.IsTrue(updatedObjects.Count == 1); - - VerifyObject(comparisonAttributes, updatedObjects.ElementAt(0)); - - return updatedUid; - } - - /// - /// Concatenates two attributes' values - /// - /// Must be non null - /// May be null - /// new attribute with name of ca1 and value of ca1 + ca2 - public ConnectorAttribute AttConcat(ConnectorAttribute ca1, ConnectorAttribute ca2) - { - ConnectorAttributeBuilder builder = new ConnectorAttributeBuilder(); - Assert.IsNotNull(ca1); - if (ca2 == null) - { - // if the second is null, just build up a dummy one - ca2 = ConnectorAttributeBuilder.Build(ca1.Name); - } - - Assert.AreEqual(ca1.Name, ca2.Name); - builder.Name = ca1.Name; - builder.AddValue(ca1.Value); - builder.AddValue(ca2.Value); - - return builder.Build(); - } - - public Uid UpdateDeleteAndVerifyUser(ActiveDirectoryConnector connector, - ICollection attributes) - { - throw new NotImplementedException(); - } - - public void DeleteAndVerifyObject(ActiveDirectoryConnector connector, - ObjectClass oclass, Uid uid, bool verifyExists, bool verifyDeleted) - { - Filter uidFilter = FilterBuilder.EqualTo(uid); - - if (verifyExists) - { - // verify that object currently exists - ICollection foundObjects = TestHelpers.SearchToList( - connector, oclass, uidFilter); - - // verify that it was deleted - Assert.AreEqual(1, foundObjects.Count); - } - - // delete - try - { - connector.Delete(oclass, uid, null); - } - catch - { - if (verifyDeleted) - { - throw; - } - } - - if (verifyDeleted) - { - // verify that object was deleted - ICollection deletedObjects = TestHelpers.SearchToList( - connector, oclass, uidFilter); - - // verify that it was deleted - Assert.AreEqual(0, deletedObjects.Count); - } - } - - [Test] - // note that you must create at least one ou for this test to work - public void TestOuSearch() - { - ActiveDirectoryConnector connector = new ActiveDirectoryConnector(); - ActiveDirectoryConfiguration config = (ActiveDirectoryConfiguration)GetConfiguration(); - connector.Init(config); - ObjectClass OUObjectClass = ActiveDirectoryConnector.ouObjectClass; - - ICollection foundObjects = TestHelpers.SearchToList( - connector, OUObjectClass, null); - Assert.Greater(foundObjects.Count, 0); - } - - [Test] - public void TestUnmatchedCaseGUIDSearch() - { - //Initialize Connector - ActiveDirectoryConnector connector = new ActiveDirectoryConnector(); - connector.Init(GetConfiguration()); - Uid userUid = null; - try - { - // test normal case - userUid = CreateAndVerifyObject(connector, - ObjectClass.ACCOUNT, GetNormalAttributes_Account()); - Filter userUidFilter = FilterBuilder.EqualTo(userUid); - IList foundUserObjects = - TestHelpers.SearchToList(connector, ObjectClass.ACCOUNT, userUidFilter); - Assert.AreEqual(1, foundUserObjects.Count); - - // now test for searching with uppercase guid - userUidFilter = FilterBuilder.EqualTo(new Uid(userUid.GetUidValue().ToUpper())); - foundUserObjects = TestHelpers.SearchToList( - connector, ObjectClass.ACCOUNT, userUidFilter); - Assert.AreEqual(1, foundUserObjects.Count); - - // now test for searching with lowercase guid - userUidFilter = FilterBuilder.EqualTo(new Uid(userUid.GetUidValue().ToLower())); - foundUserObjects = TestHelpers.SearchToList( - connector, ObjectClass.ACCOUNT, userUidFilter); - Assert.AreEqual(1, foundUserObjects.Count); - } - finally - { - if (userUid != null) - { - DeleteAndVerifyObject(connector, ObjectClass.ACCOUNT, userUid, false, false); - } - } - } - - public Uid CreateAndVerifyObject(ActiveDirectoryConnector connector, - ObjectClass oclass, ICollection attributes) - { - // create object - Uid uid = CreateObject(connector, oclass, attributes); - VerifyObject(connector, uid, oclass, attributes); - return uid; - } - - public Uid CreateObject(ActiveDirectoryConnector connector, - ObjectClass oclass, ICollection attributes) - { - // if it exists, remove and recreate - Filter nameFilter = FilterBuilder.EqualTo(ConnectorAttributeBuilder.Build( - Name.NAME, ConnectorAttributeUtil.GetNameFromAttributes(attributes).Value)); - ICollection foundObjects = TestHelpers.SearchToList(connector, oclass, nameFilter); - if ((foundObjects != null) && (foundObjects.Count > 0)) - { - Assert.AreEqual(1, foundObjects.Count); - DeleteAndVerifyObject(connector, oclass, foundObjects.ElementAt(0).Uid, false, true); - } - - // create object - Uid uid = connector.Create(oclass, attributes, null); - Assert.IsNotNull(uid); - - return uid; - } - - public void VerifyObject(ActiveDirectoryConnector connector, Uid uid, - ObjectClass oclass, ICollection attributes) - { - // verify the object - VerifyObject(attributes, GetConnectorObjectFromUid(connector, oclass, uid)); - } - - - public Configuration GetConfiguration() - { - ActiveDirectoryConfiguration config = new ActiveDirectoryConfiguration(); - config.DomainName = GetProperty(CONFIG_PROPERTY_DOMAIN_NAME); - config.LDAPHostName = GetProperty(CONFIG_PROPERTY_LDAPHOSTNAME); - config.DirectoryAdminName = GetProperty(CONFIG_PROPERTY_USER); - config.DirectoryAdminPassword = GetProperty(CONFIG_PROPERTY_PASSWORD); - config.SearchContainer = GetProperty(CONFIG_PROPERTY_SEARCH_CONTEXT); - config.SyncDomainController = GetProperty(CONFIG_PROPERTY_SYNC_DOMAIN_CONTROLLER); - config.SyncGlobalCatalogServer = GetProperty(CONFIG_PROPERTY_GC_DOMAIN_CONTROLLER); - config.ConnectorMessages = TestHelpers.CreateDummyMessages(); - return config; - } - - /** - * NOTES: - * - cn and @@NAME@@ should be the same. Test if they are not - * test for proper behavior if @@name@@ is not supplied - * - test bogus attributes to like attribut named BogusAttr = hello - * or something - * - test writing to a read only attribute - * - test attributes with special values such as *, (, ), \ - */ - - public ICollection GetNormalAttributes_Account() - { - ICollection attributes = new HashSet(); - int randomNumber = GetRandomNumber(); - - // the container ... is a fabricated attribute - attributes.Add(ConnectorAttributeBuilder.Build( - "ad_container", GetProperty(CONFIG_PROPERTY_CONTAINER))); - attributes.Add(ConnectorAttributeBuilder.Build( - "userPassword", "secret")); - attributes.Add(ConnectorAttributeBuilder.Build( - "sAMAccountName", "nunitUser" + randomNumber)); - attributes.Add(ConnectorAttributeBuilder.Build( - "givenName", "nunit")); - attributes.Add(ConnectorAttributeBuilder.Build( - "sn", "TestUser" + randomNumber)); - attributes.Add(ConnectorAttributeBuilder.Build( - "displayName", "nunit test user " + randomNumber)); - attributes.Add(ConnectorAttributeBuilder.Build( - Name.NAME, "cn=nunit" + randomNumber + "," + - GetProperty(CONFIG_PROPERTY_CONTAINER))); - attributes.Add(ConnectorAttributeBuilder.Build( - "mail", "nunitUser" + randomNumber + "@some.com")); - attributes.Add(ConnectorAttributeBuilder.Build( - "otherHomePhone", "512.555.1212", "512.123.4567")); - return attributes; - } - - public ICollection GetAllAttributes_Account() - { - ICollection attributes = new HashSet(); - int randomNumber = GetRandomNumber(); - - // the container ... is a fabricated attribute - attributes.Add(ConnectorAttributeBuilder.Build( - "ad_container", GetProperty(CONFIG_PROPERTY_CONTAINER))); - GuardedString password = new GuardedString(); - foreach (char c in "secret") - { - password.AppendChar(c); - } - attributes.Add(ConnectorAttributeBuilder.BuildPassword( - password)); - attributes.Add(ConnectorAttributeBuilder.Build( - "sAMAccountName", "nunit" + randomNumber)); - attributes.Add(ConnectorAttributeBuilder.Build( - "givenName", "nunit")); - attributes.Add(ConnectorAttributeBuilder.Build( - "sn", "TestUser" + randomNumber)); - attributes.Add(ConnectorAttributeBuilder.Build( - "displayName", "nunit test user " + randomNumber)); - attributes.Add(ConnectorAttributeBuilder.Build( - "mail", "nunitUser" + randomNumber + "@some.com")); - attributes.Add(ConnectorAttributeBuilder.Build( - "telephoneNumber", "333-547-8453")); - // attributes.Add(ConnectorAttributeBuilder.Build( - // "msExchHomeServerName", "")); - attributes.Add(ConnectorAttributeBuilder.Build( - "employeeID", "1234567")); - attributes.Add(ConnectorAttributeBuilder.Build( - "division", "Identity Services")); - attributes.Add(ConnectorAttributeBuilder.Build( - "mobile", "554-210-8631")); - // attributes.Add(ConnectorAttributeBuilder.Build( - // "mDBOverQuotaLimit", "")); - attributes.Add(ConnectorAttributeBuilder.Build( - "middleName", "testCase")); - attributes.Add(ConnectorAttributeBuilder.Build( - "description", "This user was created as a test case for the AD Connector")); - // attributes.Add(ConnectorAttributeBuilder.Build( - // "mDBOverHardQuotaLimit", "")); - // attributes.Add(ConnectorAttributeBuilder.Build( - // "mDBUseDefaults", "")); - attributes.Add(ConnectorAttributeBuilder.Build( - "department", "Connector Affairs")); - // for manager, it looks like the manager has to exist - // attributes.Add(ConnectorAttributeBuilder.Build( - // "manager", "Some Guy")); - // attributes.Add(ConnectorAttributeBuilder.Build( - // "mDBStorageQuota", "")); - // attributes.Add(ConnectorAttributeBuilder.Build( - // "mailNickName", "")); - attributes.Add(ConnectorAttributeBuilder.Build( - "title", "Manager")); - attributes.Add(ConnectorAttributeBuilder.Build( - "initials", "XYZ")); - // attributes.Add(ConnectorAttributeBuilder.Build( - // "homeMTA", "")); - attributes.Add(ConnectorAttributeBuilder.Build( - "co", "United States")); - // attributes.Add(ConnectorAttributeBuilder.Build( - // "homeMDB", "")); - attributes.Add(ConnectorAttributeBuilder.Build( - "company", "NUnit Test Company")); - attributes.Add(ConnectorAttributeBuilder.Build( - "facsimileTelephoneNumber", "111-222-3333")); - attributes.Add(ConnectorAttributeBuilder.Build( - "homePhone", "222-333-4444")); - // attributes.Add(ConnectorAttributeBuilder.Build( - // "directoryEntryWS_PasswordExpired", "")); - attributes.Add(ConnectorAttributeBuilder.Build( - "streetAddress", "12345 Some Street")); - attributes.Add(ConnectorAttributeBuilder.Build( - "l", "Austin")); - attributes.Add(ConnectorAttributeBuilder.Build( - "st", "Texas")); - attributes.Add(ConnectorAttributeBuilder.Build( - "postalCode", "78717")); - // attributes.Add(ConnectorAttributeBuilder.Build( - // "AccountLocked", "")); - - // used to be 'Terminal Services Initial Program' - attributes.Add(ConnectorAttributeBuilder.Build( - TerminalServicesUtils.TS_INITIAL_PROGRAM, "myprog.exe")); - - // used to be 'Terminal Services Initial Program Directory' - attributes.Add(ConnectorAttributeBuilder.Build( - TerminalServicesUtils.TS_INITIAL_PROGRAM_DIR, "c:\\nunittest\\dir")); - - // unknown ... - // attributes.Add(ConnectorAttributeBuilder.Build( - // "Terminal Services Inherit Initial Program", true)); - - // used to be 'Terminal Services Allow Logon' - defaults to false, so testing true - attributes.Add(ConnectorAttributeBuilder.Build( - TerminalServicesUtils.TS_ALLOW_LOGON, 1)); - - // used to be 'Terminal Services Active Session Timeout' - attributes.Add(ConnectorAttributeBuilder.Build( - TerminalServicesUtils.TS_MAX_CONNECTION_TIME, 10000)); - - // used to be 'Terminal Services Disconnected Session Timeout' - attributes.Add(ConnectorAttributeBuilder.Build( - TerminalServicesUtils.TS_MAX_DISCONNECTION_TIME, 20000)); - - // used to be 'Terminal Services Idle Timeout' - attributes.Add(ConnectorAttributeBuilder.Build( - TerminalServicesUtils.TS_MAX_IDLE_TIME, 30000)); - - // used to be 'Terminal Services Connect Client Drives At Logon' - attributes.Add(ConnectorAttributeBuilder.Build( - TerminalServicesUtils.TS_CONNECT_CLIENT_DRIVES_AT_LOGON, 1)); - - // used to be 'Terminal Services Connect Client Printers At Logon' - attributes.Add(ConnectorAttributeBuilder.Build( - TerminalServicesUtils.TS_CONNECT_CLIENT_PRINTERS_AT_LOGON, 1)); - - // used to be 'Terminal Services Default To Main Client Printer' - attributes.Add(ConnectorAttributeBuilder.Build( - TerminalServicesUtils.TS_DEFAULT_TO_MAIN_PRINTER, 1)); - - // used to be 'Terminal Services End Session On Timeout Or Broken Connection' - attributes.Add(ConnectorAttributeBuilder.Build( - TerminalServicesUtils.TS_BROKEN_CONNECTION_ACTION, 1)); - - // used to be 'Terminal Services Allow Reconnect From Originating Client Only' - attributes.Add(ConnectorAttributeBuilder.Build( - TerminalServicesUtils.TS_RECONNECTION_ACTION, 1)); - - // attributes.Add(ConnectorAttributeBuilder.Build( - // "Terminal Services Callback Settings", "")); - - // attributes.Add(ConnectorAttributeBuilder.Build( - // "Terminal Services Callback Phone Number", "")); - - // used to be 'Terminal Services Remote Control Settings' - attributes.Add(ConnectorAttributeBuilder.Build( - TerminalServicesUtils.TS_ENABLE_REMOTE_CONTROL, 1)); - - // used to be 'Terminal Services User Profile - attributes.Add(ConnectorAttributeBuilder.Build( - TerminalServicesUtils.TS_PROFILE_PATH, "\\My Profile")); - - // used to be 'Terminal Services Local Home Directory - attributes.Add(ConnectorAttributeBuilder.Build( - TerminalServicesUtils.TS_HOME_DIRECTORY, "\\My Home Dir")); - - // used to be 'Terminal Services Home Directory Drive - attributes.Add(ConnectorAttributeBuilder.Build( - TerminalServicesUtils.TS_HOME_DRIVE, "C:")); - - // uSNChanged should be read only - // attributes.Add(ConnectorAttributeBuilder.Build( - // "uSNChanged", "")); - // objectGUID should be read only - // attributes.Add(ConnectorAttributeBuilder.Build( - // "objectGUID", "")); - - - // now set name operational attribute - attributes.Add(ConnectorAttributeBuilder.Build( - Name.NAME, "cn=nunit" + randomNumber + "," + - GetProperty(CONFIG_PROPERTY_CONTAINER))); - - - /* - // a few attributes not used in IDM - - // country code is not returned by default - attributes.Add(ConnectorAttributeBuilder.Build( - "countryCode", 23)); - - */ - return attributes; - } - - public ICollection GetNormalAttributes_Group() - { - ICollection attributes = new List(); - int randomNumber = GetRandomNumber(); - - attributes.Add(ConnectorAttributeBuilder.Build( - "mail", "groupmail@example.com")); - attributes.Add(ConnectorAttributeBuilder.Build( - "description", "Original Description" + randomNumber)); - attributes.Add(ConnectorAttributeBuilder.Build( - Name.NAME, "cn=nunitGroup" + randomNumber + "," + - GetProperty(CONFIG_PROPERTY_CONTAINER))); - attributes.Add(ConnectorAttributeBuilder.Build( - "groupType", 4)); - return attributes; - } - - public ICollection GetNormalAttributes_OrganizationalUnit() - { - ICollection attributes = new List(); - int randomNumber = GetRandomNumber(); - - attributes.Add(ConnectorAttributeBuilder.Build( - Name.NAME, "ou=nunitOU" + randomNumber + "," + - GetProperty(CONFIG_PROPERTY_CONTAINER))); - return attributes; - } - - private static void VerifyObject(ICollection requestedAttributes, - ConnectorObject returnedObject) - { - - // for now, skipping values that are very difficult to - // determine equality ... or they are not returned like - // 'userPassword'. - ICollection skipAttributeNames = new List(); - skipAttributeNames.Add("USERPASSWORD"); - skipAttributeNames.Add(OperationalAttributes.PASSWORD_NAME); - skipAttributeNames.Add(OperationalAttributes.CURRENT_PASSWORD_NAME); - skipAttributeNames.Add(Uid.NAME); - // have to ignore the password expire attribute. It will not come - // back EXACTLY the same as it was set. It seems like may ad rounds - // off to the nearest second, or minute, or something. - skipAttributeNames.Add(OperationalAttributes.PASSWORD_EXPIRATION_DATE_NAME); - - ICollection ldapStringAttributes = new List(); - ldapStringAttributes.Add("AD_CONTAINER"); - ldapStringAttributes.Add(Name.NAME); - ldapStringAttributes.Add(PredefinedAttributes.GROUPS_NAME); - ldapStringAttributes.Add(PredefinedAttributes.ACCOUNTS_NAME); - - // for each attribute in the connector object ... - foreach (ConnectorAttribute attribute in requestedAttributes) - { - ConnectorAttribute returnedAttribute = returnedObject.GetAttributeByName( - attribute.Name); - - if (skipAttributeNames.Contains(attribute.Name.ToUpper())) - { - Trace.TraceWarning("Skipping comparison of attribute {0}", - attribute.Name); - Trace.TraceWarning("requested values were:"); - foreach (Object requestedValueObject in attribute.Value) - { - Trace.TraceWarning(requestedValueObject.ToString()); - } - if (returnedAttribute == null) - { - Trace.TraceWarning(" no {0} attribute was returned", - attribute.Name); - } - else - { - Trace.TraceWarning("returned values were:"); - foreach (Object returnedValueObject in returnedAttribute.Value) - { - Trace.TraceWarning(returnedValueObject.ToString()); - } - } - continue; - } - - Assert.IsNotNull(returnedAttribute); - - // for each value in the attribute ... - foreach (Object requestedValueObject in attribute.Value) - { - // order of multivalue attributes is not gauranted, so check - // all values of the returned object against the value - // also attributes like the ldap 'objectclass' might return - // more values than I set ... (set User, return user, top, inetorgperson) - Boolean foundValue = false; - foreach (Object returnedValueObject in returnedAttribute.Value) - { - Object lhs = requestedValueObject; - Object rhs = returnedValueObject; - - // if its an ldap string, put it in a standard form - if (ldapStringAttributes.Contains(attribute.Name.ToUpper())) - { - Assert.That(requestedValueObject is String); - Assert.That(returnedValueObject is String); - lhs = ActiveDirectoryUtils.NormalizeLdapString((String)requestedValueObject); - rhs = ActiveDirectoryUtils.NormalizeLdapString((String)returnedValueObject); - /* - // if either of them start with a server name, take it off - // it's not important to the comparison - string []lhsParts = ((string)lhs).Split('/'); - Assert.LessOrEqual(lhsParts.Length, 2); - lhs = (lhsParts.Length) == 1 ? lhsParts[0] : lhsParts[1]; - string[] rhsParts = ((string)rhs).Split('/'); - Assert.LessOrEqual(rhsParts.Length, 2); - lhs = (rhsParts.Length) == 1 ? rhsParts[0] : rhsParts[1]; - */ - } - - if (lhs.Equals(rhs)) - { - foundValue = true; - break; - } - } - Assert.IsTrue(foundValue, - String.Format("Could not find value {0} for attribute named {1}", - requestedValueObject, attribute.Name)); - } - } - } - - // this needs to be replaced by the real one. - public string GetProperty(string propertyName) - { - string propertyValue = TestHelpers.GetProperty(propertyName, null); - //Trace.WriteLine(String.Format("GetProperty: {0} = {1}", propertyName, propertyValue)); - return propertyValue; - } - - public ConnectorObject GetConnectorObjectFromUid( - ActiveDirectoryConnector connector, ObjectClass oclass, Uid uid) - { - return GetConnectorObjectFromUid(connector, oclass, uid, null); - } - - public ConnectorObject GetConnectorObjectFromUid( - ActiveDirectoryConnector connector, ObjectClass oclass, Uid uid, - OperationOptions options) - { - // get sid to check permissions - Filter uidFilter = FilterBuilder.EqualTo(uid); - ICollection objects = TestHelpers.SearchToList(connector, - oclass, uidFilter, options); - Assert.AreEqual(1, objects.Count); - return objects.ElementAt(0); - } - - public ICollection GetDefaultAttributesToGet(ObjectClass oclass) - { - ICollection attributesToGet = new HashSet(); - - ActiveDirectoryConnector connector = new ActiveDirectoryConnector(); - connector.Init(GetConfiguration()); - Schema schema = connector.Schema(); - ObjectClassInfo ocInfo = schema.FindObjectClassInfo(oclass.GetObjectClassValue()); - Assert.IsNotNull(ocInfo); - - foreach (ConnectorAttributeInfo caInfo in ocInfo.ConnectorAttributeInfos) - { - if (caInfo.IsReturnedByDefault) - { - attributesToGet.Add(caInfo.Name); - } - } - - return attributesToGet; - } - - public GuardedString GetGuardedString(string regularString) - { - GuardedString guardedString = new GuardedString(); - foreach (char c in regularString) - { - guardedString.AppendChar(c); - } - return guardedString; - } - - int GetRandomNumber() - { - const int randomRange = 10000000; - - int number = 0; - - // having trouble with duplicate random numbers, so try a few hundred - // times to get a unique one before giving up. - for(int i=0;i<500;i++) { - number = _rand.Next(randomRange); -#if DEBUG - // make sure the debug numbers are in a different - // range than release ones to eliminate conflicts during - // the build where both configurations are run concurrently - number += randomRange; -#endif - if(!(randomList.Contains(number))) { - randomList.Add(number); - break; - } - } - - return number; - } - - public void RemoveAttributeByName(ICollection accountAttributes, string name) - { - ConnectorAttribute ca = ConnectorAttributeUtil.Find(name, accountAttributes); - if (ca != null) - { - accountAttributes.Remove(ca); - } - } - } - -} diff --git a/DotNetConnectors.sln/ActiveDirectoryConnectorTests/ActiveDirectoryConnectorTests.csproj b/DotNetConnectors.sln/ActiveDirectoryConnectorTests/ActiveDirectoryConnectorTests.csproj deleted file mode 100644 index 29bfa34e..00000000 --- a/DotNetConnectors.sln/ActiveDirectoryConnectorTests/ActiveDirectoryConnectorTests.csproj +++ /dev/null @@ -1,140 +0,0 @@ - - - - Debug - AnyCPU - 9.0.21022 - 2.0 - {DB8F7DA9-11A2-4BEB-8C14-25D8229EBE7E} - Library - Properties - Sun.OpenConnectors.ActiveDirectory - ActiveDirectoryConnectorTests - v3.5 - 512 - - - - - true - full - false - bin\Debug\ - DEBUG;TRACE - prompt - 4 - - - pdbonly - true - bin\Release\ - TRACE - prompt - 4 - - - Project - - - - - - - - - - - - - - - - - - - - - - - - - 3.5 - - - - 3.5 - - - 3.5 - - - - - - - - - - - {BDF495CA-0FCD-4E51-A871-D467CDE3B43E} - ActiveDirectoryConnector - - - {F140E8DA-52B4-4159-992A-9DA10EA8EEFB} - Common - - - {8B24461B-456A-4032-89A1-CD418F7B5B62} - Framework - - - {5B011775-B121-4EEE-A410-BA2D2F5BFB8B} - FrameworkInternal - - - {4700690A-2D29-40A0-86AC-E5A9F71A479A} - ShellScriptExecutorFactory - - - - - Always - - - - - - - - - - - - - - - - - - diff --git a/DotNetConnectors.sln/ActiveDirectoryConnectorTests/Properties/AssemblyInfo.cs b/DotNetConnectors.sln/ActiveDirectoryConnectorTests/Properties/AssemblyInfo.cs deleted file mode 100644 index 5b458efe..00000000 --- a/DotNetConnectors.sln/ActiveDirectoryConnectorTests/Properties/AssemblyInfo.cs +++ /dev/null @@ -1,58 +0,0 @@ -/* - * ==================== - * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER. - * - * Copyright 2007-2008 Sun Microsystems, Inc. All rights reserved. - * - * The contents of this file are subject to the terms of the Common Development - * and Distribution License("CDDL") (the "License"). You may not use this file - * except in compliance with the License. - * - * You can obtain a copy of the License at - * http://IdentityConnectors.dev.java.net/legal/license.txt - * See the License for the specific language governing permissions and limitations - * under the License. - * - * When distributing the Covered Code, include this CDDL Header Notice in each file - * and include the License file at identityconnectors/legal/license.txt. - * If applicable, add the following below this CDDL Header, with the fields - * enclosed by brackets [] replaced by your own identifying information: - * "Portions Copyrighted [year] [name of copyright owner]" - * ==================== - */ -using System.Reflection; -using System.Runtime.CompilerServices; -using System.Runtime.InteropServices; - -// General Information about an assembly is controlled through the following -// set of attributes. Change these attribute values to modify the information -// associated with an assembly. -[assembly: AssemblyTitle("ActiveDirectoryConnectorTests")] -[assembly: AssemblyDescription("")] -[assembly: AssemblyConfiguration("")] -[assembly: AssemblyCompany("Sun Microsystems")] -[assembly: AssemblyProduct("ActiveDirectoryConnectorTests")] -[assembly: AssemblyCopyright("Copyright Sun Microsystems 2008")] -[assembly: AssemblyTrademark("")] -[assembly: AssemblyCulture("")] - -// Setting ComVisible to false makes the types in this assembly not visible -// to COM components. If you need to access a type in this assembly from -// COM, set the ComVisible attribute to true on that type. -[assembly: ComVisible(false)] - -// The following GUID is for the ID of the typelib if this project is exposed to COM -[assembly: Guid("bdb18bb6-ec8d-4a9b-a56b-7c600534f7f5")] - -// Version information for an assembly consists of the following four values: -// -// Major Version -// Minor Version -// Build Number -// Revision -// -// You can specify all the values or you can default the Build and Revision Numbers -// by using the '*' as shown below: -// [assembly: AssemblyVersion("1.0.*")] -[assembly: AssemblyVersion("1.0.0.0")] -[assembly: AssemblyFileVersion("1.0.0.0")] diff --git a/DotNetConnectors.sln/ActiveDirectoryConnectorTests/TestParams.resx b/DotNetConnectors.sln/ActiveDirectoryConnectorTests/TestParams.resx deleted file mode 100644 index 735169b0..00000000 --- a/DotNetConnectors.sln/ActiveDirectoryConnectorTests/TestParams.resx +++ /dev/null @@ -1,147 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - text/microsoft-resx - - - 2.0 - - - System.Resources.ResXResourceReader, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 - - - System.Resources.ResXResourceWriter, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 - - - cn=users,dc=dv207518domain2,dc=central,dc=sun,dc=com - - - localhost - - - etegrity - - - 389 - - - etegrity - - - etegrity - - - dv207518domain2\administrator - - - administrator - - - dv207518domain2\administrator - - \ No newline at end of file diff --git a/DotNetConnectors.sln/ActiveDirectoryConnectorTests/project.xml b/DotNetConnectors.sln/ActiveDirectoryConnectorTests/project.xml deleted file mode 100644 index 748bfeaf..00000000 --- a/DotNetConnectors.sln/ActiveDirectoryConnectorTests/project.xml +++ /dev/null @@ -1,14 +0,0 @@ - - - - - - - - - - - - - - diff --git a/DotNetConnectors.sln/BooScriptExecutorFactory/AssemblyInfo.cs b/DotNetConnectors.sln/BooScriptExecutorFactory/AssemblyInfo.cs deleted file mode 100644 index 95eb3457..00000000 --- a/DotNetConnectors.sln/BooScriptExecutorFactory/AssemblyInfo.cs +++ /dev/null @@ -1,53 +0,0 @@ -/* - * ==================== - * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER. - * - * Copyright 2007-2008 Sun Microsystems, Inc. All rights reserved. - * - * The contents of this file are subject to the terms of the Common Development - * and Distribution License("CDDL") (the "License"). You may not use this file - * except in compliance with the License. - * - * You can obtain a copy of the License at - * http://IdentityConnectors.dev.java.net/legal/license.txt - * See the License for the specific language governing permissions and limitations - * under the License. - * - * When distributing the Covered Code, include this CDDL Header Notice in each file - * and include the License file at identityconnectors/legal/license.txt. - * If applicable, add the following below this CDDL Header, with the fields - * enclosed by brackets [] replaced by your own identifying information: - * "Portions Copyrighted [year] [name of copyright owner]" - * ==================== - */ -#region Using directives - -using System; -using System.Reflection; -using System.Runtime.InteropServices; - -#endregion - -// General Information about an assembly is controlled through the following -// set of attributes. Change these attribute values to modify the information -// associated with an assembly. -[assembly: AssemblyTitle("BooScriptExecutorFactory")] -[assembly: AssemblyDescription("")] -[assembly: AssemblyConfiguration("")] -[assembly: AssemblyCompany("")] -[assembly: AssemblyProduct("BooScriptExecutorFactory")] -[assembly: AssemblyCopyright("")] -[assembly: AssemblyTrademark("")] -[assembly: AssemblyCulture("")] - -// This sets the default COM visibility of types in the assembly to invisible. -// If you need to expose a type to COM, use [ComVisible(true)] on that type. -[assembly: ComVisible(false)] - -// The assembly version has following format : -// -// Major.Minor.Build.Revision -// -// You can specify all the values or you can use the default the Revision and -// Build Numbers by using the '*' as shown below: -[assembly: AssemblyVersion("1.0.*")] diff --git a/DotNetConnectors.sln/BooScriptExecutorFactory/BooScriptExecutorFactory.cs b/DotNetConnectors.sln/BooScriptExecutorFactory/BooScriptExecutorFactory.cs deleted file mode 100644 index ff822ba7..00000000 --- a/DotNetConnectors.sln/BooScriptExecutorFactory/BooScriptExecutorFactory.cs +++ /dev/null @@ -1,81 +0,0 @@ -/* - * ==================== - * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER. - * - * Copyright 2007-2008 Sun Microsystems, Inc. All rights reserved. - * - * The contents of this file are subject to the terms of the Common Development - * and Distribution License("CDDL") (the "License"). You may not use this file - * except in compliance with the License. - * - * You can obtain a copy of the License at - * http://IdentityConnectors.dev.java.net/legal/license.txt - * See the License for the specific language governing permissions and limitations - * under the License. - * - * When distributing the Covered Code, include this CDDL Header Notice in each file - * and include the License file at identityconnectors/legal/license.txt. - * If applicable, add the following below this CDDL Header, with the fields - * enclosed by brackets [] replaced by your own identifying information: - * "Portions Copyrighted [year] [name of copyright owner]" - * ==================== - */ -using System; -using System.Reflection; -using System.Collections; -using System.Collections.Generic; -using Boo.Lang.Interpreter; -using Boo.Lang.Compiler; - -namespace Org.IdentityConnectors.Common.Script.Boo -{ - [ScriptExecutorFactoryClass("Boo")] - public class BooScriptExecutorFactory : ScriptExecutorFactory - { - /// - /// Attempt to trigger an exception if the runtime is not present. - /// - public BooScriptExecutorFactory() { - new BooScriptExecutor(new Assembly[0], "1").Execute(null); - } - - /// - /// Creates a script executor give the Boo script. - /// - override - public ScriptExecutor NewScriptExecutor(Assembly [] referencedAssemblies, string script, bool compile) { - return new BooScriptExecutor(referencedAssemblies,script); - } - - /// - /// Processes the script. - /// - class BooScriptExecutor : ScriptExecutor { - private readonly Assembly [] _referencedAssemblies; - private readonly string _script; - private readonly InteractiveInterpreter _engine; - - public BooScriptExecutor(Assembly [] referencedAssemblies, string script) { - _referencedAssemblies = referencedAssemblies; - _script = script; - _engine = new InteractiveInterpreter(); - _engine.RememberLastValue = true; - foreach (Assembly assembly in referencedAssemblies) { - _engine.References.Add(assembly); - } - } - public object Execute(IDictionary arguments) { - // add all the globals - IDictionary args = CollectionUtil.NullAsEmpty(arguments); - foreach (KeyValuePair entry in args) { - _engine.SetValue(entry.Key, entry.Value); - } - CompilerContext context = _engine.Eval(_script); - if ( context.Errors.Count > 0 ) { - throw context.Errors[0]; - } - return _engine.LastValue; - } - } - } -} diff --git a/DotNetConnectors.sln/BooScriptExecutorFactory/BooScriptExecutorFactory.csproj b/DotNetConnectors.sln/BooScriptExecutorFactory/BooScriptExecutorFactory.csproj deleted file mode 100644 index 469a1f3b..00000000 --- a/DotNetConnectors.sln/BooScriptExecutorFactory/BooScriptExecutorFactory.csproj +++ /dev/null @@ -1,95 +0,0 @@ - - - - {0747C440-70E4-4E63-9F9D-03B3A010C991} - Debug - AnyCPU - Library - Org.IdentityConnectors.Common.Script.Boo - Boo.ScriptExecutorFactory - v3.5 - - - prompt - 4 - AnyCPU - bin\Debug\ - True - Full - False - True - DEBUG;TRACE - - - pdbonly - bin\Release\ - TRACE - prompt - 4 - AnyCPU - False - True - False - - - - - lib\Boo.Lang.dll - - - lib\Boo.Lang.Compiler.dll - - - lib\Boo.Lang.Interpreter.dll - - - lib\Boo.Lang.Parser.dll - - - lib\Boo.Lang.Useful.dll - - - - 3.5 - - - - 3.5 - - - - - - - - - {F140E8DA-52B4-4159-992A-9DA10EA8EEFB} - Common - - - - - - - diff --git a/DotNetConnectors.sln/BooScriptExecutorFactory/lib/Boo.Lang.Compiler.dll b/DotNetConnectors.sln/BooScriptExecutorFactory/lib/Boo.Lang.Compiler.dll deleted file mode 100644 index 7a4bc09ba3dce42a0b0abf3030063f29df749338..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 729088 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
GU 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

T_?v+9kPqlvw@YaP6tl3J0exTmi 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�FL@bc7u8lj-pJ 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 W7|1Y?VIadmhJg$N83z7uG4OBPtaM!f diff --git a/DotNetConnectors.sln/BooScriptExecutorFactory/lib/Boo.Lang.Interpreter.dll b/DotNetConnectors.sln/BooScriptExecutorFactory/lib/Boo.Lang.Interpreter.dll deleted file mode 100644 index 407d01147637f06a2a95f0ad3445cabc552c6683..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 86016 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 V1`afEpn(Go9BAM`1OFlo{C^)m7oGqB diff --git a/DotNetConnectors.sln/BooScriptExecutorFactory/lib/Boo.Lang.Parser.dll b/DotNetConnectors.sln/BooScriptExecutorFactory/lib/Boo.Lang.Parser.dll deleted file mode 100644 index 8bf0313d832a327a3568d7b39af0325641090f04..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 421888 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

|?;D`Z! zDhj;B0RJHhJjDP%9|az6fFLboNWlQV7zO4G@QEn!KRhMr>rvp34DcIK;Fk^X$tVyv zl#SFCRxO94zU62SyiIp!`#7u$F_b9~KJhHy@X3?Qk45r3W!w(82v!FOR*#F|@fd<- zA%Y8+PcszU(pc=LW4ZnUans&XxFyJSabvE}#BzN;%vB0HkRuZVXna-*I;9= z&&6_G5awDH&vlR3&UKigIuc{bA2_I>sRPhh8qVC^_sU;LpV-GeNtGx9Bx6S zWse!M93C||^Rc%5S`6)Lfp+vmFq9RKb$1+!!r#Y|UlmfJ?H4Bea4g}mVZsZAj4_RFE95MzYc*L1ojNoyRqRe} zC9Jv%nMcl%;^5%pxtxgbQ;yr93(6;;D17IOQ9i?^vRNFgxJz*aU7?L;{Q^ z&)x(6y||HQ{VxIB^!84CoI*!qE&{Rz@JV*Ju*ToX=_fq9^OpvelC6}I_l zwtnd*gpMm*U_w`^eN}cN_SbCGxl1i~m z+%vFrw&z-2<@QQYoY2?%xrUT;3rh<}DRB+uPO-C4-nz4ngMQJVU#_>q;?iJ%z1gT2 z{isSi1QpFtIg1~rYLQ-uZ;z~VMnBS^YlO+7Cf=+4e-IymF`f^h3V6Jdjnqty%l8(! zhV>Q}sA}c(KBEwV7QyxX0nYU^ppPpGz5KBYadFr>wTv6!P_yx%WiQ312;I5FsNo1*<~ z@P@cN?d{feO7olRlo2$))hebHuNXSWyj?W}->qU(T^CekBwCe~Pce72uU|2A2>Q)c zLw)!=0)fS0s=|m$OZlaews4h^*&3p*Fz+|mast(F%hB-?pE|4j6Bcib+Akez(}xPO z6X>o}bQ9|Y45dwb1g~$arjSohOlYt+jEyoZOvM`0F}d8t9^(Q|#2#BZytWUJV<-7L z1L>Om4nthhudQ&|@LF_lQQcJE^}iBo39D$6v4f6A=(xg(CWNz`p#d==7x$cLLmQ(N zJcRvPZ)5bs)`4A?qU4j#_%CS)^FHY>|08YUy0N5Vdq!r+w9l7foDJd0kK`TRHo*`f+{YIK#Xp zC*Om`jew&F#94}YuN)dM9kq0xA-2I7A~vxD%{H794JQs$y8DK4^>Z^sL2cMO`IPn0Cq%r%BVov)mRoEl;9nt;IwC_mSRZ9=S z-xG%p$KNZ4*U~6P4{+8&(77tYC&T4)@PZ`Q!RSk2*t)5FJiCJJ4Akd9LZ0ne8w``E zv{MiZ?AKDB9Z6XrM^e-PBPmIWkEEDBfw*8KeLmjpNP3+e85>7Z6jwiv9%EC>IEn%q=MMPkX7%hnlqKModEOTAVeJ%zpHLi#F!m zrWvizA+2Vu@P7)e&Gh(H=$DK3Lcjb9+7WZS2Ak-7lsBN|nK2c+JOe|AwiS+nxp~$& zqpB=$z;ea7$|;n^(lGkkbk`zXN=&|n-oG1bCH#)Z>tLs2S*f9JSLQe)m!+#imlsDL zmJI^#w98)klKd!KsHM!j)o!2LiR*K)_xo4u+4SJdxGu#nlC^mFQWypXh*7@yC0X`# zc4QZ0;>>R)xR{aRQXNl1?skHFZAtZcFEgB;>g zOvZ;WeR!r0>o2lYG89Qak&yc= z*IAxWYhqAu#oy7PF4>tj2;`K5Y3PZU=owkZzL!s|bR9Z6&IsbWV3>+smwI9RJ5+u1;8 zvXXdFxFILQ^fz^w9vb~v@)?a8Xaucm znHz23!$nDaP!%NdcoB{kqzVTQU5LL+hfc-ci-t}p4dc0K=^XsU&KgzRfcX(Vw6Js( zo?C|I;crFHX*11i{P^Ur)JGHYTjZ+rUDF&B_uWQL?T)-0*@REEDe!3_YP&#{{)MRF z2!2=M2WZH*jR=j$x3!3G;M*c#3HXMan3iuxq6aT9y_v`E%wovC=HWe8BG9;RSxJ!UMn!~s5QF`ucea|%P3fwY!#Q9cf&D}b-Q}wii`cT8MO?wt zU$%&JVi8++vxsuE7TdwCMn+w>n?l1ma@4oqL&FsBg*boBZb!$& z9drJ}Rn|+mOkRHmAZdh1E;hL@ogS(Agu1Cwy|a@}+4lSl*!&{skGeD9{bPXJW12eD z!vvIp{yRF*pFH~UhJhYQWSD16Fw9S6PB&BtSf*4Rv2W7Qe+jl^|E*G=f@vya#K`!) zaCq$nh^*n~LCM(PK?uXpcGlxZooeAYjUZE%YapSM`PqEY=N5zSewAJ{*QAd!5`2H&r zJ_GTDah&3_AK}e-htof&1J6V8JRQ$}HR-;9_m2Yqark`^zw%B1FczCLp5-p!K+voQ zm3L{Z*;Xt6MuTS8tNddP-lBS-{B;eQGZf{|Y0xZnmOrRLbJo86Rt*{(pnRVO&0b^q zb`6^CuJX+qG`msdt2Jmgi^{xN1e(oWPWdzqnvtTsN`pqLmj@X{>dTGt3XhXJhZ`(2TO>&uh?};Vt8&H2z_A%{Q`+gFXc_aXxD4 zOkEb0D@k_6&8~_{Cr>;?Gs(l$>C#Bgb?%gZst8RF zF3W*Lx9~i}oG)nX`6l?V2JL~ww`=em6AMoPq|Kb9D8n{m@C1#8hIb3sn|Dqn&ob{< z;{7=Dej(nsnRiTya6v#XcRUO4SDJTPpU0bbTA$aL_lXqZ9-Q%we^z20))!3g*kekV z{xY_KolrB~PQTOPICo>jq|=N8$z}W-doa^?Am+WdFdYi*PAUPcT1oW7(m`PqrviiZ_~hU0&E5!_^f7ky{Y_E;b1xqgD`fbRh7~nP( z$B9!UX{wK+OnNY)_n2Qf__nLu z*>kI;#V!6E1^W-;55_=3yl5aAt-g*rffv(Rls#YW1`5{W`BeuNgIz&B213BF0a#}= zzhX&IXEag3OHldZuT6?g_}n*1=!Fg|tm`=n8yL;HQwC?EFwq)|TX z_erCC_+Rgn<{x2i+{$r>K55>IKvSPI*!wyX(ci%c$=@u@7o+bC@9xjyy&w1=!4G~_ zox||M;Tz{Yczys+&f{%47}w;T>m(=Q{Zu?B^V^Da^qN6D*PAC-;o!OEBHbe>AjKE( zy92+tKbxN+J=ZQuc$WVef9MqAKXsg^!-ZV=H(as-@DEYo_YCkaQQ+qd@UKze`wVbb z6!=C1#BvY~3N7e%18j)`D+cIBffpMfR;a@4CmSHV>V&}M2AGWk=Nn*a6xe2f)1tuN zW9GoR&xitH@(B<=eZqp^a6^CxM1gSZBR~`wrhLc%4~zm|W`GApf%wRjWM32o!k@4J z7e|4o8{olF;E@K16H;O63jj207nAjo(-+C_l<_%Ji(s-oa^}PkJP;xn*GCQ-Bp_S& zMGW2J`pC(}az*^{jO!z(J(ertk7ryTIUTWF5q~@b!EuTK+1llLP_{ceC@zP(W4R*! zc*gatlaJ+!_~RMZv(D^Tu82RL-Sez7FNXFj&>E&`)&vzlw6WsFSgzM1*XUPhxXTd~ zyL1-v>E9y?1MMFcQtyvvXDs13{&-R`0=Q~s$gcOt(-}+nZ|rb-PNv=;PggAABVj^q z_pmb4V+py4Y%oR~_lT#RnbCxSKc0H8J+oq|>_LLP>W}ALf7oaKcuq!9*oNgiHtLV( zat!Cs$FB`=o{Pjo{PA1|*sb_+IPV3VXA)+kabF~MKr??l>V?Pd#QcsjL>D|?ZIDPL zfzF(8Gl^T}7%qqfE_f2r$a1hrGho`k@s^@)4gH6r!oiNc<$_1{cs7D(34Ag>c&wwH zJ+`DNW$yQ#(+#fI|MvUN!4lP8@tyNFaQn^#U2xN+&fTC}{}=mW<1RE0DvoosY`4xa za?E*g0?pN&88~q5AUBk4D|F&)BAmk8({-iiKmpogcXo_X=kjvOi&s_6LA=SHYj09> zp7hfbdkxxQH_wypd0$75Hz;)>QS|X2RnboQOnfks|F86Q^e3sk8H@f8`#MrSl*Fb` zZpcZp)A##2+D%_af8H-(+h`g_1FeeqTrMs2+F?#7ftGUq_8c2B&(6n&Zp^ z7*7(`*9sBeLsr{81K&KC7J8a^E>^?F_kR(olJksxU@V~r?|>!A6vA7@IZgYT z)L{x&LDUz8@632%=!>G63`KlSHBe~ed5_CbGg>14sG8BT&-zxxS5-4w_Bdaf^)2|Z zs$3BW(w^&E)Gf`*-0#b3w?36I-acJnzb`AEliu&ks@a0kcs6LGFRTAfk7RnkrkM`8 z7JBQdy`Z;f6MPqz^gRhSLChZ(KAB8cScQS*2N)MvvzOTqi^&H`;Vfaz%`9 zukvq!-PS;jF63UxFHU2<*!sUR^ZM5A*21Af?f84l&^2XzZ8nyuwA2~JUB{$TM{?hm zalw6C<5nYE`tpQ~E)9_s;~}PLWyGl}Pm_SAiBFU?49~)|DOhp1<1vqH*zC->MeR7p zIijWOSuL$RN5ej_0(~IKF1{K4E+VPr*HD~HOAIVOT06z;Wy|zMzu&Vc=pfeNxxzcm z2Qmsc8)k1vV!gq7!ZQ~gBR=RR-{-how-@^y(N7EdoKwb)9A^}_fumBiWVvMqj`px~ zX{9I97DO$AM|6SU!F#;SFmk~sBs$G`I`l8(kezEemLR@gVD{7f{pEDH?pC%2nx#<= znK{r7jq=6lsma%L%GYpJ^=)=l}7aAM~+X!s!I_pKp!uG~6;K8{w6)fL3dWr_>Nnk7J1A!f#6t zOW2ZN;1Yf^KIU3DNv`l3FF*Q_Z}yl;XYprT88*=|&sM*{eI(;{3U+?KjO67@bVh=E z%fgF`0$wb9X1vJu^#Di2ad0ppsod7Ui;VIjI}R`S%{Jl1GE*1(U7{qPM1cb|V+__! zDW+%)lH)TVSTaMIlrhL0NycDn`QpFQ7>qgttlSl_Md9WfOO1vRu};YhH1mu{of+SE zWNzU&PqobWna(q4H#PH_cuPay3*|XRY=deNOYu_FwEmttQYz@D5YNJe@GJ6Wyy)za zmLH}blEBA>tjuDk)SG|<{caks=QnP&?Zz2b>Yz)ZgD^F1*oQc!o)6aPUW^H#RQk?> z292h3Wv-$9SMwg^9FL0G`XmG3I`9k~Ry|6zZM2#wmWlljQ z>HbxzWyUd~4muJ7dRs!@`ycLybAU}>q9>&P0e(1}>G9q6eY;Qvd}#EW@%}@(qt=$L z!nmNTm_@o&>75YO{l0I}duHP&1}dKF`&MO#p0oa&eBaWESkijQ^RT{nZ(x=4PC)fz25 zW?Kd8g`BUIE_6npYE(pIh78At7;Oc|%luNhX&dyjaoYxo*Y%{hWLtU&ZEf~18pmIv z(;bio{!H~e2ixOuh+)$C@fQMa{FLH$S=&%gn~3SGh?t&@cPpkRnvEn%Vbf*Xi5&Fp z$qxXqZu|X1J1H}*Ym_J2Q2l0*j9X*-{X;t``Cd_IZYMM9|GIx@xh;tyDe;*saj;m` z{@?E(I?)E|9}D$vA?_b~i4HF(kAACR46-&6V**onB5m6+j7)WymA#t%{-Lpmv)?~7 zW~KZ6LyzYlTGxi_*L?yTGV$Wk?)is~Y3C&W(6I#d>U_U{=qXGPp~J_U>+CK6&?6|f znO|m1{PwzE=JCqk?-#ncpKZ7+s`E%q`5E6QHGMPhO`p`)gt8Rzhu!}N_=VoQw+bGw z&M0@+pZ9W|pO;wY16gMj{om^g8#KINU(wLaA310s5qqooMdq8e!M^kb{x-+~{}qQ| za;qQeN@q4BU}3 z?+R>MUx6}f*J3qjV23+`RiRvMJs|GL>-b6{4AFR8&XAWX{zf)e-QpV558l3)x!$be zaq{j=9?5SLkUZ;|A(>VDbsP!L#6BPOd==|C@_N*OUe~{bM16fD_~u2@%dfe{M zZsSRXjQR9}qx)Ayr+1aJLeJ`YPAd)&znXNR#6JHCk1GJU6C=mA~SjoXbN>jMs&r%adNg_ zbh`66mL#6QZv`5|>I-bOJNK%-$mhyXRZaEAwTV^kXdOH;$nyW#dlN7@i)w%PP4`T1 zGm}hDPi8`v=_DkflTMlr!SFJjkOT+>0wD<@B<%YR9eMy+Ct(pm0oeou6$AuDM8E}H z5Jbfd6>z;SsJJiJt6r~O72*5+s_N8R-Tl5j9mN0l{J-a$XR2#?Yp+wMPMtb+N}UWq z@7yZ$CT?1zy5e-446HKla75uDP6DSS$PUd${AOg57~GWc2D|+uKy|Q3La--N*I`dq zIp`6MJ?Eh>2!UAT9(gptn>8$Yz`{y5xE-B+fiBOCX!h{armu+fa9FjkZePJAhZ(Ot|!DwXQj7J>gJS4PTw!NvC_h%(Jr* z4suGtSFCUlDwJueFhFGjsD^t`r$qH$w`T2udwqQ@xSRUKy~Vo+@cszu1KJC8LaeY( zqn-7%1T4Ff`Tp|MWPU=akj(dKMe#YLL3_L*nBq>!{Nz%1GC#YNOyV+CtO)(p@xsB( zgO``$g$>FX0eGPZO;6tdp>3(_UxUz4w#?u2F9_N*2dklOkJEx3%OlBrw!A7iSh*N5 zm7KD9B^Qt7aPMUd-HRPR!x<)V#O?FFKJ-atk+P@j$As}bk4H1j*#0E-b>q?}QA{#F zx6-Nn^Ea>LrLMIw2e)A)o@(xGtGLOb`3+NVWuq zb~1nF=9OtUNav4bicl!3DauxB@gi^ZY@#j)2SJ_(ut0#SV(w?E_u;2-fdWtJxPoX$ zckxRRt70P%)xv>Da1O@Dqt0PIk>KK-e;^!`y}~&ljPnP(xTbmPf^MHEm`3CJk^52U zE<2x;*vEnTiE2K$K;liUi)&2A$-;4LU@U)-4v6luSpGmAr3rzc7DOQLL&?eN`|*oH zUR>PWjLY;kBV{mIYILNolTrqg2_F0OXM=fmDJqUF4*I35zq`K&FKJNPS?y>v9?o?% zRZHDW9KyBF4vmQ)K+Q4 z%V-=NM^^qD4s{6>RmtnB2o6xW?We(*Gk+iN?FnOrp>#c__TC|#6UR4oZ_L8q65 zUy%j4OT zrL)Q-&q2^CE1QB?JNSl17$NhYm_bIj_! zxCFO=!<#<^Fw$v>S+Njsuk)(D|-+zqv#a&y;|S^JESaA1%VL@+LpQtu4V{z3yedL*r=j5w0hM?z@^0Q# zD?ywC(aDFHPA)tNnCotCJWHBsO3Ih?8_2rclAzrTr}C3Guk3__ zqEj^3uPH;f|5f~?@_R4crtFzICJ9;~X=Ui9!M*g&D+l1%8)jc)=ynam<-3GSn1qX8 z@(_P1?uqVo*D5MBIfLXCkBkgapd(~|j2r4yma{7~g^j3WXodD#BjRN#{G z&Pn!m66vl(@QDbeDZ^|)*i|r}f=T9=lqUN;#82jzSFl*Y-n#;BoagU@Kmg#92wVvw z1T$jxMP$R~ryrx(v7XId~#M2>3P%bS9l{UeBLxtwCA^)Hz`yI=MCO>VO)9^ z)L`@esIQC-+=SBZ*x{9O#iiWQ&E$@*Pw(g;MjS63LJVAd1DzX{6U?i4;ZWsN`@Buc z8BXQ2*Psi+8ie}FK5xs}0sVO&`tF@-P0tt#G}qCe>+H`Z`g6(tT&h2p?$2fVb6x$p z?*3d)e+~>m%P{EIqa(;7m(6DLIlb~+1gQBN%*SC6x*9q{viS|APM>d}@lO_@)#|2{ zVyqvc)*@b$>dW?#fmk>beUVFf#nm=;z>RTwZjc_qJ=pxjR>=XaLS{c6qz{MT@DAD&)s89rUi~=R zN5b_Ja1<(>4)A;uo?IFZtR$IS2977_=*o7f?Q9N9s{s-TA_XiK6>{}3J6^ME=z=;m(0Tb5jo&wYZbX1Re5)r7Ux&K-FtzLqdCz(s8V%elZ zF8Vk%l{I@Xg;`Q{lP~4Wy;SwHXi`r~ox~mv-U#%B_fKOOUimXT{<#Q-&oGGp13^b4 zgf>FWz6hAmqgjD3G<5sit0T(t@~QZpt+U6mnbL`j5K!lR@EriNE6fX6gz0t-!(78} zZan$MaG%c*BHO)7EAVS}K|A8Z`w>nMF|jN6DX^}TvrjKYy?KZ0gl4# z#M|i*yv+iLx8(>Yn{LG0n&$yzEqI&F-UB!q%)&an{pPrW)keGxn3=6%#sWz?n^veZ ztxzZQM_6)HQ0FcRqS)dl+zBEp95Xi~Pt4f`=Lzxz8_^tleV+IoNY0_kqH(7}lFX6wl(aArsQIFscQXR?ZYMNpS| z71?gY$$X}iv-s^V?L&8IxgQ&cnUl_!OVjW>T-q7GV0QukdDD#Xe*_15u?gRNUxg3k zlK!J~jb0owoj6?Buj*60x{;Uy<}WKUfZr*lDMgk%TpD8erAfsZTo_JK3j?-v>B33M zi7j2aaH?{K10IDZvPn)?7L4htWGR}aYZB8niRqg0_u+Yc1QfdyHWe~qSp+AnoK$2} zjT2VRDrCMA9S&n|?gtalS#giYcXwhOc!tdLoXG~WH%`XpdBuz6Ah6AJdIr-8>63cF z43E9gOS;o7Sx2fQ9a1Hpv{c7HHwin#$6yFQrN(yca#8%U1;arK?e|yEQD!j0!e6P17CcsDXgV7hTZ( z5D|M64j_BeZ8*s|s5~o`->p|W^Qix2KKB^8RVz<_Hk`;2%cq#6E|=ujp+AVTRwwrbd3LIyZPa}saXiL*)#8_Vfs)wZgU@qFy) zq&48+4oD@l0l#rI_>J30FEq*e=8)U)1VRTgybKv)K{Jg!0EsS*PV@^v9h=jLJ=veD z%d_x1L4#BY(H6qcM(BdlRK_UHEba(p=1d8K1Z%^@FWMjlVyy9^H z$VMqpRTyYFT~%Gj5n#p)H8EA6hod`-xeQ8c8=B(g7D3;6{srhAY0-iJ>JP1h`c3RV zS?XFhu-%UI{8u?DrY{!&6fcVeMQ8+b%2&L?W~m(2H#@cJn?&^m4kLRAFtZImdd}!N zh~)d_(bZTo)`*OJgJS`WW|y1V`o)Y_4Mmp^@uhVfvK0|LZq)1 zY>^ns2r-mV{V4LSU;<~1z)BYcD7xl>r$AN~{9LU$e1?nwxk&pl!3h0OK2z@t160t*qL7oR1FNqcCW2( zcYfWXxn^O@uPaRv=N4?7^m3O!+hD5!QDay@F6*M^3$>p>=??Y~#f>um)&A_EuydV{6v3B>&<79J}))9 zibXIp@wCIL-(duxKzG}zSmUzy6o46n_GiWowEvY3OaRPdzDfro+1feRQAd6OpTwR&wnCjQ8Vri)I@QpbKaKN~Zh3H#DFf z4PJHmnVd)i9tq=r7C(|1&W^W442DvHyTH>zX}OLh&s3-nc7k5qz|!3fBZNa2P_2b? zdH>HVZk2p-rh9g)e3yG#gpiz$0qijWGMT~6)uU97kwvAUFiVPxSWtn$!z2z_aYD2s zaY%Yu#;aN4aK%*ilE%{qV!Spsz%`)Ll^Iq7rp-hNHZ&0R)9fTyR9FkpS-D+bg?d*nyq`Pn61a_fJQ2}rlfV~5u;!PS zQ|^9Ra;Wk<)Hj*$)qqQOCXEr3pHTq~jju)h^$B=r{J*Qv4drSwKde7tN6uY2;;z`d zvLBB9jCm_;*D#!Z`7XAEUFCe2bd2L)i61NRzB4>$!_c-8?_8qCGQ~Z)dw!aPw$j2^ zw=Do7s%K}Ic$%Hj4R%J0$=lY2iMP~^nq8y?ZZTBevG_R-W-;7z(ICpnZbGx}H~3ga zQLV_8@=7enz2$*y=TPO#NI_pkjo?4EZ$<@z9}J#fc|beNhyf-D7oLw*i{ICk55zA< zk6m~X%-uSC-_0w#;93d8A)xKL8MfYhlkfhe2tZ~VN@wBsFumcNg|HK0Xd@K(4MPw) zUd0ty5k7`t%J{s#0nf*kr$>7}p*$2M!xFJSm@rf(4mu%<(050drA0%<;7tr8#xH$n{dMA9K?Gc zhEvfAg6af7!R#`;1`au)iMHi?G5 z`P&URU{d0P`WBcHAJhj-IBSX*PDcNmYhwDwSSa`b_`>eJHp)X@Q`Z{?>*=*>ocGw{ z(ia_S>3P3~CoMb3@zvC4-jJUGJ!KAO8|0CC3?bP}tCKel2~d{}lODl728XY8=RdBf ziWNi&ZXUMHcz0Xv5Z}7pY47HDc}oPpcVGelJIj6yw{}FvC{$*WT+i`>OooF^5M&}H zl(LN1qT54wi@9uW0W^VXj_8CzRiK7u}reqMOr!84v-WrWU#WY(FN!1^A`pXg}cp4L+uws-}Uk z$ZIC7EINY%l@|*{HVFdsG(((Dmt;EkPSS7`jjiLS7(b#l;bj~HP6i2mrtby2gFK}X zh_z%O;xPf_v*wZNfNTonw7S;y5k!Do2jYWXz&;P0r%%9P*|9$ zdjvQvO|y>KcxtJ+u$vb=cN5NI@o}^9Uca$?W=M^@n;8Tm;0C zFw$NFxn9cXyp53WY>3T7TR<6|RN-tjPkHFy%R_$x0amo>0;sOK55(96dC9?C4l+7S ztA%keTs$E*MR0ynDny@YHR@LWqyne7P4#Y37$qwPzSp(FX*b+tUX4v1ZZca^K9bM^@&=PX0m1maBs%DlP?X;()ErNYS|^uI)JB3HJD4C-tkgP8CC z5i*DcQNjuB3nha(qSw0c!RYe99@bP4Wa(l?T)ITWC7W!VHL}4MJ|5>XFTFwu zVcncL0^#rBoB<{|gSBlGD8bUm==rj=IvcD8g zL(s*G#`njoKf!dc5(E@C1cxD*v@@f2EsVJnt^ynRORjLZ?p1w>>r}S0J5}2_h<60l z^g{D}uIqL@Rk=THEcXywE@uDkW0h+}K&0HC*>W=jPPq{%LaIVp4B+YUN_LMDd>bJ~ zZ_jF}FUR2chMsbJiRv$5xjG=e%3%D3b8?wa9dx|CZjdx|2q!nV7txOlcgGm< z9L$B0rx1=Ey5ZS2NGo)wAgw@kBuQ!^**<9nS>6r6HO&YxXM3@a)o4QB7$g6%4Q}c! zbqzMiR>Nf6d(%|`OnwS9$-@f%WnO~`y>2PBL!^eZYN;aJGhj-NVHgd=T(|gBd819L z4-sf48atJryLlxhpE}q$1vbrz#*XqghD&_f;tq@Beit|G!%aFjtg#;aRy;{*kU$qH zJh9H$DLJ6o9RVcpXs2FxEm8*MV?5@Tk4b?#enV=gaw_^UmG9ZSGKDgi!$WhcvBfQZ z|3($t48=@%XebIt8~IjvQskeZJnNR;shTl=^U59JI3MQgjyc|OyWtYgwZ$#*#7|oc zzll5SyBh|GERFb|M+!S0Kv;xZkG36;z2Uh7hPEA#L3qY8#WR5a8gHr;PY2gHgAs!O-wa`!)Mh{|r9^{(p&O*<{ zhuicVmr0HT?cvfS?q(rmwV^Z_kF8m}PsIqbgD7&HtYQdPBft%h@eH#D(xNGoLDzFmK3lw2*+m$ewM$$NFN)P0g3Q_ z&CZ{W#PeZD#wc<;R>VaPp=x<_nIF(Q1I6GsY`!Rn-=0%@4(%k=T-7ZDdgUC{5%ig2 z_Ke=q+z0QB>;owKLO$m*y~wwD#Wkk2U za-#Zc64TkP4Agl122Px68EF9@XWA@YFnC=>=HN=`1h5+R#P6Ka68~$63FjiMg9;mV zG%`>LCTz4~KQd_+kjSxsKB5RRxDwgS$bTXyW)rba`|CUGh~#;<6h=TISbi-a`>m( zynavc0y;}n|4YGBCr^1h3r_(w01?jJF@ zL-dMAQ6{4=rDUUju`W-~$ap0E48&R~vI9Y}YWHe)%r8 zguTuAF6ktV#L>DWEEJZJvQ#?P~~$`G1M_#J|AX%*}V zypNz?xE2A<)uI`4UP@luE?)fHoiU#?{Rs{EnpYQBf)nwPKLMi1rulE!$?Bg8U*ye% z+z(Y(zQLQ3+wb+4-$g7p(Npx&^EAJr)7!8)&^i7EwBe(s&fuZ(UbaIC5rz{AmnMcP zRDlhjf#F&3(fA9H1A=E%h_m1@FiegIjo+icZ-5SzgupP^PaX|IjIi#t@X_3X71A{l zCf~(o9Qh{hH6}g7{aX+L*&FOxLlE$mM;oCAZn>`=%lLSY0e``oV{lV5e!!6%*{y~i z{~tAko+RzfE0=v`cL^k~aYK@^GiC{ zmi*oLTr(w(e3x>>FW+egpgSJEv5ZpiXGS@iGsBylcQ-?3#N|~253ooCoC^Tv`P5aIXdgr0 z>ir_tJ1gfZvx%p+f=iD<%D)2`X(Mza?i%8qFZ?yPooZ;HRqwCF1(rb)9&G|HF%fa3 zNB$STSla!ERHk7y*cFAH1Vj9Q6KTOsUIMzh>RY-9viV_$}t|cMGKae6yuL(zk=l$&cFy)V( z;mv_SS_fpDUPjL7hDf?G`itdpMf6C-NxgK7b5ifJJ;3_XheuEW3xG}8x0m|RGYc`u zd%?KydJQ1sF$J%dc)$xv^UE93`GNAn^pJWzBu%1>+>##~1B%h3`Hf@y*nxRz&}rXL zwjtA=hKEMop+z5;&w>6YEyjO8f@#vE(FWiEFe!5wY}YWHe)%r8gh|}6@U=)++>{nN z4K%U07YyyRkZI_Wv9}jME1JzkHAk9X6*Y2x$bp~G(K2qy(R&;`Q~#{qim9j&{zxnA zkMU_d-~0`5=Hq(I`n%!_;{C-$Tq$%_YL7Hckl%GP@#d8~An*d1$q09CaWmXC3~&7AhKXBj$rq*ZP-=oM-mpS8A+x`sp0p9c z5c7M&_fv$@MyMg?`{5hQ_zxo)de!J`^gl{3Ie3?=ZK?kz_1TQjD!nt~Xz-K}$;|s? z7PVn@!c;s2!?1ZJcW6N8UK~-Wct;GwXy_L=l9|DH319vILz|hYTkXE_q4#GXoYQ7Q zgDqhUca2Q3u&{L|4~Zjri0zi)y7?V-Go4Q=h8qgn2;CkI0Mv|{o{mPlYWq$JhlD?( zo(YJU#MHXuUyeG+VC2)7P~o?`DOt0?s=|hYI?fvYP#l~nau$Z!31-BG1>6lksk^LC)`edyZeliY)~nB zaSo+-pGLL;-L2Ibi%eG(DTl=@?DjLOLF&u7{p{XWx3{0?)ysTvqxv9>yN+FwfZlXK zjMl9ugQjS$x#fPW?){}*@H@SkvG=<+)xWKV6Iwurn`!7O;?lI#MU498F_ zaDI5MNN=ILRN=bUmv%-~Zi08ip!@#K$c5rrILz{v|40i%q$^EQoja3atXAa#w z8Ob5{t!~3$?A99kZG3SHVA`nZD4vfZ3_HY0IvvUEsT+yDf92FI3VCd&W-Sg1JM{+L zse5s4hJ$Ye{+VyrF|1`uOy`NA~&$oDAG@$IL0n{DG#h^~oiaG&x zdKdr+K0}=h!B>FU2ZjgtG(I={h+-B*3Pw=?>gVWUgwC(XSA_mZ@ z6A|q`V;+XxXu`e`Fd5Fc@?C5RL+h4mE4y8StG4LmP3; zn#fghEQ1YNtZ)^(zql5ztCfpkU|%9^30$`-7p{pEZc{Gks3`!c;VTK<+m$PPPc4IQ zuq?M=>C!s<&Mi$Z9st|mREjk#%efd;(kWc8n8?5%?H=7{Qn zN#DF;GW!9KY;gt7>az7KswB*YB~b4io`lPk@W7z~_Wb}^he;6VNnCLP8i~JGVSJd~ z08rdHkv{;-`x5+5!M++k_J@Q|!JHp}e<5r)oXf^=0?a5FcfC6W;h#WvYTb+Z-6BMY zA>jo2fwW^=Vu;jrCe5nflZY&0p(wJ5ogRdncrDUB8&@ae&=ov@L-G{HSUu}$;Y6$G z8-ymj-72d&)2JzLPdNO`&>c!a-mW^B3i8fU9X5u7Jl#6M52^qx2Gs1gOYsyI{Ja)( ziM)_OEqml<;6~~J5+A&b`eW#j!+D*q)lF8nQU{fXP^s?H9Q+=rDUDeel$~H`7iS}U zy;4xPx$)Kk6+z;*KqQO7^U3YRpIhE;V2DuJVGh3=vtERV{pj}hgEH})`+t@@4)rnp_F<4#LfzAy7$K)Ek~%9$)Q*kOazXF)2_AXFg`Kxfo64 z9<~P&4zRNa*;e1HO>aJGT-fdX;8%fugO||9x3MqnJcNMcZtC@7J>9VwDFI2^_)?lv zJPG2QnybPEVn1W(-bcluq7bFaXB6Fm#zLyba1J!-*QCqG#KUerp#2u%iLY)5WK0Rf zcoR#*QfdThr-;Ve6t#4RLI7SwEe+f!y}kAi;GTyApE31>9EhMY%5Di};1R_Zm=!Px z<&|1ca9V?1vF5bQ%#-lx(cwX_ZE(333xH8@6%`%&XCpu}ye|ltb(6oXzPSIPpf8TX zY3Em5r#@If3P7xXYxaBwBi{= zst40N7|T=vfNY;~FxC+AGpynFLC*_L@-6RMwc(vQ#>YwV5A;Z+)r-dDd$1EW3*Vz- zu%}(N2OH6&xgH(ST#q!F9@5Y6(siVgu%Ln=Ob>}~m>xd^5(;B_JQO)7!|y^8{FzeJ zkqnt0_DDuCJ?xQ8G}FVL0@N`*cnYAH9yoq+m>xJD0n z##*L_JqV~{df0=2u^1lw{_kaaL}80CGK8>Tm<@Eg%!Y7u1PRyygApR=Rp!HW(VW{R$p-^5`E+C1rK?R zUxb?`MV8?#IC#8F^T7}!-ySgE#AZ0%@?C7^LAT@;=9WC%IFc7_d%2E{9Lp%}gi4&S z2}Z$AQZW2y%`h8NJ7IgdXzHl7cEWJF3H7O?n;x!%2%(!~AIufAHBmxn>N#m<sltk~St^I6W@=WQQJaV1hQ$<9ZJE zc>hR^vw8q|xA7U%*Vz5=G7aBO#%YJ0lA4`dni4RO$w+-S%;_-xJMiP6ow*U5PsQC$ zk+Fx<5U+TlBmqIVSL-C;=}lr12$X9k0vKN_TPFeIYX>F?ABb4t-5Aa=h6b6@xRx)R zBwW14h7OFQ9PuLg95|22pt)EJ!qj#!IoAT&I6!U3BhCzH5(Yv}G8af}lXBnt%7C**UB>^5aiyDt;VBHiyE=Kyi1hy zjyf^G^S7gIK*u73C&LJqmXUTb;|fq{*#;=bFuNnQT8#|;8|uB@%F1_mpTJmrDX6!| z4m$NW5_WCWA6!K|lVm!7V-_?4jjdFZ9KR^m}!&YL(3y380*_G!5WZWy|i60za}74(pSWH!TgGQUq$zS*^aTr ze|S6I8RqFx*oO6iq87vdnZlMNs#9U%ZQ<%P+Q%?wo@RJLe2xC}lT3Ol)O@%!6! z^o>h@f=&YX7<~taA(-Y!W8(|xtPU2G>i7NJh zLTW@GB}I+8y3dGF|1gHee*`}q_4|}9qs~nP%~oTJTm1foI{MslAsF&Q5k|vdr_CX+ ztbxNnK>MKFqKN*v35I<+mXUa_EpDdW$H|M~V)#wm65sW^VNwS-%yqxM;|xOne+_`iYymV495#prCihn{VWP~Sp_XFx^mt1s0*s$mi`0~ zgZ!gVmr|Sae?RK7np)~mm)%W{g|_&;J=DeFZA4u=$=E`qr+w7bIO+;@VblZEg;7_i z%Z_?`sEcZS3c-*!p)RWD2~bxm|9>OudR@o673z}lc2U<{8Si=RjCTZkOtz^Gd#r(C zt3S?_{ZKbRbs2Ef>IS^E4orh*8}Jr&fSTnPdkV7wHK;3s=$&g(7lhBuMHW!O^Vwvm zz~!x;Dvsk73D!|9*$(jh_o8Bg((;3rj2^z)gmZU>jm)5sdStmok>2~-vUX-!LMX6G zWOF;!faYUcyDIjG8IZ~gnI?;UEXc&VI`XARid3ilgO$=aLBQM~Oe!B{X~*}gHGN9u zJzU`LRGy*A&B_)k;xzbZ&NQ~T#m^io)K8`uaU>U9a$*TS&@3}HzgHG9HtJ5|Vtxe+ z1|1N6-w7SK6^7hr*A_R!!@kLP+F~T0_}w^B`A8c1?)sS?wKNnW1?$H$P@--?64P!{ zD~e$NKwz|IXp%v}f*Ml}v|&d@(}=G>%`gG&`)3U2wU}!z&2T5eziQ?l&dQ~Mm~)1M zqvu@2*(P?shFaJK&C1~L8j!#VmZ(C=%(SyzRHd%5$oZ?~q3-?qp@vDJ3K zbWD>TN)J|^8aS=|kBG33!R zR`4-)`*{}to>Cn?rSdq3;bY1(RQZ4&g&wW3@KX3_IL5A9{LE3~)Ksk%Vv&+eQY zP-v2<4zm}s9ViJ*!TS~J^Y25J7ClClyuiK)i8#fwXuh z8Mh?y1ZqH1=_=NLb}A_pQ_^3rC??#RC*$5sRMF?~nr_W+hn!=>SwTHvj81fEWAf~- zfeF^qrA3GPe;8jpeG_P6J z-O%WVIS!7Vi}`prk0LroXogu)riB{sdOZIn0x;y=WjP*8;T@6>KcJeauj-^Q55i?u z3=_lYxPGLY4>KJ`by@SxC38MdoDSx%=nf1&lQK7dl3E6)#WR0d_0FF(n|k`t?BAdY z&7C(eig&T zuz9u(%i}rUY|PW$8ffda@IR04Q@W%YUy(EaP*(Do`F^KP@A$6Y@%z*5cPd`Ob)XFAQyj z&Mob#4!7ZS0R3`&RDe$5fgHXO(9gAKlF#4N4V1_tlvR$?i^U*2!Ubi!Xd`qL^#4PZ zh?*rm(d46@J`t0~e+#l>n~59nP|5$>6>3KR^$YiME+4p#pynrbg)6vps}sO5F=VFY zd~8?jLDtMPy99HLZ^#5|W?apZ7lL``tX}3Q+72cqW4lX3_?=fO;&-hUmg4|Uf}!24 zGz`9-%_M<@mB8b+sOAgNJ-~Vv1PyQB~ z@eS14+TJb=i<@-QC1X*?+eH!cJvXtmrt zj8KSY#L(n2VEtXddWCpso8}F4dNwE1*ky>A9EU}$E2z6UB;#ElxH@-1M1gh!_g z;x}FSA&Q{4g8V#x1F#Q#rguJ$Aka#@7SGUC6<$7tV=ZW{c^Xg2U1@LhV_dm4&7sN9 zq0z@07V`#5NS__Y3U5>-LGhsGECxm&Z^**X1Co?4zV$?|kXq8mv*Z^U7z?^;b8v7! zGAHI?pCVwQdt^M?mM$&CFTrF!YzIsR&~}CQ`07JXktCd^4qh|jYla`zZ%y5NqIkPI zqd+E9)s(`v{Za6IM^>sy2j4zw!l}pSwd9W>A0oDKEUIA}caJmqnznIuv^-U!pabjh z(hC3x%tfIG9K5to12sUrG^4x_&v9#+$quB1EGR|xjq1ifp|5EevGQv;e9{K+nU$Zg z`W=`r8-p)KZnBIJnx!E|z73miVl$j>`7So|pj+~a3YYH^U-F`jnwixf-8Hu0Bv(^z z47M8!2fRvf!xXP_zwJW2tt4TCeH>+c--4040mg@$G$M7Dk=y)jG}h=9wTP%Eify5l ziORwOE}johIZ z=e_wI!M)pqfS9*$AG^@*W9`ail@8mLi+k2!<-*=fGqjslF12T!h5mO}7k75Z4B>aK zDD;ahVUkvCNhA40h0Aw|&wM3M?C)OZl}o5CP*($n@XGHMyraBw5uWlOqs;Jjm6jnT z#_kn&8T~j3L5IBL#VT&Cz)g-2nr?=-*LfSqD#>OA^iqTLy1S1X2N>h)(e6Gj2i*UC zGv~TTAor0`x~-Lh9k7%m75j3}GyxZr4?>Dmb*;u&LdT+oxc10bYuA?giaDP8F`gFZ zY(c2n728a1GiRH-kfl%JUHo+{SR`caOJ(7b|4h`d1$OC;=DySOw8#6**MfP9b<+Dm zfTPGScpp$E?p~f}0js3>>@4p{hX6J5jygdIUzqXdS+X09F!1H`Q>q8EiRnZYnLJR;R-epw>iSO<--Jp5JB980LmK^wRsf^hob;}_DQGkK<&Wt#LfTlbTXO;;Slybx zL1k$EY|M`x+v?W*Ap+B)0v&QGluZfKt@#LoX{>I|weZj!)?g$|zKhLx@=e_07n{{wml&2hG#583}7^Bl0{1jIun5vUTjad=84Ww)$6g5c@4HqsBTSMOAFEC zz*o0c8+IAB60~@m3Y_-_PL6sht2+rb7n76u&D;6Z6?#}*edZ}j-s*ig9Hof1jMuj~ zP7b9#;w>m*vms+&4120o-&vP)#KdTQpX*b^KwmPtRqYZ9@{8e?y~n395CCe>_oSm_ zny72xGjNpLIaHa76xfHEy=4YI8idTKjDy3cl1F2>f2gjdkpy3ks4Eac8x=Gwbsv0V8Gk&IVUnmD z)BZ$y>U(W5){oBoKBWo8$%v*Dc_|fW@F<;MK7?QDln=Ib$6D@*M`|5x)-2b)PC?>Y zGGxpR>l=a=sq65cB<g(`eN8Q0>4#_cpVDwWBIT?18B)wZNNTGag zN}H#pc5xT0h7k&@d+}i~RE5p!kr38&ta#)ek zYk&LX6SHi*i@cC)BL3Q61VgE&d4T3!nGbZp5V~N01Gamom?tL}VXi}2LLD8&eH8XD zX8vpB|GMp?Tr#DBGppBYD4nwVsJMsihxa2@7xmt6$o;FsqPPyciMPPgAb$gnn%#p- zM#JD$Rw42P0z_S<%5|7aSm+L1c^rMB8AgE+>!97hC<}my-?*a_7OSfCEBhC~k zL~%IN2biClUY=~O`^LLoH&ui9sphHt}1gYQPzj8k$MjBAUV z;jUqL<2MQ84nf>vyZMS+(u?ipFL5L<)N*@va!|}A-P{Um7?ItKf1c8UjQ%Ho06Uk% zTY-2l5L&8mA;c%DAn1uYfH~S!ndquX!Jffz{_s3K#OXjM82s@#^6Y{6JPi5uq!VCy z6JYX~pQQ%;F%$kDt2qn$f#=xzJ~5$%4T3V}sSUii;IN<@XG%OQn1?eZ92S&j;+NYm z-0#pz`0T#QP;@OiRMrMJ_W_mrEGYM5?o)$&>rZxa9qs0etD4M7*YGg&Zl02_QN^kc zdzHo)r(*^ivGgL;dD^ixVzUvnwYjF;8C{#^4JDXH!#k^4YTju$6v(b8J%X8=sczPY z50|?rBBjnflKt5DlhZU87?B?g6IpLTb&z{t;z0?i4(+e1Gs*Pq>fuN>cue&O=8k>4 zaq9`CELlmK&yB4IK)X;k7pKYriI-BXI$>i zojc&40d{sc8=?lmo&j+#eG9YdXD}{LV<+J5pQyRy%p~5>PxM2hr_$&7up*HJ*deSk zxOwHd$nGh?z-Jxf`so(GYlpd+ha2wt-TYlQ<2R;pzc=PFR$NKj7|$&yEROqK-0j#T zjT+-M)>-^+Jf?Gv)TtfY%_qw3e;OSN^lK#1FxX&A0`A@hBA#rx5|}q=H({ zzrsyOjitx5N=ac;B%R5m(BL7cHP;!-A3H|6PNbWq(uM9XK<4lWA(v>zv25!?&Ty@6 zlngFnE2tw@EUv8ZxRlCK#pTMjej*8Lb|yshjO7rMWJL5VGx~HrCTXEx^vhU>UusyJ z!TaLF6Qu3YyqkzRnp!8C@(d@mNuvzD*Xi49FBf5AU?FHr-`;1Pb_%~p^f$&mSKr=J z!1zyv-iXgn#j7V^Jc{FZ{FiFFcTX|sh}{hI=p?HrYSeC~SR`jX^7Spe=J&p-37#Zc4U_9YgdPe*8=+ zf0zhcE(zC%76`YWGK8SG&h0ZTe$M3Ala_u$ngbS%2jba|+yWyzb zgT_1rLqB@!KS<6j)YC}==ciEE-)@^QeXHl(4XGODFiZgHMlEjUm4?*FJR|2GFU&)Acs|J zm{$~Px6G>+@4bAGorSrlm{%S8YC>nUdd@5_W$qB8dm$vDA6~95Md3X6PE^m}r#NW7WN>JW<;9@5GEU!sDeXE)dGi;=Y( zz6(k42Ywz$rZKHEG5~QqGU(1RMka%i$+R4qIi#=dBO~BUj!${K_6=0eg10y>==g}q zNzPW-JAUjHQpsIHC3lVD9SnEB-K8$j%>z%IrvUgn6{ZK~JQ!>oEKloc3^>ET4%-bM zK-e!ZA76$E_UE16IT%X}f##Ki;2h{_dg2L(9A63_44|=LfCI);^N^#Jic;Pw^m4wEh+z$%Hb@P!=HCMB~*$R)RuNdvnHzJcTc6;=>)iB>P#Pn*?v_?wBJQs^ov83?buOyC+GF_Z*n3dIauTigtH z4Z|D1Nf_x(af|KdD{e_oySNGU04;&prI`177`p-+U$%$&j#Et2)zM5;=LNzVdGFz4 zx0vWltqm>~3jk}Ppf`=sWI~`d;p_!qv#{4M2qwg4j=B`=W{0Cw*28=d zhO~q5=jq-VJ-8Sqz*%wcazF@(Gp-~0F`!ssy~6Vb2q7FXyF4qI@6+d_J0cqoN!G$j z=BJbn!{<^#$&V-VOG^tVhH(f0s6Hgf60*>5Q4P9F-6?ah#SSn@@Jm7WD4BjyK z0?O4e;`H0BGDK$5C34M5lZrbe^9xIujH__@jZ*ew5#Pa3&SDk|k&H{%LJbb8|7RI@ zMF0N_{j7N#eppp7>UHv(5k_wNYqoFeY6r}P- zzrq#Wkz^p%)SK-c-iKeM%W~OlHoud8Pi6{&&jgNs0)}oHd_7LjF_&lT->ueRpEx&dz|jhW^9INp*X_uM1a_Tss9C5sC__Q)-qywP#R4Fr#b0I+2qDDU zO!o4VPy_}z+pW&;vfU$RgZ4spQK&GyuH=+=Ih=BEwZYNWobnzc4T_+&mIuxDWRSl( z4Da=?e;ErhDgW^y%QCCUwQ{^r{^Li#TEA9~H>xux;7of$Ap158!*UtsDjr0wTN@`~ z?)|17CjbL<0xLkH0J`&0jRfu`$c=uj0SSQ8j~HCssUODn5`P8~xXH!i*BkM>xv~x6 zaGa0nDo`Y4DdxClUF%e-Tw)SZQNO%Sg1Ku% z44cQ21*N@oE|H(cH2X^$p3$7tsvLDjlgQ_pDil;r&xR>UW1$2fbC|uJ0EnY?Y|}LW zfkg;r8A^bRodF{0&N)0ss_tx1YHafSkMiU#|zChP(Y!*}eLzFVEu0D|TtC zm*H;(KOffca}{A>^tlH3;UaP3;cEI@_*eCwOPAZ`JlUt{noJ#^uyy7KN5P;;A)=8%1l7N#27&*078ldzc@L}-! zb~KEz_x>#~vSmAAt$=J}ic*du;QxsJ^GL0#@=Fxy~e{?u)Bri~$y>kCA4_ zTxW`DqNX&P6Y}~XODto~bvR1`Ha(cWdPXN?M&nY1K05{7WhZpn|A`D%o3a-#)DKqU z6{b2FjD_fW^4Z<-Iv8uj?;6GngY{80STak|C?j`qIJ*8Q$GCpF#ZQ|<)fgtYZ9QV7pU#l(k3(Nnt=DEG;ibu$_0FJGDnJ-K+ zG6h4ytuw*_HLh4V2}o2&)H>A(X($~3V8!ah$yqc5%fAi)AoDwaMl|Kf;*OR2VcMRd zr>#aOA@h&%OIBV=EvT4&=IAxsdiG`f)0B2BET>_!k6Cj$btIp@(Q@{!FzabIn~Hfh zzJVS)8h#+U<~`D6Jf7oC{9Z1 zwCp2P9+EnfQF%|Xg`|M9S{Ja`7JzMK%N|7mI0Bl&0w9i|>g;Uo0Z*4lHv_!2h%Sx% z&`ekp5_a|avq^n+wgA$MB3i`ba3flzg7L!ANb~1ZqW`S=x_sk z@8)+L1M&L-<$3sBQ922~`)*#zqsCsC^NlTTaQ`84f;dM&BDgbOE6r8!P=1z6u;0y9 zG*Q3GM zr7nQ_bSJi#KvE@C3+-LnJ*Go-M7?A(k!p{^3xM)dqa^l09BEU1*}f1{sABau zh&k*IGm9c(c667yP}n4)ru!=GJ^htHKJB;Yt3Q~&((hG6^h0LSnt+SBz;ZFU>;sd5)*4SH!3%too=nm!IX`v{C1`cSShs{?wFqX)G@BsiMV8=Z7&>1UX$l6mn%Xw}4Y*YDAy?Wax$tAM1N$-#&0vz{d)cwDu z?oQ3?&MzZGhHX6)BNt0O+tTgif>p$Q%e_`f9U8|2rL{%kl~y7PNUz%O650{&&EeXep39M1|g8M?T70~W0oN|*D#!Z`7XAE zNnD0=QcAc#tgB@NA@{^5-oc=aLXF@83m(g;19l)dGa2O`hMJRMOb4zky?AKvFX&h- zFMrm{kXCfBFz|X{;Jd;}Jf>X|l-2slr;tK$SVNh-WbwxOVLYhRk4CS@vW+W{(vlV| zkFlwbaFGdFRUMHD7w)mDHY(C~`3~0t=HCGm;LW)AWsDGj)yj2QBxu1D7-6uq29%r8 z97}9W<^gSlQY9>INoWnM&C`OoZ~{(Xfe)Z_Mh{ktj%kzXIuXMwd%Py`QY-6z+0`=N z<4WfsAf}nWl1uW2h~5S(=JFh)E96s^ z+0EEXb(+=Ca!K}rgHR$#I3CP^gu}jxGfPrnaoRmTtf&1{_l`)DPT zFDb}OnbRssCzvTngYB>_g*=Q!W27OsPbtR6PRG$bb8BidihrcPwMWk>L6kt-g{A4 zy+dL3j-kpum~jX*Sp5e0X#6X{P(kpF%H{gI!RlwjN8_KMzppEwh*$=zZ@>o{gqYhC z3HWGU!5+;u5+>ipW*qq@ZvSrxM3x&gV~|w$PZ6>oT8>bI-@k=WoH%>55o+-J{qT)t z{M(TX{S&=(lIBQxqg(ZQFLRL{IRk1~(1jhkP}7AVi?oVQ_TI1I@n`Gr8<)NvJ|r`J z`6c*h5P}Yz2G?q1u7TZXqP{Vlapk+%S2yIvaE3`-+K0gSB!^H1(~)KP&W53l(Cv}% zTLCmn;nc1o%4)Aa$%9bxtSuSvhOxwm0WM_!A6VgqPB_+AK4JP8sO?cWgYr3XE##X4 zqjDUiG6RPFF890)F)!H@alyB7Db`Us%z!-u9}PmxfUSa$=9~}{^-bc-cd^$ugsjf3b%0ozXf1VXtAdcsEXu*5VL>4hQDDqL5i#&mLHsfoiP>3m^T4H5YshnNenl zqnFhdU&7fS&FeVpJAiB9+b~HJ$<)ZDBxo#=w3cO&)1$^b0oyeUr(eE{EnyOu;mEP_0Bj|Pkj@`k+B+?f z^;QIvWlY;@ER!R~R(A1Zwb*cRg@y?u2Vp?1dE79I7ZkjHGTiB$iXkK^W@OH%B}X76 zn-L<~%X&+1X+^$?abZSa6=(;f9by%{T(!;It#I3GZbi!M1KlUpb7GL^-7j%2R?GR z!}lRNzdF%vcf{>JB+#w0KN>`S&T%lD>vK_CSjvs6s5^=QeHHh3i12p>3tAsUur8tO zA*cdK@E8RM!{eI&EtozSwcb7l2fE^GT+RIND32Yi{h}yK?yMSF&ESWF%j4hdWC6N7 zl3Q)EFx+N6wmOUA2BB_2rSCgN6*R9^Gg(JW`fw~A!G4lWV!NzYOY5f5PynMh1mljy zgBYoQn6c1&*Z}$u(5?3(gy!zUEGG!eh4sY{~~-| zjd0osHIy2W*I35q$%stHo{-g_LxiiuMsNZ`a*A1`SQ=@Wlfd(W{>I>F41*M?XJs~H z0G2qxa7}_DEI)%VZb~DD$R7xX26R5Q&(K&J(Ms?`oK{Be+hX|x4IsTvKrMU^!t_yq z-;-fnm zo!fGSmrzY5#C*4oZnotc;GoL%Jb>aPRTwZF6r5e4(v1;WaPRpD2 zDK1A<_9;zz6-dMEi@{$56VR}6?@b8*r@*ZPwY8{KpHz7x;qg`tyKU0i_4TcL)k4l^ zI=vP91iVllSHc)xXh2z^C-O5&2jX`HcKKHzmeYV0Q4Jt17`!2J4Xd6QtX>NnTj`CG z0|=)XW8`3-pVc`GNDdLCPw3$lt5Ub+tnV6;aPPnsiH>>FH%aefPXv9l6^(v1Wc=nw zRi5J)U>&fD)xn_udYxHSmeQ-s5ZrA_&>ek^s z2!t6kbyM$Qv+oqk3RDruzd`(t1b0#%0tooiktRS$Ivy{gk2Gj!G@_C%(2riY0RJOV zU4etZ1-pSZ+gI_Am8uv9P0$iA5Fg(SJ9cpE^#p-`1cEFhH>fW2$v+TS!0zIlR)rh@ zErhxdsIS1;83>}x;N~j04ah#aT)iX7b_^EILBWF~H>#4OIq+eZec2$MiMM-lCG>%! z-!K*=&|UhC{KU8&Q=l3dQc|z$)Y@_GYagm}qk55K^dd7oD?t6;V38YquC$PvyIi(gh89gQY=a@oKcNh>#jahk zA>C%LxXg6rQ1m$}lVa~30RqstkyNh+$e^^&Y4~;rguA64&0822O4TV)g~oWO_EvAu^CsiX^K#f{kgtG~+^o05wOIPcb*Bahl(T$@|+?g7j@#@is^?N^F=GkP-Hzdt2f8DzGKc0L=r`h@{F88#X z=H=)#vzzPYUD3byG251uQMc*YUe9Z4PiUGYL7GBubD9Au$M#cxrkinOn{VB^^?R?r zs{851C2jT7F9#2J4SjpSt53fAq$;P)zSU!R@pg?N)0}?e_NDQT&3RpY%dchKet*>Z zW?0Ef-&*)meLFavLKaRZvyW#s#huP6iCBn^HPVT$V0Vo8(;MT5!3K)l+q*5Mib&NL z5jj6DI{4DJZZ&NNN^FAluySthKj4IIpIXdvvZ?3lnxe`HOS7%Ndlp`l)O8Di+mLyP zF+5p2k`jL%j z$IM^ey0!Yk#Rhk_yT-hVw!f^?gyF$Z_~~dKhp>3t+JrN2d2@atus(gP`4IwhUKaDP zSLR`@c^;0L2Kcv~x!d6_-+SAVJ~em4*3htWa!%cj&b3v}^s(l9Sii-3Q=s}1YD!?7 zn&xU)8h|9yXdE8ao~ejXPY2CF2>!+eU`;p;$y>)WI4c!0cf$JWc&>>n^qP2d$eK8S zT8AZ5P(palpZ3 zJw6YG!G1`ZdU*S5d+?^}wp`IH&$L;$Uf{4BmuYzWeVgz$vyQ$ES*OC*lDj&ZX^gOV z+d|f+kpA)5rvkr6w+C0fn2*66eypuNRsFNH#e59Ik05zSqZ3~j#(4!7jYZHFQ2pkn zS>Bu`qz~#~{?((JVSenoHLoQhaWdd!s^C_1&fwi z=WyoU)Ndhm^MIEv92~W=P5i?4d`9D!t!=i4IO&ql=y|_2q&+iHceQ@mJ)NZ&xlgMb z&?x%v_=CsUePh^ohPA&AxAZl`KuDTit}%9O9&5(qQDft6(_=H~`sTSDQjVhCCN0-} zHaalS4(`|Mg-LVNcC48}dzxV_q;9}ryV$-yDg=jxr8ynrvtHJ}Hs@O%Opj{|rZKNu zmlIMq@784p9C!VaC8SSo-8#}ti-x3`{u+2%<=4Q|>dhVOtT7>TC!}t@J2-UE=wF** zG;Hm_PO~kXfic;Jd${Z?4i8CTOKC>TO$C=O&DaJVGqVm}j($1bn8woJf-_j3SnQZK z>chMPTI}eT^KIq5O+AS?R$CkLer0Q8-XY^vp9ZwyPaF6p?WAeskB0Y;=lyu>@!AEn z>h{u*4Ycai_VJaW501K}EgV+|+pmPs_@S`fk!phN=em?ELU#JqfS7Af!`>~E7^tnn zJCJuL25TFXJ*jq#vUjMRq3p@E+mt<}_JFdd);^`|X|-pSJyiRFvh(TMU$i4r1M!L+ z^If$`+RA$E4aHv}1?bE7~!+);mSjXG(2IJEqo3+A*!RTswwp8?+-|J3%|9*J|1^ zqc*A?Gi%pr$BwnzwPRN8ecCa*_6hBnQ+q)>cB=hKI|{WwX~*2!E7~!ymYS;SQ>^uA z$Nbumb}XpP*N#$cv3BfSTdp1D+D7eISUW*GcBx&g9gAw$Ysap&yS3vDwNGfr;@a1= zW4GFi+TquJqaC}~Ue=D`T56iAPo*}X9ZPC+v}0+_*N#1E`)kLt+9vH-UOPcMR@5%m zjy-E{(T=@p@6?XHYY%D1%G%S~u}|#GXcrde5Nu zKdkpmdcSAAXVLq8>#frJ1MA&F?+>l_YvfdGTe{8+y(EAhXJ(u2>td|!X z3qQ5qH`4nv>ph>|pIh$*^!~znFQoUE*2`B`3cs@6i|PHf^)Ee-rrj9R(gMD zy*yzq{HOI^O7HKj7aD3i3jbxjm(%+P>%D^BKU(jb=>3!RUPF6MeqMu zFYh1~{$jmvq4%%W`&N4Y*Lts^_ixsFExmuY-s|Z7hxNXV-j}WSdV2q9y*JSNiuK+| z@2l2(6TP@|V*vPOddFGs+v$y2?=AGkt(Usa3mw*b8@-*@`wn^&){Co{9fhRzQcp=C zWxemDH*LM|qBmo`@20oQdZ}is&~3eU(c5Fa@1b|R_1;bI1na$r-d^jym)@-PzL(xU z>xJgxjsldWn$EeO-hS(SAH5T;_xiWxZZx7^; zOOcQ#iz`tp2=sS*wX?9;j^I*r=E8X4RwbSjizfS2Dsx?Br0rDq$1<$f$Zae?mWX%s z7Y;)DIa*wYeX|qNcd5SV=p5gn`Xz&>b@oB`btO9r8xXtt5mqsYTBWoM(5^_+tDT=eh`Imd-#H%`eP$5g4W zU=K&hDCLP)4@G3i-9ctMUU-qYGXdNE2BaI$I>bkAXXl{ucfeK+uAMN++Y+z76aH&X zMfL>sFT)8*lX8lwmvy+pE#-p|bOisQXR z?LvT96)k}uB3zKEgK%|P4>lTI}RZ~)C;{0SRW;SnRC8`lCfjQ8$0-h zhc}Yd`{96^_hZ!{zu2c9{icLyG9}Z>d+{JVL83E%A7ZDg@5e6{0YVcMw5?tA0kF&q ztO&wpZJFj-!SmzB|q;z$tZ@H*PuS z^$IVyL0!5;7Q1^3PxA)9qPi09OTGRjjKc7@E44u5V%{9@{;zt|5ZSAtR%+yQJ3fHw z#4)|pOrD4Dgy*%e`xHD^{{c9USD|nozyHMV2;@aGnfSRS6w$?-%|JZplti*{ylQuG zvfJuJya4%cY45yS+;8!Yj*D(Uhbf`x!Ut`%dGlh0!`1Ba=Di?JJWO*4!(tuPP0H@9 zu2c5-j_RS>(OKO{ho_lk@H}477&MYcyB|zy;ON>?f|Os-;-XD8pTZ& zOrCRMwd(-jwG(#{d%nWqpGqC|RS0xcJ<>02S#NcM=t&(GpBv zsIr)TKf(&@S+$QL&ogjQ6tdk;=wk>4h$*pL!uXwt|NpRe9&mCLSN?A{&CCW#%PzYD z3C$uD28}c<5{8ymLVyt<5J(_`M9yF^4AvSeVZz*TG;+Qi0yiZJUJ5=keM% z(q9;H^TokI=eBz(TK)Y*Co?mNbOodR++b#7K&N`Q)X(L2)*fS>kSyIOb6LQvZ4@-uY*({*Wu2agh%HlbA~3JcT`Er22|q zvvW^}p)@0vYe=GgADKPIk77G|E$tJLge++;UB}?=s6X2L9>K4reG!k>8H^HkZ`01n zP#;ulIbP4|?4-ijqL^X7WpwLg6vLw*(_H0diWw}qA=vh=;o#~H>Bv)<2h8tp`JJrj zrqUzfq%9iunH1<2GV zCXXH%!yH2E6%48dz@_oI8q~Whp_`x*%je?J-;U5xwMH>D|!Dpu7Ule>O4ZlzDebVqB3chz5 z{u{y9rr{3?erOurI|BSsY51OkuTH}k2|gd~rQtsoJY!t^O7I!!@DB)nP#Ruj+ANpT@L|EjG<-hrjNx*yf*+9%ew5(-Y4~Y^ zkEP+~2;P^5UoH4R8h(S|E7S0&3cgnw{yf3wr{S*>d^in%v*499{GS9rBn|%;!AH~Z z&j`LI4Zm0L!xQi&$QZfr<>;QLK(BYyE0_wf7@&mZG1a$#=fUEw$Lyq>3IAMLoy zTs*~Pl^2BH=7(aOX^ql+i_f0`!ZaDY0>_sd?H4T=x8gV~8?c@TLY9ZrceL_UO@tjc z!W?z=hIe#X;4_oJZ4UUXB=Bqpe0CCetOGtL30&cT&rJg7Ip9Aefh7lgUK03wnV6uQ z&rbqb&lunfl0c+#2Kd4x5Gkhtz9;Xi z*2?g+qm|br;s@b-;sg$h5;&}70D z^^%5I#W>dLDAww96<^&D>t68Kwb5Zwti#f={;?qzLMb0>zuK^G<63W&(p*>9H8o%B zDVOF*LwmoWAzAq5VCv^x;t@qi>Ys#D75^XA_(=`HS&jHo*fxy=zO*5r?7q07*=CFb zzN{hOC*y#&<>G*^Y6$qUIG}AT6OhHvXyvuZfZVNVs#0ESsvn2?r-o1)UGb(^M0CX= zxq!C5pW&rps{abg-Yhy}?7f*(Gno4!G50|=pi>k!POJOd$Nl-Chb**?9VINOn-InN zaV7`j8S1|O*nyxliMi6A8<0&6c3p#rqht8NPTvhfz#cEyjjX}CCO4bx5EPy&9)F#D zjHd8Mf8tim!S3h|=noSx z>A3MgGO9m|^!v-}wM-Cdl-CNe%-i7}u@GS(j~l5xSxp;MS55sQg-HZ~NXlMb>-Hm+ zr`S|B2HDA&oLrbCt|a5jX;V|Cl@E4+=Dt98*rYgz+#QS#RNscwt3HQD@9kVVfQYtd z>KM~gHpUWtRbTfNY}Jd27AS75KaYl}AFD1y_Gs_{;hWm*9kkz*(rq?&J8(BJvB0?2 zT1=3@UOiy$I6l1opq6#jDr>g2?tQu@N)_E0D%_F6tL(p|zq85?4w#jp8kNFhr)L$A z@+0Jykx$a8!Q>x&gq-P+b8Uw>!!G6X%Qw`!)%(?J>8&HMf@OV|SXvyv~sq(`c*Mq~fo`N}*r|8HFPQ)h)pkn@7#F zmKxcRiKp4|zbq)pB!R$?f&-?ZL8Z|x1K1|$s=ihkTC%bsB6^SYM9(Bw_aP)HD{sIG zTzC1ghyith`C}7+CR84e1V_3e9%qY9@Rt)T;G- z7dd{qsl5xiE~>%B$r`NsiJH|VXDYGIRN`V+iHj4JSl4bPlF4BZY^E~6Nlqv7r>ERL zZMrJ93%LK=l1lDq;gSjaK7aaQ6SsqHJlh(!x5m|5M^7-pnH$GyBjF$H>m{|DOh0yq zKLpUwI=i@wLlkbBay;7f$0e>mE=lyq(e3s}E>dj+slt(DmgU-tLCkL$vh^YGCnj5~ z?z1DXT(jANV9(f%U^g@)m>-!(N8|011)x8^3=jGFs$I<c|yE3ZEWU?x&?o;gKSih)t0ORH|3`BLC%@D3D#EO#{tKrj3Sgwb*LzgR~%NM!# z$N79B((m)J^CjDDb~DG4eGKkcqI{((jiJNrGjLa=NqTykz*L$$A|@j;3oG9S!uT)cC>SHn8S}TXD0cv@vZB$ld-lefl?Ce$Ssvv|bi?EkzvrL<6Jgjfi5#wBl z@&@rCiG>0-#T?^kfu%ZI^Sz=HCfnwx6gfPs3Ug9|^XLvQW~VHJ-P69>4xpCW${Vk^fFbn=ApEpSX^NiwFv4;gm7cD+@?@B|HvgkE!>iD7RX|Av-W=N?SKG3ZL9Umw#O}*3= zC2uY7_`vS)M;xEpdA(uB`O!?esD7o^*(bwzy9w5}RCBckN~=C@#^dx8FhLvmJE6Jw zyRPz1;qR2(-zR^kl3g}_0KZ4q4(9hFD+^}?1Sq2fl8Wdlf4G?!f?NsHr#<{@!y`2m z{9q70ClR&T&gH>aM8Vb@4(!(^KR=TyL(Qa8FBs)lY6Z!eR8s-e)z75Y^X_NTEB(xv zo=M5Bc^+NkLz{V&01tZ>wRJLcXa?J1zl!NFQ>$<|MfVA_0~F4VsS8N(i5X*RGh=F% zWey&Z{5Fx_(nm1A)5-6qM=-xj$#2mv$nTXo{dW=hon!O!G|_G#fGipqYwWiaPiOJ% zM9Xg|9VOKJy}%4=YYHnpJsW+4uT z1^pMR?^8+55Oet~ro8tLL)OC-Ey-;$Dq+je>Rm9l^X7%im=`#t^oY(y=a6U5F3K~nUC*ao z4|@dL^)b|O#xAO3USFO|p2NE+&%AcMl6IZi1?{T)yt3w^^Wx3?)%Nb!4yeQ;9NH5> zmD|kCu?)*vIq{-7o8ch05z5ib{h|fB*UpJW-8z|JV+7TWsqnGt6mkNyq}Le_AAUNT zSxv9FcYJaaE%_~V%93N9NZ+`1nXj|aNHVjXuk#43*p!>k)+3;ZmoNqT#vuOE{1HM{0qf_x0~E=N#Onh`t53Yb3q z6Q#|X`;RsJS|pc7(!ba?Sc9aWLF=3MtayXUumf+g>_BbmH`zK1d(uVbTb(nOrp;)m z9;1u!R??g<=X5KGMC<@VMEy0k(Ui)>?Hg0=a8_13Skh(i{w%iIyUL4eXYxBqIcTA70 z>6Usp_;P4YJKeAY?X-Z}&bO1xnbdF+cPS2N+-|zzyj8oc<9n*z7LrQTZu`gWb|~Y1 zzU@BKh6~*LbTi3L9&^uT$?}X0e)}dj=Zcz?FF>N-T^_er#CMm^n!xghR5u*@oJ&7^ z92as}R^Uq}MyW9`S54fmHthCz)Qt}d6v5|t{tH1uEL-r`ypOjn1FO@7NSAAjxzSm4 zn|)@eZaY{L^P|{bTSOKZ&dJE)s)w0{Ig;wL=+4aI@eebLFj4pG7i?X32DQH#ho#)e?T;T$J~$UAV(P!KEpOIBSAd%B zI3F+hyvn8bLFmBGar%bUk0_61*u3|5B*I^xcvtu{6aFvrt^);Yc)pqEGhN;a_f_71 zjFV2_Q~7=tbvg|9okVv}1ya@Vv9>{Y8&!4++pXU7oq<;kN zGq~_U08GeE*)g0!T6mqnDSXI&b^A--mC|AgPO-kU4|2=BFCr%Vuj!g`%7HGH@A zI;HdQb=K=9|L{fD>o(oOJFM63`-In7uiKUhA7j04*Caf{dflc*c)azxDI#2Ly^frR z%jBho7domH>s1HgpbhHgudu^<-J~4;jwU4HwXU{5v|dNU!~d{eH@$|RvRsZG+RR&jUrnL#LqQH8(G)w+p1qmtr@KdeTn6x z>{m#8NBL^^KFRwg_s-?p3P9$&v;fNgSZZtY--BW|wZlW5D#jE+E{d<-Q>w7D8n-fa;%D z2H*C(x(uDprWQ>RRNe^(W^J45NMq$)kyq!^D*qgLb#$ci?#Qd1oXUG5uXf)m?~S~Y z*;L*adBws-CVH@v)Ky3Al%2Ki`ab5_c{9c>yD;tX3M`l1jNMDwE$T3*mQDy0g>)u8$f%`{65K3%lB`)ycG_sq9**~ft|^_L?!LGrx8we)szPF zqIu5d9g8Su*?oaBA!IM*6}Z?=io_LIdo%_%59b*^z>lmV+^xye+ezuxBn&v(GTCV?9q@NY@rp$^ztNObXhfKHatuI-gyCK3%o*{E>|v}=3i zHw_8AGEU&kNYCLWvll8kN@P>yI}Ne4LE^H#DT;M=I@Wg^Vl9tjwY2l{y@ptSWk9*S zTH1N}enYHJ#j%z|C0vp&;ZGW3JvWZk(mKp98e&O`7IlKRKb8)7YpV?8|U zF#pw%tPDZ9!WEHQQ~a}WCEgF9{RH0&cRR2b z<21z&1J|Am_Bk`=U>Ig`0WPM@4&A1$o#>&e}sI{MDjCM>fx|7|g`prPqFVf|!o_e!;nlJQjkCDmy8V|eczD{a`ns3w@<{i zMNJcqseqX){@qRjJA6LC_dk}0yivCQMfQolXln1!{Qt0hBHIqT?GvSqUOi0vL|@vC zV8%XC62bhkWVd~yMym>vyD?LGs|vgA6QxXD8s0vHZ+TLhZyAG^*=?VwYkG^J;xVSP zPh^*dkGOpzlP%nE!_(O(GO+*O>=QLq)K}co$4AsYktxXzpOE?cwNEtN1pI%)K2dV| zwcM`Jt=(;(2y4=jr9Y%_yX_OD7qbsX*IQeY8M41}!d{T4cH3c3=d50YrIO9=ED?_3 zEAq;kur;&;Wj*^5E$iQ-ouXTFa3Eo)C?(DT=Md&pK4w-=JE@n&_vDo&VZ$h|EdRQ* z%aS7(FQ3FY_Dwo#vXkxOm^)nHompn2&XFW+Dy8c0Ptm&GFh{S<8S3-xwvx^*o{HqH zwJnfDmAL0}JMDoimDTO2NwQ0b<&Kkb$C|0b4hZC|U5Ty{-XxgsV|D<&`YwsX$JYUXuzHArJE%5~3 zfRlc922apn8*H(&yu4(^zRn!j-pl=6TILcFbLoxO3u=QMxGJj6_OSy}-Qfa;k;;FH zA4^RrMjIyHnG4?!!-FyBZnwoFBWFZoH!Nx7Y!5-wM$VMgX3yC4U{$)oW?90@+0P7X z7~EgP${81U-;WfR8^d$WoDFts3sI?a>};fBVrN&x**Ob;OC@{_B3+(U9|DTJ*xLeg z>w2#JE+1Iy<9AK%3UhmTGEj9qnCdVMAQsM_Ei{EO$Aa}OIu`6qe-x^ppSGc^HRa;0 z<>Xez7($h%3k9EE+a<;Kn_}(&7sr`}xoWG+gFSDud|VJbYL6L@-lHfTAB}-S7z0^+ z@wJS*1g3^B6gs({Sh8ZmqS4;Vm)jRJ5XKiq1H>CCx9|aXsYMyJHGh~Bd4snK=+0y& z?D4ff=GtVz6E)dx7JOzj=7R89G{RJ8BNi=P;Tr?BBMmNF>FKD2N^THO$wi2V#dov9 z$WlB|46}1>R3-vRgkk$-vi?Gu>FL<^_=vKk@lcT?>X61SecmzrGWPqv5$ySkc%9I` z{XR>P8U~~k2uv;akYa5lmlc=_w|UZ6_XkDvAzj-YR8kPXaRC^RHK*>j-=~Y;b_j)x z3@Wc(ciZpVHSN0Fe&25Uef0-81=~@F$Lg81H)y8j4aPLA*eQPLXM^TPm*dWlFUOtl^aMwTU`LmAhuer?u2eD8 z`hpdnwYhQH<%Hs&?wRmzOjKqw;oEMF%F24eFydX75$|rnh;ul34^zUcG8kOtd~DFs zf#C^V$>}T+*@wB3)LqC~`H0wm_qoCP*3W{-`WAftAI&NuJjbp`NYAvzSy5Xo*uJ(X zj1G~;N;s`%v&-9_l4^?{+ZKfuZDI5pRp;fdE~buH7|gPp)KPYJS{yUjw4~TUW>C5B z31XQQN?XjJVq`Ic#qjLEi5W~9EZp|Ys97xB-HViFjFjq*&Ea;P@ws5nZv|#;5d`<` zvFA_iIzxQZpBOh!&lr2xr^a54&kco5ktUOWm7YfXs6@pb})3Uw}o_Ny{+VgOm5dThS!lvgY0lEb&{mtx#YCITrZ!lr+ZIm^9XA>)9d<0ffJ?MNqBKcTo;xty z?EY1Ck3HAKG)ShOo{};4{?2WM23iPLWwhz#NdH-w?yf(!#=`5XnGgR~~ z8Y)gm*HwKhs47JB^LCbH;51_Gpb!8g)qM89#6GXZ9U1xhAF|IYduLhll53DJz93>t z0;H%J(+jJQG*?8on^SvVNE3C!=gYWuOgy2!mQDIBx?$~%Kc5ZW9zV)G+}F#8j3sC zGj<%Z*N}C$X8F1lKjn^FHtY2e*Pn!5dV%LWpg~A}TZZsDhTx4vSzR$bK zlE7Prx+JEpzKR}odluQ9Vs|}K?X#mg9O{p|LCy@F@$!_jpURCsrP@%*V5-didrUn! z#?O+KCh(wB(uosQC`=8uOn zzwWkKz1wCr%g^05t6SNuwnE@$0Z+s@W}Yx=b0<5;W_1Hu&azqE5TKck@3vXJ0}>^> z-pJdt+Eq5IC*j|nUFH7RuClcijm3stZY#P~{=02e=iC0qWKxrrY~j!UtgY%RVq8kt z=pK6jTh*8EDkIsg>P)^5{pEkoJ~vta&)Vmv{t=l>q;lfNF^ z*I%UzZrj=?mwCKOcjT2-u}V+mmFbvDA@aflbybRySBkThQsfl_Q0a}lQt7MoMP6<2 zRQe;YgxHmV$SaXkWk%!`tG{FsI)^tuTW?okko3l9@m7DKJ>+lRU!-?q(_HFzh`d{Xwche^olZBw+AKeW?dIo_@Xe1!th40f9g~Q6dT)Lv zkdDba^{Sj)F*-WCD!(!Np&gZBii$DuXjw;&k}BQbdqZ{dh3`0(g)(Jv|}1{G)9 zH?OwAS_!GvX#LA3$Lb+1LrS5S-Q&^vmk4chpPa=;78gz2e@LI?(~#V*8|k?&lRk-K zOQ}%5Ux{}Vrv5?w`aPRdXHd97x(Yq^q6oWxRSB+oQE-UW)$I(1$?y59FEw!$sExR{ z^151|e?6N0*5|47Y~8%bjy>PL!m>V27tS?}fLzanvPqdx+$u{y}$XXILUCyG_t<=us$6h#{2zu5BKRc!=-MN zjN`v2pULk&y(DaV-+da*WlH8bT^JvCN1pAJw>l#L>*fjTxQH@u5LUTs(PDSmYCeGf zjPA}#*$x1^mDE@LI^+1LVCscJvc}QyqCC9gGKS7N2d8^0V~uIOU+M2>VXQJ-_9vO~ zPxhE0?X1k#=V%!EQrs)5feE^1szEHMMvtQ!Jxx>tVguD!a)i|7Yg~JZH zvE?}jzEiei{>jh|S0si-ZKPOUXK();BB)&WZ~Um#hW>@0moFIU4gXP3UzsLS^Ww}l zg4Uf7Md^<+5<<_wnLCEp*=(v~Y&IP2-gD?FVS3ngAyt}-@M)UcGrFZW7##>tg`>&C zh_eEb>msondNAOUvM?{ZrqD`UgoGI4a;(Gchi0_@5Ez)0Hb+>Cq%@=Hl9cAmB z>hb>Kvf-e!r+y5BY z{&lqe}>)W?B3e3?A8OoFkf5{J}+SitswVvg)7?5G=+nXo5H=5XVLcRbcOSi&Hbx6rkC9Wb2z)#?GSXZy8%PHuorDnH1Ok1 zSfkrw*W*_6?(vl}1*{(6Q9CLN>@>Gf504dnh4SF|+yeR$l|p%@#STq8#1H1-s*4Nd z*|q*cdH-6WP+p1t#E53hMM^^l=Q=Bk&1-h&W>27&ki-+{%Nhosn?T9i)A;MIk9lsa zss^t%_3<^xC%!Mwq@m3`_bU{tkz~FyFJeB=9aL8YQ+G8bg^nJ%sln>612^g{dV4F! zndX<~AZjyx)z7xcWxR^CV`>{Ul5eAXz7V(3pJ_W*V3tOK-6vUOwxhf^g>B5?!;Z4r zDQ@x&l(R2F5u-(OyyZ)D--aO?W2C#mrCX>xN_FZepI`y`W3r=se9pk45HndFzLA^@ z^*8ZLM)x1mpB@_ogmM?%z$i|=x}jZ)Zdf?n=?*^p@wi>qfjg{1X_vlX)Gf5M93e*frdKS_sBD7xc*d+ zm~iu5PUV`tOW5Y?=!?mhT*0Cqqswwzx}{7i^sJ3oXBO^tb_|2;SyA_IUH`nMz+pTog#y7q|XM_qCHe6_KYk}%h+E5qN@_E8HR=eX}x-!Rbfake@9cevB_ zmJ$ioj>N6SRS$=R9c%{=NlNJZXR~Q{22REi$zI1pmEEtVIIuPv5mZ(Y|7=wr@( zf~TDP#qyZ_2nS%ScS9rsy~~BK=BHR*wsoCo@xHh*_tbmRpM*ERd%vFECF6UTUvzlC z4^u2}wxeHcgIpC$*;Z(gKhzCw7NKxqw^XCEXM*OX)IT;LoF{UAj?+T42bAD!R0B^r zv(wFSPC7=reOQ>|aD*vt0aM(*6N+0X&z;ci#lR-Uc!tk0z;K*-j%HRb>8_3|fu7C# zKj5?kE0n|9{^5lt?COb4CV|6kqkI?7UVI4G@dFZuITVgrk5RGkMu`#2W9J!hVi2k> zL^?1(62~KKq`J z?<0joI-;FOy2aH;f@(2t`oq;aIFvnZnmMS~wH!BWE(QNiRlbU27KnI5rdh`uCWgw) zahSdS+Df>=+E8_kxWD=0{`R!o-%_!B6;a{pqx5=!c}1|&Sy^IU4Nq7#uMdYOe0^q( zVxI8o$HnvdJ~ZCk#F}h?RoU=R_wZ29@K9lRs5m@S8XoE$9zxU_EpG;GulR}&4Ga#H zXSqoSWVsf%8V7WJtvl5H?I4?*bJm*!JQ7%!Pd3w!PVdYZoFQ3OWi3@7Dg@Qdap(ga zCs3}|=J?m+YB~gD(%9hA81c#EbD6IEt(X**<7ifj!<-M%`<}el-^DCE`Oo~YT&>Ow z3U=}00E1nQnR&P7>cK9;zsJnl;Zt3$wIpOwG%E*_O+GUNw%jQJi+ML|mvvo|j z(nB}h6fhfaQbW~bdn5(!Ff_IP0TsG~MdH8kQsMG~9=_E-s6>mrd`K@PUOuds-a*Tb zgA~4B4GvUmkEhR%i`kFsXoG8T)oVpJ1N)KQVR;mMTkCxR~>> zHcMPgeP5>UEtwY2dk+>XHJg}YzTGk}yS120Xn>DI?bBHI5|?HSlsDH_R##EDlC4Nb zdsEEU-;5p|0pT8EH9yYD~Fqc4)#@!urIxp<$B3Zxi9FXs2?*42P~5_mI4L= zv`p^z%jEv4B;8<{-0#-|o`=|vuKXbUg5mql{{&^|FBk#(K%xFgd{ByLV5s`l+70}=fuR{?3U?z8=;qor*~7y|RJ*O` zDwZ~fN7ppNqifpX(NORWrr$Q#2DC>}tQ;bgYcVZ>KEBTvSI>=7v(zN`E-JSq9kHIDJ^)dK42NTI5 zb$8AFZp22DTrSFJrFXoSro!L$_H8%DywYNfPzAo+wUUH`V9NE4m~sFLc2T(jc~F=UFU;;vV5Gx$u)3EcWLd9Y#&~$JiBdt)ZvvG z9v?p?d{n{BKupgL3Vaas&PrO_BykjZq+gWV^KkHA?$t^os(YBLq{PD&DhJuIJwGVw zWRWstn9oyWu|i4Bg5v0wepl+v(*P27^l2NAB(+KE(0 zn*=?6OGT?CIdj^#fg#Ef+Vuv7;`H2*m7RbtR!xKH;$aOUz@qaLI8g+WTtZ zj#g=owPYRFEJ51~l=2d^D4Z9eeKBVx+AP6&(=_Y0!;gs9ZCYNu#^SUeT5Q7ROF6^iB4Ye!Yrsqr^JS9*i$fj*`3rWKt%Ec>Y2sHU#C z`E5u;&CTiBds^@;DH2I?v`9}KbphKVZRkF|rJ+Tv?$Z*#-tG)O_>s6x?tne|$3|LY zacQW3u%GMK_Tu+cc8d04+B;b_4hM;W=LF+H{mz)+aroCCucrMvbhYNAhz_epKlEMWr)osH)Svyi|Ku z$LI0eZ&#!T6YNl&p88gs58KcW!?1|o4Ye_TPqAW2kC5$g-M-GI2iK1;ARU+E_sFS6 z@$^(q6JO&cl;~7BPJGUSU8br#tN0+4z)cl8C2#GchF|Cf5qE>910^SgxJO7^8m)i9 zJi*^821y!dmB9|(>*$9|>ud&t(!%<`liKJOn~Tk0j&vq=M@CAZD|+kI8Jz zmDCn-5@Oh^8KeFsUit?a(r{%b(c#I&a`>|#oTP1hg{m>~pAw0Z)HbK9^*krrx_)go zV-RXzFwS^n>g~A*iM828_C^Ab-ZwNYv#3SO^wd$ILZU?b(B!w+QDb}3Yd5|lzX%i4 z^u^?t)eEetl6W&{_TqAy-q-!KmLquCda8a}YrO3-618o2VlcrX1BRLmGWaSNg}29U zXN{p~IZ6^yx1BP5(We=5uITTNie4HlH59$HlZ&2Hrz)g+^-zX-ok1%tNqYT~q}Q~g z-~2|Z(`%L_Jwtu$MD{I-?#;dhG@FfkF-{VS13+RP<__(ZOgcy6l6=t<8Hjgk9;3rI z+Ya{)ZPX%G7WN$fA6-xhhR1d**><6;j!lM=}%ugA+(es;7Apv@vEau8LWUI-~oY*%Z+xk~5ZQ zixtd1{rO#!p4OD$xqeCb6-a552&h>94-xOu(w^a>H508Edj`dY_51j+*X3u_zp4PN zdXo}GBSLgy#RzkzS?DO?CK<%L5=g>;&z3>F7o7MgE;*(<@W0UFi4eglvyJ5R4f|+l zo%B?$mrm!`Om#f^K30a`%U#$ z=^h?f4MNFvMi6{~KE0z`>*tshbF*USXw+ww!(ZfxeO2%#v9Hz-zQZDAzMi2T=Chud z7HU0z0{wMb6OJ?PlKNdnc{9zPu?XDHjqjvdm(|OObgUdfO6qtmBzaAr*h|_}D_6&$ zZGN|@eNC_Sl(*iKXot?gD{pzd+n3(u@wU_8?VZ9~gFaeQF-0#KKfF+$F}`nM?!+(o zP$&;r#Qhc?xo~e*KA!kgy%%( zp3Ae@>6?f7aQ-Q{-k;^d=skP}KLu9CM-|Fj>~iivAdkc8Stwtzb)C9g)9`ezdZfe& z&(V@2s5Z-TAVrO`92d^Ya{Ae+Xfb*-k32N%8@#un-?HZL3uyTZamhJ+5&k!N+U#-4 zlM93@ZpFEP`o9LJXCz+{AN6GC^nSbfC@I4fR99P+nIpi6LCh+Ib~?`IRkm7VjF1%| zw_ioJnTl=2f$FeQ; zv8N{y=PcYf4t)-b>WAZ5f~&3v`=#@-f9+u2Y745NG(FG;)-D97bM|Ri%uOL67!W3S$%*O&JbD69Gtlf+hSJG);~)0q^v9w zv{jBYq{d0t`m7P>{(8Q2txxtzNqp6lJLxeh;UqYjx-%*S`z}^@rWjO@_o18W@Gtpw zXu(3c#D-=K&H}#=<##<~dM(tiHX=s0S+6BOjWm5w2hCHnch|pdJ3xF>k~XL( zoC3kf&_MO%U<#wxDIEf(1K}d*qwNg7KZ<7BY19Yimobl<$(=?*Y#AD0uW@GeO(w2U zp2?C}DwPKHnef}pynalH7Hn(~TPq%1aOGr5&C$_7bq$WT&sX$MCM?KYI zuJ#S1QjFF%$N3&Xo^mGN4o*9PJWf>5Q8|i^8!Ba^h{?EA|E|f{>>E2}>1-_S81(Do z9Qx4EmpMa>wa1K{=vsTs@UpZSQ%$qZ3@d;+;$UjZ-{WyeDuUZ^Ng3Ka{FsLh_F*1c z&AXqMzRmDuGr-GbXd6PJd z5>GcA)cSHjHrEbmYiC*w-PX=@vT;Xi`^K5}k)fQ)bZ+<(UTJ`)eeCc>7F?*THe}7y zVAW@2d`R^E$R1=lM_$x2 z4M$!+3}y5y^Q2D35pyX3e_DII9GrhGPJ&cF4tkl&-b(v8`6QARqk~LkEaM}IhT%R~ z(X`8bAek5U(cgH|GMD?X!3zY>Zh|ziQjE}(@NX~x2TDom&H31vTz8f%qg%4P1*9NZ zGsjyL{a9?^EjT}t#apOF5<|Cv2iP9oqPvN=P`Zx@Z=sCDTTs7D-lA)JcnkGKUm*Qt z!&*qg&?+L1zy)698yma8b6mTZAC;xTq-0>FOO-VdXQ6ZYC_d+()JOGY%aD$dm8vgR zRLfW}qZBe`6eXk?#am4oEKDWG4C|O^{p!bz#;^|L+>J}l9c(gsy_CvfDy}CeoxTWJ zOUGu76E3|;j94#=@N36?=J>T^XYeUl8`xVvM#;{ch>hEJ#f;(3`u7+I(N?VJ!|4Xq z-DEbF$NMYfBFU1k(+PFWW($Y9>)+=I(d;j7J584$8@8QV8_oKtA5FGDXwLSOC|jhi zkD4aighs!zMM~hZ?Hx&En?o3U16U=fePZ<|F#vKvHrM*&tst%Xi%_#M;hWtEAMdy(RX2dx4 zEb@H2!6`{28c&syy_!~-6Q9_K4u58hEhfrkV_qrZNA#gf&+TxtVb{BeD&Fc6Hku`w z3u+~_HOf3b&put|?VdTc3Z~ad!8B_DqU;Y1dbFn1Yc=SxyUu3IzW!q&XHWenJd#IK z?-7{4Y@AR!4HGptwI_zX#fik(RF50Oq$pb}8=7!K1{Bi!L`}QE`FlVgC-<~81*sOg z!;Rae0cM>{=*GREPvj+#FUUxZDYBaxbS3rvX}v6Yqm{OKid`_}iJ6>;UR#>k8T|Xb zF@7(FW~b!t8`mtS`PdnMk{)xnt~-WSz8rUD!pZMp$@gR!z58$e>A$z~>zfakd?w-k z-9MAz^zPxwr+@d~y+6-8U zN7JL8(Pn_>eOo=-R~hIklK)B&K*X!lj8O)KPt@-DWOeKmaP~fmt~S6>bLw*aa%jgP>RZ$DGbA9 zNzSEVKcHw%6w-jD%6h|e!#TI9&;4(S;S=uw_`WOACRzKx-=*)65sQxvM*5~crKzNG zcKLMX?4p{idIyO*d4K|`gO+fO5qT+2)2iVLJE{vnc{guryQn;5 zSyeN1oh&F1zwO=Mb~mAzznM_bUV{2{HYKm9Y`s{HJX%jC_vcZRsn4X_;Ip}HfVmUW zImV>p6gGnDiJpiI#w)C$c!jmutNfUP3@CwPZ2@vB*CreDIZ7E%#f@x?m&u(*4X?s| z5+@2JnFEhNGs(-rg}ZP`z8B^8U_T?1b2=W$y&?*}*%UlHgkVu0BQPyjCv?h;7e@`X zh<_9O$TuzY zTH+jnL2rq`&D`m?#N(h&VR}~e?mY2QtVbQn;9-O!tUkP))E*`7$*%qqFNn&mKAT+k zsurNZ^)?4T$p_VlT9uEEs{&2-b_^qx?^)IVklop5*)ecXvZN>ux#>N-gmb(Jx8JB$ zAxJ&Ct62+e%H+t0ws6o(S;wzKf2qRbbLdZp1ajfH1(zqji;wS4IpquZB-HjO zs~iK;byuFjP5-cH@#B4du@zOoNq|Hr^{;Ewd05LJ2bvO67Pwp5e%PWY$x$ui_c1Eu;OdGvTBO;&Med_t9b0(_D1$M7`KsR&7)Q@k6S!3z<;ffM>dVz|W`{r((zcd+TycyHk* z<|O2juB;IT=+N`QvLp7zCT(ysH~33=MU zXn*F~!Ae{f!KrtpIdvWOZCIzWgfRCR>qG7m!e3%M(6odokW0T=L6q_r5JjyYl>Dgb zexB;?Hmw+LH)y6D8S3_nsP!urPJOAV`b+oha0{ynB8eW7lj%C8c(Y$f^>D#*c4I{H zcspSx1ed;8I!C2>c#*L%LvifNsN1fLdh#na*zJ?R)KlejRV)bKDE?(+sE{Qmy|<}i zaZOg~w5P93adZep#w#)m5EyA>BzrKY=iNjsbrzJ+jA|>WdLlHbADT3ydNst%sS#GM zBMYY=B&^R8vMSQU5whmcBj2b6+o&z1uY*SXs?JrTUSJ#bWbv+vrS|%Gsa=Yf+V+?Q zmUTROoatLMcb(l)xkx=9u}i+3?JkRbIWM;5d=um`#^lK4VkuJa@Zm49;jbtB)R(u5 z04yg8f60DaK7KL1x!?G)gZ*rdinWkwB-COfhbn(?fb;8{98tEapSbW#w^dzj~zz@SWorCx2sBBd?R5$TQgJakWX&th~d`=x5Exut@tvF4L8ejQ(|4jp%H8BaM(GDFoydp=>2{wZx)T(i&f`jEkVg(0Tw(xaryvWPzI8>@J zAM$MKvLLZvGStQVhrORB{V>DmubB7>g@C};W$%{R6)5+!pEQ7(&wpsoENT6w&)sNPuGwGUD;Hbnk zNnT$w!-q6>>`6EziR?Sa2$s8-(I7QrGSQOKo}tXRyGDT_Zl!v`*3RRI)Lu_OpSuV` zc2rk_{E>iTg+ax1Gb^dSKuuXRY2~AnuLbzzP5^C(P0f~vtL^yj55D=6_-x(xXK*B$_`Zt~bJGm-_;n|97x9sa zA<$Fbph5fb@dp1p&sJL_D$ z(EH5Cu$ZiD1za~nlU-6XFu4xO3`=f;^5Za5xm2k)C=%Y67=*FLzip@P;O;NQa{EoR z(?{HR?zWfemikRa*8ze%gV)GURI6zAs1xUX?ZLCIZTx`xa0{Qm~^VcB%`L0(V;?MZ^FGeIUq!$bnW4KvKu_E$4axdoQ2@X^)b$$ z#+>s8*Y*)%pa^VJt>brq%gMUuPJD*i$T#tIUW{D@eC3IETJVyIH`*7sDfJ?Ja^bxe zym9<^g1b$r>w&$>!r7D(N*s^VLPJjP-tV7VfDwOVZC!YqeLb~y6#%y)5#w|*PEVj% zB!M7DXN;&`!+Z4zQ0#U_V{f<*Xo%;@#zQ;H^AzLJmOh?4j7JMDJWn;AAM-vE9l@Rv zTX&018)|fHb?e|<4XpBvCVD~_OSaRVu9UKn5ao#~Q&2tGlauzW82Z>xvavG-yjAL% zjdP0KKwn$qoZt6-EKNF_M2$u#^bK#1$LMRR z;3r&L^iFb_N-rwC&Fc*G7iaRNavHswvBnftnHTsw6;VDxMbqj~TB)!M+GUqtF&jGk zs|`f3QKilCAAP~;7sTcN3w{4dQ+_d`mQTVQ$P8?(j!ry>kHxtY8h=H<5B^swEEis5 zpi9OV6`?ji15)%GmRiSd#C-{%TzH%b>h>!S1S0pg4ClYSIq}WE>zm%?d^rD9T6*_s zCf@~1rjZQe(<*vlNi#{x@*Ars*?X0&h0RRpG#I9}deUAhb|!3X^(1?aY+jpxtpPp! z9O-!g$rV3FSA7C^2=Af;8td zTRWUzC-j+5W~VqOLL_IlKRYC|FV+V&c#M9p5l_w4Fft-535D`airD2-vE*e#nbb66 zJSyI_<1w54Qq(5)t4a3L$bLoZ%mJzlFCFMQrX>G$IsQ+%wl_Az>>elw6Qkj3WFw1f zg9r}RvM4OEHFJeMB^(o?#W}Ja-NnLIXhC*fT#XviqTf(*UPboC(-O08&7#ObB^;E1yh zO=fKKeLXP@TCDvwiIGXLu%S=7WPdsMbkrwEBO-dgpW_@XAeP9a>mZuKdHk{Ko3UsC^LhC*FxiqjDnhvB?xn&LR~ zIKDXZGm66&b9=>+Zgq6_xtKwe{V?{oj}8`_rRq-gkCW8Mh?jsveqP&)_s=tizuPd5 zJA+^1UzElt-yv|Pj-c5s(X9SQ5*H2QN#xj~OkH)56v|X5ZBQ%GIRYd)C&CNKe1#V! zgBPXt@j}@*<3(?sEiCY&G3!cW)@|U0E!3qHY8Bjjgr=?V!WE|hFM8|jwWW%KZd|e@ z@{6Ov7~w@0^KA2!5vKdmUgqF#+8*{KOT1;))nO{v2|oKeCox!U*qK#@l;zNL!Hh8uQ&?~ z$tl)Xs53ZQQAcUnvC8c(8I$~JGxreB!>64c&%?LSWt@{}9v_K1OyqT+Mr))r{3sRP zV^5kzvSE7+Hr7AnD|iSmiB8RqChDtzP~YV7lzr@|QNqZ{dn%J=h(yOziX2bzBg`<> zBNjt%qOas2g>xWbBWkpDNfYB~2TK}GQ_`JJ$^94;9oKzWqT9)}Qyn-$QSj8LVL@L$B!8- z%pKorkR$%Dg=pZJcp1-7GZo*Rp3<%Oo`};E=)8$1QdvcxxC%%pksiJi#*gH8?Zg?@ z>z3QA@vU`E?{Wllo8B-fMm?#}&*T>pyGGen=(j33QJ z5VBl0+)mlQmNQN`8ll^UZ6Rx#n@y;nspbUtv}m2QS2R9Sv!w7lUD9 z>Kq1X%k{lrrJg`ftWEGMt?AbRq#ujslyAlaBnF}s6#aM!pJu}Q4x!_nhFzMnDdwKi zcD6=UDpzB!MX)4A}y1fpAY0D2%);pNkLYGyRIqEj<2=Acb!wDuTYU4KhE z{cN0`3V@3CsXW)#4&v8Qh9SNkT+c%(c4g$*Q(M5eJu?w z=1F~WR(kt+ZS}$KVrdd$)oamBtE<1Uu%=u&Vx;X510^}13^7o&epH|Q5hO1%&f;;` z8Qg>)`F1?+5@XX`(%k7CM& z+BYOSCvO-2$-SRo&CY*2Ch^U`>zm%?d^rD9I(ql%CEMe9szcTb9$HKt1z9*3>@tX zpA3{gKd@|$MwZRBYDR%^g-LiUPMEegh7-k+F7{}( z32k$fYnx1s0$*=6p6v9xmVxKVZPlqrx*P-O8)#wm&P-{cfKO7_h%p< zUx{(R@!pZp-fv$usq;jL0lP-SU>tn3cCub;8^RCJlDw|19l@`gJNF0nLg(}@$5-8T z^mJYsx}Gtz7K{gi@F@n~l+l1PT4^$>R1Y?}jK#UA2WB~^JtH|?uI=XI9i}hou7Qg{ zPUN_BVXLezB=ME0*&-~A?7NtgC)XL2#&@(qXQRYP=f+bu{BV{CPdRDKw=2T)V_u~7 z3H0p~amji4JNQ}2XN>trH_gf#^E1X5>#{0jw(#9^C$!-M)R~OXmYiIO`kTNyp)2hg zv7WrR6T08V_gx%=%ee5Hyztr0z+Rty4bW!izpYAq^Y8kmcR3%HF zX;C^i0}G?3b7le)leyjWE|OFoYmlQ@UoYIq+uBNzxR&}19uEqn_hR}-OMsB~1E`Sp zt*3aukB)Hm38DetnsZ&uw(J@r$}vsgdXJRukU&e)!+rp)$L zU%#E;kXYe0>0Wpq=^4+9_0gXL?!V8}IpT~c+LG~8B$GG@kp$`oK_rpTG$8li&ix?q z4F3y{1QOphPbZJ~cRX?-3<+Nk8($nrBi;=}E)qt(7LQyg70yT&u@cXtozr{L37+-c zhd(e8&%=CpJtI+sU%9k;Y5{Hyv6^;)IgseXg@TQP{v5!5gZxjvy4-@+m!>{-)_z}Tnca;8nSeF~iU***i4 z;OsVElK9-gndIV+YV6$XG(ybjg{PCn>5feABpa5}Ns`$25E|FdtskNzzHP_Ja9`Bs zVi6fE>-d!s9oQ+p7P6)0s_2Y-p+S!E1{CE8Sef5sjzzYaXdUc#g^n$a~*_JYd)NSA#gp(dV9oo z-9%&9)HPVbet8-jZ#jw~-Q`DfxL!B7n*)|1D`v&H)Wl(fk7MQz$IES;{S~P_-RV5% zpOR0V{B8dD@fJ?q4-HP5YtBBvu0*6ZeX5;cHg1PiNgwrbHfpB) zq#75>;HVb5T#0cmZN0cwtDeDgEhqoF{bH_2c%i$T7jE%&Yi(Jp!>B4rVl|t;7|Ro0 zrdMG~(w#rGsdx6+op%b>{yg}dta)&W*Z=;cP>S@v!Bf1n#yAfnmy@x-DO_@WwS}+S zBlpw_3%f{Wg@s)tv%-q(YBjE~VjD*M%u-tV72tPk(gLL|78S6F^NN$S)dX5^5y>PjPrTolV8g~5_p$w`1M z$JecHHk?KpnMTB&E7QwFNN*X)uj?e!>jl?_a7p|V3&slD@TL8eC&DBeJ8s5$0ekXp zf=yRJ1863i0qpJc7VB^jT8MhtF7<;|DDrz^ZOs379{UW0u$Ldb!P*?A%9n znDZoM+>=!B-=3kIToI>PTDR652tIaC%+uDX=KZF~ll$P8 zLQB5m`%d=vtUj~4!7UxOrL}&^cyDuB7?YqD9UGA^btB97 zjrLj6P|4Pt5eL#mve9C6m}e_wm$#nenKYdW{C>KSpUp317tCemET{ClD_5EFnGI2o z-J`Kx<#n-LWoxY!6Cp;AOq%9Qz(%>=#~YHys>pZW=){41AMBeuf!-`nFCdd#OfEE3 zy$|MZjF07sKadzNj<5RxKDp3P^}aIf9FvS8&1;*UBBz2iJ5PS|;35!mKAe9h)A#RL zc_d+a_i0gy?IGh_v*NoTpJ1Puob_q24KWk{5My#aE=gn0#gCGvrLlgLqER42pV3g=r%4>d2tramgWpnZT<^6d z)hf@m*plis)U}r#HggrEO(WJYZ_!w8^a@F%)JII}3oy!{)aUY>eGOC#vk4K7n{=^y zrj&{%r8BB$nm#cq6ZW${ti^)R7|ScC`dP&Uime9fRZeR!XIwfh(VqNTI?d1Yw802m zh3p5;%qnS~4ylt>>B%j;)VGT8Az(+rrx(Gy**`M(nG$W;692{OciO{bF0i|6%xpaA z(n|*(t{VutXZ15la`e3VS;~P`jxLl@qO*NaC-t|fl0=m4NED*W>$H%MfQ%4C$uMt)HU;p^(~BKi9rNg9p3to@cx+*?pp9yIjMS z`CR|7!iWmj&zJZ0%pz7tKeOR&Wfiiwvwi^&;$3J`5bvEeK2>~eW%#WswA6n9A@}xB zGREgzx|Tn&XVNd&r)eA>h5viBzqd1CZnl%PiJsx1!U$#PsN7`MJ${r(vv%J2=E44X z;}~!oop=MxlQD%vLc#O!ybz~n_+0Zv`{vq&ujAW>OFos<#3c%Qkqx_ad=%K?iIbgo zV!b{Z>!sN7nV7I(w;{GKzA@+YE=TO+x2_XU`cZKD<2XH)zkk=W`Q5{ka34=`y$k7H z{*p~`Vy#S`XW3Q6F?_rg_W|5*aYnt(gm=70uI7@=3`ujTkknj)#SZ^SGEoYUv$OKJ z7o~vK6!)y`MXv>3btfVqXSa@6BI2-#_@rcxXyBC3X%$n{fvEEpmBVzNVqqYzeC{;y zx{0^I#LH$jI*dZ)P=-l))peXPYAKe)yHclRP#6L4pqKCh>ajB3L6qP#(g-E zp`~80N7p;`!^XkZ>nvSkID4>f$q{a>!G{Gu0T~~{CFeZNk>59CjY-a|Ch~0^xYa}I zglDs@1IbmUPsI!G-UVCP&LvVmN>tO^SX$!vZS;~7whq=XB7R`&G~Cy@wAfj{i0-KF zp>3UF>X?&G;N`3gIM-UJUu=n=8(_@uBmQ-5eB=3%`Sy4v@m)&WYW10J6-{gQ{Olz* zA2?$j)pL1x_Wj2K%{C{h49KzXirj)T&OSh`NDT+5lSc!+PQ?=N!tNF98T__$kI>6N zwcYzI)3(?@SA$lrGo-jW=}C~&Sk=N4(XZ#5oWiON-sa}W+;%ocp1M5?$)6-}D7}!h z^(_Qx={@NLZ~k7LB<`uT3;A{O+!F|OI!;fZ zj^8iio6c1iMTi4x9~&c#z~0;EcRUL`u%lUizgJ zm%?I%HD!2|_4rS-2eeXP)YR`~Wv%ncT=q^^b}cj~gGaa6)io`weN~KBo@q&8EH+aeuX7Tp@g4P|Cy#rKg+GeszIXg=g63*!qKhk zCI!K10hV-r$IZjMzxk=}g86XGPaodHeb^*C3)Y;j50ixZH2kxjZ$50Rr`kiVIZQJB z=J%vu@#N2g`ET3%Y%WihznzN!IdXIZ$Vv(G)``)w*kN;;LI@!dViqgcqdZ^#8VeYb zgF(tttWsF5MiPouP11t&*~o)Qj=$&Qv?uT;9IRusCm@;Z^PvYX$0d0uG5Q=391JEe z(^xl$w)zTYfhA*I8u@QX51OCn4w_gJ*J9+~XdBZ?;aKJpUYT2oRa$89r)#87O2_r&$FO3@nYvW5KyB`fR@cE5j(M)3qyAaw z*yJoEm^To@-Wgr2zB#A-YXjT%^((2u$b%g|D{e_nd>9>{VSOWYP7T2o7NXkbVJMiE zWn{r)nm5PA&D6i3SrY4BXKcjFqaH)E4B)QCCGkr*cQ*|U)o#GA75MLDcBbPp?@DB| ze(&*ep*+)S?am{af5IJ$<4bL0$WouT{cH0I<(0LHWc|mO>+6u`%(OE{_+o z@1Chf1qy#3rRC-J2>$$+*aq6q7{C9i_H_0utz5xK60+;pdYn zu#`W)RSKn`TpH>vnuyO!Mno2PL92+xVBewMl8L8Nc~OzsZix6tZ+9ZQ@V!BKAB3Sj zCJiiwB+@8ECM$BI?oCu%-*4K85^|Oo#@5mfFdAL58m^W)GA7=di(Dn_ zjd&UBFO_DWpgfx_=-|PABgGu-pL`rNlH~G+Xm(kVgY+fjAc#f=dz~C)z-hVxDmgjG z0JFJ2UpOoLhrhaezGh^Qd6iO=`wTDrm@)OMMv^6jr%&7poR0VPt zrJyv_Q{Go`j`MLOYrCjbK1SBY{Y{FMiIlDlv#c^GQ&7cS(Q>jdjX9NlMJqC6$!C-z z0d+f6(dy5mN?&+|*+*h_E@hyK5*MnJJgStYe$$95-cnpIsL~6nL}%Y5s(k}p`XOAB z-}?*xR=9GD#g#jR4O73(!4WE^n zHOfBl`rmnU3-!luXNejUR>lV&aL!=Y+Q1 zSCQ%f3ynZev1tTzyGq)*MBT<*ZQ!5A93(;JO&n&!!X;`6FpRI+jRKPsoG5Fzu3JjT zGjL}+*ZiLJH;2);q)UEJ#`EvVXENUQzO$ViBdA*oSflS*Hl@JuWedIFZ<}TUB&~5JOxfW>GWW0 zlpC1nJaxgb@_(fu@l~Kclg@$A_^)O-OawsHh_!=~hib42}L@ava ziag3>W3in$n^;<#Mtsdr$~7O(2|KDJeMBGy6fqW zYUb=VolO+l$zCnr(6lAab`OkBd{aYzz)>3Q0RJ0KPr2}8{Llf8+}wrlHMr!Huk$8e zWP?FzBoTOQhN}aU(>MSAaG%Mvn%|RtpO1&>nRLnTK8#@gJ^4(=+unC|#i6+Bl@W5V z6l;ltAP4)z`v^HUM3Gp^w!}grVUBH%BIVwhLMgJbEzPiNEUXBI2Mfp{^u8$Cde*jy z+j_QYBWyjJtrKlH8?kAIP{pJnLX_B7{_jRYl)tas3C)>(0?=8w z>zvcO_wUPl^;*K6fhtya`RdvM#vb=`HND$o2pyGwFrJ4PDGbN!jpx6t=LN>|BkOsg z@qEpCUSvE*a?(+GvGIJwe!0_l-igQ67*ZMS7GD+b7T1}Y9>^vS+Qibqf)z?CFgJ`R z47_h4;-B&(p6GUta?QL~o=39Obvtdlt3c1U1rmk#l~ZZ{oL?Hn+Gb;^hkdL>lRPeN zl5M2*m;AJJOyVUbt=PK!#yIvQvH#jS_DgN-?D2)QLy@cuxXhWBl7 zu6$~nTr0*Luh;ihHn3zJ;+OS#6pINj6oU4nryU*U!Z|3_qi-e&gE8-Xp`- zlpl^8Vku}fl7ecbb@uez(9tEi<*IF2Mv>7FZ+Blx`G`zB&60_~F(Be{jo9;0X%>oD zt3IN^l<3+GwgC1dg4wK>j2CI9G;*cLdP40n)yvt6h-{cj?+2uJRyT8xFsO*j{L17B znp?rIV*NVan9+Oc*V~s;{RaEeYK0OHi#*K9Sth6uP0NHPG-zD;#&qaJT4^@!5}X#C zo(>_>4PFjjybc%7+g-sw1OIIUuEgYJz_oGI?M9kR8~Wp&R-quOWtwZzxS!=3vs9eC zktTyZpwd~mufVEwyVv?SeWg-x@l`SIznuE~S6rV2#vGPc8+QX(y7aIwiJNdF8CT&m z?bax0+XT+jw^PoH>DyB%_wRhU6QyprttUsF%-`12GkKFQW5!L&M)Qm^dbQo+R1&3{ zj22^Oit2}?Cl2n)xBR=IrR%&bG8Xg)2;;*3Uam4g zLy)`3Z>BEwDSl`fMh5e_N^!Xt)#v3j{4;JZ$jOAw{T>V>7pl)|?7|aj!&F-L`f-%Rr`}?jlr!x{ghv!5 z%Lt{4C5nG*qz1W1{JH0s8X2<*|v^s(!uY1rvAk5>TdoP6~?EDz(TRQE{ec@ zc;2nH<`@d4(hXDDj5`CT%_APKg+wG&U7C(yMdM~K>Mq@u@8RNc(;aMKFZ90+KG|{O z$&Y3wL(;npN$;9F@pdo|m?P;g$0rxQ9DxJi@`P^F<;9WooAAj2aU}h6d~)ICHpa&B zllbUJ`U!lH1L8Dmb?|mJA?5U*bRL%c9>TjQy1S)%J@x%tXAnFY*dyOj*grfAf65xe?o(zPR3dFFrXwx!yY#pWNd!ToUfX`RB<^@dOvlr{muRuWlp1MAw}KYzMk-Y1DOx zWNiBK7b`x6~jA7>Dk+?ftf9FrPExu%v*X+?d5_HyYW%%|Kij%jA z%e~!lx!IF>YH9JF1SR$osp#B~WKW~e+IhI7yy;oMml^$bdp`ad8VuRRi}vMy4iD1L zX_{Y%XY^P18Tp0uGTJQd-!e+;mGUW*T?=&w$87MtN4L3~?E$dW*|fL&d&WZ5;U#8? zeX&r5r9BKex#1yhLM~impyi1r=II8<-gxA~vVkV8Q9Vcj+;@6FT_!_bSLV-&*ETM zTLpTZ1GWAD8fgzlhL-t+Mqq zV9!f*<)4=*NzYT6K+xg_cg&~*AXxNwh}j7u-2+OYO0%OK(rZr$+Q#p(w}|ZpxHx&W zy5Y(E&>TVdM1Hakjy#yXG*b?1DPyY=%n62zt~xU*%u|$W)bF{yoxPM&eoafMWQRvf zsU=jy%fyf4U9zYPa7mfC-laDqnR(13b_N&W|Fgjb$;1Z|>kM+ATiYKAFC$Wz^nBdC z&h5$b6kK=@KQze8K;>Rzdd~MC9d=9}ORXhhlcXGdE(ASJaoWn-SzhGI+HDFA_uJVN zs+)ZQSZf^Bx&ZcS5-TE}bkVb?lpNuiU+T6iJoD?^5^W|GB@xGDUx*9;i|Y$3Lyock zI9nIh&a49I9m9JfhDR!c^&Z_X}-yYU!x3wH;!M) zuVY=_%m+Cjur5Qqsjuavh|}zna39V;k0gBa_wZty<*BvxWZ-y}6(qhMxSl{An<=!L z&V@$V%MD5ngrshHR4?u8h09L`&m{0NqM4E(nAJ;tyG|#WSq10PD2dI^kSltcvWVm| zFQ8J%UOXPbOwaZRW>)7^GCCEfO6IOUJIcrZKlaWAzQ^+a|JQZj_jT`R8)F-08)k-K zGz`O>no~2UIV47_&|*F$qS}}xQIoS4lU2@1ixsk%kyMkZRZhual_aKA!~gw$U)L^V z`ucu#{Lbq>jY@Sqv7x7b`7UZ<nB1`Gpz_wq}|Ed8O0N`IFuECw~eV0z< z&37GqgMUutC!lz>8~^C~wo~~E1DD2lW`lp|zWJ~GyPNG!fdk~r0&!z0PW_=9R~-1i zQ+c!X0QgsbjcP+nQZHqJ-@s7q7aliM)7+h=ivP;<8s*~;R0q2sBnJLkf?KW&OnQ4m z8T7x~-)VoxcPHUOS+_e_XW(xA&5avRu=Egx)eEFnklzF0AH7@wz7A}ASlU$$?*_uZ z$k!m7V8>rr(L>amV8B24M(r|Sx5B?jc;X1ZR_n+C@KpqSgMaA0`LF!Do9z%%RWlAn zc+(;Np&PG;{s+U;aR>^vn~7(&w~rNAe)#IJ*-&)Ok-XaUU%Zl2ets3*wfy|bq#v__(;mv7`%SAXsKvm* zCcQx0!B)j|YAibQ?e3I$Tp8*&pwGc<+rH!G!vC-XFrfVCVF4BR&k?BKXOt26lt*xf zHBg1u;4mY%?0WxGEy{9nVmaGxl*_Yl#MdjRw}yAhIo6vVabU;$zoo`*oD={3UFz^~ z=51}NaOyZ@jsY1i>-x`(olK3^_&9MVsN{bFX;FiPwQ-eCJ-3_ zVQ!2>(jUGwas17%^}2Dp>pzS;`s`u+or(e5yN&Hh}x4n5paZ zZ?k-FnlvW-=T$Y~PQ{3e_vWgqDp8T)Hg>cDE6uUdA+w>w~9ESI}I zV=EsQoY?DYZrtzS0%g;ryzkxoI|lxDIt&~p%kRmkoXW~XV!Tn<@ z`!}ZNTQ+qncq-uLf`cDy!|fYrdv~Ah>o@I$-gYCGoR3^skA5=GuB;*RhTr$72Kk07 zr+Z4yD)#g{SRU4-JZ9U45PANmgx<73&LB8Z#$j}!#Ldt z1Eqdjn2O5a+Qs85e!G5MmO)$3yDEdSPKQ>U`n|l|D&19>>*Ef=GaoVxIPR+4uzs@t z&H8~b%l^$UoswAIbgQTY%b@Ho+riCYGVe0Xu4}G`ZnT3Pchz>HZ^~Qd;?UdMXuxf4 z2iv}H5X{ZDGyrVRH7oWTm~c}|L4OsrEeOdfr!;{60a3$J{-zLj>|dSCh6yKXvJ@=W z#ImD*UJrx^GRnK<9nwj8#qh44^|);JhTrY!RFt<2!*qd-jDII@yPyr<)J|@1!xf|h zebU=6tFz3TT|pUC5C*CNN-J8HTSZ~;GBJ%^<>L~RuX6=)SLDyn<@WNa$X{s6?fxnl z3!uKD|K0kcjTOi*m3@`)+taD@_v7;k!Z5OpRtdT#z@5h2>wg~qYo{8cDjGW~DEE|% zPSB1XzH?pGzAazWZ)zvN_bl(Gh6;%Js^~bNUgdH8tzz$V9Ay`eznb&A8fzQg-sUQ( zlU>)Z|MRg|RNru6{I>pCk-s3Shg;{#iu^%?ye+;vl=)5l!}+!_6_ue;-WFh8c5{F` zmH7?-w`L0cSCl7s^rdoE-OAWLE1X9w$P@Nh$ck@JLiDW#bcZzH4n+v@4}H9Xd>Igi zvjpll34d!o)ZQuxgK2OUB>P%LYk~^ePp6~{2c}moItPaXb6W-TM}_G?n=d~eII^3o z&7l>fQ$gN#UF&hvdtD_zEc&18y@LEi zC$JP|DcAz)V_UAuOM%`t{aE`+*!Vc zyt}o-E*Ni?<$Ois&5Ff$H{Q@3;H~EDlBKuL*%jpN*5iS4`};b&f;MKr)>jI=gcrM6 zVvw_OH*E}$r+oap+vB;dPXC^_3XW&jH7IMnUcbAt?gVWo#m+l{ZKt%NepXRBH@BVR zciDE5Sc+lgQyX1!m+9O*2EbbN_IanGF+Vi*_I_WHzn}`{nhNV1D!aVCVNrBjegDfa z6||8G!gOM;(K?+_7uVa;sl)!Z-twspLtJ^@0uyfzaEI|8(kS;Ilvq(3732$&fHPBE zR>1l$`tHiC6KnX-$5KI?y0sk!-98@O>3kvKZZxxi)-{ywf=h2G7lo>aj|J?0-?fK8+Vg<)D{S_yD&h+rG zTbFoLKe`@oI{$&^0B)7io4{6KjBpR)?@vbVPG!*Pr|@K z+;ZF%9YYF44A=H<@mE29c3p3HV-0hC$6dA4+twq0&rb#E+;$#&{9otoZ)=#_$IS}T zkztM`;GDl?@jG`luHBZmK;vJQ+wEa+{8guIcWR5boXfv}={P|D`#X-% z|Fs-O4Gb6H@T30&T^zyE;p%829d3^H(&6stFCCQ}W2D2wFF{cBJQ{53D9W@+Hq@$)IS~~n4 z{iUOpBT+g691iISbUZ5^L5`QCqqbw2bkuRImX5lP52Pd5@r88MbCgI&h~uJk)OTEy zj!=iQKjo)^ql$DibOcC8BS)BYggIJDM`K5{bTn}!N=H*ihIE8GUYCw$jt``xx#KhG zxX*D+IwBlDN=FMvnRK*tIM<@oTRD8BBhnEf9jzUa($U7zLps_z21!ShBT+iqIi^WR zd&g|)=-|kdj%de5>FDT~Ce!NV*d@Q|?D#@Dx;RcsM^}ee0A=HTM?hV2#5nxrH{BdP zq@%lIvUK!tOqY(Hjs?=u%kidkJmAQcj#$Tj>FDh^A{}v#i_+1@VFXeVeI4=A(a$kb zI{G^t(lNjhDIM{SS<*4kv0gd`ISQm>u%lQyhB$tdjt3nqh+-bGjCA}d9S=LI)TZExjsWR+#1SPOk2?BF$77C1rDK$1x^#?o zWJ<>vN3L`{?)XMJk{lPM;|Ygr9g2CZqlt8kbF`6;WJfpY81EP<9S+At>6qY1la7gw zWzzAaW0!PHavYJ4ryRdY$7BbSH8{o5Tsoe143&5&r8^DX-K2Xax(AUvB?B%@g)WDif+lQ*S1+gF z!-AnJG`@nErExyuN{tH;U)Q)0ah1kJh}jw!Bfg<=3F4a?mmonr)szcXn#1~qJZqS&8xKZOu#J4rRj<`u9KKB;-j>c@n%^Kf8d{^U}h&dYZ zZQ!BrXS4GhlmF>?nL}lV;_!;66jrjVu(4!g)5szs+hjfWAx*NC_Hhn~`izk?ZiT4OQd8I4~fp4IpbVu{9Y5r5El1o20WM-hM0cntBJ z#^Z=TYdnE?UL*e0W#}&&@pmReFKGN8@mG!b8zG?=HJ(Pir11=5sYd+yn9$1_@i#m| zf76IRkP-U3M*MM%&?_48wf~`iXgr4qr%kF$;_Jmj|I&yrH4ZJ)i0>T^y{Zx4V;g!+ zM|j7?$v` zLcPGht8I>}0iN8PtkC{&kIlFoxPgf|xi@0Y@_va32op|WOe?f9rr@E1orOEBm|brw zoJ(6GbSd1uF%c$8;~8XmI|ZiM9Ktul@Dt!UW82l50OJFsW{&g4;7+#jY!CQ-mT@q6 z!9S;Qi@_7zOiRf_o(;@8?uEg9dIqI*4bBf5!x0de;|5|l)(9RIHA9bKjI5yp?nD_1 zUmKwhf{t4U88ab+o{;U3h5<(C5cn4Vhv!`2t;L=C^?(N%AO-li!{4OlO~RYB2yfLK z?i0fY6p}#WTsf-?H#2UB|05q^tZu)Fj?rTun}9w_$8mNp7`_|OkDY*ff4tzkx-kQ~ zcY*&e0K;;E)$NotZX|k!4{RWM4Gi4k;sOr#C%+GWFauNn9RB9PUmBdWR&!J`9 z$EzNyYO~J1_i*z^X52R*0^x25CD{0^M_{|j3-K6;)@S6vtQTLFR zkUDk5ZyJ(5K$=XtkaRogS<*V4(5FXltkWgEUy88VC%v)EtHq&vYTW2Jn=K-Jn{*%P z_oUZIefyxFaMIqSNu-PV%;++3%6_p#X@q`rNzW=E2aBArCKlypDoc~aMY z=rg1r#^06PPk?%`x&3B9Y-{M7Uj5Neab47Ga_=HNOX@iQzaKMTzyORmSkN} zn{^w~c!(>T)fc}>q3^Rvza@2g5WjzubOY(fq~DO9A@vxFex4$Ip7c%94@iF?b$$r_ z^dg-^`WER1Qgawu14tuDA0>T;^fl5CNIxg_8jk)ilU7bZcT>_h(xs#yke(tfB@G#Y zKGR7Tldd7%NqUi#k3>J!Nb8e!CVhZ(3aHICj>K``Bhms=@i2Zf>|tz;(;vq6xsdd; zhqpfL%6=fXorvEzAZ-O|vmX<&j(#QOkIZ+)(M^tVIC}FSFBZTW!{}H}_W?NrS@7q@ zVt`!01yd7HUkK|0e(SPSpy6b5Vj*lLd_R^sK%L;;SPYk@Y#oU*6g@~jmjK~N`!Gul z@?xvV=E@Q&&UHi{@Dc`$Wh+oP{95Z0qFqGQ;Hf3FeM&z4SSH2!6%d1cNa$Tl4aZTK$M%JAU1C)xfY+dM^HCdM+KRRND)EDXp69#yJlV7YV# z3S*0zFWDXd$_EM{dXQ)tt4nc?0m^2t!drqcmT5pPKn;jyDQXJ2@L~&yvRG@ftsu%~ z(G<%TAZPX_>q53qfKCA2Pqq@EQ}EsWuHKnrc_Sv-Yn4dene(4f9zVPAQV zr4c<0R0i!ioyehR7SS{yi|4Z0a8HaEn@Qnv*^3nCa*FeP_7c%%pk%h4EhYMhY&+O; zq60+vY%|eW>w@LN_OKixCxNt=ZK2Zj5WlkO>;OAztc3cn1;n5?og`ahpk6?yC|oC? zaCVS+nOG|WfkJr^^M_mBz1V1?!>l&Zvp_EFTh_qDI2Qroue-J&TA`>F#quusJjvQp zxKDt3@sq3*(Q%+G2-l71B2Xwl!zNHHrj2p_z@8?XH;@a^G@|-Iq5KE`79C5keL-c>Z8XqP7FEbI_aE%<$og(lKjMN5hPQuG>8 zB`4`~B~hTFH;CFPT0=Bc(FQucu|O`6!aGE%M3>nKq6I*a{4)GjDpVPR9WPcH2Y3r> zEQfqLDLSCY$-??O29(R4xI58lpzYk5S0*Y2f>SphK(Uz4n7TW!PqvysSwLY#;bimV zkwjg9B6)Q_hG;n1YHya+U2lX!~AlFRbESU}T;}wj$?q&Pa2K zxC>Gke~qY`OQ=gPcMjL_0o&>~~YV=yidw*aL-_eJUc^ye(f1^ z(>HzSn*k82#B%_+e*vGI{Y5btUJZG_lmzc(yd@h2%Gna{EI74W=e-7W2kB>|-;(}9 zDypKj8fjh92-2>kgGon|P9&X6noarvs9--=T@N|GSoIo|hE;77xGPs%2I@~*zZ!nu zg0yF~a43iV)$pi?R?7h$T`d=MB3K1Wh3{?F+tFj-&UhTMes1L&Lj2E1>~ zw-M+gpz$dgV2w{%PP&$CC~P*-zm93MRI{fiGd`t| z^a$x0(o3X&ftr*D*uJQh1M$1p`W4RjE7!slC;BHsY`4X)Vy;&!5n^sidIWsNr<@_Z zMEVzny%8tob{W*dA6U$kad3;JzEv($&I1V=VkNuK4n6m`UA%H5>FQx z$^PD71k0~gA418n#o*@byIR=BN=dDNMsQr%CT-R)ptcpCGB^NBX>>pUw7_SWteXIpI?i}rwGDLoA>>Z>}Bg7ud=*BECH*Tv(VM7kQr88~Wk&j2-9 ze%)oDd%-<`{0vAzKLg0mfE4t@+0S*?fR@&64C)e`F5vAz!P_cK{g2=P!-riD#u9L< zhhc+BWekZS;gFxPArHa$Jtbr$XnM$Fpz}g-&R8BY9&}yEB+xA(Q$cr!q=0@Fg0a0@ zAN76GJkmX+2T6-TIs3jo9`8BQ%k?qOSLu7qReA4txBdw`KIMJVJkmX+2T6-TEp}fh zuB{V8G0w+9eHfOB55qFF*p$%S&`w?oJqIcf%0Mdj8coeJ1 zy&lwuZ6?hn-2vK+9d9rIa&n;ow$DG{8w<7~QGH26Nn19=e0FYl5=QU74Rav=kqy^_ z_G{D#X3U2gon-MTvl?OQas-}3?)jw4NV7=aBz=eUJ<<nl3pP-!_aDzx{-R3`jYyS)*)>`+KjXnX%uNR>HVZVNPCmUlO~XkCQTwuCY?Y! ziS%jG=SZiM&Ln+_bUx{8q;HU}C*4B&A!$D8KGM%g50Ms=9wR+XdXDs0(#xdRNv+0M z4sN7XNNbP=k%o}ACG9}kpL8^766pler%6*uXOX^4x`cE&=~mKw(tV_#k$y#bmh>X& zAEc}a=FNrFi?l9jE7BgM!$=<|olH8NG@W!lX(nkl=_b-`qDQ#kL0ht` z&FY&inO}3%`$%I+N0X+Jt^mE)r{YuY^{IP(>Rt*Hiv=PqEm#=D0rm zsQFHKtpu*$?tVUkck@QzoYjo97wIU_d%f>o@4MIg;#2OX_oYSP{#b6rAs34kM|=hP zeFW}_oda#j`gP*}#`^G1{jnt**9rUS9MUzU1*G4Tvd(C&McRgRAn8QXd88XjKOrq4 z6C>c(NpnaKlAb4Z>xrR4NxPDcBz=xF zlk|PkFG(+vdiBCkjY)fuK1Mo|G@Eoc=}A)l0QwIi?Laz=^cm7j(ru*Qkp4mH7mJ}< zk;aog2^ya=BNlfLN}5iCUndCY{WEC&-gwrtjpJ_c?B2Lnc5koX-g(Wv)!M(FFK+EA zx9_C=&+VagkHg)P|2;ctau4nQ=I)u?ue;M8n%o!qukWPYzK6DzcFg|&>mC0a_R#M2 zpnE;&|J}Xl|IQwCZ)S;4nMv18?#(R!uV172@9Y=<^ZR4>=Ae6X(7ibbuY_8x;@z-& z`(Q2Ey?wC%z58HtckA8?>fQ<}KIQ+9yIlS|S5SpP@Sp%o_q)%{8}2>(R+BH#Sxx@% z)M=?EZ@2<-U6U`6TWtm(jKv@BO7zp@4bMS^YVrkw*L|Q*mf;owPnv|*k+d4fl|?AZ zb!!7p((YAs$gLwhpIW5ov|9}GWRu~USonn}Uh38h?)iq7Y!ET`{_u=cSa3NF2I{YA zIP+$cH9ZRNanIB=7M^Iz(=>_IV5OR#fj3!)*OPHNxle*8rV?>7AopIZHVegvso@yH+);CXB>_<1I@J?TCe-V(M?(Jc2Yc>b&lKKe|x@)kS={g9@&;hCaw znmW=GW9YM>Qm*@ZtO+{dQy>mNc{X9G=WHI53uL zK+QCHRocy(YpMx!pQhH8K4$o|IDQI5M<9Go89xOgu2O%v;wC=@V%RP8M5Xs`*eX3) zsqiLxwo=Ru63>H;9?PaUU#%2z3+-U7bqcRmDq?Lky;*4o{fHNSQ6O@FqO@&yC0}?p zPJRl+Cy-uyO@|<79W))eg?=D)e6 zLpE8{ec1(~RQ8gG;QiPr>R1KroVy2)XX_<#Yy|^ZsUmCzgIG5Ho*dLBwt~SdlDb&| z$5t?eEm4H6;6YZZ2wTBW7EWDIwt|OPk|Jyc!&sUkYz4zurXp+w!&r_aY6ZjCUPagn zhOq-gvK0)+Y{92MU@N$pcCZoBM7AO}l6lhrP{3C}c|Oc)DZ)1KFbh+JZ6cAiRfKKg z5!PN2wuwhsccMjXqlY(tj17`Ds+G~KDh(fL8|^%epCQ z3N()8DZ&<+%!(zkD9=KmYeaHvO=cH}Wa~+0HVr?r?IyEkL#bE|oZGKi|;FA+knS9rGI<5`5F)t;?^Mk!hcIdiauiZ(%<4wj<` zd%y%%Otgn@g}h8)ZD}aPeD>rM*ifPZhIyID{?fL={7L3d!=$uLVkd|&Tnu}Pb)i8s zomKK0&L^`>MOD38^C>K!D3t|xd9$ZkT^djecs;KepzcKJ)HkQHA&MG;&#AgxT6!gN zNkK5jMSG3DiDH4Kkx#S@2YNh;JXO;wpy`@&fo5nr0FkgO|5}m(9{!Xwx$H27d1TzG)I$BWhS4isWQ+@n(9`W z!(Y~PA5ey-`+??Z8VvM`rg1>?HBARvplJ!vLQNZi7HQgBWj@h`H51ZLEMOY8-vU!TI9&*^5im>gz$F?cLw!4KDD#EtA zm7P|EZ8w+wsR-Nd`^>Ym%qO;pwOM($*ViY5~kcfs@u_?&7nV5{1-oc8lP7N=-wwHEv%Hi{^nttCoPw%yeV*+=X( zMV~?}AF=g{z5<`SSiYjO)!t*f*eOMqsvQKIb-zr{@c9zRUy+y3ZlF*_0X~KBi>BQa z;q1GcjZzfilgM|o6h$~*?`Bts_V5-yM|eKl6eCl>(R&a3LQw~w;d~D}p(%zHFu!in zX9u7Cd@qaB0 zMi8w~^oUOk*w!mbhNCTH`HH53&q8)clP}O2MX5gL`9XF?lP~5faicZ7vearF`;fm!5`%2NzK7aC~?3ALbKFm1AA|H?`T=fyg zah9UU)3-JMj%6$I_4Q^aS+SzJzP><_u`*mEpzqltMG@fh6w4%%D~MC9UvKn@qoOa^ zhHI*7oMulE;mV`7afam(6);6AcE$NRQ`_kctxN+IemiH`4AmZqtQ zU1Zx8VLmUhA|jd3OY8)Z%;zO`PWq&LUSeK-Wtm|#H8-(M_4Z@M3RC)xT z*v@Vcjy9LI4O`?5!rJ^z+pw%P(OL5E<+e*Oclrx#@E3CDq7m>=U*&C5$5bKwn`D^>@T)j5$3Fnxl{&9+AcK z6=AL|enb(bZt*jUFm=H%D8lnpn9+&k7P3-PPhNv>)ijvbq~i;MmGN7CbGW~zT%cN- z4gkqt%0-_ifC9Dccc36mZq?`T+M4PB)zQ=%sII1d)#vkIO^*Q8)ATe@h^BO)`n-^G zEm!HGTnxTZW}&=+rss`N-knIU%Ny`vif~=tkWWyA>+(i?mLgo2!y8W&;kvvr->eAN z+(x?=F0yB3yfS4!xRmuvC8Pd;}j*;SZnm;6BWHs;~k?H zU!v%(8o9;;)OsKmfvfabzFpaHl^)AWHSOoUd6S1Qy*)g)#!e%SM-Ub8&ua8%eR(fQ zE{ALEGy3vWMWJX;aYPW^bkBE#np*p4Xj@_8QUjG|SrI_%HO6!Ds0 z8~wT8FnR1AHIEqsc(@|}nl1PMeMGB08Q9Pt9$#q9BRrmWQ9f~%K9G-6)B$Fafqb>5 z{d^EVr>HB~2J^S$&jx`q=5AqB}@ijAzp*%-Xa?Mr7 zL%dkg?3!zhVZ2Py+M4ee!+B5wrcl5?shMje@Xm^G?LC6`*W}Aa^7V>v?foz}Mlkpk z@Z&Yl8i~BBB3wg1${Q#;Tk})nF&?4la!p@0ig!_jS3gGcI7Pz$G9SYyD8epJ|2-nGDd9RT&XShxt$M+G*IUt!o`7qjW&Vd~>{=BBE z#(18kNthG(c1@McCwU1`0mIh%6faYRHT@J1N~H7{*7RiFg(#JE_pfSB;hi6mHq6;n zK0*=ZY#M)#NX~!H@N7-K>>0jCQw{T3ULwPBY`fDrY=Oe3fa4lvI`>nAYmMnVT+twZ z^w~`kt%;`dG)c~*$QCgQW09#(=Up^~o6~ugrZ(pDyi`+X^F`imG=@uMtNmlmmw6G9 zjAb4#)im0i$CJmDr!di6#7}8@)?CI1Jzj2`Z7%0|niiSaJSeH$w$fa~Q-}&E*Bklz zv1m(WHEVrsY~*g^%6;x&Z}SG4HkzAw9FYvSneWlI_sq@wyr%7D4tGz+SftNfzEBd) z)46;zkvzU!o-c_-LF&2O8c(USc%b(=x?z+O@wGlSKj72JRseH<1iX)HrluY2Lq1DW z5!=aMB$6wZJU*WY)AMC{JWJDI^CP}p5__>0>=5uGqCJrM$L4OH;=t79wTV6a8WF}) z1mmXH1j-rtEZ|W@(&s)tj!34rk5AR~t@$yZt*M52fM*gdVvB2?Gz)o&rV{fDo;VRx zSj1L>t%zr7Dlxz0?oUeF`da7BV&0cXmgl#8h^A}iw|p*`;M0?s_J!wpXC0N(Wl&%{+>@&guBw;^OZz$SNas+tO$3dPw`?&kis?d z6u(SVzzb`|u+v;j!8noj^E2E_5$=4QegW1eD8f{F){> zOXjZ6Vk~Lwd_X7bckZvLyLE-XK$Olz;70RLzD!dDE92`l^|7w;U77}4+&HMHGK8~? zlbRyn9if*r^|71{*XJD+&mVU>-&jO$nBl5v^&X>237Y^q5u6NYs>U z`58}Xnr!(S%M^tK#;_n`ou-q#wy|AP5xiUUQ%%#YVB?sk*;a^gPSZTAf$^szY{_AU zdx|`U#(~SM#zwfJmVt$=i4mu$dm!9xU`*7s(rRKHAd=%!xN%O|4ppkb!wvUT2A_0j z6DzG|Mwq4v#(hTebZHv`akexvG_AB+8mAPEg481oYXcjGH<^JYDbPl)9DrKfTH78{$1DHQOsz`a&a zqpBjeAj^8d2vg)A^cm1ZMc5l-jT}WG;4{`Zp{QZdH8a+@s>zr2Hfp5H6q*KkvpA!U zq88vY&Ir@w%la5mL~@ksYs6@ZV114Ll2}ww47@isK~dMBLN>sdOeE`lfZ_Ihd6{{G zErLj{^#>Te6ybV)fRU&ON0b4^EJe6VA7ErF!c}^_v7KlS@8EOPiZ`w(TL+(0)*vHd z7N)m{cLm!JBbi99wuc(Ci3&Kbg@+o;HTklKjID}rEj-L9RD|o@;l?+LaD|&-oK}RZ z-x0c-$DOXlc-Xo@5*$ zn#Zz(s*5KKcu_Tc3ix_xA!Ch5O))^T6yZ+kIOBq%%~0>-jO4i#j;@D~Gcpw6eGub} z)i237--q^+Y?Lb68x$bM8<8(d+h;*7xWhK)-RB@*xK*GZ};|JC}|{79??AJsNG4V7>6a%y(+23F(TQP zQjH%p#jsSPjHrP2X{Q@i7g3xpxKBIX2q2olo`GYZVW0(Cfp`(t!fBeGul=<#(`Z0G zQ`w^0-thM4NEr*qGrn}AD-ou?gFkN!*R-3@GNve>xCilq@q(f+YWEVejirikN8&|e zjUwEUm}9)J2zMmr8Xqgd9f_BWuNC2r#LLDHif~6F!zhzP=XmpsE{pMq_VClSPx5)j zWJQ;N<{Rr3u{!<4LZd`cU>$F^#PDAt!$lFrDH=kQMpVGDl$ID-im;TH8ru~;4L+9{ zUQ02S0**Ti%ZxBd@J_^6=l3J+c)_83X8OP_6nSBSmF97UaiW7x;W21PxA z_8Wynd-$t9Z-`Hf=v6XYe6Tk=V8kiHvOZv-1xKRCg4c;pHQ^QF&oxa5eplR}bfAOU zwgBjmrfi_YM!HG?3_AA0s_=@q3A{>SPFwQB$bJ#x(|2Ht#auoj4h*N~4aG8;&2uI;-MxG)Z zg}GU(2uERVhQ29d!BLo-Ly6=l%+1M)a1=Jo1&VMKHq8=A3`b$pwBDi;TV8IgrjLCGf5GyTRqG)O}@<2%v6Mf96Ja!yutC{&&*X+vtA5Pz9hdc~rl zrV&74nv#K*$azYWqSj>$93G(|WjrV(ql|G~D@3!}t#hQFsff@C_wDqd*%l4XC ziddbG?R}>62P%a+G3LQhr{+FILx4UrL$^tvxbFGfJf~<_{h9XXrvG+n!+E99 z9IFWDm4jxsBAi#gFe7$IpE$1^GSd~|yi#OtQiSu$VKeAM=@aLbFHMIcS_PR;D#DTI zYjd(99ErX)rz*k`?5O#yA{@bvo2iO$RQ%3NQ-q`9_vZ78a8x{P&Q^q@;#qU9A{^O& zFf$b4$o`W#UlESGKbwmb;W+qYW>Uny4g+9TR_a}FMV<&v<2r@k|KAaH0cxO)Ppv+wo4n$PR>?R9;PnmRA=iI zq5_&Xovnq6upV5kC5o^f+^ts?VLfZMc6`otoIaQEPhrlk&MO9$|KstqeIbVe8k{Wz%Uj+tF9u9rH0joXb+D8pZ?ZE zM6&+;t?|+)$2RD1y{HJ=V1V_8B0TmWYo{VS_Bz(rityNjtxJmV*h8#JyC^TPBbX;b zt@?_v4K}noD#A7xW<96~+h7yxNk!NO!>yMUVH<32ts#=RjxAF$jD%5(Ojh_z-B70?XZ+j>zE&al0$?TT=Qjk7%VQk)!jqWV~oig3Q| zYt2%G^KC!NZ=dvu^KE}?j3S(G2UrUf;d~o!SszQEINuJm1}Vb%c94~)2Iqa!VV|x5KRrML6G%u;Ay;;8VbzVg7sAO45{RKWY_gT4j&6x_nyh zbA$bam80pfnQR4pCT%$1I;;#$+w3PTxXGJR!1;EHm8@yLJQ-3pbDKTg z%GI>WPPc*&mfOBHXImMHaK4>u!42S)LRkH6c7~O#h&PB~3#>v#IQuTNq7F%)Q83>w zva%H6?7P^qilhx^-z8R(qCl8oGpz%PaQ1!83Og));_SQJnxhD3-xXHGm(qr_?+PnX z5zf9@))qy$W46*dt_bJi*R85w$#6Iqud=cg;ar?;{iX=#;y0|IV(Am-;y0}fMK~9~ zWt}9FqsD6MXH79|wN)m4y3}n@$ktd@zouARnn5Y8v4RwJXwZVMwIUVuXmA;*6A_LR z7wmObg0#^%@wWA`BpMmswhk!5k>MTdb457vy<>erBy;wz_027GS`+MpS*0=@rI2F< zeIt(+=Z+k!t|Clfixr{>N7F4<14THR=2~Hja5VkEYDy$WqU}~IBAJ)%RwqqY?CsW2 z8O~)2lv18GQPFfLr93M|(TiaF$XcRkG1xw`UMIp*x?=CL@}!N9p}=bTEgpj$O$)3p zMDP@01Ljm40^TNap%8>zpP(r_Zd3M=*sn=P?c8sR?Td zk-X-8$SNjEh3B@t!B(PZZo@==$cj6P;ZoV#4U51QaZJ)Spd#xTMSFn`TN#Q@Lo8of z`HFrA+m}|6qU%6kS(g=6YUIs|t(wPWoHZL20YxbaX;jDQYipvS=8fL6j#vwbWGNlB za*6P0!<~*=#gd#88x^u+)@4ZyQ$J=opTHE-+1N&j{J0gSXi}qAPRFf0B3TkAEqIUu zJ_QV)jW}t!5v9VrZjl-iVR}9JNvo}uuG?dGSgWFmPt?-?uMtV{u~ zr=GEr&LgF>^Gz=UW&MJb#x4S#wT51hbQP$?8gx;TbNFTcgLQx?op}QNXr22*+NuKm zWQG4FDFEo4)ul{QBcPwHqN_;hEE?!M{KPyb>I(FWwM0=bpbKzMfwc7l`qi4OXgJVC zt3*)}&?ReuDSbW-RBBn4r0GDHt%-_e1N~;5QuGSY?^b{KRVd8MGN3C~zM|KG{;(o! zX;o4c(Ti^Kp2u98BOOo_OcWH1;pVXpM45_)5c$y!;Pcp%(xxawh6|x^ zbWPDmEFzL4n2%UaG=m*zo)78eEBdOrWmOlfK8Bmmes2DmOLY-Ult zqBJ%PsGi7eD{a{OLqu^W84i1Yec|6l688R35#Ci2_WlMU?|vB$dw)YwwTC3^{f$JL zBJA&BBD|-xVSjHdjwr(Z-bBpmC2iQ>n~JIrNW%UeF0zQy*kh2-W}<4Wv|;aWE(Z0M zguVYhaY_;P{s@sACvDjKTZl46*!x?Gr3ia}q!`p!+OYSx76%kz?{6cb z`$-%2{_(IRXxQW~2FwvHm@ zK}k3=bP_{{N}o6~bQT8`;mFWML_Z{LI5Knf+HfQqDpo7Pk!Yw0PLVdJ2(l@vLll-OZSAB@(I9D? zPBt3BhKgfEY3y=@HybKS6p0q!LJEE}WGn$KzH@moQD4kS&dkY!M)a zi$O%G?5P&#T@pmHvf=YUBgC_kU>zdN5n>Awj#@2he`(t_^HJfPhN+{^ z=Z(iiG!eAq7MEQ{iDE^ITU>P+Eu3FKpJ^<+g*O``yk<+<(86>bBi6qtX)E}ATqMtt z^a)UsfS*IBaKE;2b$vnoX$zBT1f- zU4Tk!j1Hqx8T6j_RPNA`7{DF%HdZO0-90Ywx`a%w%&^?BiUL{g>J-fWi0 zJu0bI>z=N&MA~trbk?+W2iF%w_IJ`2Llj;j>B-ik!FEc~T(Hd+MZZYfN}{xjGM4v< zB1@%hU+eMUv)3PzzJ$EID5BuKW7u}jx1Q`eN8~BuZJq^6v81hGo0+b2#Q`CyLz@63 z_?2o5_b|{)qSQ&!k~S~8zATbmByDQb1*nUwq~bR7Tr%s840#c zUXr}qE_R(Kk}FGU+x9h}S>BR*g6$PiT2<1hw%I@nd?cl{T?;hGSJG>3HvxJ1N!r|Y z3x=yM>GQVRfzAa;I@@*^P9p+_wXGvM@F!gg?B)!!RkKsgDNgog` zh>>)#oi~GAcbD`-yYb)?ZgHi0zYZzP7a59t+jju!(ns1Fws&;{TYpJWU|S$g43ade zePy==B4;pCI-A?x&uyXbdQjTl0-uY7HB?d#5#01iK8xE2x-Al!36g$kUk@leQ4;S^ z&uy_dHA+&o4sdsv$QUE371)*t=f@@W0NYY=jtK5g?$E(?sel_F>4=^qigrkvL6kUA z(k!B?lO(-Nlrcrpaw5NHB)v`4B}LL!qRFX}iaUh4Efv|*CH+jcLb{1Bja?(knkj7_ z(U`NL>5_to*z=N_6FJY8)INF(_`INKZ%`s%Dx&8~+Y`~vA-&bFNSYAc+HILwut3sF z(UGpNimXMF7DJqwB5aAIH;LG*lHQH>W|<-@Q_{E5(QcVy&12voig2ZqDYh%Zl}@I(pa@qwnId?djAeKyq(O?N5zSGwlqgrx7NQbGhlu>v z%UCWD^-@%+Gsc;ss3B2~q8OqQMTtcI8)Pi0L~)9i6QwEIMwF|lnCOI}%S6r_Wh_;? zVCvzDni3@{>P56f(P*N5iqeV76s;l(e_O_xN0g}OC{c!@KZ)`c`F6$BuPBNj3fd%N z=|j{_(O9BnMK2O%D_TudtY{CB`#Un0lSI*qt`kjEAQnZ06OVNI!d_`x8t|+o%FukyMWt_E%5)`#3TA=7bqJ4@c6O}1iNEDtUW7$lU zsOWQ|C5p}w9Z=-b4O54kBB?)w5Jf2JOf*VS0#T--XNU?FEhDnF$XK=#MJg&HN>cPI z(E>#t-7)p;iW(7JP}Gemc&m)_5u!ngrW4Ikw1Oy4(RQLzMPCzz=E_)pBO0p6y9cJ8 zp(vavU(o|ZR}_sQ3VUD1@;p(3qHLlJMIRC6D>_DWMbTeGVIRm?{CZ;Q35r?}El|{# zXrH2SL}iNR5QT4(v8*9VR8&B;MA7#|2NbbhRMXpKxLQOJirNs3QZ$e#Q_)1CLPhh5 zt|;0}6t+Xgd5|bU(FLLfiaZ~nnpV_==z^jLi2Od3u_O`oQuHEGilVhdIf_0eI-;nA z$ZMyJ#VHok>!K)xXtJWNMC%nL5*10Jv(`+p=@X0v&ke7c&_0P4u~%YK-Ch%?G|h2a zDd4tQ@VSV+9y{M{mGIZJ%q?3?Ry%Zgd{n~AdSVB|)_jI4LOJ1=>z$ z*X4Jk?NEewqn+nFHN}RUbj#C(cTepW1r!UGgg2a<7Zas9hxfkdmM<z6b&UxDa3I3JU{ljTY<bK>@`cDz zKJkq&Ux-hL9~aJTmtQK|@cdoPH@-=$C7?fq4FU6F*ly%$A_BHZo0BrYhz{oPWL z@Q3t?`@5G#p(5Ph{Y^ywDQ&pF`@7hr2zPC-h~U4Z4R>w-5K9!{uI-r44s&uZpN^l5n5)nz(QsNnW$LE)FmgD4pTXEVHAyB;1+h_9jKRGi%tv zhP2_%tZ6S$ggdjA?QBXL?#v2%nj+kpwe53?aA(%Z_7l=4?#w#dezqjsDRr^WIZ47D zR#&^!8A)EVakKqhCE;GIyPe=B33o~>*=3%RaHrJ6-sdF=cS=3&;%Z3g40lSs>~20t z^8BH)U8o4pA1d2%zS4&050&j~MR@*D*$(oPHavf*Y-cLM^9Qh1mo_|qfN+v%?x<|n zpxdlv->hucA(Gc@D%;%@;WeA8b{dhqW>eK(O(d_`RI_(WqH8wQY*rJ~OJ#V?#@DV+ zB(K@{+6@)qH5*^MEfJ;;?@_XQX@d7C*$-)gcO}_lHSOj#>{&#z-`2Dz*2eT?zpZI! zG(eKqY--xMbPKlZxBhlmBebP4?6U9$~w~GI>gRVguS@F zozOx0#9ka~yVLF2=?r^u13Om{_Tq+imyXgW_Tol%A(6ah6J~emByHHI8{4z!*6nnL zeY%NVwTrZ2pKfYrDZ)M-Zimrr-S7@fpk{WKBD`kP+^%}R47UsDK6{oT?EMjTnIi1{ zE$rkN=@WZ@OWVJjB<%gI?0t%`_ea{1bmO?ZV%yq2r3kOtw6O=#ZRF_;$AY%@&<8LU zdCexu-qc$XUbAUu&xw;M;KnafLkBy#kF?>)5N)4Qgd;;oyFcB2p3ZP&=w$Cx zgd;;|JBn^bPiHtXbg|bf!jYk?UEE*Bi6g`PcJTm7I3~x~-QtntHJfgB{$NRX&8EA3 zV2JdI*KB&&-RMU3G=}3uPkYXAX~Pkvm%V^)TbCos1NQb2(uUV;V(pZXl5iyIZLfb= z5?-^3v;FA?cWCoKee9@5(3ZyVnoVE3>Z6iyBI1&xC^AzDoG|-Nk zC~dft0X9XrlQGcl@}#ukP6pT%;Z6qFCXtOsuz~h(MDm)=K$|^`yNXH@R3Jo%OD$2Vq*6tTmMB*8m0DtHC6-pA*b=3Rm0GmaFZ4?*X=(fW ze4caf%$-^Am;U^=zgJ%`jeF)i&-ruDeeQXmbMATWy_jkA(yTEaHYNIH9ya>4gptTq z@v!l{NF0~0IM*7VDx}p_JZk(^A?@EQ62^ss^g-u3BRr~)PQOgjaF1z;ewiMl^*vgm zU#8dC`(7>4FVkzx-=Zb@Wm3jwm>6N9u5X^Ebg$BZ){))GB28;z~MsU>=1vPO8X zmgtG;H}cPGiJq7N5E#Tw*}Hqv_x;q2aRh#)e^le+l;|WTB5gQo6-6Y zTB5gQyK%{s=pWl|Y`v`0=zH02?7pHUdRw*|o&TsMdRw*|`Kwx@w`GTM`Jc2zZ_5s2 z>(8}BZ_5s&^_rIGZP{U5HzoR4b{Osds?+FS*@ZILhnDDHL7n&)Za9X} zzp}%aS)nERG!ZNc#ZTB5fFEi)y0ThQvDPNTPFhmmd261^rnjCGA#qSs`H5sqkyUXvZh zw#ize*97M`MN9O=JY!T&(-J)~&lq!W)e=1_<x|i5`__jJ`MPv^(Fi#`BDE`3^17 zqk^Z9->M~gRCXG(?xLhUD$g1_-=@>(QTdRud6t&wQTdROpQCfqqw*nR@4Z^0N99At zwRu{i4`r9J^zFJF{Up1LweQdp{Up1LZ40zSKgllR&_XTIPqNEc-Kr(}Np=}CmuQK8 zl3m7;2em{$$u8rahPC9XBb4K>O-^^&}%Yow3-sVCO9KgqSpjxWJ>g!;EYU(UK5jo0M6U_X$du?c!5Ntny(Ty#Q=-=dy=SYggNZ^`y(S>l zl;|}9sis7)2}m_1dQCv8DbZ^(ZcN{g6pm&xyKtf{^hq=j`w5#UV*mkrSuU+|HB?* zzA2qvp7A_yY&IqOAAZ|7CX)6)>^J^Vq!TFjAGI$S_0L;%2Gjwgu|&d$<<(oetk9|6 zR)}l8rMXA+{8qkGt(5q-3GwGduS2Ff)B(x!)=7;2zGxUa)seDzh5ELKd5%h|jvtde z)Hu}FCWI$MqeFd^8FCbz^OB_MHhY>_gO0zioAHlI_%(!1rCZCm!;F7Jr%pJcy$z=% z&#Mx?R>*Iimp!Mh?FW+QAA&qeS0ls!o}*lm<7x}&@L!;Ds_O_VR7IRE2})>hbJ(WQ zaprd@9smD!zIUQdr9;Bj|L>hIMv{uPO9y*++P^t!k3Ukh-k zL^DI(+W)!XSvi;QN%%kaEOg6tpVy;}j(;r5Yoz1VD;d|Qa>(`^DbrhXPr+!cJ z+iTEe?XA)+*Y)eT&R@(~TEEU?Pt|toz?F3>TCYO&hS}PQ8a*QYNLuw13I9>TiC0Ir z|CbM|rT$;Op8x(D^c8s{A?Ij^(q#QB&ey5%4{t)F+pI0%7L8`jPMoGpU(GjiES+Z} zU(`6&G&zbUOZSR9Me|k(|M#tImDV@OIIoXu=jdL^$u>Kcj^7WB71w8{_4adFD0%Gl z=(z5y44tYCc`DTOHz`%AI^-zq=T5as^p7H}#P_Te{>|r2k516P2O6h2 zvUM+He2e5^{gsOKyQF@HvZu0secVz#>o3)R0GbMEt-Urw&dVyR^AzjE5mBl%wOF&2 zs{2ooXl*M!N;}p5!ien@qAP^)N|W(trTz~~_!>r0r_vf;9jCd@{FtN~vML)bgiiIO z#O({f!w?zM)Lg-ZAm-tU5{8xldRg627cNNb| z>vfz*aVovO`o@%ET<2kGg_)`~%wuVOnX>j{-Hui{)l7s|&2JNpZq>w8-4g06q*P@| z3n%J%EQc!A<4~->R8wB3Wu;)Q1E zt3$hG*sH;m?Yd}9@mws6mFpd9q1doZ&h)g@uvFsh1&u>ZjI%vX^G-^)P0s@QTI;^5 zTe#Jx*LNkKv}t}%;$J}M zR64F}m{^b2yd?SU^&b}vLyXK4*Y)c=Q$0WGxV~T0;SVLJUWe=1^Nr7pIw$SyR6mw_ zbX;3o-wRVyq5i&*>QMT+=xf9)Vy%$%bgGrE4XIEsEF}F zhfbw6x+QupDAH%mS-;E`)OT&V&R?dU|LU^3g`2^GQzh@EeV&%M?!(1+h1vmqrD9Hp z8kg`_rG9y9@cRB|`nK->dZhZOTq%xw73SDJQL{wug7v7yQr0-B5jny*?xRQFPm=vD%|| zAbE64bF(@p&QN;9;9QBau)k#!-CBG2Mn)=op8smn|10X(*7zyxP$4_IdbRA5eqyMf zL0_f*8DZ%xY@dT6kjW8#%`}z6Q|iv45B2>bQLs z$7B8H$FyIKY&RQ)wEq9@$fnmP|DEHTzP{|4H|nbsN7S43SY5--Qg5c$L~wr8V}7MM zW7@}}o8|GZ;p`Sc=umGYK1K4s8DWK*gRoL9MCejo2=Q8l>@rOZ-z}-d(5c=JO@;cf z=rv!Tu+^aNyAMj9FN*#TC45=JJ7le^?-su=nqP%CvjDB?R8km9p*y zer4O6S?|9`ul>Jzyn3u~bu60no;=sO4)e~A_j*pHHTu3!v(PVPe_KMmBlN1oCx$xD zMEyh!SCZBaiH_TA)^WRLVhwhU&RO)Eqi5dF`gIE>LS36Z^!G3|CZTSRwjmjDiq}u?4OXfbBoB8NEmFQB8l6+e?WyOG>QsMc%R@~? ziH6q*`|8kFir!@&Mb}x(Zymi{?@<3N_3M7Z{FbNEe#ffwFm%ejZAHnwt)3U&)yuN0 zQdXNQ%$EF`JS)Q845!j@J$LB444!qRcqLq>S3>W;>M>mJjq8y4OP_xEA&zB@Jj`FA z+-XZ=^>+K*@vCv^jf_9P8sXoFmHz+DV;|vl5C7GuezQ^ijg04d4A)k@*+~9>Yosk5 zA*)oKa;qr*>QpU$Yw_z>tty}v;BP7ZTGcJ8Pff*Nv)YWmJ~dsz~3Sjf@Xn=p`La%)6u0`9o=dv{#qSz{KoOS68cs6U9HyQuhsD|{vN^KTKqkV zzjxv5n1(F{D3+^nU33@jC$B0Dg1$&EaVV? z)w5gTMFT?`gP>pH^?e-yP~1$j{>MRp;Hc|KxZLVI%&8Ddjc|#&A0hv3B8Jt<3Q-F2sPc&>iEwINQZ4LyQ{9S+ zT-RIb)*%1Ab;*iPbZxK8NZAz?-|zZ*-QyMi-1X;mPb$^jsKzRS-QTJkt9V=Y*}5ky ze!cq#Qub#R+mXK!HUE0|&+FExRo!1wNtNopUiX~npRXA2Zk+T&#TUD8pF|DY@Wbx! ztH%|;Tm3k`P|f(y?|jC^maLrgxU_IgdE=Q$K6S}6HfanR{0pKg8vnqg7bJB>#j8_4 zhWNbr??`wMIlo=^CB+&pR;;LaAU?&(oQ>);6|3WaIB7*iDt^CXMa4k;w4#2ATRtEE4k3>cK*&~&sqOK*>RJE2_0V*;A^dQhse9vp<9JEzvt4~X{u}krAzV}c zyp(-jU5>vS@qb15YDMG97wT!ZXIw9+nJan31uMT^_gclKm0zuYvErGPC+Z)s7+?A4 zb=9gb%9ekkYpP?7`r67@<+wks=eb<2|4fDFp+~(x5lhA@c#aGw<6J)R&m!opaDpgfo$vPdCpGKTlWD<`4omJ2HSD;n9{j;T%uX57rkNbU< z!PTEc=y{0oxzLO${(H7#YT4>ZjxqJn>StVQ&`*rO8r8qr6PQ*x^Ua|E&-Bi~^JveU zKvMnA>hGifuv8LPJQc{OKY?a;vJ&dg$SgAmsI} zc0Bs<3;0I>c)nL8d>!GA%4)~p!-s+n$8!&VAs9qG$AjA|zxeR0!D`1JJ>00C!*LG< zpR2r#_!af~!&ib=RQM4i6qTG;)bvMg4`m#)9(ijhgPeDVuBZndd3(qssSyXuMjWrB z>_(PAhha!&bBQtAPI9X@J@swzGLR0Pd$Rnen7vzYakt4oPdGwL@gkFSZYv?7> zzig&ndF0v9XDa^#$9ko5+S+l1v(`S3vWwT=j(Epf>igDyA~aoE@+wLN)N2xMuiU%# zd}zDm|Dn`&zqIoeiT}h5*|wQd_72BiuH`q=+5XqH9V(-bE>Tx1=RSH`Ib1wzhb#H$ z`OsX+|GMO?hW=2n+V$k4yBb;?AH4k|2%o$CqYc%L=N>)W;6v))Hms53u5s`j*Eo0< zYaH*b`$Q-rWg{*ffAUe)xW@6tM|%-|_0bH%S0sK0@ubv{bo}7arx3sL==R2GQg#}? zlC2PDtL_lZ9io|y__rF@$hBDGWS%jpVa#F0jqsRb^*bkp=gYa=FJ)U?d83SID`na9w%i#qbIjui6@<9u2ok z&h65M?T(+m^An-%j(>UQu7-B$^UsO?IkC)h4%YuXG)DLZX~_%H&KDf4{{@GUxIOfu zvS4uQKgy$pG4$cv;N~u>JZ%$NAepTvu)p1`! zHS&B91z&ZvCLXVO)zOLiUv;dN_+yB_CaJH~==gx>_elJch-Vz$_yd!FA~}B|Ie#K`UJ=a|#}|<2isMg^|BB-;5w^$`Y;pdW z?G!s_aE+dw92a}eMk=l>^e;|+U21sU!JIsIkS^Rgoa|k+!=a$hxnN!B7N3(nEh3tT z)I3e%PfGoFNc;|G&pOpujX1^=CoQ&Cj#w@EdBj7(G1uU_mv0$!y$3nRTpwV5$L@8{ zxVB3w+YnIOT|DA;*JszA$CqMHuM0-EyS}?_a`bu0^E_&phPaVbjoV#*33+|5I9if3 zqA59IiyXbh$t%_3d^EWw9FrFMaJ~Vo(>P`#>^>BYIs1|oHQOs6PuA45iXNfp-;@0F z=x0#sTa7Q`xIc@&Bu9TqNO?(m?90&H9b!1%^opdu;$pvg#q}Mu>J`_I5WWiicbZ;9 zXoT65e-^zTW$UN>(DlmHHB;6&@9TMV$`z#cPRUBkd5&AB#2o8;J~kyUnwa!thrwgD zAoWl%Dd&=Oj=%F`Q!zNd;M$K3W7~k$WKGh-id8OiKJr|~O7QmRMuJwEg|IV%-|HkDsqo#KyX24r|CpAwq z-rPGC-!_?p@WqPdy?;@Cht%_;bK2VJ&9jZq_1@XsVsPCOGp_Z{YK|HI*83&(qH{{> zp5_&XH~wqQNuxcru=z!2GS!RtYRjsz4^_RZv0!!_aKZ`I9zW0d!)=i zy@IvHD|qzP6|8e@1?yZ_!8)^|9~6CF^rND$tYm&SK68t+sAPU`rJO}2>kL=2ohixF zCwY!acuevfmpmsV&&f(2{Z!=xRc>{pKYB zUEf96>G}b}xa$JK)vli)TROBNm@A3!xNAMa6RwR2Pr3#Xo^p*KJnecH!ZWV-Aw28aitwCk zJHqp>4PHl`w6YfMMD&bR|%%D4+*pK&+BtT7Mapz-Sn z^TtAiqs9`1n~ipaTZ|P5w;B&2+-5w2aHp{j;cg?1aF6jA!o5Zg;XWgeaKABz@PP4N zga?hM5gszOAv|n6i|~l?VT4DG-$HoI*oW}A@dCmV#-|XTG=2}^DdRB0)5ez&UN*?o zRf9BLGf2~QgEXlsl2TbkQruM}#ZyI6yj3J6SVdC8RU{=^MN*opNXqmok}{);q|B@$ zDYL3b%G@fFGOvoH%&#IT3#v#;YZXaZT18UYt4K;`6-kL#k(AX{BxP+CNm*A#Qc_hU zrLT&lWUENZU=>NpSCN#_Dw49fill6*A}L#|NXoVn15dH%SS*NlMgBQkvZ)WxAWB%y5&GnQoFY%S}?|x=G4BH%XcACMgTtB&F3&QkJ?& zO1qn+bh=4O+)YweyGhDgH%VFNCMhX5N$GQwl&qVi47y1Q{#7FMrBOFY+3Y4MTihgN ztDB^3bCZ;vZj!RwO;Yx_Ny=U~N!jNnDf`_d<$#-{9CVYELvE6C*iBN7xJk-UH%U3> zCMn0=B;|ygq?~kr)g)zIHAzWTla#({ zl9H_^DTCD{C0|WaMypB6=4z6%rJAH{ttKhks!7VuYLc?MnxyQhCMkQXNy@%zlCr;= zq#URwDF>@b%AsnKa=4nL9H}NLN2^K7v1*cXyqct(s3s{Vt4YeKYLargnxvemCMjpD zNy@oul5)P9q+F;bDHp3r%B53D=O6Xbnket|2MYYe>qB8j>=zhNR4@At`ffNXon#k}|)Bq%5c*DXldmWoZpb zX|EwEoi!vSUPDq=*N~L8H6&$S4M|DWkd(d}l9H_qe8j^ClhNPUSAt`5TNXoeyl5)O=q+F;WDHm%<%B32Ta=C`2 zT&*D~*J?=0^%|0*YDr3EElF|Lk`zxZN%7W_lwd7M3D=U8Xe~);t|ckcYe~wwT9T5g zB`JNiBqdu*QU+^DO1_q)jMkEr&9x+DOD##+T1!&4)smE*wIpSCElJr^OH%gMl9YY5 zBxQdsNjXqUQV!OVltZ;7M{wIt$N0Bc}Pm7horbYB*o() zDP9jr33^CM*h5mH9+J}RAt}>6BxQz&q|Ee?lvy5==6OiUd=E)k;2|lk9+I-u zLsHs3B&E|sQsN$xvf4vZ)_O?FIuA)nc}Pm1hoodZBxTS;Qt}>>GU_2In>{3Di-)9a z^^lZp9+I-tLsE8oNXi}$N!jZmDf>JmWxt1{9Pp5ogC3G{$U{;Ndq~O=4@o)dAt}c^ zB;~k=q@3`Ol#?Eka>_$ePJ2ko84pQ0>me!UJS643hooHakd%ual5)vIQZ9Q)%2f|Z zx#l4$*F7Xf)sd9SI+Eh9BPpIblH#o+DZx6D60RdD(K?dSTt`x-*O8PNbtGkG9Z8v0 zM^fh2k(7CLBxQabNm)=wQd;Xs%F;TL(q2bWI_pSEypE)-t|KXH>qyGFI+BvABPo4# zBqdu%QU>ctO1_SyjMkBqzIxKJrJlTOttT(rM6dDK|dh&9tp1d5dCod=J$;-)l@^Y%4yqvBlFK6n>%h`JJ za;~1doUbP@7wXB&#d`8`sh+%Ct|u>7>&eTtdh&9;p1de8d8zc07q^$Zc)aAr>m@Hi zFL?=j$xGBrUYfn+WxAKV%LoANyyWG& zm%Jz+d8zb~7q^eRczoo=>mx5gA9)G;$V=2mUYdR6Wx9{N%LV%Fd?e+%kEAF+NvZUc6t|zGc>E;A>nABeKS>GuNlMgDQkwlF zWxAiF%KS`PICn*d3B&F3)QkMEjO1qz=boxn3+)q+g`$@`L zKS^2VCn+gEN$K;Gl&qhm4Ejk*-cM3S{Ul|xpQLQ@la#G~lCsTDQg-@D%5FbN+2bcE zd;KJ3pP!`c_mh+Zev)#~Pf`x~Ny=eANjc&tDM$Sz<(QwO9QTuy6Mm9%(oa%O`AN!Y zKS?>`Cn;zBB;}l+q@4GYlnZ{6a?wvxF8N8yWj{%|>L)4J{3PYNpQNY&NvRBw6nB86 zcmgEF8z3pc07(f4NJ=z7QknxKWqN?5%m|Q_nE{eAD?n1_21v@h07;o2ASnw1B&9V# zQkDitN_&8$bOuODJU~)b2T01=07+RFAStN;N$Cralx%>c3kAwVuJ2FS&w0J*pvAQx8y&LFwi9V8cfg5+XvkX-Bwl8gO8a&aI?E)E9C#i1a%I2_c zxtJFs7xP2pVnK*pw1&vV(h#|550Q(`5V?ql$i?aqxmX(_7wbagA{8PReIas@4Uvn% z5V^>Q$i--gTx<@Ji!C8?u{A_4wuQ*W&JelS9U>QdLgZp^h+OOok&FEya&aI;E)Iss z#i04dh}$1G#8zAQwv;$VGbtx#(;l7x4yi zvATg=tZg6{>l(;Ks)1beHIR#J1GyM%AQ$-taxvOKE;cuii!BZ0Vrv7r*w#QUb~ccU z-3{boPXoEw+dwY%HIR$_4dmiL1GzZZKrRk7kc-0&l(>Ls*zmuHIj>LBe@uC zBp3NcaxvOSE;cuki!F`hVrwJ0*w#ocb~ciW-Hqg8Pb0b5+ej|%!zB6($#bVRDfTlZ(MHxyXmf#b}sZYz~u)En#x8HB2tHg~`Rv zFuB+rCKr3c!sOy;m|PqSlZ)eFa&aO| zE>4EY#i=m4I2|SzXTs#-Y?xe}3zLiUVRCUHOfD{l$;G8GxwsrA7gxjN;#!zoTo02A z6(JXu5pv;muYL6(JXW5pt1@kc+_xxyVPz z#b|_FY>tqNEfI3DH9{`7Maad@2)WoDAs2fh1?s#iw z$i<}yxwsr57grhQ_nOt}$lZ)VFauJ?PE~1mkMe}5G zF?}+*m@%1L%$!UvD&Z+d4N-F8iINL%lw1U((xoB=87t@=_#f&C$F|&zW%xWSRbDPM;ye4unzlmHdXd)M_P2^%}6S-(_A{U)a z<^@i?vPUVqFutNHvj*z9w>!Z6X(gP2?iqL@q{~$i?O+a}etwdz;9`z9w?9zlmHNXd)K}o5;nXCUSAOiCi3MA{R%S$i=ZHa&f$g zT%2ek7bly@#i=H8ak`0IoM|E#XPd~yxh8UPzKL91Xd)LEo5;nbCUSAPiCkQ5A{W=1 z$i?+0a-pV>i^?hF!aaptc&3mG@03qf--Yjr_Nq_c#@~(Vu>3xvJ}$qtDj>hNnZIvQ z5s~j!OXc_1O#LGB_d)ZwTRnB#0*6vx#2y0 zEw^j=_}lp#@$R3`-yZMr2l)Fr@A+2#zS(ykU$0^MoVt(HeZ1~db)T#Ia^2VJeq866 zR5z(<(px9pH);N)1(RAQEuFM{(j$|4Ck;%>PkP^^XC{qL`uwCXO?r9KHz)mI(w+6c zUf)%ptbeRNSHG$L>H2N;&(@FE@2&q-{h|67>yOr-s(-Ei`}G&<|GEC(>MOj}-bvn| zcfa>j-V5FU<&JRNuRNPx-d{KIHp|@8iA$zR&vp!1t%V*L>%FKlc5rufkvB z5BZz?xB2h%&-TCF|A2q7zr(-ApYW&s8~r2xcln?6f5d;#|9Srx{a^8)@PE^P#{XUa z-}x{5fA0S`|1E+00}BF60?Pv{1CItC3ycNc7uX&c4}3K6nZV}*UkH3H@QuJX0~Z6Y z2OPngU@$l(I3qYa_-n!T;Hu!dU|(<~_*C%I!9NZDRqzMFi@|HbP-trCflzlS6Z-wo zk3$s=O$~Q8{CdN(hBXZv8$QwS<%UxY-);Ec4MyWVjSn^sHtuTN-}u?aFE*ZNJl7Zv zza_jR+!G!M?+zabepup{D++#Z=7Ss3YxBqPI-_e4G#`D)~wk-v}BO}=CD;>kUe zhbQlw{ME_lCSRFcc}w(``)-Ndvig<{w~XDg?UqBgoV?|`xBT>$*KfHm+8G^;zAL&t z`qAhYqTi1GZS?2S=B9Tvt!^4_`e@TohzMk1nd7_3cN`{w)djOZdCK zZS3n3{;_W_;%9vO5dKKQD-yZ_{DB7;oBu_5A1<{jfB6r{x@FF&06xC z5w_uXB>aMeUz6}V5w_t+68_>gypbAid~^dAt-`xnZoE%bi+8gEcpJAt8LAQ52(**& zock@XcoR~mC=YC22b)h)cPcNQRrRU6a6R6JQoo^sY8l=}SdNk%Dy%wD@*$L5gSQ-# zD7_J-A6K`j5j>?oh8o_DC-pa@miMW*;HmwY>iwwcS=6NPwc(c#-e3FOvT%N5t<%!$ zJQH*Nypd(?p)NH)dJSd29A)anP#^2g+lKJ(I6iRO6A1fnl>gyiqNUnI~t++3VM)R{V#-0^_U|9%^#vaVQxm~RAY`?5dSKAmRJ1| zLZ^D((FDzL^f51empW6F8?Kep1~E{VNEas?K>EG~d9J zn_l&22r(@>--P&|!^W8P5js`Sc{?=UR5PIY3xp1J-gyV~KR}2zfza?OLZ|Xpyak$X z@i}hwZG;Zxt9UCkXVAaB>N^M>YHG#XkaG$`xB5#p3;Mr8=)kjjv!TBYA=VaZ4)m`f zbf`C1+ztJk5W3ZOah<*DdkCHCj*5GrIj8Q0=C2Vt)TWC2pdUf#R^P{)LSFR)gbuZ} z;(q9#Mu?RP-X8L*A0l+9@rqxA=5G|9yjDL#=ulTHejPdg4??VF@Q#sJ{VhVgH(K!k z^#6p=t$vKRYrN|35IWSgiWca9j?k+<lKR--{ok9em6pg`j?6}Xr6N{ zhUUWvovOKV3G~wtdet7sQlyR}bgEk`W6L0SGf$S0|>o%?s7Tw`w%+RQ9u0azp9-}$^fUy+9}K@79-sWlTkeYX zPkCWV>P!v|r-$xJ zWwWYfdAz-=ZL~K%n9t+}RB|An9n#9q({5D z8_TE7=%T*lkS)4mBt4Q=@!ZDrKzAO8?N_nZbkE3!?vb9ya6t3$DXLVAWxIQm0|V)y zl1w(elDO1VJl&!$)?w4;`ulSOZ0aH_W}SFJu_}}ATbfCw(gV`O;bPYE6su@VGU_uj zbK5jlE`0z=uv9U99Bl<%MCW?LdaI_znQXc}Gmu`K8|qKy^RRzwdVO*vo4-NANdF)# zU|qZR^k{mh8&*!I2RMGJI!q}n{t`WLq%g|7AY~tv1SEhe3 zs}^SR-RZnJ-fc<`co+rS3U%31Z?5bf z863k&Z*8JuhsGYET|H6&h`x#4@xjxglL_gsCXhUnWc4%hOMA z04NqR6@@}p@9qTYnO;C%NYRH~MrzA+(zyz{_z-$ULFyhG$YVmu^xmw(LQY-(q8#QM z&M8C6eu7Jr11ak4?YDH&Bsn8XBMmCiOgOF5)Cr9&NtQDz(df2{%DU8AWu1)}yVx-4 zg*F3ncpKIO%TS3$rCf;xi<%*`K%r9W;a)ND3iWY6Fb8B!GWs}3h00Gl$UQS4lk>(g!zQWbUJ}?Fzg&&-$5aouJm(D(fGH^|P5C)jfv6uU{3Xnhw2ki!I9L2GX3dl}z(G z-(8DrE6G@4<|uZgWmukU>y4k?x2U?<6y3CEG4Tf1OCblm~)Z&l>d8{Eb!-KhD zIrv?A8scg-rI$gNUAW5SCDCn7SSjXJOQ=4~bVE6I;e=Y69PZ0YRe6*cNcN}a^y&q2 zLJg;N$7Nl%YSmDB1LuSx>w?`NpVdQSRa=AR)W^YoO-B)FE$&^*Id7*Tji-&Uk$Zj5i60L{8Px%BcxSN-r@p1H<{` zKySJ;m&+#9l5`&HHBqR}kz#y^9iUgbOft2J`K6ulT-yLTQEqSyJroU4n|RZvQn@Dv z)KF$a9~9}Kp4@O+Hwm*eWR9vT&pR|;AKd?OWv07cZdGn*BgV4DrgewX{kcv0P&5gS z@X$@_i|6FbQW(QiX_X$3A9IL^btqbB6<4W4YP3JwogUJbWk)Gdu%lxH#m7->xu}H6 zu*R}$X)B_`)({rzHI?f#=tH>r z?JZnW4l^n>$^Ff+$_#5swYNyku<{cyjT#;s=(XxyxN>o#JN9s!(vuzf4epaw&&YZ? z>8^B=QyN6xHxHvXwPRlEz(PG|wy|f3_v#s3h~yy7Jf$#B_HzlGSLx9~ETE((;>f%d z(hb!Btf{9bz?DGp^pM>0=sY%sB#&nDC8?Io#8!;2X4N__B{;H}%axRsDzjxl$!x2V zLj%C7pfitNu4Y!OL%5J^+my_XU2zvB&Z53($TrJHVxQ7eFF(Ht=YVCEz5S+WHpogWG=?o)n!$)7D}2k^HVre# zVpHFATRH41TbAWO1wFYem!*}A?9_#O_(H}~ZD~fUxOs(fgKySgB`~|mgOFp@TB4Io zXd75ED`HC_*-qBpva(|ok18iv6s<;-9L%hGc}HT=(v~jO*0ESe3ab`#X@TvxG^QQi zCz?w^841%=3@2o&kVMQ|7G@$pm>$CtNt0Dju(uQh&4u)xGM(9j3R?T!5$lx`FQkfu zb1W)M^G{INGnvLH6huz zjERmDjd>KvxCxTACcBhvGom2swqe$iL@gmJZ~K`?hI?~O#zmnX{`(0sW+sHV_&U;s zO=L`n=_jz}Nh=sb7PZNqZrX%3nNdtFnO+!)TNTnv&A8b+A+5)nq7T1llx~kit;J4> zHFyjx*NbNixp2ma%W2?Onr&U{e0mrO`tqSwxUtCR+H+5^r|_!i`$#LUe>*U>j^L&M zJZoriyV+5^RmtR3KF>v1pX~AKO)pbuSzVM0`zWn_7-- z@kG3bt)b?p?Z=jeR0J8*l6c56Qbh! zP}any66|7cQ$aM_gpE5~VYBhY8%<;@3OIxo%(Z7Wy$Qn_NkF%j&Y9O+@|aW2K3Y`B z#lcF*?4Rb|nZEGS8+prO%|X(1q;WM;s*$o58^}z*xs7y&QZo(Vb_jP|935d8>zuX3dmp`F2Dx~q5Mnp! z6G)Oan|=zLi8t!e5qHB`J;1<{GNzm6l1pO6`%Nc~+^w$9jST1|8*Vn}5yX8mHuKQt z9vn^$o4f4#)`W{HxlmSBT+gQy331X%bzp#*BrX$?r7a5GI13KZ$ZAFN9GlbtFSJBu ziji39Xvs2S0jU7z*q&aW@6T~>X(+QvcGP4C>4qtVk-LSR2_7P;JDD}_{AnWICW&?! zH)Mkglf4_2#ISpUO{ij2QjlK{j;Rc+G?ZGF9NwtRIH#PnD0r*3EQhTGP;Si&xNv&B zmvg~I!-j=cSZfe{n};J{->XnJMC8)d%E4uS!d_D!lEDcacZw5PyyL;P<%!tx);4_X zv~6+A%Jz7ov#VoCSIe@*@|I<7$`WGBV{v^C8x50YerneNqCY6hjJT+lGy#m z7H{74F-h||OON8_6}QE32f;@4`O+28L-S#Ajh(knU6>;n_Ce?xNtsp1;jje)50##f zb=;i!usETw%B+P~NL$Q`bj#6XVH3}FV8bIbkjz5KP8`n(Q>KIx?Kf&T$-Hp_N?6S< z?gUvHoN@06wlZl9F#z09;*q6?co4obYrRQputyCmte|vvf=0(-4Rft0Hp2lIjSOL% z1&b|9Va5h?_$us(ti|Zn9~NI6o34+Ik~+d2IV@Sw1@dt?4zQicHD6MD-cl*GFc$At z%VP@{wzsMFjwK0^mvyuzmbG+0s8Y#%5{@{`J=k-=Uu>XLtlyKto(TGOZ#In^%T#&; zeXSdYa!M)qnww{801M{OjTq%U{J`lZS2dR;-@ z+>YxoE!355$I8a+d$S~@W-3@)6 zaaGOxxx#!5z~P<60#3GJM6&i5C`_1hirXZTJYmW)w-Rogh`uG;CO1hf>^p3LgQGi} zhOa|kgYrhhI3t5{w)DWNCveZPQfBysbXq+JS*J)FdnLxdWi4F~s;;(G7$Rkc zl#KvIjpw*PFmDd`UJvA*u<5#rLC!hSKxq@L71eMl8lz z!MabDcu^~{VNqi3%6#0Uln6#ujKi>^+`1L*l)}aY*GRmQwj-GfRl5o`+A>JRHrcG- zrh%n!yO~lL^e~8*TFBX+zUvkRV`i1W6PJQwyv&@F4-@Y>tNk2CM3f9 zs04iSxSQ=OZj=GK(xq&X8r>FI_?hjJI2I3j~7 zj1qAjrByTXg6$ULojv1j%}baqc{u} z=_;v5FjLv9mm1RJxKC5W!fxcd%#6!CKhh7MGO{OB+wyoD_LEn~@DB=fEN@G6 zwsdu~#Zm_+c$Qz;)!mm_pSPZS;dG)`C<(AAi@U@VSQ6o`NA6@2`kLAl`nuW_>Dv2(w=4Agzn_}^b+3raiyrtZe>5CgkBzFnVHZBku#UsyEUyf`biVL(Yr`ecoMu( zT8^vQCO>WMF>GbDO4!!jhRfU%#~jtx(XLt+g7TI{aXhcqiYI3ALHxFs2Nf0-=&vi= z)S{O5_7*O&7jZLWQF}`_&HyWpc-P8BtOB=U_$!JX3dh2BW_x!VdRO<#F8-`srrI82 zbJ|ufYU_-v#VxURrYw#vZ)sPHyE>Ms#i(m>3rj7*z_X+y-l1a4Iy<`Xs4n~H;+92i zI>r*QZv4MR;W)A7YI#Ro_BIvH8kKZBg!wntiu*w}m3M~Soh*!Vj>k~8bLGPJ*dpF7 z#@iOf+gcHeJ=79!Q(Y}F9K8$Y)72JV*|l7Ca|CQ_U#z-Uc7hT-^%qA$eX1(H6ys(b z|GpiJx4b*XlY>dx6gN=Okd@2Z+i>`YVqNi-IOwXSF_>^otgXEj*;npU1b949Dat*U(?E*jQ*`llUdhxT;oADnn>3EIDMp}w+S*s+u9o2r8!cTU#$^{sbgx{zIJTOz z#VVL80b@%mU^KR(O|4jA)`&LB(R(E17ME}S2M#D>vK6&F%c&_@+)wgg=#(H=v?aLx7Q zREyg?TF^IQ(ht$r7=~GsE?s|jJ8P0^mKCF`y4C80t+wTDt5{UEbuVh^M882JyKvfU zVC#zR1k|eA`lW!PGM!5$5`%^bt+6H8A6e4Xv9c4H01K|9T?cOfYUXuDti3JH2}07< z(zew)q8%*#=!N*Rax-dX@7LM-X_fv$DXle++<&7(_p|f$>)wYScG`Yh-zmg-?fOeD zbH52dzezy9iATT5hnCxvmhoHC=lcsCmofHSG~K$?{jF_DXp&-T%w7o67Q@*k6JP9-5M&pda0-|As?JztwB{ zt!~uc&2GZ>OSfS}6x@ul4+H%h6M1pNVDT{v>p)xFo;2H*gt$Fvjx7msd(z#uB*g7W zb8Sh8+mr6GB_VE4y4RM3cw%^CW>6Lc!(0$_;a&slu|%5tjq=zgcTLO(ru2ke{ybxB zxD{J;`uVHwlFG0)-k7$}vb^0Y&0-#}Jq`cy4O99jjuZXX5WtIilpYa6$%{#ar+gSXG=odo^-!032}SU zYgu6^NnN6*RYHK#5itL7Fw5Lv9Q*lSd5cv(8%my;V={lKg(5eLI+TjB9n zc(Sswf%Q2qw^xkdJ&ngla{08G9vjAOR0g~Ay+hbD+<+&+aK9Ai1Hy%3+_Qz_WF`79 zHn6dMruPMS4m<*u-f%o%mot^?#p<26T?5?GB?XBTwqLMGj;9jvA_I1|ZHl5@Y=+Cz zM{I7FZph|(_~Zf}nuhtZV@0E?m{c~QPTVeX=`KcAwo6d7w$zX3Vr9=7eD;e0v3HAu zBy9Q&30p5`P|Tdhjh;Od?3$%t-mY6h&m-OoNWvSyhiY5Xs9V2kVmr+ZI#*)Q%7!Wm z`4Zfb!2P0BbmJ_1X8sq|{|oA+*}Kws+71sP!kcAcml7lByct5L1ygS zTgH;}H!lvK#EZd58`4AOGYYo8N9!R0CjtxLvDLz*z|sdjg|Ek0eGQk_>~;FIllfe~ zAX8|g&1^l%;dFvc9@Y&nUNEcA;HGT=ovdg~+(DKohAZ3&1-Z}&+j-+S)^r2^DjPMhA%V8wj$a;d{RJb3 zwhAiPWIl#(+963t2`y=>rhtG=H_UgI(j^Vx&_+*cVmRN=K?Fge2V)>IN5x{VwSeVF zqx*TuHI)Igv?J@rm38Mr>uy+$?r^_2k6L6hUV5}bp|S&(iuwTE>=N?yeW6RMVbMuf zw$Uf0Id;M^B_IrWKSCYLn{GeUplK(ipl{zw1iE(^&x=5ldaYYIkVUU8>PJM@Y4M@4VgkH>T3bAM&K`J(gg~+DPVCz<*l7l( zX?aUHWtf_h&-W8F@vqJ&b5Z4R55)_1}$liY;G4* zdbroE%U~Z5&m&;(Qnx#q;dAVkNrx!vlw$WNCKnB)^ZdV92^)ocSkOkkE~ZaZ_2A+B zbkd%ThY-!Vv246Bdv5{A;RGLZ(_=Ec(+R74%Q;y>d21)?@hrIc8h8R87kR19c1p$a za6}g_n++EG^^n3l>_LJ` znH=qT?P@*u*fr)^-(>2;{KkitWEa?83PUMZX%kiU0);JMTmhT0@L~X-O2s*0CB(N% zkOZd$dYyiCc4PpLKG^eH^L;NibglEla@Jg3*v`MO;;@%SpGcSWA(+Y59FC8<^>&Rp zj+W&~C1f@*7kBoFtO&d6)^nn*QJf{|tLKv1UdURY6jmx2F1gGtoFRKmLFmIf7nY#= zHs8XSxJI()!?wx93>f1rd*a3;J23;PD`Z(dlD$Ee0cZ13c zW#Nj;Oz7l?8Ep9mDHm5ki=_*X>6=G+7NP7muw+}IY+qrvePe+_$ql4^YhiZT-okAA z<^qM1lJ@O|*=73+v+WxU6iQ0kZ-Zx--3QOM-w0DEDQUkIo?Uh?JjZ@7Ou=4qj@?2O zN)qiBnp0+>Id%(CC@E>T(3~<0&9PgELP<%xh31r5XpY@N6iQ0kEi|XhLUZgEqF^t1 zx7|V%N)qiBy1UFmciSyQp`@hULU)&0=x)1(D3p}6Tj=gG3*BwE5QUPGb_?BIW}&<7 z7NSs6(r%%<%Pch4ZXpWxl5_1AqEM1(x6s@&3(d7#h(bw8yM^YKS!k}^LKI3$+ATD< z%tCYR7NSs6(r%%-Wfq!iw-5z;$$RV;qEM1(x6nOh7P`l7AqpiW?H0PH%tH6rEkvQD zq}@XIlv(H=yM-u}l(bvuo-zyFW4926l9F}{-BV_vd+ipYU@v*E-9i*f673ecx6DHK z+ATz(q@>+K_m)}cUb}@Tl$5kv=-x65-D|fHg_4qX3*B30p?mEXqF^t1pWQ+fN)qiB zy06Sa_t`B(p`@hULid$f=svrJD3p}6Tj;(r3*Be85QUPGb_?BCW}*A+7NSs6(r%&q z$}BX`ZXpWxlJo2qqEM1(x6r&Y3(d1zh(bw8yM^YJS!kZ!LKI3$+ATD%%tG_*7NSs6 z(r%%7Wfq!ew-5z;$@}dVqEM1(x6u7%7P{YVAqpiW?H0Pf%tH6uEkvQDq}@XIms#k3 zyM-u}l(bvu{xS>QZ?_PIl9F}{-Ct&**|Y2>63Je6_AI-RL@G_Tn`!o}GCR$lWjB;a zrKRnrnmwz`RjU`fPX}h^*&nmOm>{)h$iBwwJZnD|4%4{}!mfdI~m6R@ZsZc6S zF0tC|vc5IDbg?~QwapcF!2>jV!g{-O!98Kc%~iIobXgypUApL|g#B-ROU!yLu#=Bw z6>mE9$7}d1pN{DVtaZFgZ_`Q%?6mPNTs!_mGh_<5x6_{}(P=s(?@zG%&aVX66s99) z2v2MA`B0VM4v<9fCY$yCA|D15C7BI#uoASl|R{KVR`Q9`3Dl8Wsnl4@^e3vCT){QO1f)5T)mFgsX z?16jf6B8Dp!(fkgVgg=&O5wrWp^X!h7U3%u@=c0~NpL`e|VC+T- z1M4#zMzr5)VsdMGxEF8maIa`$Vmy<_SLX`HGA+}ZN#eDuVSKB``tTS%U&U;NN8!<5 z`8tPuCFd^mG2ZOsD>(X7Hu$o|CTy|{%X=UheV5+_1CPr6{&2B6v!I+{KGT!QX7aYv zL^a|`FKq&!66aUR_@WfMq(H=;AL9pm1bXSIbU_u;(V_01qfBhk>ETCJ9X-R?&&=D- z&MKlm1cRM0@j&1#@MRJ6QBgk3mL~{#kcoq-Bnv6N?LmW}_a^(LRPiKpgLqj0&)mTQ z3%3+T2fX)<1Iu%&nn?L3h&rZ9|B-MPb&(v7`k)Ql9*dhy^Vg9~#M!|A3BUQ`nYH_zF6td?;;fhy1I zCd>yXb0gOGSmYAOm5%3BMlF)Je7fZQUi~ULo&&Y|BVM#Aa3e1PB;-?|*aA(aRDWhb z#^_9HRQ2Mma~x8>;(_<13g3Rh`B`7w(uc65VQ%cRgWx>rpJ#(rkLtx|0OnxROX0&* z?CECf^KcHrE033n1{3m~Aoj1}Tz@*w*}`OViMZD@@6vm@=8z>WOX(4KrZ*vkPJWtCn)yU^0%(fSMs5OH{VC$ zTlDfCCtd>6?{Fr#cc|a+ltzOaK2u>-h1e5-VT>3C+F@2`O>Y>}H=v({I4k&L{fG|WOo z(aZTW!1u`b+7s}VVS&Eno)J6>F=qB&J%aXgj7;%C^WMTE=w|pUUi3GdNAVR+(i(W|_y7|Hhzd&VsBh)rDF^f51tKuu8I2qVt8D7BP zW8oYSarxv`2k zS>swUb@(oEU(QG6dDC6|uXiJh_;OyuSmT72|LFTRU4N*S%h>q2@> z&*HX+WO%jptY*ITm+O^>s`N06Ud}#%HR)S`}UwvNIVk^Arhak&lbdfOq2mGDH&1Zyzhr<_Lm&CA4t#OzNJLB7G=6`#6N zi}}!k*m#L1S+~&oGz-3RCQ)i_Q5nP6qy0QPDP5{DTUThZ?cQ6r@kS}SyWKpUr(HZ> znTjU5Dfu?uL@kSQxs30q@~JFab9iD_-_`5=vIhW73U1mb+Vq$0#1O0ub3R_N?iHiR z!Em2qKHp&-r(iT&|I&b?je6!~O&vqJJR5JtsgL)i2Z||0Nz#T!(zdjM#I(hDF4LA+ zCSrikW9m$&ba7oElf@lzQPGC44wlFTKdw1Er&Ds}xp=VI_y#)Lu&CEYw(LB#%`&nC z;3G2yJDMYvwU}XkYgfaZ4Dgy7wO#VL$d&=DcexTT%$C*yotCv9sXW8tY!aXBW-c8Q zqlj)Lw^D3v7TIb1$DUE%Xq_%6NWdzUYqOk}0d#3Ys%g}gk4IJy%aJjTC;AScA`q{Xs&HOM7} zRR=z!B`dx{jeM5Fwkj}35j+ip!CBI9ucZ$N(Rvt+a#i_Ibo0?krtZ|en`zaJ;Rnu)x4f&_40hXIosfDabu-3 zuf#WMcw=aNp9b$x%XdF84Gz=9iubn5@S#*}V!&#+>!G6C7GH`F8)z-=r|_bHj<+Vq zxP{Cl0N>NWZxRP; z#@=7*GAH?g(0;S z58kX|suiVfSB{GRr@ix!k*m7${H<42U9Y>{W%sj7o zJW8aA6t6a`K^rE^YNLV8e!l0t*HztaNLCs_v?{mj+~4P(d(OG%o_nj_eSC7^BfE@( zeb69mCuvb3ev}@@-xa&)0>h@++H>g|QJ+oRv|cr0u}^gQn3jYv ze_cA}ZDAKL-P+1aZ$kS+yzuNWeB|^DHZY+i}i<4 zx`o&141`?ibQ)IZiBi%Q=6OG3p4NGK=)4j7j=obTW+{^jO!8yy$BZdM%&S z7#PwIDx4-Zo^#EY;(_foKDOu$fbXiX~}UEc0aU8d!$((;!m2!4bn^DfnU1n|lYL zbVFkRN9PK#9VLf&{zIg*GRaOfq6f$tO(@m00E+^yJqOV*!cWSugSINtm1 zAnc6O#SM2bYdgOI`rDUjn@?KRR0UrL&cU3(RjeU$l@Sdtk5lK z$lLAXKlUkN|`dU>({YI(M-;y-=N}zxUon( z+k27x81r2FiVfy&hh@_2W_ny38m%S2aO>=~8m}P8gMUe5Q^l_wjOGST+4*p*d+mNylUYfAAw%s0SgApL`RrUpI;6ZXv;|DQ zwJ4X5ae*W-?<;aF!+0rEbneD~}E_VZ)?3sRdqjMH<#4YIwi)l}*-M6f#$|NOG zE8Wm&#LaOVyIN@mXz%d{VLcr7EvkM=AX_NcD`MW9XzKY z-P8B!NnLSR$7++NnJZYAMUk%qVU=vlyYKC$_Z)8BfjySazT38IMw@B; zYWPDlYIVw5i9h0u&ml>P)LcJ`kS=Ks>y%`e`N8E`z@4%VqJQj`5_6Nz)QEVu=B@FX5F12yrN8=48wWgQO{WdCVS>$JL#&Lc+MyXcZfGo6A zl$=ACY`EyR+MUuQ`BQilCD-IIU#qj|mIs!$T9oOX$|k`qU8l`_@!xr?5vs>fw2>FZ zkI>yH_p{&7DQl3$xnYAAvJ7ekzJ(gC4yva00xm|0-=tBx zzFkynhJ=!+7$fxKs?eUTRbj69c?gWIJ)%oC(PbzKTuac2Zmq>mlg|>hm7UG(Xz13d zqfTVARXddA^TBOOq-UxriljBFD{P^wd7Pp->-`XvXI9qpZTxUSIwA^rTc63;ErGN^ z+9;k8o2&Ie)J+;yp~x+q@akjXH1M>Y4d23R)kD$FgYnxr^(_fa1W#PeNzc-MQr#L0ThXAW^*_nzO2Mz{vc4#|T#h36aceYCup7rBSx2Mpe9XTD2q zjqVGdA%Ab64rid2m%jlIL6%7z5~e{cgYOQeI6N}Fd=q?@1hSohTgkz27df)|lJ$P@ z3}Z)JHADtb<{RU~Jw z`r$@OR@GO8|lR=-@#`)SnuJplYZ`LqHwF?3zr%nnl+qU#^AP#c)Q{!(jFSt z(A&<`@GN)wj3BpbDI9d&&r@VZ$iBR&`y5(`^A|Q06v~I3t|RVo(CjvV_Fw zS2IR)sFBhvA_bSFADO(fa@P=2aI|4Nv-%CoO3wVH;BB_;-l}2cNJBwp&q+bQTcfOI zW_P^IEr++tEE*{&ZMF;KEK<%)>q|cJY~;J8B^Q3k780X>w~AS=@x{s?O3Qt9q8!S_ z3(YLAE!&>OOmg%#(=L0vY%L=NrJ`f{x=KF0n{G+TW1P(^vbVl+?aNv>+i>!bS7R+E z%~*YnBo5~0)z(XW%_Gi8Gs{W}JdKsn+iHGfYeFL@Tc;dD<`IyhXJ40yGk(RVuc=uJ zx7wtjui1C-oaWV<95^=Jx|0X34X?!0jdhjuz*4g{!&rf>*n$VbC^ksv6!NgR!!9k| zhOMMFbjx<_P@I;ViSG&4kA`r3cxkO;|zCx#x-;ZqGF< zCND9oGc!lKR?%>m*FiV6>)L1|6!y$YYDYH0-v(h?vy@Qnc#VOLbPrsSt(1ixCTABh z@ogjUE*l}OdWBmY9R}|erEpQ4Ywk~0-YAEv5pZs$)Mc_qRNT;58#Wjf!YS(Su&e=@FG4*ibuH)9;M^jz`P zbHlq}-NAY{%;L zH5b&6=2R-5n}lDz39Fx>sFpk^wVN8J@o(!C%}vx&G6IflG)*N~pZn;GzLD^5YP;5Z6I4j!WJ9GR+-Pk1(=}l;vrGK+7PJQAj+uFIQnOh&y@pe>$#m8H zHDuPHvMC?ldflnk=!|qyc1rU~`Y#K2M zia!wY+JW3+dy)ZQj(vo3ZZ`P)g6w~E;=sNtuV1;yt7BJ*zIu$lssFDJ+<4uC|83-3 zh5!BJ+kf$q-68(gBYW>%eA_=g9;k6DtQHjB$El+Qke93Fi}#f zH|eo3sa4c;plk@zngfP|m z_mMOL69rQ&C$!GUOv_KN7WR zB%^#z@|h^KCDo^_kCj%x`jj{8Q{|OKfE6bba06)>%;&x81yL1Q) z&|B8Fyj}IG7m`|^HKaa+oo8BAeaRKK`a@|e*o;@w)dI4Q<3d|^^=lAXlg4aquM~6n zd_GFH#*|y6LlKI}GX$e?0l|d=gARo@1KrhccF?f5%q)nbb{beT>P%y5-C1c%}|Ly~%`FXDmuio2--53?IIgeYcr?7f~2@S?>YS{m#(0#LIG0C8SrQkx*75 zkAnFOn;_e`N``nqHOqnNM2y@x)?Bm#@Fukd5{8C|no1RuJ%S8W0+~Zu+@onR=904- z>ovTJ%CQNchT|64z(iw@hHA#9Rw1$8#?sp%Ui5av>a~ymg^=q|w^5ARl7^j_8S+sn z)2j}!m&?fz6{RFe?Y|f&Y8ps{5S=g2in&87Uhl;pmF5j$oTvb6@-fl8%Gc3Z4zif7 zNo})8kCP!uG7k@!I72GxNoqe$!*yzOAx-Q~!|^nHEe*exhTloUA2|^hiV#t|AnZL? zqlC!;5bsV7$(H7l16j^EIa&N`P4VwE#b0ZR|EMYcQ{tk~rp|7~z9aCOz>freDljha zStp7yOIJ(-G@nd?(Ql~=Ea`KNMwe7CpJ&#RPts^Ek`mYBj!KE#o^l*Ta267}Yu})g z>UYg7zy~#o(#Dv+Kr4qG#5+0^@95~zJjWC`Ia1HKEK=Q2)vz4-SjB~s=65W>VPGim zU0W!03UD463LFxKLRnz0K(|05FkfJSz(Rqm1QrQgEwEVN8i8vCdIWj}t`oRkpd!H8 zeJCsy;IuFl`UI8<+$eC90AK2b0^gg30!NRbz?o+#@C8ID3z?}kj34Byw zSm56Y{I0-Off0di0(T2+7Z??|M_`A*PJvwl_X^x6!1t}8ut(tc1o#3n6gZ6yh5H45 zUjTP26h1D%fp{qF6WA|sK;S`vg8~x*hXg($@CO2i1&#=C1RV;Tv4;X~Stxu`fP?!` z;EXvGaKA!fQs7epeE%2<4-0TU9}15MaM~LRrv=UkaHt##e<)BDs0q{s&I){1;8B79 zAn->5j|t$ocSt9H-p+hLHRI$YwkTqiPmbi zYgXfw&A#fR9z0&eUR9s)#FHMJv%IH0@r)(Td*Z7FY704w>vJA_-O8W$1^3aUdeO6A zP@mNov&t8#)m{A?%A%y|_+Pe$-!u4%!B@TM4=i!X;ExUd#NcIvKQs8c!8Z)PY4Dc@ z-xjRp4C0^>Z#URwu-o85gNwbhUs_P>^JLBn@jL(d*k8W*&7%{4`S`wnx%TV- z>)QuQ`@Zn~JIDTO|DXTqGXtN0?(_3rcx~UtiR=FA3m+VR)X>My{tv&QlNWMoGRijzqLI z$Gfxq`Ihm@zEc&RGV!Oqc>+vt&rP3}UViAIlPfAG>=~*4%85g~NuZ)f*SWl)vU>3L z!PSEw%!M!~u;A35XQ6vOj!UI{%w{Vh)IKR{D{0@<(_ShlAfuyHR2XZ|QqmqML^vZb zE!j{6)Lv}EO-X7@u!yZxG_O=j2Jl|~2B_><^L7A-qo=3SFtN1)mE%_oe7UIX=>?X? z^%(FpJzJ-1;6#~E%%`Uag?&b6pY62Dz2#D=fUntGR?!OWmtw1>#!;jmB6z9+Miho* zjRrfEtYOfFWQ824kVgZxPhs0*u&nB%WvkIcQ9gunSE&PkoPRjKC}pY5B6;xV%Dv!^ zlF{CtO}Q|{yCiwL29G0-&nkn;t`i3;NG*K;YH)n=*z*1gPvxKF$uq5!j504TN&=Ej zkM`z8F@d&yMY1E9Vl<&>Q#=eo%CA%yEP6(_l0;*3@ z5eE_`Oho)=#^C^>4{C+40{U`K9FZd^S-*~jVEDDy!O`2J`8UvAdqW6M!+MxtXdg2U z%DX{{Ap?a1NyNrdaGsGg&uYFTJY{Fu0FQ&|s+xCT+6OCRCn|?dR`!qSQP0Z00~NiY z{?ItLDh#62(4LEja^dzFvm~7y_Tc#Pcb*ulKw%X95PIXNw}*MMvKiF~7u}B?JWx57 zzH?$0!LHQQDzS0y#Z_b@7onC};+wr?@znJRwPM;8GA&|eNs4q_rsEyCFw)%dj`V%8 zQ%?Y5B-#-$bF(fMKS0=4E z9aCOgKveU42>%rk4k;1$BrIpxESp(F(i7S)D3{`74?|M5e>Fm6{rwbu56-YKoLCB4cP{ zd)Bx(M5k=b=a4(ZP6~^<-lo$K`zZ-D8ItD1mYu=?OTHmvftl2jRqyUi2H+3$ltC~+ zMp7h86HDfd<(XQw%Vt?>9MXs#U-96NJ$TIcSNme*$uGn1kkl8J(ZW^T^~F8Jqom&y z`#bHBQ~hpI{X6*JvG01T&!MQ)hyJuaL+^;NK^@AB|1&pjI5T-@eMj9mF zVwp}R;yFpZKUp{r4M3w9?S*7}GLe@JwTfy%eqMr5B#!b~tdXY;lU184#FnL5s0(|j za(8{8qohta*)j8l#CtpF2LAldWc{2{yLU&!e3}3Se_1Of7AEyo$Y4ze{sHl|x%mO+ zb=QY9JI){$gK%?0$*Z1H-uxil#i6~BoHAxtAD3=dFJLY;N{?tp5rb>=Fx-W3C-|!m zE7K%2l6GHx!esG;3LFp92oY8<8hn9i1{oOrkQ|Ycfn<&KVW?~U3rp>I(1u+-Kvne# z7=$>Lw2TQ`@#KzU@SnHyiTbW&QiGA-PG9E?rVjYi8Dn;>EX_}`81bV?^=bS6JS~#y zljd=~fV%}Yb(9kRTu*6^HLGvIPfx0ktLmg{LmVwGb$a0^XO{Trs?Wq4UV`CdsJN1J zNl$QB%C9gL+hnH}t)xC_nWcgXr-WL4`i2tcLr+ry66&Wot*Y0O`Wb^YYEl-vbgJj` zmC(VWn(L6`0!H=`=N+5#_`m}380S-eV5RZ-IO?MEBzQ?qO0TeIkO~R5K{}(}=cQ7p z!vd8dSVS;6h&mar6v93ge?6(A1<%7OxC`c(VjC~Lt9pG@)nKIcvZ&Nyxum{s&CiJx z7eRg0^=`D=2INiQX?;|_1PY0Aqt<6c9~}z02K79%QY#ukoW-li%I>f>cmC>ZmnB}f_S%Ep``kPD<3MP(N`~JQ}6P85LGXe zW40ltpBPJCV6R9NXc|K3bB1wb%J!wPBd(J1QD2lE%Dj#p1wujiCB+ z6dT8_uGNgA1xTp+J&{i}V^H;bZksErrPtF^XzS>#*C-ID05 zLieh5`KpOjjpJycs%B-Eyx8YJcl~*>w0g-1R%a7rS&d6zHVc&0`i=M>TOAGO$4=c( zrZRpKvHO!Y)KKzkowZ_-By^Dh`qY6~3BJ~AHGVeLWv$m0EUEsCIWGRaK2@gfUeCmL z*&4o)7FOR7Z{9Fs-$)t!CS}gBH?cPzYzwo>kimbj{nDwZzmQ6_wq!v=;Vve%C8{+U z&0$ST&afo~-(+v8OWGpbV85i?2{!=M%aSDQecO9>?!FzH8G{?hL*j%nKA~!;y49%_ zQ!7-R} zvhf1)<{o4dr~35rQZkLqlvqLafA7?1zXeNv3 zySx5sk`7!Fm4A_!U2)tJXB^F!^!tq;u4MgjbXD3v>d#Q?NL!NU3_M)|FoG!-S7FzS zHp5Gf)z%`fek|eU_+?FY<;l>S?i*Tp0Me*zNnnaCP8|#9pFUIRr14_r)$VkdW~z@T0^XiwduqhN-GTcyxx!|H+XVOn%u%_Bw2N_*;jzY zX-{gyhRxlQ+HhvAIXx4BwGow?-K~wd?^PT12EfVWj0Ts~MxFFsmM3;F0vzsqZ-@Tm z4NuwzqNY=~vt69j_#e`LC?H+~QN6oE+9J`+pUaTQ79sSk&evwYQSmKl|p6 zn#{1cSN=aIE*$>(Hpxy5&n}iY&i2adwWG|leh_+gKRi_;|cCl&9cfmXD z`*K~hn4_IWa&|4zt~APiud7SgI?@amM+c5W8@-DKbk8+B>ul9#YHVq^(#lQU<4>kOv=H8i|CRMIi$|Fnt$42ab7_w> z>!Vqw+3vK74t(Y(X<_PtX1l8`3wQx5fIcxVvGizL>UqS`)uqnNA#!J^p1MLyWI?Z3 z<}XUiw9;c*Z9Q*V?7fWjo-%9wMmjKG>nYBbdn%Z#-~*^nt=n-l<$h@5r)#x!i5a;L zGlIc(W=&gBgpUET5T{Fl+jLtG%q5w=62J|yQ@y9otxqIPF(#M#V{X~hSf{CB(_vjw zU)O`E(Q&REKUcAOXd9j%NXsN8*+ zm-QYxJ{H2-T)2%_j8z6IV-Hoh?eoAxCk}C8p^m0>F)bIp9;_VZR@zCOSMg@Y3Ks!a zHf`nWuWNJRDz)VH;KRo&`?x1@ymHKLR0<)^g_2Q2BoU!pFUxA!{uvM>$|m}^K^aP_SR@ycxyu&2kV=6{MGmSz7YM{S2ulm`=9>xXI?9u zQ;Yo{+6$SwzxBjkC>c2MsfYI3Tb%b!T}|azJPjTj-$(t!_p|~1-+xrRlBK4H)uwul z5^j(>zWFd$RC8PIkrTY``@n(0@x%IdEree!3zhX2Yk?LAS{!I`pv8d}2U;9xaiGP4 z76)1!XmOy$fqzR5J2rEnv-S_Z03^#s~(iT zlgDKAi2E=Px9AaK{p;h0CV|Irvd2rCueY!Y;Twg&qnjMm=^<1-8hmgTOwU@Km5kf} zZsV~Wc)$ZZi)fqmyqVrl@MyO1=xPbSMnnDVgC1Q1qlaAmHsK0+dfeNu=I{r7mC~<8 z#a}KCn8JInDI3v2F89zcSeKKl`%zaLFK*?+T#wyO z-aXVGPit$u8?6_Auvlq5EKot-df;5wRp=^)F>r1-`tMF#N($;d(5TeS&#oW3)Ykc^OC#zk{9B6T%#eo(F zS{!I`pv8d}2U;9xaiGP4U&n#%Z9Hle!dLW|XX{^!11%1;IMCuiivukVv^db>K#Kz{ T4zxJX;y{Z7Ee`yD$$|e13;By` diff --git a/DotNetConnectors.sln/BooScriptExecutorFactory/lib/Boo.Lang.Useful.dll b/DotNetConnectors.sln/BooScriptExecutorFactory/lib/Boo.Lang.Useful.dll deleted file mode 100644 index 73b494556eb679b91547b5e8e616973ae92ae8e1..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 81920 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 diff --git a/DotNetConnectors.sln/BooScriptExecutorFactory/lib/Boo.Lang.dll b/DotNetConnectors.sln/BooScriptExecutorFactory/lib/Boo.Lang.dll deleted file mode 100644 index 83dc5fbc52b26ef9a7916040df05d3b79b5f07ce..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 110592 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 - /// Description of Assertions. - /// - public static class Assertions - { - /** - * Throws {@link NullPointerException} if the parameter o - * is null. - * - * @param o - * check if the object is null. - * @param param - * name of the parameter to check for null. - * @throws NullPointerException - * if o is null and constructs a - * message with the name of the parameter. - */ - public static void NullCheck(Object o, String param) { - String FORMAT = "Parameter '{0}' must not be null."; - if (o == null) { - throw new ArgumentNullException(String.Format(FORMAT, param)); - } - } - } -} diff --git a/DotNetConnectors.sln/Common/CollectionUtil.cs b/DotNetConnectors.sln/Common/CollectionUtil.cs deleted file mode 100644 index 5e57742e..00000000 --- a/DotNetConnectors.sln/Common/CollectionUtil.cs +++ /dev/null @@ -1,861 +0,0 @@ -/* - * ==================== - * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER. - * - * Copyright 2007-2008 Sun Microsystems, Inc. All rights reserved. - * - * The contents of this file are subject to the terms of the Common Development - * and Distribution License("CDDL") (the "License"). You may not use this file - * except in compliance with the License. - * - * You can obtain a copy of the License at - * http://IdentityConnectors.dev.java.net/legal/license.txt - * See the License for the specific language governing permissions and limitations - * under the License. - * - * When distributing the Covered Code, include this CDDL Header Notice in each file - * and include the License file at identityconnectors/legal/license.txt. - * If applicable, add the following below this CDDL Header, with the fields - * enclosed by brackets [] replaced by your own identifying information: - * "Portions Copyrighted [year] [name of copyright owner]" - * ==================== - */ -using System; -using System.Runtime.CompilerServices; -using System.Reflection; -using System.Collections.Generic; - -namespace Org.IdentityConnectors.Common -{ - internal class IdentityEqualityComparer : IEqualityComparer where T : class { - public bool Equals(T x, T y) { - return Object.ReferenceEquals(x,y); - } - public int GetHashCode(T o) { - return RuntimeHelpers.GetHashCode(o); - } - } - - internal class ReadOnlyCollection : ICollection { - private readonly ICollection _target; - public ReadOnlyCollection(ICollection target) { - _target = target; - } - public void Add(T item) { - throw new NotSupportedException(); - } - public void Clear() { - throw new NotSupportedException(); - } - public bool Contains(T item) { - return _target.Contains(item); - } - System.Collections.IEnumerator System.Collections.IEnumerable.GetEnumerator() { - return _target.GetEnumerator(); - } - public IEnumerator GetEnumerator() { - return _target.GetEnumerator(); - } - public bool IsReadOnly { - get { - return true; - } - } - public int Count { - get { - return _target.Count; - } - } - public bool Remove(T item) { - throw new NotSupportedException(); - } - public void CopyTo(T[] array, - int arrayIndex) { - _target.CopyTo(array,arrayIndex); - } - - public ICollection GetTarget() { - return _target; - } - - } - - - internal class ReadOnlyList : ReadOnlyCollection, IList { - - public ReadOnlyList(IList target) : base(target) { - - } - public int IndexOf(T item) { - return GetTarget().IndexOf(item); - } - public void Insert(int index, T item) { - throw new NotSupportedException(); - } - public void RemoveAt(int index) { - throw new NotSupportedException(); - } - - public T this[int index] { - get { - return GetTarget()[index]; - } - set { - throw new NotSupportedException(); - } - } - - protected new IList GetTarget() { - return (IList)base.GetTarget(); - } - } - - internal class ReadOnlyDictionary : - ReadOnlyCollection>, - IDictionary { - public ReadOnlyDictionary(IDictionary target) : base(target) { - - } - public void Add(TKey key, TValue val) { - throw new NotSupportedException(); - } - protected new IDictionary GetTarget() { - return (IDictionary)base.GetTarget(); - } - public ICollection Keys { - get { - return new ReadOnlyCollection(GetTarget().Keys); - } - } - public ICollection Values { - get { - return new ReadOnlyCollection(GetTarget().Values); - } - } - public bool Remove(TKey key) { - throw new NotSupportedException(); - } - public bool ContainsKey(TKey key) { - return GetTarget().ContainsKey(key); - } - public bool TryGetValue(TKey key, out TValue value) { - return GetTarget().TryGetValue(key,out value); - } - public TValue this[TKey key] { - get { - return GetTarget()[key]; - } - set { - throw new NotSupportedException(); - } - } - } - - - - - /// - /// Delegate that returns the Key, given a value - /// - public delegate TKey KeyFunction(TValue value); - - - /// - /// Description of CollectionUtil. - /// - public static class CollectionUtil - { - /// - /// Creates a case-insensitive set - /// - /// An empty case-insensitive set - public static ICollection NewCaseInsensitiveSet() { - HashSet rv = new HashSet(StringComparer.OrdinalIgnoreCase); - return rv; - } - - /// - /// Returns true iff the collection is a case-insensitive set - /// - /// The collection. May be null. - /// true iff the collection is a case-insensitive set - public static bool IsCaseInsensitiveSet(ICollection collection) { - if ( collection is ReadOnlyCollection ) { - ReadOnlyCollection roc = - (ReadOnlyCollection)collection; - return IsCaseInsensitiveSet(roc.GetTarget()); - } - else if ( collection is HashSet ) { - HashSet set = (HashSet)collection; - return StringComparer.OrdinalIgnoreCase.Equals(set.Comparer); - } - else { - return false; - } - } - /// - /// Creates a case-insensitive map - /// - /// An empty case-insensitive map - public static IDictionary NewCaseInsensitiveDictionary() { - Dictionary rv = new Dictionary(StringComparer.OrdinalIgnoreCase); - return rv; - } - - /// - /// Returns true iff the collection is a case-insensitive map - /// - /// The map. May be null. - /// true iff the collection is a case-insensitive map - public static bool IsCaseInsensitiveDictionary(IDictionary map) { - if ( map is ReadOnlyDictionary ) { - ReadOnlyDictionary roc = - (ReadOnlyDictionary)map; - return IsCaseInsensitiveDictionary((IDictionary)roc.GetTarget()); - } - else if ( map is Dictionary ) { - Dictionary d = (Dictionary)map; - return StringComparer.OrdinalIgnoreCase.Equals(d.Comparer); - } - else { - return false; - } - } - - /// - /// Creates a dictionary where the keys are looked up using - /// reference equality - /// - /// - public static IDictionary NewIdentityDictionary() - where K : class - { - IdentityEqualityComparer comp = new IdentityEqualityComparer(); - return new Dictionary(comp); - } - - /// - /// Computes a hashCode over the enumeration. The hashCode is computed - /// such that it doesn't matter what order the elements are listed in. Thus, it - /// is suitable for arrays, lists, sets, and dictionaries - /// - /// The enumerable - /// The hashcode - public static int GetEnumerableHashCode(IEnumerable enum1) { - if ( enum1 == null ) { - return 0; - } - int rv = 0; - foreach (T val1 in enum1) { - unchecked { - rv+=CollectionUtil.GetHashCode(val1); - } - } - return rv; - } - - /// - /// Computes a hashCode for a key value pair. - /// - /// The pair - /// The hashcode - public static int GetKeyValuePairHashCode(KeyValuePair pair) { - int rv = 0; - unchecked { - rv+=CollectionUtil.GetHashCode(pair.Key); - rv+=CollectionUtil.GetHashCode(pair.Value); - } - return rv; - } - - /// - /// Returns true iff the two sets contain the same elements. This is only for - /// sets and dictionaries. Does not work for Lists or Arrays. - /// - /// The first collection - /// The second collection - /// - public static bool SetsEqual(ICollection collection1, - ICollection collection2) { - if ( collection1 == null || collection1 == null ) { - return collection1 == null && collection1 == null; - } - if ( collection1.Count != collection2.Count ) { - return false; - } - foreach (T val1 in collection1) { - if (!collection2.Contains(val1)) { - return false; - } - } - return true; - } - - /// - /// Gets the given value from the map or default if not exists. Always - /// use this rather than map[] since map[] throws an exception if not - /// exists. - /// - /// The map - /// The key - /// The default value - /// - public static TValue GetValue(IDictionary map, - TKey key, - TValue def) { - TValue rv; - bool present = map.TryGetValue(key,out rv); - if (present) { - return rv; - } - else { - return def; - } - } - - /// - /// Adds all the elements from the given enumerable to the given collection. - /// - /// The collection to add to - /// The enumerable to get from - public static void AddAll(ICollection collection, - IEnumerable enumerable) - where U : T { - if ( enumerable != null ) { - foreach(U obj in enumerable) { - collection.Add(obj); - } - } - } - /// - /// Adds all the elements from the given enumerable to the given collection. - /// - /// The collection to add to - /// The enumerable to get from - public static void RemoveAll(ICollection collection, - IEnumerable enumerable) - where U : T { - if ( enumerable != null ) { - foreach(U obj in enumerable) { - collection.Remove(obj); - } - } - } - - /// - /// Adds all the elements from the given enumerable to the given collection. - /// - /// The collection to add to - /// The enumerable to get from - public static void RemovalAll(ICollection collection, - IEnumerable enumerable) - where U : T { - if ( enumerable != null ) { - foreach(U obj in enumerable) { - collection.Remove(obj); - } - } - } - - - /// - /// Returns c or an empty collection iff c is null. - /// - /// The collection - /// c or an empty collection iff c is null. - public static ICollection NullAsEmpty(ICollection c) { - return c ?? new HashSet(); - } - - /// - /// Returns c or an empty collection iff c is null. - /// - /// The collection - /// c or an empty collection iff c is null. - public static IDictionary NullAsEmpty(IDictionary c) { - return c ?? new Dictionary(); - } - - /// - /// Returns c or an empty collection iff c is null. - /// - /// The collection - /// c or an empty collection iff c is null. - public static IList NullAsEmpty(IList c) { - return c ?? new List(); - } - - /// - /// Returns c or an empty array iff c is null. - /// - /// The array - /// c or an empty collection iff c is null. - public static T[] NullAsEmpty(T [] c) { - return c ?? new T[0]; - } - - /// - /// Given a collection of values a key function, builds a dictionary - /// - /// List of values - /// Key function, mapping from key to value - /// The dictionay - public static IDictionary NewDictionary( - TKey k1, - TValue v1) { - IDictionary rv = new Dictionary(); - rv[k1] = v1; - return rv; - } - - /// - /// Given a collection of values a key function, builds a dictionary - /// - /// List of values - /// Key function, mapping from key to value - /// The dictionay - public static IDictionary NewDictionary( - IEnumerable values, - KeyFunction keyFunction) { - IDictionary rv = new Dictionary(); - if ( values != null ) { - foreach (TValue value in values) { - TKey key = keyFunction(value); - //DONT use Add - it throws exceptions if already there - rv[key] = value; - } - } - return rv; - } - - /// - /// Given a collection of values a key function, builds a dictionary - /// - /// List of values - /// Key function, mapping from key to value - /// The dictionay - public static IDictionary NewReadOnlyDictionary( - IEnumerable values, - KeyFunction keyFunction) { - IDictionary rv = - NewDictionary(values,keyFunction); - return new ReadOnlyDictionary(rv); - } - - /// - /// Given a collection of values a key function, builds a dictionary - /// - /// List of values - /// Key function, mapping from key to value - /// The dictionay - public static IDictionary NewDictionary( - IDictionary original) { - return NewDictionary(original); - } - - /// - /// Given a collection of values a key function, builds a dictionary - /// - /// List of values - /// Key function, mapping from key to value - /// The dictionay - public static IDictionary NewDictionary( - IDictionary original) { - IDictionary rv = new Dictionary(); - if ( original != null ) { - foreach (KeyValuePair entry in original) { - //DONT use Add - it throws exceptions if already there - rv[(TKey2)(object)entry.Key] = (TValue2)(object)entry.Value; - } - } - return rv; - } - - /// - /// Given a collection of values a key function, builds a dictionary - /// - /// List of values - /// Key function, mapping from key to value - /// The dictionay - public static IDictionary NewReadOnlyDictionary( - IDictionary original) { - return NewReadOnlyDictionary(original); - } - - /// - /// Given a collection of values a key function, builds a dictionary - /// - /// List of values - /// Key function, mapping from key to value - /// The dictionay - public static IDictionary NewReadOnlyDictionary( - IDictionary original) { - IDictionary rv = NewDictionary(original); - return new ReadOnlyDictionary(rv); - } - - - /// - /// Returns a modifiable list, after first copying the collection. - /// - /// A collection. May be null. - /// a modifiable list, after first copying the collection. - public static IList NewList(IEnumerable collection) { - return NewList(collection); - } - - /// - /// Returns a modifiable list, after first copying the collection. - /// - /// A collection. May be null. - /// a modifiable list, after first copying the collection. - public static IList NewList(IEnumerable collection) { - IList rv = new List(); - if ( collection != null ) { - foreach (T element in collection) { - rv.Add((U)(object)element); - } - } - return rv; - } - - /// - /// Returns a modifiable set, after first copying the collection. - /// - /// A collection. May be null. - /// a modifiable set, after first copying the collection. - public static ICollection NewSet(IEnumerable collection) { - return NewSet(collection); - } - - /// - /// Returns a modifiable set, after first copying the array. - /// - /// An array maybe null. - /// a modifiable set, after first copying the array. - public static ICollection NewSet(params T[] items) { - return NewSet((IEnumerable)items); - } - - /// - /// Returns a modifiable set, after first copying the collection. - /// - /// A collection. May be null. - /// a modifiable set, after first copying the collection. - public static ICollection NewSet(IEnumerable collection) { - ICollection rv = new HashSet(); - if ( collection != null ) { - foreach (T element in collection) { - rv.Add((U)(object)element); - } - } - return rv; - } - - - /// - /// Returns an unmodifiable list, after first copying the collection. - /// - /// A collection. May be null. - /// an unmodifiable list, after first copying the collection. - public static IList NewReadOnlyList(ICollection collection) { - return NewReadOnlyList(collection); - } - - /// - /// Returns an unmodifiable list, after first copying the collection. - /// - /// A collection. May be null. - /// an unmodifiable list, after first copying the collection. - public static IList NewReadOnlyList(ICollection collection) { - IList list = NewList(collection); - return new ReadOnlyList(list); - } - - /// - /// Returns an unmodifiable list, after first copying the collection. - /// - /// A collection. May be null. - /// an unmodifiable list, after first copying the collection. - public static ICollection NewReadOnlySet(ICollection collection) { - return NewReadOnlySet(collection); - } - - /// - /// Returns an unmodifiable list, after first copying the collection. - /// - /// A collection. May be null. - /// an unmodifiable list, after first copying the collection. - private static ICollection NewReadOnlySet(ICollection collection) { - ICollection list = NewSet(collection); - return new ReadOnlyCollection(list); - } - - /// - /// Returns an unmodifiable set, backed by the original - /// - /// A collection. May be null. - /// an unmodifiable list, after first copying the collection. - public static ICollection AsReadOnlySet(ICollection collection) { - ICollection list = NullAsEmpty(collection); - return new ReadOnlyCollection(list); - } - - /// - /// Returns an unmodifiable list, backed by the original - /// - /// A collection. May be null. - /// an unmodifiable list, after first copying the collection. - public static IList AsReadOnlyList(IList collection) { - IList list = NullAsEmpty(collection); - return new ReadOnlyList(list); - } - - /// - /// Returns an unmodifiable list, backed by the original - /// - /// A collection. May be null. - /// an unmodifiable list, after first copying the collection. - public static IDictionary AsReadOnlyDictionary(IDictionary d) { - d = NullAsEmpty(d); - return new ReadOnlyDictionary(d); - } - - /// - /// Creates a new read-only list from an array. - /// - /// - /// - public static IList NewReadOnlyList(params T [] args) { - return NewReadOnlyList(args); - } - - /// - /// Creates a new read-only list from an array. - /// - /// - /// - private static IList NewReadOnlyList(params T [] args) { - IList list = CollectionUtil.NewList(args); - return new ReadOnlyList(list); - } - - /// - /// Creates a new read-only set from an array. - /// - /// - /// - public static ICollection NewReadOnlySet(params T [] args) { - return NewReadOnlySet(args); - } - /// - /// Creates a new read-only set from an array. - /// - /// - /// - private static ICollection NewReadOnlySet(params T [] args) { - ICollection list = CollectionUtil.NewSet(args); - return new ReadOnlyCollection(list); - } - - public static bool DictionariesEqual(IDictionary m1, - IDictionary m2) - { - if ( m1.Count != m2.Count ) { - return false; - } - foreach (KeyValuePair entry1 in m1) { - K key1 = entry1.Key; - V val1 = entry1.Value; - if (!m2.ContainsKey(key1)) { - return false; - } - Object val2 = m2[key1]; - if (!CollectionUtil.Equals(val1,val2)) { - return false; - } - } - return true; - } - - public static bool ListsEqual(IList v1, - IList v2) - { - if ( v1.Count != v2.Count ) { - return false; - } - for ( int i = 0; i < v1.Count; i++ ) { - if (!CollectionUtil.Equals(v1[i],v2[i])) { - return false; - } - } - return true; - } - - /** - * hashCode function that properly handles arrays, - * collections, maps, collections of arrays, and maps of arrays. - * @param o The object. May be null. - * @return the hashCode - */ - public static int GetHashCode(Object o) { - if ( o == null ) { - return 0; - } - else if ( o is Array ) { - Array array = (Array)o; - int length = array.Length; - int rv = 0; - for ( int i = 0; i < length; i++ ) { - Object el = array.GetValue(i); - unchecked { - rv += CollectionUtil.GetHashCode(el); - } - } - return rv; - } - else if ( ReflectionUtil.IsParentTypeOf(typeof(KeyValuePair<,>),o.GetType()) ) { - Type parent = ReflectionUtil.FindInHierarchyOf(typeof(KeyValuePair<,>),o.GetType()); - Type [] genericArguments = - parent.GetGenericArguments(); - - Type collectionUtil = typeof(CollectionUtil); - MethodInfo info = collectionUtil.GetMethod("GetKeyValuePairHashCode"); - - info = info.MakeGenericMethod(genericArguments); - - Object rv = info.Invoke(null, new object[]{o}); - return (int)rv; - } - else if ( ReflectionUtil.IsParentTypeOf(typeof(ICollection<>),o.GetType()) ) { - Type parent = ReflectionUtil.FindInHierarchyOf(typeof(ICollection<>),o.GetType()); - - Type [] genericArguments = - parent.GetGenericArguments(); - - - Type collectionUtil = typeof(CollectionUtil); - MethodInfo info = collectionUtil.GetMethod("GetEnumerableHashCode"); - - info = info.MakeGenericMethod(genericArguments); - - Object rv = info.Invoke(null, new object[]{o}); - return (int)rv; - } - else { - return o.GetHashCode(); - } - } - - /** - * Equality function that properly handles arrays, - * lists, maps, lists of arrays, and maps of arrays. - *

- * NOTE: For Sets, this relies on the equals method - * of the Set to do the right thing. This is a reasonable - * assumption since, in order for Sets to behave - * properly as Sets, their values must already have - * a proper implementation of equals. (Or they must - * be specialized Sets that define a custom comparator that - * knows how to do the right thing). The same holds true for Map keys. - * Map values, on the other hand, are compared (so Map values - * can be arrays). - * @param o1 The first object. May be null. - * @param o2 The second object. May be null. - * @return true iff the two objects are equal. - */ - public new static bool Equals(Object o1, Object o2) { - if ( o1 == o2 ) { //same object or both null - return true; - } - else if ( o1 == null ) { - return false; - } - else if ( o2 == null ) { - return false; - } - else if ( o1 is Array ) { - Type clazz1 = o1.GetType(); - Type clazz2 = o2.GetType(); - if ( !clazz1.Equals(clazz2)) { - return false; - } - Array array1 = (Array)o1; - Array array2 = (Array)o2; - int length1 = array1.Length; - int length2 = array2.Length; - if ( length1 != length2 ) { - return false; - } - for ( int i = 0; i < length1; i++ ) { - Object el1 = array1.GetValue(i); - Object el2 = array2.GetValue(i); - if (!CollectionUtil.Equals(el1,el2)) { - return false; - } - } - return true; - } - else if ( ReflectionUtil.IsParentTypeOf(typeof(IList<>),o1.GetType()) ) { - Type parent1 = ReflectionUtil.FindInHierarchyOf(typeof(IList<>),o1.GetType()); - Type parent2 = ReflectionUtil.FindInHierarchyOf(typeof(IList<>),o2.GetType()); - if (!parent1.Equals(parent2)) { - return false; - } - Type [] genericArguments = - parent1.GetGenericArguments(); - - - Type collectionUtil = typeof(CollectionUtil); - MethodInfo info = collectionUtil.GetMethod("ListsEqual"); - - info = info.MakeGenericMethod(genericArguments); - - Object rv = info.Invoke(null, new object[]{o1,o2}); - return (bool)rv; - } - else if ( ReflectionUtil.IsParentTypeOf(typeof(IDictionary<,>),o1.GetType()) ) { - Type parent1 = ReflectionUtil.FindInHierarchyOf(typeof(IDictionary<,>),o1.GetType()); - Type parent2 = ReflectionUtil.FindInHierarchyOf(typeof(IDictionary<,>),o2.GetType()); - if (!parent1.Equals(parent2)) { - return false; - } - Type [] genericArguments = - parent1.GetGenericArguments(); - - - Type collectionUtil = typeof(CollectionUtil); - MethodInfo info = collectionUtil.GetMethod("DictionariesEqual"); - - info = info.MakeGenericMethod(genericArguments); - - Object rv = info.Invoke(null, new object[]{o1,o2}); - return (bool)rv; - } - else if ( ReflectionUtil.IsParentTypeOf(typeof(ICollection<>),o1.GetType()) ) { - Type parent1 = ReflectionUtil.FindInHierarchyOf(typeof(ICollection<>),o1.GetType()); - Type parent2 = ReflectionUtil.FindInHierarchyOf(typeof(ICollection<>),o2.GetType()); - if (!parent1.Equals(parent2)) { - return false; - } - Type [] genericArguments = - parent1.GetGenericArguments(); - - - Type collectionUtil = typeof(CollectionUtil); - MethodInfo info = collectionUtil.GetMethod("SetsEqual"); - - info = info.MakeGenericMethod(genericArguments); - - Object rv = info.Invoke(null, new object[]{o1,o2}); - return (bool)rv; - } - else { - return o1.Equals(o2); - } - } - - } - -} diff --git a/DotNetConnectors.sln/Common/Common.csproj b/DotNetConnectors.sln/Common/Common.csproj deleted file mode 100644 index abf5a07e..00000000 --- a/DotNetConnectors.sln/Common/Common.csproj +++ /dev/null @@ -1,94 +0,0 @@ - - - - {F140E8DA-52B4-4159-992A-9DA10EA8EEFB} - Debug - AnyCPU - Library - Org.IdentityConnectors.Common - Common - v3.5 - True - False - 4 - false - - - bin\Debug\ - true - Full - False - True - DEBUG;TRACE - - - bin\Release\ - False - None - True - False - TRACE - - - False - Auto - 4194304 - AnyCPU - 4096 - - - - - - 3.5 - - - - 3.5 - - - - 3.5 - - - - - - - - - - - - - - - - - - - - - - diff --git a/DotNetConnectors.sln/Common/DateTimeUtil.cs b/DotNetConnectors.sln/Common/DateTimeUtil.cs deleted file mode 100644 index 55c086b7..00000000 --- a/DotNetConnectors.sln/Common/DateTimeUtil.cs +++ /dev/null @@ -1,44 +0,0 @@ -/* - * ==================== - * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER. - * - * Copyright 2007-2008 Sun Microsystems, Inc. All rights reserved. - * - * The contents of this file are subject to the terms of the Common Development - * and Distribution License("CDDL") (the "License"). You may not use this file - * except in compliance with the License. - * - * You can obtain a copy of the License at - * http://IdentityConnectors.dev.java.net/legal/license.txt - * See the License for the specific language governing permissions and limitations - * under the License. - * - * When distributing the Covered Code, include this CDDL Header Notice in each file - * and include the License file at identityconnectors/legal/license.txt. - * If applicable, add the following below this CDDL Header, with the fields - * enclosed by brackets [] replaced by your own identifying information: - * "Portions Copyrighted [year] [name of copyright owner]" - * ==================== - */ -using System; - -namespace Org.IdentityConnectors.Common -{ - ///

- /// Description of DateTimeUtil. - /// - public static class DateTimeUtil - { - public static DateTime GetDateTimeFromUtcMillis(long dateTime) { - return DateTime.FromFileTimeUtc(dateTime*10000); - } - - public static long GetUtcTimeMillis(DateTime dateTime) { - return dateTime.ToFileTimeUtc()/10000; - } - - public static long GetCurrentUtcTimeMillis() { - return GetUtcTimeMillis(DateTime.Now); - } - } -} diff --git a/DotNetConnectors.sln/Common/FrameworkInternalBridge.cs b/DotNetConnectors.sln/Common/FrameworkInternalBridge.cs deleted file mode 100644 index a72c0e71..00000000 --- a/DotNetConnectors.sln/Common/FrameworkInternalBridge.cs +++ /dev/null @@ -1,54 +0,0 @@ -/* - * ==================== - * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER. - * - * Copyright 2007-2008 Sun Microsystems, Inc. All rights reserved. - * - * The contents of this file are subject to the terms of the Common Development - * and Distribution License("CDDL") (the "License"). You may not use this file - * except in compliance with the License. - * - * You can obtain a copy of the License at - * http://IdentityConnectors.dev.java.net/legal/license.txt - * See the License for the specific language governing permissions and limitations - * under the License. - * - * When distributing the Covered Code, include this CDDL Header Notice in each file - * and include the License file at identityconnectors/legal/license.txt. - * If applicable, add the following below this CDDL Header, with the fields - * enclosed by brackets [] replaced by your own identifying information: - * "Portions Copyrighted [year] [name of copyright owner]" - * ==================== - */ -using System; -using System.Reflection; -namespace Org.IdentityConnectors.Common -{ - /// - /// Description of FrameworkInternalBridge. - /// - internal static class FrameworkInternalBridge { - private static readonly Object LOCK = new Object(); - private static Assembly _assembly = null; - /// - /// Loads a class from the FrameworkInternal module - /// - /// - /// - public static Type LoadType(String typeName) { - - Assembly assembly; - lock(LOCK) { - if (_assembly == null) { - AssemblyName assemName = new AssemblyName(); - assemName.Name = "FrameworkInternal"; - _assembly = Assembly.Load(assemName); - } - assembly = _assembly; - } - - return assembly.GetType(typeName,true); - - } - } -} diff --git a/DotNetConnectors.sln/Common/IOUtil.cs b/DotNetConnectors.sln/Common/IOUtil.cs deleted file mode 100644 index 41483294..00000000 --- a/DotNetConnectors.sln/Common/IOUtil.cs +++ /dev/null @@ -1,47 +0,0 @@ -/* - * ==================== - * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER. - * - * Copyright 2007-2008 Sun Microsystems, Inc. All rights reserved. - * - * The contents of this file are subject to the terms of the Common Development - * and Distribution License("CDDL") (the "License"). You may not use this file - * except in compliance with the License. - * - * You can obtain a copy of the License at - * http://IdentityConnectors.dev.java.net/legal/license.txt - * See the License for the specific language governing permissions and limitations - * under the License. - * - * When distributing the Covered Code, include this CDDL Header Notice in each file - * and include the License file at identityconnectors/legal/license.txt. - * If applicable, add the following below this CDDL Header, with the fields - * enclosed by brackets [] replaced by your own identifying information: - * "Portions Copyrighted [year] [name of copyright owner]" - * ==================== - */ -using System; -using System.Net; -namespace Org.IdentityConnectors.Common -{ - /// - /// Description of IOUtil. - /// - public static class IOUtil - { - /// - /// Given an ip address or host, returns the - /// IPAddress - /// - /// - /// - public static IPAddress GetIPAddress(String hostOrIp) - { - if ( hostOrIp.Equals("0.0.0.0") || - hostOrIp.Equals("::0") ) { - return IPAddress.Parse(hostOrIp); - } - return Dns.GetHostAddresses(hostOrIp)[0]; - } - } -} diff --git a/DotNetConnectors.sln/Common/Locale.cs b/DotNetConnectors.sln/Common/Locale.cs deleted file mode 100644 index b1620142..00000000 --- a/DotNetConnectors.sln/Common/Locale.cs +++ /dev/null @@ -1,210 +0,0 @@ -/* - * ==================== - * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER. - * - * Copyright 2007-2008 Sun Microsystems, Inc. All rights reserved. - * - * The contents of this file are subject to the terms of the Common Development - * and Distribution License("CDDL") (the "License"). You may not use this file - * except in compliance with the License. - * - * You can obtain a copy of the License at - * http://IdentityConnectors.dev.java.net/legal/license.txt - * See the License for the specific language governing permissions and limitations - * under the License. - * - * When distributing the Covered Code, include this CDDL Header Notice in each file - * and include the License file at identityconnectors/legal/license.txt. - * If applicable, add the following below this CDDL Header, with the fields - * enclosed by brackets [] replaced by your own identifying information: - * "Portions Copyrighted [year] [name of copyright owner]" - * ==================== - */ -using System; -using System.Collections.Generic; -using System.Globalization; -namespace Org.IdentityConnectors.Common -{ - /// - /// Represents a Java Locale and has facilities for converting to/from - /// C# CultureInfo - /// - public sealed class Locale - { - private static readonly IDictionary - Locale2CultureInfoName = new Dictionary(); - private static readonly IDictionary - CultureInfoName2Locale = new Dictionary(); - - private static void AddMapping(Locale locale, String name) { - Locale2CultureInfoName[locale] = name; - CultureInfoName2Locale[name] = locale; - } - - /// - /// Special cases - /// - static Locale() - { - AddMapping(new Locale(),""); - AddMapping(new Locale("iw"),"he"); - AddMapping(new Locale("zh"),"zh-CHS"); - AddMapping(new Locale("iw","IL"),"he-IL"); - AddMapping(new Locale("no","NO"),"nb-NO"); - AddMapping(new Locale("no","NO","NY"),"nn-NO"); - } - - private String _language; - private String _country; - private String _variant; - - public Locale() - : this("") - { - - } - - public Locale(String language) - : this(language,"") - { - } - public Locale(String language, String country) - : this(language,country,"") - { - } - public Locale(String language, String country, String variant) - { - _language = language ?? ""; - _country = country ?? ""; - _variant = variant ?? ""; - } - - public string Language { - get { - return _language; - } - } - - public string Country { - get { - return _country; - } - } - - public string Variant { - get { - return _variant; - } - } - - public override bool Equals(Object o) { - Locale other = o as Locale; - if ( other != null ) { - if (!Object.Equals(Language,other.Language)) { - return false; - } - if (!Object.Equals(Country,other.Country)) { - return false; - } - if (!Object.Equals(Variant,other.Variant)) { - return false; - } - return true; - } - return false; - } - - public override int GetHashCode() { - unchecked { - return _language.GetHashCode()+_country.GetHashCode(); - } - } - - public override string ToString() { - return Language+"_"+Country+"_"+Variant; - } - - /// - /// Creates the closest CultureInfo that maps to this locale - /// - /// The culture info - public CultureInfo ToCultureInfo() - { - CultureInfo rv = null; - String code = CollectionUtil.GetValue(Locale2CultureInfoName,this,null); - if (code != null ) { - rv = TryCultureCode(code); - } - if ( rv == null ) { - if (Country.Length > 0) { - rv = TryCultureCode(Language+"-"+Country); - } - } - if ( rv == null ) { - rv = TryCultureCode(Language); - } - if ( rv == null ) { - rv = CultureInfo.InvariantCulture; - } - return rv; - } - - public static Locale FindLocale(CultureInfo info) - { - String code = info.Name; - Locale rv = CollectionUtil.GetValue(CultureInfoName2Locale,code,null); - if (rv == null) { - String [] parts = code.Split(new string[]{"-"}, - StringSplitOptions.RemoveEmptyEntries); - String language = ""; - String country = ""; - if ( parts.Length > 0 ) { - String l = parts[0]; - if (LooksLikeValidLanguageCode(l)) { - language = l; - if ( parts.Length > 1 ) { - String c = parts[1]; - if (LooksLikeValidCountryCode(c)) { - country = c; - } - } - } - } - rv = new Locale(language,country); - } - return rv; - } - - /// - /// Weeds out some junk - /// - /// - /// - private static bool LooksLikeValidLanguageCode(String l) { - char [] chars = l.ToCharArray(); - return (chars.Length == 2 && - Char.IsLower(chars[0]) && - Char.IsLower(chars[1])); - } - /// - /// Weeds out some junk - /// - /// - /// - private static bool LooksLikeValidCountryCode(String l) { - char [] chars = l.ToCharArray(); - return (chars.Length == 2 && - Char.IsUpper(chars[0]) && - Char.IsUpper(chars[1])); - } - - private static CultureInfo TryCultureCode(String code) { - try { - return new CultureInfo(code); - } - catch (Exception) { - return null; - } - } - } -} diff --git a/DotNetConnectors.sln/Common/Pair.cs b/DotNetConnectors.sln/Common/Pair.cs deleted file mode 100644 index cad448f7..00000000 --- a/DotNetConnectors.sln/Common/Pair.cs +++ /dev/null @@ -1,73 +0,0 @@ -/* - * ==================== - * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER. - * - * Copyright 2007-2008 Sun Microsystems, Inc. All rights reserved. - * - * The contents of this file are subject to the terms of the Common Development - * and Distribution License("CDDL") (the "License"). You may not use this file - * except in compliance with the License. - * - * You can obtain a copy of the License at - * http://IdentityConnectors.dev.java.net/legal/license.txt - * See the License for the specific language governing permissions and limitations - * under the License. - * - * When distributing the Covered Code, include this CDDL Header Notice in each file - * and include the License file at identityconnectors/legal/license.txt. - * If applicable, add the following below this CDDL Header, with the fields - * enclosed by brackets [] replaced by your own identifying information: - * "Portions Copyrighted [year] [name of copyright owner]" - * ==================== - */ -using System; - -namespace Org.IdentityConnectors.Common -{ - /// - /// Represents a Pair of objects - /// - public class Pair - { - public Pair() - { - } - - public Pair(T1 first, T2 second) - { - First = first; - Second = second; - } - - public T1 First { get; set; } - public T2 Second { get; set; } - - public override bool Equals(object obj) - { - Pair other = obj as Pair; - if ( other != null ) - { - return Object.Equals(First,other.First) && - Object.Equals(Second,other.Second); - } - return false; - } - - public override int GetHashCode() - { - int rv = 0; - if ( First != null ) { - rv ^= First.GetHashCode(); - } - if ( Second != null ) { - rv ^= Second.GetHashCode(); - } - return rv; - } - - public override string ToString() - { - return "( "+First+", "+Second+" )"; - } - } -} diff --git a/DotNetConnectors.sln/Common/Pooling.cs b/DotNetConnectors.sln/Common/Pooling.cs deleted file mode 100644 index 063c8610..00000000 --- a/DotNetConnectors.sln/Common/Pooling.cs +++ /dev/null @@ -1,189 +0,0 @@ -/* - * ==================== - * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER. - * - * Copyright 2007-2008 Sun Microsystems, Inc. All rights reserved. - * - * The contents of this file are subject to the terms of the Common Development - * and Distribution License("CDDL") (the "License"). You may not use this file - * except in compliance with the License. - * - * You can obtain a copy of the License at - * http://IdentityConnectors.dev.java.net/legal/license.txt - * See the License for the specific language governing permissions and limitations - * under the License. - * - * When distributing the Covered Code, include this CDDL Header Notice in each file - * and include the License file at identityconnectors/legal/license.txt. - * If applicable, add the following below this CDDL Header, with the fields - * enclosed by brackets [] replaced by your own identifying information: - * "Portions Copyrighted [year] [name of copyright owner]" - * ==================== - */ -using System; - -using System.Collections.Generic; - -namespace Org.IdentityConnectors.Common.Pooling -{ - /** - * Configuration for pooling objects - */ - public sealed class ObjectPoolConfiguration { - - /** - * Max objects (idle+active). - */ - private int _maxObjects = 10; - - /** - * Max idle objects. - */ - private int _maxIdle = 10; - - /** - * Max time to wait if the pool is waiting for a free object to become - * available before failing. Zero means don't wait - */ - private long _maxWait = 150 * 1000; - - /** - * Minimum time to wait before evicting an idle object. - * Zero means don't wait - */ - private long _minEvictableIdleTimeMillis = 120 * 1000; - - /** - * Minimum number of idle objects. - */ - private int _minIdle = 1; - - - /** - * Get the set number of maximum objects (idle+active) - */ - public int MaxObjects { - get { - return _maxObjects; - } - set { - _maxObjects = value; - } - } - - /** - * Get the maximum number of idle objects. - */ - public int MaxIdle { - get { - return _maxIdle; - } - set { - _maxIdle = value; - } - } - - /** - * Max time to wait if the pool is waiting for a free object to become - * available before failing. Zero means don't wait - */ - public long MaxWait { - get { - return _maxWait; - } - set { - _maxWait = value; - } - } - - /** - * Minimum time to wait before evicting an idle object. - * Zero means don't wait - */ - public long MinEvictableIdleTimeMillis { - get { - return _minEvictableIdleTimeMillis; - } - set { - _minEvictableIdleTimeMillis = value; - } - } - - /** - * Minimum number of idle objects. - */ - public int MinIdle { - get { - return _minIdle; - } - set { - _minIdle = value; - } - } - - public void Validate() { - if (_minIdle < 0) { - throw new InvalidOperationException("Min idle is less than zero."); - } - if (_maxObjects < 0) { - throw new InvalidOperationException("Max active is less than zero."); - } - if (_maxIdle < 0) { - throw new InvalidOperationException("Max idle is less than zero."); - } - if (_maxWait < 0) { - throw new InvalidOperationException("Max wait is less than zero."); - } - if (_minEvictableIdleTimeMillis < 0) { - throw new InvalidOperationException("Min evictable idle time millis less than zero."); - } - if ( _minIdle > _maxIdle ) { - throw new InvalidOperationException("Min idle is greater than max idle."); - } - if ( _maxIdle > _maxObjects ) { - throw new InvalidOperationException("Max idle is greater than max objects."); - } - } - - public override int GetHashCode() { - unchecked { - return (int)(MaxObjects+MaxIdle+MaxWait+MinEvictableIdleTimeMillis+MinIdle); - } - } - - public override bool Equals(Object obj) { - if ( obj is ObjectPoolConfiguration) { - ObjectPoolConfiguration other = (ObjectPoolConfiguration)obj; - - if (MaxObjects != other.MaxObjects) { - return false; - } - if (MaxIdle != other.MaxIdle) { - return false; - } - if (MaxWait != other.MaxWait) { - return false; - } - if (MinEvictableIdleTimeMillis != other.MinEvictableIdleTimeMillis) { - return false; - } - if (MinIdle != other.MinIdle) { - return false; - } - return true; - } - return false; - } - - public override String ToString() { - // poor man's toString() - IDictionary bld = new Dictionary(); - bld["MaxObjects"] = MaxObjects; - bld["MaxIdle"] = MaxIdle; - bld["MaxWait"] = MaxWait; - bld["MinEvictableIdleTimeMillis"] = MinEvictableIdleTimeMillis; - bld["MinIdle"] = MinIdle; - return bld.ToString(); - } - } -} diff --git a/DotNetConnectors.sln/Common/Proxy.cs b/DotNetConnectors.sln/Common/Proxy.cs deleted file mode 100644 index 2534fdd0..00000000 --- a/DotNetConnectors.sln/Common/Proxy.cs +++ /dev/null @@ -1,267 +0,0 @@ -/* - * ==================== - * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER. - * - * Copyright 2007-2008 Sun Microsystems, Inc. All rights reserved. - * - * The contents of this file are subject to the terms of the Common Development - * and Distribution License("CDDL") (the "License"). You may not use this file - * except in compliance with the License. - * - * You can obtain a copy of the License at - * http://IdentityConnectors.dev.java.net/legal/license.txt - * See the License for the specific language governing permissions and limitations - * under the License. - * - * When distributing the Covered Code, include this CDDL Header Notice in each file - * and include the License file at identityconnectors/legal/license.txt. - * If applicable, add the following below this CDDL Header, with the fields - * enclosed by brackets [] replaced by your own identifying information: - * "Portions Copyrighted [year] [name of copyright owner]" - * ==================== - */ -using System; -using System.Reflection; -using System.Reflection.Emit; -using System.Collections.Generic; -namespace Org.IdentityConnectors.Common.Proxy -{ - /// - /// Similar to java.lang.reflect.InvocationHandler - /// - public interface InvocationHandler - { - Object - Invoke(Object proxy, MethodInfo method, Object [] args); - } - - - /// - /// Similar to java.lang.reflect.Proxy - /// - public static class Proxy - { - private static readonly MethodInfo INVOCATION_HANDLER_METHOD = - typeof(InvocationHandler).GetMethod("Invoke", - new Type[]{typeof(object), - typeof(MethodInfo), - typeof(object[])}); - private static readonly MethodInfo GET_METHOD_FROM_HANDLE_METHOD = - typeof(MethodBase).GetMethod("GetMethodFromHandle", - new Type[]{typeof(RuntimeMethodHandle)}); - - private static readonly object LOCK = new Object(); - - private static IDictionary - _implementationMap = new Dictionary(); - - private static int _count = 0; - - public static Object NewProxyInstance(Type interfaze, - InvocationHandler handler) - { - ConstructorInfo constructor = GetOrCreateConstructorInfo(interfaze); - return constructor.Invoke(new object[]{handler}); - } - - private static ConstructorInfo GetOrCreateConstructorInfo(Type type) - { - lock(LOCK) - { - ConstructorInfo rv = - CollectionUtil.GetValue(_implementationMap,type,null); - if ( rv == null ) - { - Type impl = GenerateImplementation(type); - rv = - impl.GetConstructor(new Type[]{typeof(InvocationHandler)}); - - _implementationMap[type] = rv; - } - return rv; - } - } - - private static String NextName() - { - int count; - lock(LOCK) { - count = _count; - count++; - } - return "___proxy"+count; - } - - - - private static Type GenerateImplementation(Type interfaze) - { - if (!interfaze.IsInterface) - { - throw new ArgumentException("Type is not an interface: "+interfaze); - } - if ( interfaze.IsGenericType ) - { - throw new ArgumentException("Type is a generic type: "+interfaze); - } - - String uniqueName = NextName(); - - AssemblyName assemblyName = new AssemblyName(uniqueName); - AssemblyBuilder assemblyBuilder = - AppDomain.CurrentDomain.DefineDynamicAssembly( - assemblyName, - AssemblyBuilderAccess.RunAndSave); - // For a single-module assembly, the module name is usually - // the assembly name plus an extension. - ModuleBuilder moduleBuilder = - assemblyBuilder.DefineDynamicModule(assemblyName.Name, assemblyName.Name + ".dll"); - - TypeBuilder typeBuilder = moduleBuilder.DefineType( - uniqueName, - TypeAttributes.Public); - typeBuilder.AddInterfaceImplementation(interfaze); - - MethodInfo [] methods = interfaze.GetMethods(); - ConstructorBuilder classInitializer = - typeBuilder.DefineTypeInitializer(); - ILGenerator classInitializerCode = - classInitializer.GetILGenerator(); - //generate constructor and _handler field - FieldBuilder handlerField = - typeBuilder.DefineField("_handler", - typeof(InvocationHandler), - FieldAttributes.Private|FieldAttributes.InitOnly); - int count = 0; - foreach (MethodInfo method in methods) - { - GenerateMethod(typeBuilder,method,classInitializerCode,count, - handlerField); - count++; - } - classInitializerCode.Emit(OpCodes.Ret); - - - ConstructorBuilder constructorBuilder = typeBuilder.DefineConstructor( - MethodAttributes.Public, - CallingConventions.Standard, - new Type[]{typeof(InvocationHandler)}); - ILGenerator constructorCode = constructorBuilder.GetILGenerator(); - // For a constructor, argument zero is a reference to the new - // instance. Push it on the stack before calling the base - // class constructor. Specify the default constructor of the - // base class (System.Object) by passing an empty array of - // types (Type.EmptyTypes) to GetConstructor. - constructorCode.Emit(OpCodes.Ldarg_0); - constructorCode.Emit(OpCodes.Call, - typeof(object).GetConstructor(Type.EmptyTypes)); - constructorCode.Emit(OpCodes.Ldarg_0); - constructorCode.Emit(OpCodes.Ldarg_1); - constructorCode.Emit(OpCodes.Stfld, handlerField); - constructorCode.Emit(OpCodes.Ret); - - - Type t = typeBuilder.CreateType(); - - //assemblyBuilder.Save(assemblyName.Name+".dll"); - - return t; - } - - private static void ValidateMethod(MethodInfo info) - { - if ( info.GetGenericArguments().Length != 0 ) { - throw new ArgumentException( - "Method not supported since it has generic arguments: "+info); - - } - foreach (ParameterInfo parameter in info.GetParameters()) { - if ( parameter.IsOut ) { - throw new ArgumentException( - "Method not supported since it has output paramaters: "+info); - } - if ( parameter.IsRetval ) { - throw new ArgumentException( - "Method not supported since it has retval paramaters: "+info); - } - } - } - - private static void GenerateMethod(TypeBuilder typeBuilder, - MethodInfo method, - ILGenerator classInitializerCode, - int methodCount, - FieldBuilder handlerField) - { - ValidateMethod(method); - - FieldBuilder methodField = - typeBuilder.DefineField("METHOD"+methodCount, - typeof(MethodInfo), - FieldAttributes.Private|FieldAttributes.InitOnly|FieldAttributes.Static); - - - - classInitializerCode.Emit(OpCodes.Ldtoken, - method); - classInitializerCode.Emit(OpCodes.Call, - GET_METHOD_FROM_HANDLE_METHOD); - classInitializerCode.Emit(OpCodes.Castclass,typeof(MethodInfo)); - classInitializerCode.Emit(OpCodes.Stsfld,methodField); - - - Type [] parameterTypes = ReflectionUtil.GetParameterTypes(method); - MethodBuilder methodBuilder = typeBuilder.DefineMethod( - method.Name, - MethodAttributes.Public| - MethodAttributes.HideBySig| - MethodAttributes.NewSlot| - MethodAttributes.Virtual| - MethodAttributes.Final, - method.CallingConvention, - method.ReturnType, - parameterTypes); - - ILGenerator methodCode = methodBuilder.GetILGenerator(); - methodCode.Emit(OpCodes.Ldarg_0); - methodCode.Emit(OpCodes.Ldfld,handlerField); - methodCode.Emit(OpCodes.Ldarg_0); - methodCode.Emit(OpCodes.Ldsfld,methodField); - methodCode.Emit(OpCodes.Ldc_I4,parameterTypes.Length); - methodCode.Emit(OpCodes.Newarr,typeof(object)); - - for (int index = 0; index < parameterTypes.Length; index++) - { - Type parameterType = parameterTypes[index]; - methodCode.Emit(OpCodes.Dup); - methodCode.Emit(OpCodes.Ldc_I4,index); - methodCode.Emit(OpCodes.Ldarg,index+1); - if (parameterType.IsValueType) - { - methodCode.Emit(OpCodes.Box,parameterType); - } - methodCode.Emit(OpCodes.Stelem_Ref); - } - - methodCode.Emit(OpCodes.Callvirt,INVOCATION_HANDLER_METHOD); - Type returnType = method.ReturnType; - - if (typeof(void).Equals(returnType)) - { - methodCode.Emit(OpCodes.Pop); - } - else if (returnType.IsValueType) - { - methodCode.Emit(OpCodes.Unbox_Any,returnType); - } - else - { - methodCode.Emit(OpCodes.Castclass,returnType); - } - - methodCode.Emit(OpCodes.Ret); - - typeBuilder.DefineMethodOverride(methodBuilder,method); - } - } -} diff --git a/DotNetConnectors.sln/Common/ReflectionUtil.cs b/DotNetConnectors.sln/Common/ReflectionUtil.cs deleted file mode 100644 index 38a455a3..00000000 --- a/DotNetConnectors.sln/Common/ReflectionUtil.cs +++ /dev/null @@ -1,149 +0,0 @@ -/* - * ==================== - * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER. - * - * Copyright 2007-2008 Sun Microsystems, Inc. All rights reserved. - * - * The contents of this file are subject to the terms of the Common Development - * and Distribution License("CDDL") (the "License"). You may not use this file - * except in compliance with the License. - * - * You can obtain a copy of the License at - * http://IdentityConnectors.dev.java.net/legal/license.txt - * See the License for the specific language governing permissions and limitations - * under the License. - * - * When distributing the Covered Code, include this CDDL Header Notice in each file - * and include the License file at identityconnectors/legal/license.txt. - * If applicable, add the following below this CDDL Header, with the fields - * enclosed by brackets [] replaced by your own identifying information: - * "Portions Copyrighted [year] [name of copyright owner]" - * ==================== - */ -using System; -using System.Reflection; -using System.Collections.Generic; -using System.Linq; -namespace Org.IdentityConnectors.Common -{ - /// - /// Miscellaenous reflection utilities. - /// - public static class ReflectionUtil - { - /// - /// Gets all the interfaces in the hierarchy - /// - /// - /// - public static Type [] GetAllInterfaces(Type type) { - HashSet temp = new HashSet(); - GetAllInterfaces2(type,temp); - return temp.ToArray(); - } - - private static void GetAllInterfaces2(Type type, HashSet rv) { - if ( type != null ) { - if ( type.IsInterface ) { - rv.Add(type); - } - GetAllInterfaces2(type.BaseType,rv); - foreach (Type inter in type.GetInterfaces()) { - GetAllInterfaces2(inter,rv); - } - } - } - - /// - /// Returns the generic type definition of the given type - /// - /// - /// - public static Type [] GetTypeErasure(Type [] types) { - Type [] rv = new Type[types.Length]; - for ( int i = 0; i < types.Length; i++ ) { - rv[i] = GetTypeErasure(types[i]); - } - return rv; - } - /// - /// Returns the generic type definition of the given type - /// - /// - /// - public static Type GetTypeErasure(Type type) { - if ( type.IsGenericType ) { - type = type.GetGenericTypeDefinition(); - } - return type; - } - - /// - /// Returns true iff t1 is a parent type of t2. Unlike - /// Type.isAssignableFrom, this handles generic types as - /// well. - /// - /// - /// - /// - public static bool IsParentTypeOf(Type t1, - Type t2) { - if (t2 == null) { - return t1 == null; - } - Type found = FindInHierarchyOf(t1,t2); - return found != null; - } - - /// - /// Finds t1 in the hierarchy of t2 or null if not found. The - /// returned value will have generic parameters applied to it. - /// - /// - /// - /// - public static Type FindInHierarchyOf(Type t1, Type t2) { - if (t2 == null) { - return null; - } - if ( EqualsIgnoreGeneric(t1,t2) ) { - return t2; - } - Type found1 = FindInHierarchyOf(t1,t2.BaseType); - if (found1 != null) { - return found1; - } - foreach (Type inter in t2.GetInterfaces()) { - Type found2 = FindInHierarchyOf(t1,inter); - if (found2 != null) { - return found2; - } - } - return null; - } - - private static bool EqualsIgnoreGeneric(Type t1, - Type t2) { - if ( t1 == null || t2 == null ) { - return t1 == null && t2 == null; - } - if ( t1.IsGenericType ) { - t1 = t1.GetGenericTypeDefinition(); - } - if ( t2.IsGenericType ) { - t2 = t2.GetGenericTypeDefinition(); - } - return t1.Equals(t2); - } - - public static Type [] GetParameterTypes(MethodInfo method) - { - ParameterInfo [] parameters = method.GetParameters(); - Type [] rv = new Type[parameters.Length]; - for (int i = 0; i < parameters.Length; i++) { - rv[i] = parameters[i].ParameterType; - } - return rv; - } - } -} diff --git a/DotNetConnectors.sln/Common/SafeType.cs b/DotNetConnectors.sln/Common/SafeType.cs deleted file mode 100644 index 31c4621e..00000000 --- a/DotNetConnectors.sln/Common/SafeType.cs +++ /dev/null @@ -1,142 +0,0 @@ -/* - * ==================== - * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER. - * - * Copyright 2007-2008 Sun Microsystems, Inc. All rights reserved. - * - * The contents of this file are subject to the terms of the Common Development - * and Distribution License("CDDL") (the "License"). You may not use this file - * except in compliance with the License. - * - * You can obtain a copy of the License at - * http://IdentityConnectors.dev.java.net/legal/license.txt - * See the License for the specific language governing permissions and limitations - * under the License. - * - * When distributing the Covered Code, include this CDDL Header Notice in each file - * and include the License file at identityconnectors/legal/license.txt. - * If applicable, add the following below this CDDL Header, with the fields - * enclosed by brackets [] replaced by your own identifying information: - * "Portions Copyrighted [year] [name of copyright owner]" - * ==================== - */ -using System; - -using System.Reflection; - -namespace Org.IdentityConnectors.Common -{ - /// - /// The equivalent of java's Class<? extends...%gt; syntax. - /// Allows you to restrict a Type to a certain class hierarchy. - /// - public sealed class SafeType - where T : class - { - private readonly Type _rawType; - - /// - /// Make private so no one can create directly - /// - private SafeType(Type rawType) - { - if (!ReflectionUtil.IsParentTypeOf(typeof(T),rawType)) { - throw new ArgumentException("Type: "+rawType+" is not a subclass of"+typeof(T)); - } - _rawType = rawType; - } - - /// - /// Returns the SafeType for a given raw type. - /// NOTE: If possible use Get() instead since it is statically - /// checked at compile time. - /// - /// - /// - public static SafeType ForRawType(Type type) - { - return new SafeType(type); - } - - /// - /// Gets an instance of the safe type in a type-safe fashion. - /// - /// The instance of the safe type - public static SafeType Get() - where U : T - { - return new SafeType(typeof(U)); - } - - /// - /// Gets an instance of the safe type in a type-safe fashion from an object. - /// - /// The instance of the safe type - public static SafeType Get(T obj) - { - return new SafeType(obj.GetType()); - } - - /// - /// Returns the generic type definition corresponding to this type. - /// Will return the same type if this is already a generic type. - /// - /// - public SafeType GetTypeErasure() - { - return SafeType.ForRawType(ReflectionUtil.GetTypeErasure(RawType)); - } - - /// - /// Returns the underlying C# type - /// - public Type RawType { - get { - return _rawType; - } - } - - /// - /// Creates a new instance of the given type - /// - /// The new instance - public T CreateInstance() - { - return (T)Activator.CreateInstance(RawType); - } - - /// - /// Returns true iff these represent the same underlying type - /// and the SafeType has the same parent type. - /// - /// The other - /// true iff these represent the same underylying type - /// and the TypeGroup has the same parent type - public override bool Equals(object o) - { - if ( o is SafeType ) { - SafeType other = (SafeType)o; - return RawType.Equals(other.RawType); - } - return false; - } - /// - /// Returns a hash of the type - /// - /// a hash of the type - public override int GetHashCode() - { - return RawType.GetHashCode(); - } - /// - /// Returns a string representation of the member type - /// - /// a string representation of the member type - public override string ToString() - { - return RawType.ToString(); - } - } - - -} diff --git a/DotNetConnectors.sln/Common/Script.cs b/DotNetConnectors.sln/Common/Script.cs deleted file mode 100644 index 4d1bbc36..00000000 --- a/DotNetConnectors.sln/Common/Script.cs +++ /dev/null @@ -1,167 +0,0 @@ -/* - * ==================== - * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER. - * - * Copyright 2007-2008 Sun Microsystems, Inc. All rights reserved. - * - * The contents of this file are subject to the terms of the Common Development - * and Distribution License("CDDL") (the "License"). You may not use this file - * except in compliance with the License. - * - * You can obtain a copy of the License at - * http://IdentityConnectors.dev.java.net/legal/license.txt - * See the License for the specific language governing permissions and limitations - * under the License. - * - * When distributing the Covered Code, include this CDDL Header Notice in each file - * and include the License file at identityconnectors/legal/license.txt. - * If applicable, add the following below this CDDL Header, with the fields - * enclosed by brackets [] replaced by your own identifying information: - * "Portions Copyrighted [year] [name of copyright owner]" - * ==================== - */ -using System; -using System.IO; -using System.Reflection; -using System.Collections.Generic; -using System.Diagnostics; - -namespace Org.IdentityConnectors.Common.Script -{ - public interface ScriptExecutor { - /// - /// Executes the script with the given arguments. - /// - /// key/value set of variables to - /// pass to the script. - /// - object Execute(IDictionary arguments); - } - public abstract class ScriptExecutorFactory { - - private static readonly object LOCK = new object(); - - /// - /// Loaded w/ all supported languages. - /// - private static IDictionary _supportedLanguages = null; - - /// - /// Load all script executor factory assemblies in the same directory - /// the 'Common' assembly. - /// - private static IDictionary LoadSupportedLanguages() { - // attempt to process all assemblies.. - IDictionary ret = new Dictionary(); - Assembly assembly = Assembly.GetExecutingAssembly(); - FileInfo thisAssemblyFile = new FileInfo(assembly.Location); - DirectoryInfo directory = thisAssemblyFile.Directory; - // get all *ScriptExecutorFactory assmebly from the current directory - FileInfo [] files = directory.GetFiles("*.ScriptExecutorFactory.dll"); - Type t = typeof(ScriptExecutorFactoryClassAttribute); - foreach (FileInfo file in files) { - try { - Assembly lib = Assembly.LoadFrom(file.ToString()); - foreach (Type type in lib.GetTypes()) { - Object [] attributes = type.GetCustomAttributes(t, false); - if ( attributes.Length > 0 ) { - ScriptExecutorFactoryClassAttribute attribute = - (ScriptExecutorFactoryClassAttribute)attributes[0]; - // attempt to test assembly.. - Activator.CreateInstance(type); - // if we made it this far its okay - ret[attribute.Language.ToUpper()] = type; - } - } - } - catch (Exception e) { - TraceUtil.TraceException("Unable to load assembly: "+ - assembly.FullName+". This is a fatal exception: ",e); - throw; - } - } - return ret; - } - - private static IDictionary GetSupportedLanguages() - { - lock (LOCK) - { - if (_supportedLanguages == null) - { - _supportedLanguages = LoadSupportedLanguages(); - } - return _supportedLanguages; - } - } - - /** - * Returns the set of supported languages. - * @return The set of supported languages. - */ - public static ICollection SupportedLanguages - { - get - { - IDictionary map = - GetSupportedLanguages(); - return CollectionUtil.AsReadOnlySet(map.Keys); - } - } - - /** - * Creates a ScriptExecutorFactory for the given language - * @param language The name of the language - * @return The script executor factory - * @throws IllegalArgumentException If the given language is not - * supported. - */ - public static ScriptExecutorFactory NewInstance(String language) { - if ( language == null ) { - throw new ArgumentException("Language must be specified"); - } - Type type = CollectionUtil.GetValue(GetSupportedLanguages(),language.ToUpper(),null); - if ( type == null ) { - throw new ArgumentException("Language not supported: "+language); - } - return (ScriptExecutorFactory) Activator.CreateInstance(type); - } - - - /** - * Creates a script executor for the given script. - * @param loader The classloader that contains the java classes - * that the script should have access to. - * @param script The script text. - * @param compile A hint to tell the script executor whether or - * not to compile the given script. This need not be implemented - * by all script executors. If true, the caller is saying that - * they intend to call the script multiple times with different - * arguments, so compile if possible. - * @return A script executor. - */ - public abstract ScriptExecutor NewScriptExecutor( - Assembly [] referencedAssemblies, - String script, - bool compile); - } - - [AttributeUsage(AttributeTargets.Class, AllowMultiple=false)] - public class ScriptExecutorFactoryClassAttribute : System.Attribute { - private readonly string _lang; - - /// - /// Determine the language supported by the factory. - /// - /// - public ScriptExecutorFactoryClassAttribute(string lang) { - _lang = lang; - } - - public string Language { - get { - return _lang; - } - } - } -} diff --git a/DotNetConnectors.sln/Common/Security.cs b/DotNetConnectors.sln/Common/Security.cs deleted file mode 100644 index 4d7473bf..00000000 --- a/DotNetConnectors.sln/Common/Security.cs +++ /dev/null @@ -1,542 +0,0 @@ -/* - * ==================== - * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER. - * - * Copyright 2007-2008 Sun Microsystems, Inc. All rights reserved. - * - * The contents of this file are subject to the terms of the Common Development - * and Distribution License("CDDL") (the "License"). You may not use this file - * except in compliance with the License. - * - * You can obtain a copy of the License at - * http://IdentityConnectors.dev.java.net/legal/license.txt - * See the License for the specific language governing permissions and limitations - * under the License. - * - * When distributing the Covered Code, include this CDDL Header Notice in each file - * and include the License file at identityconnectors/legal/license.txt. - * If applicable, add the following below this CDDL Header, with the fields - * enclosed by brackets [] replaced by your own identifying information: - * "Portions Copyrighted [year] [name of copyright owner]" - * ==================== - */ -using System; -using System.Security; -using System.Security.Cryptography; -using System.Runtime.InteropServices; -using System.Text; -namespace Org.IdentityConnectors.Common.Security -{ - #region UnmanagedArray - /// - /// Places an array facade on an unmanaged memory data structure that - /// holds senstive data. (In C# placing senstive data in managed - /// memory is considered unsecure since the memory model allows - /// data to be copied around). - /// - public interface UnmanagedArray : IDisposable - { - int Length {get;} - T this[int index] {get;set;} - } - #endregion - - /** - * Secure string implementation that solves the problems associated with - * keeping passwords as java.lang.String. That is, anything - * represented as a String is kept in memory as a clear - * text password and stays in memory at least until it is garbage collected. - *

- * The GuardedString class alleviates this problem by storing the characters in - * memory in an encrypted form. The encryption key will be a randomly-generated - * key. - *

- * In their serialized form, GuardedString will be encrypted using a known - * default key. This is to provide a minimum level of protection regardless - * of the transport. For communications with the Remote Connector Framework - * it is recommended that deployments enable SSL for true encryption. - *

- * Applications may also wish to persist GuardedStrings. In the case of - * Identity Manager, it should convert GuardedStrings to EncryptedData so - * that they can be stored and managed using the Manage Encryption features - * of Identity Manager. Other applications may wish to serialize APIConfiguration - * as a whole. These applications are responsible for encrypting the APIConfiguration - * blob for an additional layer of security (beyond the basic default key encryption - * provided by GuardedString). - */ - public sealed class GuardedString : IDisposable { - /** - * This method will be called with the clear text of the string. - * After the call the clearChars array will be automatically zeroed - * out, thus keeping the window of potential exposure to a bare-minimum. - * @param clearChars - */ - public delegate void Accessor(UnmanagedArray clearChars); - - - private SecureString _target; - private String _base64SHA1Hash; - - /** - * Creates an empty secure string - */ - public GuardedString() { - _target = new SecureString(); - ComputeHash(); - } - - public GuardedString(SecureString str) { - _target = str.Copy(); - ComputeHash(); - } - - - /** - * Provides access to the clear-text value of the string in a controlled fashion. - * The clear-text characters will only be available for the duration of the call - * and automatically zeroed out following the call. - * - *

- * NOTE: Callers are encouraged to use {@link #verifyBase64SHA1Hash(String)} - * where possible if the intended use is merely to verify the contents of - * the string match an expected hash value. - * @param accessor Accessor callback. - * @throws IllegalStateException If the string has been disposed - */ - public void Access(Accessor accessor) { - using (SecureStringAdapter adapter = new SecureStringAdapter(_target)) { - accessor(adapter); - } - } - - /** - * Appends a single clear-text character to the secure string. - * The in-memory data will be decrypted, the character will be - * appended, and then it will be re-encrypted. - * @param c The character to append. - * @throws IllegalStateException If the string is read-only - * @throws IllegalStateException If the string has been disposed - */ - public void AppendChar(char c) { - _target.AppendChar(c); - ComputeHash(); - } - - /** - * Clears the in-memory representation of the string. - */ - public void Dispose() { - _target.Dispose(); - } - - /** - * Returns true iff this string has been marked read-only - * @return true iff this string has been marked read-only - * @throws IllegalStateException If the string has been disposed - */ - public bool IsReadOnly() { - return _target.IsReadOnly(); - } - - /** - * Mark this string as read-only. - * @throws IllegalStateException If the string has been disposed - */ - public void MakeReadOnly() { - _target.MakeReadOnly(); - } - - /** - * Create a copy of the string. If this instance is read-only, - * the copy will not be read-only. - * @return A copy of the string. - * @throws IllegalStateException If the string has been disposed - */ - public GuardedString Copy() { - SecureString t2 = _target.Copy(); - GuardedString rv = new GuardedString(t2); - return rv; - } - - /** - * Verifies that this base-64 encoded SHA1 hash of this string - * matches the given value. - * @param hash The hash to verify against. - * @return True if the hash matches the given parameter. - * @throws IllegalStateException If the string has been disposed - */ - public bool VerifyBase64SHA1Hash(String hash) { - CheckNotDisposed(); - return _base64SHA1Hash.Equals(hash); - } - - public string GetBase64SHA1Hash() { - CheckNotDisposed(); - return _base64SHA1Hash; - } - - - - private void CheckNotDisposed() - { - //this throws if disposed - _target.IsReadOnly(); - } - - - public override bool Equals(Object o) { - if ( o is GuardedString ) { - GuardedString other = (GuardedString)o; - //not the true contract of equals. however, - //due to the high mathematical improbability of - //two unequal strings having the same secure hash, - //this approach feels good. the alternative, - //decrypting for comparison, is simply too - //performance intensive to be used for equals - return _base64SHA1Hash.Equals(other._base64SHA1Hash); - } - return false; - } - - public override int GetHashCode() { - return _base64SHA1Hash.GetHashCode(); - } - - public SecureString ToSecureString() { - return _target.Copy(); - } - - private void ComputeHash() - { - Access(array=> { - _base64SHA1Hash = SecurityUtil.ComputeBase64SHA1Hash(array); - }); - } - - } - - - #region AbstractUnmanagedArray - public abstract class AbstractUnmanagedArray : UnmanagedArray - { - private readonly int _length; - private bool _disposed; - public AbstractUnmanagedArray(int length) { - if (length < 0) { - throw new ArgumentException("Invalid length: "+length); - } - _length = length; - } - public int Length { - get { - if (_disposed) { - throw new ObjectDisposedException("UnmanagedArray"); - } - return _length; - } - } - public T this[int index] { - get { - if (_disposed) { - throw new ObjectDisposedException("UnmanagedArray"); - } - if ( index < 0 || index >= Length ) { - throw new IndexOutOfRangeException(); - } - return GetValue(index); - } - set { - if (_disposed) { - throw new ObjectDisposedException("SecureStringAdapter"); - } - if ( index < 0 || index >= Length ) { - throw new IndexOutOfRangeException(); - } - SetValue(index,value); - } - } - public void Dispose() { - if (!_disposed) { - for ( int i = 0; i < Length; i++ ) { - this[i] = default(T); - } - _disposed = true; - FreeMemory(); - } - } - - abstract protected T GetValue(int index); - abstract protected void SetValue(int index, T val); - abstract protected void FreeMemory(); - } - #endregion - - #region SecureStringAdapter - internal class SecureStringAdapter : AbstractUnmanagedArray - { - private IntPtr _bstrPtr; - public SecureStringAdapter(SecureString secureString) : base(secureString.Length) { - Assertions.NullCheck(secureString,"secureString"); - _bstrPtr = Marshal.SecureStringToBSTR(secureString); - } - protected override char GetValue(int index) - { - unsafe { - char * charPtr = (char*)_bstrPtr; - return *(charPtr+index); - } - } - protected override void SetValue(int index, char c) - { - unsafe { - char * charPtr = (char*)_bstrPtr; - *(charPtr+index) = c; - } - } - protected override void FreeMemory() - { - Marshal.ZeroFreeBSTR( _bstrPtr ); - } - } - #endregion - - #region UnmanagedCharArray - public class UnmanagedCharArray : AbstractUnmanagedArray - { - private IntPtr _ptr; - public UnmanagedCharArray(int length) : base(length) { - unsafe { - _ptr = Marshal.AllocHGlobal(length*sizeof(char)); - } - } - protected override char GetValue(int index) - { - unsafe { - char * charPtr = (char*)_ptr; - return *(charPtr+index); - } - } - protected override void SetValue(int index, char c) - { - unsafe { - char * charPtr = (char*)_ptr; - *(charPtr+index) = c; - } - } - protected override void FreeMemory() - { - Marshal.FreeHGlobal( _ptr ); - } - } - #endregion - - #region UnmanagedByteArray - public class UnmanagedByteArray : AbstractUnmanagedArray - { - private IntPtr _ptr; - public UnmanagedByteArray(int length) : base(length) { - unsafe { - _ptr = Marshal.AllocHGlobal(length*sizeof(byte)); - } - } - protected override byte GetValue(int index) - { - unsafe { - byte * charPtr = (byte*)_ptr; - return *(charPtr+index); - } - } - protected override void SetValue(int index, byte c) - { - unsafe { - byte * charPtr = (byte*)_ptr; - *(charPtr+index) = c; - } - } - protected override void FreeMemory() - { - Marshal.FreeHGlobal( _ptr ); - } - } - #endregion - - #region SecurityUtil - ///

- /// Description of SecurityUtil. - /// - public static class SecurityUtil - { - - /** - * Converts chars to bytes without using any external functions - * that might allocate additional buffers for the potentially - * sensitive data. This guarantees the caller that they only - * need to cleanup the input and result. - * @param chars The chars - * @return The bytes - */ - public static UnmanagedArray CharsToBytes(UnmanagedArray chars) - { - UnmanagedByteArray bytes = new UnmanagedByteArray(chars.Length*2); - - for ( int i = 0; i < chars.Length; i++ ) { - char v = chars[i]; - bytes[i*2] = (byte)(0xff & (v >> 8)); - bytes[i*2+1] = (byte)(0xff & (v)); - } - return bytes; - } - - /** - * Converts bytes to chars without using any external functions - * that might allocate additional buffers for the potentially - * sensitive data. This guarantees the caller that they only - * need to cleanup the input and result. - * @param chars The chars - * @return The bytes - */ - public static UnmanagedArray BytesToChars(UnmanagedArray bytes) - { - UnmanagedCharArray chars = new UnmanagedCharArray(bytes.Length/2); - for ( int i = 0; i < chars.Length; i++ ) { - char v = (char)((bytes[i*2]<<8) | bytes[i*2+1]); - chars[i] = v; - } - return chars; - } - - - public unsafe static string ComputeBase64SHA1Hash(UnmanagedArray input) - { - using (UnmanagedArray bytes = SecurityUtil.CharsToBytes(input)) { - byte [] managedBytes = new byte[bytes.Length]; - fixed (byte*dummy=managedBytes) { //pin it - try { - //populate it in pinned block - SecurityUtil.UnmanagedBytesToManagedBytes(bytes,managedBytes); - SHA1 hasher = SHA1.Create(); - byte[] data = hasher.ComputeHash(managedBytes); - return Convert.ToBase64String(data); - } - finally { - //clear it before we leave pinned block - SecurityUtil.Clear(managedBytes); - } - } - } - } - - /** - * Copies an unmanaged byte array into a managed byte array. - * NOTE: it is imperative for security reasons that this only - * be done in a context where the byte array in question is pinned. - * moreover, the byte array must be cleared prior to leaving the - * pinned block - */ - public static void UnmanagedBytesToManagedBytes(UnmanagedArray array, - byte [] bytes) - { - for ( int i = 0 ; i < array.Length; i++ ) - { - bytes[i] = array[i]; - } - } - - /** - * Clears an array of potentially sensitive bytes - * @param bytes The bytes. May be null. - * NOTE: because this is C#, this alone is not enough. The - * array must be pinned during the interval it is in-use or - * it could be copied out from under you. - */ - public static void Clear(byte [] bytes) - { - if ( bytes != null ) - { - for ( int i = 0; i < bytes.Length; i++ ) - { - bytes[i] = 0; - } - } - } - - /** - * Clears an array of potentially sensitive chars - * @param chars The characters. May be null. - * NOTE: because this is C#, this alone is not enough. The - * array must be pinned during the interval it is in-use or - * it could be copied out from under you. - */ - public static void Clear(char [] chars) - { - if ( chars != null ) - { - for ( int i = 0; i < chars.Length; i++) - { - chars[i] = (char)0; - } - } - } - - public static bool VerifyBase64SHA1Hash(UnmanagedArray input, string hash) - { - string inputHash = ComputeBase64SHA1Hash(input); - return inputHash.Equals(hash); - } - } - #endregion - - #region Encryptor - /** - * Responsible for encrypting/decrypting bytes. Implementations - * are intended to be thread-safe. - */ - public interface Encryptor { - /** - * Decrypts the given byte array - * @param bytes The encrypted bytes - * @return The decrypted bytes - */ - UnmanagedArray Decrypt(byte [] bytes); - - /** - * Encrypts the given byte array - * @param bytes The clear bytes - * @return The ecnrypted bytes - */ - byte [] Encrypt(UnmanagedArray bytes); - } - #endregion - - #region EncryptorFactory - public abstract class EncryptorFactory { - private static readonly object LOCK = new object(); - - // At some point we might make this pluggable, but for now, hard-code - private const String IMPL_NAME = "Org.IdentityConnectors.Common.Security.Impl.EncryptorFactoryImpl"; - - private static EncryptorFactory _instance; - - /** - * Get the singleton instance of the {@link EncryptorFactory}. - */ - public static EncryptorFactory GetInstance() { - lock(LOCK) { - if (_instance == null) { - Type type = FrameworkInternalBridge.LoadType(IMPL_NAME); - _instance = (EncryptorFactory)Activator.CreateInstance(type); - } - return _instance; - } - } - - /** - * Default encryptor that encrypts/descrypts using a default key - */ - public abstract Encryptor GetDefaultEncryptor(); - - - } - #endregion - -} diff --git a/DotNetConnectors.sln/Common/StringUtil.cs b/DotNetConnectors.sln/Common/StringUtil.cs deleted file mode 100644 index 2fef0f7b..00000000 --- a/DotNetConnectors.sln/Common/StringUtil.cs +++ /dev/null @@ -1,92 +0,0 @@ -/* - * ==================== - * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER. - * - * Copyright 2007-2008 Sun Microsystems, Inc. All rights reserved. - * - * The contents of this file are subject to the terms of the Common Development - * and Distribution License("CDDL") (the "License"). You may not use this file - * except in compliance with the License. - * - * You can obtain a copy of the License at - * http://IdentityConnectors.dev.java.net/legal/license.txt - * See the License for the specific language governing permissions and limitations - * under the License. - * - * When distributing the Covered Code, include this CDDL Header Notice in each file - * and include the License file at identityconnectors/legal/license.txt. - * If applicable, add the following below this CDDL Header, with the fields - * enclosed by brackets [] replaced by your own identifying information: - * "Portions Copyrighted [year] [name of copyright owner]" - * ==================== - */ -using System; -using Org.IdentityConnectors.Common.Security; -namespace Org.IdentityConnectors.Common -{ - public static class StringUtil - { - /** - * Determines if a string is empty. Empty is defined as null or empty - * string. - * - *
-         *  StringUtil.isEmpty(null)               = true
-         *  StringUtil.isEmpty("")       = true
-         *  StringUtil.isEmpty(" ")      = false
-         *  StringUtil.isEmpty("bob")    = false
-         *  StringUtil.isEmpty(" bob ")  = false
-         * 
- * - * @param val - * string to evaluate as empty. - * @return true if the string is empty else false. - */ - public static bool IsEmpty(String val) { - return (val == null) ? true : val.Length == 0; - } - - /** - *
-         *      StringUtil.isBlank(null)                = true
-         *      StringUtil.isBlank("")        = true
-         *      StringUtil.isBlank(" ")       = true
-         *      StringUtil.isBlank("bob")     = false
-         *      StringUtil.isBlank("  bob  ") = false
-         * 
- */ - public static bool IsBlank(String val) { - return (val == null) ? true : IsEmpty(val.Trim()); - } - - /// - /// Constructs a secure string from a char []. The char[] will - /// be cleared out when finished. - /// - /// The characters to use. Will be cleared - /// out. - /// A secure string representation - public static GuardedString NewGuardedString(char [] val) - { - GuardedString rv = new GuardedString(); - for( int i = 0; i < val.Length; i++ ) - { - rv.AppendChar(val[i]); - val[i] = (char)0; - } - return rv; - } - - - public static bool IsTrue(string val) { - if (!IsBlank(val)) { - // clean up the value.. - val = val.Trim().ToLower(); - if (val.Equals("1") || val.Equals("on") || val.Equals("true")) { - return true; - } - } - return false; - } - } -} diff --git a/DotNetConnectors.sln/Common/TraceUtil.cs b/DotNetConnectors.sln/Common/TraceUtil.cs deleted file mode 100644 index d12f5e70..00000000 --- a/DotNetConnectors.sln/Common/TraceUtil.cs +++ /dev/null @@ -1,51 +0,0 @@ -/* - * ==================== - * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER. - * - * Copyright 2007-2008 Sun Microsystems, Inc. All rights reserved. - * - * The contents of this file are subject to the terms of the Common Development - * and Distribution License("CDDL") (the "License"). You may not use this file - * except in compliance with the License. - * - * You can obtain a copy of the License at - * http://IdentityConnectors.dev.java.net/legal/license.txt - * See the License for the specific language governing permissions and limitations - * under the License. - * - * When distributing the Covered Code, include this CDDL Header Notice in each file - * and include the License file at identityconnectors/legal/license.txt. - * If applicable, add the following below this CDDL Header, with the fields - * enclosed by brackets [] replaced by your own identifying information: - * "Portions Copyrighted [year] [name of copyright owner]" - * ==================== - */ -using System; -using System.Diagnostics; -using System.Text; -namespace Org.IdentityConnectors.Common -{ - /// - /// Description of TraceUtil. - /// - public static class TraceUtil - { - /// - /// Traces an exception with its stack trace - /// - /// An optional error message to display in addition to the exception - /// The exception - public static void TraceException(String msg, Exception e) { - StringBuilder builder = new StringBuilder(); - if ( msg != null ) - { - builder.AppendLine(msg); - } - if ( e != null ) - { - builder.AppendLine(e.ToString()); - } - Trace.TraceError(builder.ToString()); - } - } -} diff --git a/DotNetConnectors.sln/Common/XmlUtil.cs b/DotNetConnectors.sln/Common/XmlUtil.cs deleted file mode 100644 index 30ea8040..00000000 --- a/DotNetConnectors.sln/Common/XmlUtil.cs +++ /dev/null @@ -1,192 +0,0 @@ -/* - * ==================== - * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER. - * - * Copyright 2007-2008 Sun Microsystems, Inc. All rights reserved. - * - * The contents of this file are subject to the terms of the Common Development - * and Distribution License("CDDL") (the "License"). You may not use this file - * except in compliance with the License. - * - * You can obtain a copy of the License at - * http://IdentityConnectors.dev.java.net/legal/license.txt - * See the License for the specific language governing permissions and limitations - * under the License. - * - * When distributing the Covered Code, include this CDDL Header Notice in each file - * and include the License file at identityconnectors/legal/license.txt. - * If applicable, add the following below this CDDL Header, with the fields - * enclosed by brackets [] replaced by your own identifying information: - * "Portions Copyrighted [year] [name of copyright owner]" - * ==================== - */ -using System; -using System.Xml; -using System.Text; -namespace Org.IdentityConnectors.Common -{ - /// - /// Description of XmlUtil. - /// - public static class XmlUtil - { - ///////////////////////////////////////////////////////////// - // - // DOM Navigation utilities - // - //////////////////////////////////////////////////////////// - - /** - * Return the value of an attribute on an element.

The DOM getAttribute - * method returns an empty string if the attribute doesn't exist. Here, we - * detect this and return null. - */ - public static String GetAttribute(XmlElement e, String name) { - String value = e.GetAttribute(name); - if (value != null && value.Length == 0) - value = null; - return value; - } - - /** - * Find an immediate child of the given name - */ - public static XmlElement FindImmediateChildElement(XmlNode node, String name) { - - XmlElement found = null; - - if (node != null) { - - for (XmlNode child = node.FirstChild; child != null - && found == null; child = child.NextSibling) { - - if (child.NodeType == XmlNodeType.Element) { - XmlElement tmp = (XmlElement) child; - if ( tmp.LocalName.Equals(name) ) { - return tmp; - } - } - } - } - - return found; - } - - /** - * Returns the First child element or null if none found - * @param node The node. May be null. - * @return the First child element or null if none found - */ - public static XmlElement GetFirstChildElement(XmlNode node) { - if ( node == null ) { - return null; - } - XmlNode child = node.FirstChild; - if ( child != null && child.NodeType == XmlNodeType.Element ) { - return (XmlElement)child; - } - else { - return GetNextElement(child); - } - } - - /** - * Get the next right sibling that is an element. - */ - public static XmlElement GetNextElement(XmlNode node) { - - XmlElement found = null; - - if (node != null) { - - for (XmlNode next = node.NextSibling; next != null - && found == null; next = next.NextSibling) { - - if (next.NodeType == XmlNodeType.Element) - found = (XmlElement) next; - } - } - - return found; - } - - /** - * Locate the first text node at any level below the given node. If the - * ignoreEmpty flag is true, we will ignore text nodes that contain only - * whitespace characteres.

Note that if you're trying to extract - * element content, you probably don't want this since parser's can break up - * pcdata into multiple adjacent text nodes. See getContent() for a more - * useful method. - */ - private static XmlText FindText(XmlNode node, bool ignoreEmpty) { - - XmlText found = null; - - if (node != null) { - - if (node.NodeType == XmlNodeType.Text - || node.NodeType == XmlNodeType.CDATA) { - - XmlText t = (XmlText) node; - if (!ignoreEmpty) - found = t; - else { - String s = t.Data.Trim(); - if (s.Length > 0) - found = t; - } - } - - if (found == null) { - - for (XmlNode child = node.FirstChild; child != null - && found == null; child = child.NextSibling) { - - found = FindText(child, ignoreEmpty); - } - } - } - - return found; - } - - - /** - * Return the content of the given element.

We will descend to an - * arbitrary depth looking for the first text node.

Note that - * the parser may break what was originally a single string of pcdata into - * multiple adjacent text nodes. Xerces appears to do this when it - * encounters a '$' in the text, not sure if there is specified behavior, or - * if its parser specific.

Here, we will congeal adjacent text nodes. - *

We will NOT ignore text nodes that have only whitespace. - */ - public static String GetContent(XmlElement e) { - - String content = null; - - if (e != null) { - - // find the first inner text node, - XmlText t = FindText(e, false); - if (t != null) { - // we have at least some text - StringBuilder b = new StringBuilder(); - while (t != null) { - b.Append(t.Data); - XmlNode n = t.NextSibling; - - t = null; - if (n != null - && ((n.NodeType == XmlNodeType.Text) || - (n.NodeType == XmlNodeType.CDATA))) { - t = (XmlText) n; - } - } - content = b.ToString(); - } - } - - return content; - } - } -} diff --git a/DotNetConnectors.sln/Console/AssemblyInfo.cs b/DotNetConnectors.sln/Console/AssemblyInfo.cs deleted file mode 100644 index f8507a15..00000000 --- a/DotNetConnectors.sln/Console/AssemblyInfo.cs +++ /dev/null @@ -1,53 +0,0 @@ -/* - * ==================== - * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER. - * - * Copyright 2007-2008 Sun Microsystems, Inc. All rights reserved. - * - * The contents of this file are subject to the terms of the Common Development - * and Distribution License("CDDL") (the "License"). You may not use this file - * except in compliance with the License. - * - * You can obtain a copy of the License at - * http://IdentityConnectors.dev.java.net/legal/license.txt - * See the License for the specific language governing permissions and limitations - * under the License. - * - * When distributing the Covered Code, include this CDDL Header Notice in each file - * and include the License file at identityconnectors/legal/license.txt. - * If applicable, add the following below this CDDL Header, with the fields - * enclosed by brackets [] replaced by your own identifying information: - * "Portions Copyrighted [year] [name of copyright owner]" - * ==================== - */ -#region Using directives - -using System.Reflection; -using System.Runtime.CompilerServices; -using System.Runtime.InteropServices; - -#endregion - -// General Information about an assembly is controlled through the following -// set of attributes. Change these attribute values to modify the information -// associated with an assembly. -[assembly: AssemblyTitle("Console")] -[assembly: AssemblyDescription("")] -[assembly: AssemblyConfiguration("")] -[assembly: AssemblyCompany("")] -[assembly: AssemblyProduct("Console")] -[assembly: AssemblyCopyright("")] -[assembly: AssemblyTrademark("")] -[assembly: AssemblyCulture("")] - -// This sets the default COM visibility of types in the assembly to invisible. -// If you need to expose a type to COM, use [ComVisible(true)] on that type. -[assembly: ComVisible(false)] - -// The assembly version has following format : -// -// Major.Minor.Build.Revision -// -// You can specify all the values or you can use the default the Revision and -// Build Numbers by using the '*' as shown below: -[assembly: AssemblyVersion("1.0.0.0")] diff --git a/DotNetConnectors.sln/Console/Console.csproj b/DotNetConnectors.sln/Console/Console.csproj deleted file mode 100644 index 1192908e..00000000 --- a/DotNetConnectors.sln/Console/Console.csproj +++ /dev/null @@ -1,90 +0,0 @@ - - - - {D09E6A1F-55F6-4661-8E5A-C485E4B08BB3} - Debug - AnyCPU - WinExe - Console - Console - v3.5 - - - prompt - AnyCPU - ./bin/Debug/ - True - Full - False - True - DEBUG;TRACE - WinExe - Console - False - 4 - - - pdbonly - AnyCPU - ./bin/Release/ - False - True - False - TRACE - WinExe - Console - False - 4 - - - - - ..\..\..\..\..\Program Files\SharpDevelop\3.0\AddIns\AddIns\BackendBindings\BooBinding\Boo.Lang.dll - - - - 3.5 - - - - - - - - - - Form - - - MainForm.cs - - - - - - {8B24461B-456A-4032-89A1-CD418F7B5B62} - Framework - - - diff --git a/DotNetConnectors.sln/Console/MainForm.Designer.cs b/DotNetConnectors.sln/Console/MainForm.Designer.cs deleted file mode 100644 index 1782f06d..00000000 --- a/DotNetConnectors.sln/Console/MainForm.Designer.cs +++ /dev/null @@ -1,47 +0,0 @@ -/* - * Created by SharpDevelop. - * User: Administrator - * Date: 3/18/2008 - * Time: 4:30 PM - * - * To change this template use Tools | Options | Coding | Edit Standard Headers. - */ -namespace Console -{ - partial class MainForm - { - ///

- /// Designer variable used to keep track of non-visual components. - /// - private System.ComponentModel.IContainer components = null; - - /// - /// Disposes resources used by the form. - /// - /// true if managed resources should be disposed; otherwise, false. - protected override void Dispose(bool disposing) - { - if (disposing) { - if (components != null) { - components.Dispose(); - } - } - base.Dispose(disposing); - } - - /// - /// This method is required for Windows Forms designer support. - /// Do not change the method contents inside the source code editor. The Forms designer might - /// not be able to load this method if it was changed manually. - /// - private void InitializeComponent() - { - // - // MainForm - // - this.AutoScaleMode = System.Windows.Forms.AutoScaleMode.Font; - this.Text = "Console"; - this.Name = "MainForm"; - } - } -} diff --git a/DotNetConnectors.sln/Console/MainForm.cs b/DotNetConnectors.sln/Console/MainForm.cs deleted file mode 100644 index ca92a32a..00000000 --- a/DotNetConnectors.sln/Console/MainForm.cs +++ /dev/null @@ -1,47 +0,0 @@ -/* - * ==================== - * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER. - * - * Copyright 2007-2008 Sun Microsystems, Inc. All rights reserved. - * - * The contents of this file are subject to the terms of the Common Development - * and Distribution License("CDDL") (the "License"). You may not use this file - * except in compliance with the License. - * - * You can obtain a copy of the License at - * http://IdentityConnectors.dev.java.net/legal/license.txt - * See the License for the specific language governing permissions and limitations - * under the License. - * - * When distributing the Covered Code, include this CDDL Header Notice in each file - * and include the License file at identityconnectors/legal/license.txt. - * If applicable, add the following below this CDDL Header, with the fields - * enclosed by brackets [] replaced by your own identifying information: - * "Portions Copyrighted [year] [name of copyright owner]" - * ==================== - */ -using System; -using System.Collections.Generic; -using System.Drawing; -using System.Windows.Forms; - -namespace Console -{ - /// - /// Description of MainForm. - /// - public partial class MainForm : Form - { - public MainForm() - { - // - // The InitializeComponent() call is required for Windows Forms designer support. - // - InitializeComponent(); - - // - // TODO: Add constructor code after the InitializeComponent() call. - // - } - } -} diff --git a/DotNetConnectors.sln/Console/Program.cs b/DotNetConnectors.sln/Console/Program.cs deleted file mode 100644 index 23030249..00000000 --- a/DotNetConnectors.sln/Console/Program.cs +++ /dev/null @@ -1,45 +0,0 @@ -/* - * ==================== - * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER. - * - * Copyright 2007-2008 Sun Microsystems, Inc. All rights reserved. - * - * The contents of this file are subject to the terms of the Common Development - * and Distribution License("CDDL") (the "License"). You may not use this file - * except in compliance with the License. - * - * You can obtain a copy of the License at - * http://IdentityConnectors.dev.java.net/legal/license.txt - * See the License for the specific language governing permissions and limitations - * under the License. - * - * When distributing the Covered Code, include this CDDL Header Notice in each file - * and include the License file at identityconnectors/legal/license.txt. - * If applicable, add the following below this CDDL Header, with the fields - * enclosed by brackets [] replaced by your own identifying information: - * "Portions Copyrighted [year] [name of copyright owner]" - * ==================== - */ -using System; -using System.Windows.Forms; - -namespace Console -{ - /// - /// Class with program entry point. - /// - internal sealed class Program - { - /// - /// Program entry point. - /// - [STAThread] - private static void Main(string[] args) - { - Application.EnableVisualStyles(); - Application.SetCompatibleTextRenderingDefault(false); - Application.Run(new MainForm()); - } - - } -} diff --git a/DotNetConnectors.sln/DotNetConnectors.sln b/DotNetConnectors.sln/DotNetConnectors.sln deleted file mode 100644 index c59e1e95..00000000 --- a/DotNetConnectors.sln/DotNetConnectors.sln +++ /dev/null @@ -1,90 +0,0 @@ - -Microsoft Visual Studio Solution File, Format Version 10.00 -# Visual Studio 2008 -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Service", "Service\Service.csproj", "{A9D6374A-D51F-4FA3-8C02-5B1D23FAA82E}" -EndProject -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Framework", "Framework\Framework.csproj", "{8B24461B-456A-4032-89A1-CD418F7B5B62}" -EndProject -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Common", "Common\Common.csproj", "{F140E8DA-52B4-4159-992A-9DA10EA8EEFB}" -EndProject -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "FrameworkTests", "FrameworkTests\FrameworkTests.csproj", "{32804F5A-812C-4FA6-835C-BDAE5B24D355}" -EndProject -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "TestBundleV1", "TestBundleV1\TestBundleV1.csproj", "{0BC2A013-56FE-46DD-90FC-2D2D57748FB6}" -EndProject -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "TestBundleV2", "TestBundleV2\TestBundleV2.csproj", "{3E737796-3A83-4924-9FF1-DC542F21F59E}" -EndProject -Project("{930C7802-8A8C-48F9-8165-68863BCCD9DD}") = "ServiceInstall", "ServiceInstall\ServiceInstall.wixproj", "{1CBA8F74-050C-432B-8437-08BD13BDC684}" -EndProject -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Console", "Console\Console.csproj", "{D09E6A1F-55F6-4661-8E5A-C485E4B08BB3}" -EndProject -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "FrameworkInternal", "FrameworkInternal\FrameworkInternal.csproj", "{5B011775-B121-4EEE-A410-BA2D2F5BFB8B}" -EndProject -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "BooScriptExecutorFactory", "BooScriptExecutorFactory\BooScriptExecutorFactory.csproj", "{0747C440-70E4-4E63-9F9D-03B3A010C991}" -EndProject -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "ShellScriptExecutorFactory", "ShellScriptExecutorFactory\ShellScriptExecutorFactory.csproj", "{4700690A-2D29-40A0-86AC-E5A9F71A479A}" -EndProject -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "ActiveDirectoryConnector", "ActiveDirectoryConnector\ActiveDirectoryConnector.csproj", "{BDF495CA-0FCD-4E51-A871-D467CDE3B43E}" -EndProject -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "ActiveDirectoryConnectorTests", "ActiveDirectoryConnectorTests\ActiveDirectoryConnectorTests.csproj", "{DB8F7DA9-11A2-4BEB-8C14-25D8229EBE7E}" -EndProject -Global - GlobalSection(SolutionConfigurationPlatforms) = preSolution - Debug|Any CPU = Debug|Any CPU - Release|Any CPU = Release|Any CPU - EndGlobalSection - GlobalSection(ProjectConfigurationPlatforms) = postSolution - {A9D6374A-D51F-4FA3-8C02-5B1D23FAA82E}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {A9D6374A-D51F-4FA3-8C02-5B1D23FAA82E}.Debug|Any CPU.Build.0 = Debug|Any CPU - {A9D6374A-D51F-4FA3-8C02-5B1D23FAA82E}.Release|Any CPU.ActiveCfg = Release|Any CPU - {A9D6374A-D51F-4FA3-8C02-5B1D23FAA82E}.Release|Any CPU.Build.0 = Release|Any CPU - {8B24461B-456A-4032-89A1-CD418F7B5B62}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {8B24461B-456A-4032-89A1-CD418F7B5B62}.Debug|Any CPU.Build.0 = Debug|Any CPU - {8B24461B-456A-4032-89A1-CD418F7B5B62}.Release|Any CPU.ActiveCfg = Release|Any CPU - {8B24461B-456A-4032-89A1-CD418F7B5B62}.Release|Any CPU.Build.0 = Release|Any CPU - {F140E8DA-52B4-4159-992A-9DA10EA8EEFB}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {F140E8DA-52B4-4159-992A-9DA10EA8EEFB}.Debug|Any CPU.Build.0 = Debug|Any CPU - {F140E8DA-52B4-4159-992A-9DA10EA8EEFB}.Release|Any CPU.ActiveCfg = Release|Any CPU - {F140E8DA-52B4-4159-992A-9DA10EA8EEFB}.Release|Any CPU.Build.0 = Release|Any CPU - {32804F5A-812C-4FA6-835C-BDAE5B24D355}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {32804F5A-812C-4FA6-835C-BDAE5B24D355}.Debug|Any CPU.Build.0 = Debug|Any CPU - {32804F5A-812C-4FA6-835C-BDAE5B24D355}.Release|Any CPU.ActiveCfg = Release|Any CPU - {32804F5A-812C-4FA6-835C-BDAE5B24D355}.Release|Any CPU.Build.0 = Release|Any CPU - {0BC2A013-56FE-46DD-90FC-2D2D57748FB6}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {0BC2A013-56FE-46DD-90FC-2D2D57748FB6}.Debug|Any CPU.Build.0 = Debug|Any CPU - {0BC2A013-56FE-46DD-90FC-2D2D57748FB6}.Release|Any CPU.ActiveCfg = Release|Any CPU - {0BC2A013-56FE-46DD-90FC-2D2D57748FB6}.Release|Any CPU.Build.0 = Release|Any CPU - {3E737796-3A83-4924-9FF1-DC542F21F59E}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {3E737796-3A83-4924-9FF1-DC542F21F59E}.Debug|Any CPU.Build.0 = Debug|Any CPU - {3E737796-3A83-4924-9FF1-DC542F21F59E}.Release|Any CPU.ActiveCfg = Release|Any CPU - {3E737796-3A83-4924-9FF1-DC542F21F59E}.Release|Any CPU.Build.0 = Release|Any CPU - {1CBA8F74-050C-432B-8437-08BD13BDC684}.Debug|Any CPU.ActiveCfg = Debug|x86 - {1CBA8F74-050C-432B-8437-08BD13BDC684}.Release|Any CPU.ActiveCfg = Release|x86 - {D09E6A1F-55F6-4661-8E5A-C485E4B08BB3}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {D09E6A1F-55F6-4661-8E5A-C485E4B08BB3}.Debug|Any CPU.Build.0 = Debug|Any CPU - {D09E6A1F-55F6-4661-8E5A-C485E4B08BB3}.Release|Any CPU.ActiveCfg = Release|Any CPU - {D09E6A1F-55F6-4661-8E5A-C485E4B08BB3}.Release|Any CPU.Build.0 = Release|Any CPU - {5B011775-B121-4EEE-A410-BA2D2F5BFB8B}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {5B011775-B121-4EEE-A410-BA2D2F5BFB8B}.Debug|Any CPU.Build.0 = Debug|Any CPU - {5B011775-B121-4EEE-A410-BA2D2F5BFB8B}.Release|Any CPU.ActiveCfg = Release|Any CPU - {5B011775-B121-4EEE-A410-BA2D2F5BFB8B}.Release|Any CPU.Build.0 = Release|Any CPU - {0747C440-70E4-4E63-9F9D-03B3A010C991}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {0747C440-70E4-4E63-9F9D-03B3A010C991}.Debug|Any CPU.Build.0 = Debug|Any CPU - {0747C440-70E4-4E63-9F9D-03B3A010C991}.Release|Any CPU.ActiveCfg = Release|Any CPU - {0747C440-70E4-4E63-9F9D-03B3A010C991}.Release|Any CPU.Build.0 = Release|Any CPU - {4700690A-2D29-40A0-86AC-E5A9F71A479A}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {4700690A-2D29-40A0-86AC-E5A9F71A479A}.Debug|Any CPU.Build.0 = Debug|Any CPU - {4700690A-2D29-40A0-86AC-E5A9F71A479A}.Release|Any CPU.ActiveCfg = Release|Any CPU - {4700690A-2D29-40A0-86AC-E5A9F71A479A}.Release|Any CPU.Build.0 = Release|Any CPU - {BDF495CA-0FCD-4E51-A871-D467CDE3B43E}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {BDF495CA-0FCD-4E51-A871-D467CDE3B43E}.Debug|Any CPU.Build.0 = Debug|Any CPU - {BDF495CA-0FCD-4E51-A871-D467CDE3B43E}.Release|Any CPU.ActiveCfg = Release|Any CPU - {BDF495CA-0FCD-4E51-A871-D467CDE3B43E}.Release|Any CPU.Build.0 = Release|Any CPU - {DB8F7DA9-11A2-4BEB-8C14-25D8229EBE7E}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {DB8F7DA9-11A2-4BEB-8C14-25D8229EBE7E}.Debug|Any CPU.Build.0 = Debug|Any CPU - {DB8F7DA9-11A2-4BEB-8C14-25D8229EBE7E}.Release|Any CPU.ActiveCfg = Release|Any CPU - {DB8F7DA9-11A2-4BEB-8C14-25D8229EBE7E}.Release|Any CPU.Build.0 = Release|Any CPU - EndGlobalSection - GlobalSection(SolutionProperties) = preSolution - HideSolutionNode = FALSE - EndGlobalSection -EndGlobal diff --git a/DotNetConnectors.sln/ExchangeConnector/CommandInfos.xml b/DotNetConnectors.sln/ExchangeConnector/CommandInfos.xml deleted file mode 100644 index 2690e95f..00000000 --- a/DotNetConnectors.sln/ExchangeConnector/CommandInfos.xml +++ /dev/null @@ -1,257 +0,0 @@ - - - - - - New-MailUser - Name - - ExternalEmailAddress - OrganizationalUnit - Password - UserPrincipalName - Alias - DisplayName - DomainController - FirstName - Initials - LastName - MacAttachmentFormat - MessageBodyFormat - MessageFormat - ResetPasswordOnNextLogon - SamAccountName - TemplateInstance - UsePreferMessageFormat - - - - Set-MailUser - Identity - - AcceptMessagesOnlyFrom - AcceptMessagesOnlyFromDLMembers - Alias - CreateDTMFMap - CustomAttribute1 - CustomAttribute2 - CustomAttribute3 - CustomAttribute4 - CustomAttribute5 - CustomAttribute6 - CustomAttribute7 - CustomAttribute8 - CustomAttribute9 - CustomAttribute10 - CustomAttribute11 - CustomAttribute12 - CustomAttribute13 - CustomAttribute14 - CustomAttribute15 - DisplayName - DomainController - EmailAddresses - EmailAddressPolicyEnabled - Extensions - ExternalEmailAddress - GrantSendOnBehalfTo - HiddenFromAddressListsEnabled - Instance - MacAttachmentFormat - MessageBodyFormat - MessageFormat - Name - PrimarySmtpAddress - RecipientLimits - RejectMessagesFrom - RejectMessagesFromDLMembers - RequireSenderAuthenticationEnabled - SamAccountName - SecondaryAddress - SecondaryDialPlan - SimpleDisplayName - UMDtmfMap - UseMapiRichTextFormat - UsePreferMessageFormat - UserPrincipalName - WindowsEmailAddress - MaxReceiveSize - MaxSendSize - - - - Get-MailUser - - Identity - OrganizationalUnit - ReadFromDomainController - - - Filter - - AcceptMessagesOnlyFrom - AcceptMessagesOnlyFromDLMembers - Alias - CustomAttribute1 - CustomAttribute2 - CustomAttribute3 - CustomAttribute4 - CustomAttribute5 - CustomAttribute6 - CustomAttribute7 - CustomAttribute8 - CustomAttribute9 - CustomAttribute10 - CustomAttribute11 - CustomAttribute12 - CustomAttribute13 - CustomAttribute14 - CustomAttribute15 - DisplayName - DistinguishedName - EmailAddresses - EmailAddressPolicyEnabled - ExchangeVersion - ExternalEmailAddress - GrantSendOnBehalfTo - Guid - HiddenFromAddressListsEnabled - MaxReceiveSize - MaxSendSize - Name - PrimarySmtpAddress - RecipientLimits - RecipientType - RecipientTypeDetails - RejectMessagesFrom - RejectMessagesFromDLMembers - SamAccountName - SimpleDisplayName - UMDtmfMap - UserPrincipalName - WhenChanged - WhenCreated - WindowsEmailAddress - - - - Enable-MailUser - Identity - - ExternalEmailAddress - Alias - DomainController - MacAttachmentFormat - MessageBodyFormat - MessageFormat - UsePreferMessageFormat - - - - - - Get-User - Identity - - - Set-User - Identity - - AssistantName - City - Company - CountryOrRegion - CreateDTMFMap - Department - DisplayName - DomainController - Fax - FirstName - HomePhone - Initials - Instance - LastName - Manager - MobilePhone - Name - Notes - Office - OtherFax - OtherHomePhone - OtherTelephone - Pager - Phone - PhoneticDisplayName - PostalCode - PostOfficeBox - ResetPasswordOnNextLogon - SamAccountName - SimpleDisplayName - StateOrProvince - StreetAddress - Title - UMDialPlan - UMDtmfMap - UserPrincipalName - WebPage - WindowsEmailAddress - - - - - - Enable-Mailbox - Identity - - Database - Equipment - Alias - LinkedDomainController - LinkedMasterAccount - Room - Shared - ActiveSyncMailboxPolicy - DomainController - LinkedCredential - ManagedFolderMailboxPolicy - ManagedFolderMailboxPolicyAllowed - - - - \ No newline at end of file diff --git a/DotNetConnectors.sln/ExchangeConnector/ExchangeConfiguration.cs b/DotNetConnectors.sln/ExchangeConnector/ExchangeConfiguration.cs deleted file mode 100644 index 9d5e5477..00000000 --- a/DotNetConnectors.sln/ExchangeConnector/ExchangeConfiguration.cs +++ /dev/null @@ -1,37 +0,0 @@ -/* - * ==================== - * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER. - * - * Copyright 2007-2008 Sun Microsystems, Inc. All rights reserved. - * - * The contents of this file are subject to the terms of the Common Development - * and Distribution License("CDDL") (the "License"). You may not use this file - * except in compliance with the License. - * - * You can obtain a copy of the License at - * http://IdentityConnectors.dev.java.net/legal/license.txt - * See the License for the specific language governing permissions and limitations - * under the License. - * - * When distributing the Covered Code, include this CDDL Header Notice in each file - * and include the License file at identityconnectors/legal/license.txt. - * If applicable, add the following below this CDDL Header, with the fields - * enclosed by brackets [] replaced by your own identifying information: - * "Portions Copyrighted [year] [name of copyright owner]" - * ==================== - */ -using System; -using Org.IdentityConnectors.ActiveDirectory; -using Org.IdentityConnectors.Framework.Spi; - -namespace Org.IdentityConnectors.Exchange -{ - /// - /// MS Exchange specific configuration - /// - public class ExchangeConfiguration : ActiveDirectoryConfiguration - { - - } - -} diff --git a/DotNetConnectors.sln/ExchangeConnector/ExchangeConnector.cs b/DotNetConnectors.sln/ExchangeConnector/ExchangeConnector.cs deleted file mode 100644 index 9ca43a69..00000000 --- a/DotNetConnectors.sln/ExchangeConnector/ExchangeConnector.cs +++ /dev/null @@ -1,302 +0,0 @@ -/* - * ==================== - * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER. - * - * Copyright 2007-2008 Sun Microsystems, Inc. All rights reserved. - * - * The contents of this file are subject to the terms of the Common Development - * and Distribution License("CDDL") (the "License"). You may not use this file - * except in compliance with the License. - * - * You can obtain a copy of the License at - * http://IdentityConnectors.dev.java.net/legal/license.txt - * See the License for the specific language governing permissions and limitations - * under the License. - * - * When distributing the Covered Code, include this CDDL Header Notice in each file - * and include the License file at identityconnectors/legal/license.txt. - * If applicable, add the following below this CDDL Header, with the fields - * enclosed by brackets [] replaced by your own identifying information: - * "Portions Copyrighted [year] [name of copyright owner]" - * ==================== - */ -using System; -using System.Collections.Generic; -using System.Diagnostics; -using System.Management.Automation.Runspaces; -using Org.IdentityConnectors.ActiveDirectory; -using Org.IdentityConnectors.Common; -using Org.IdentityConnectors.Framework.Common.Objects; -using Org.IdentityConnectors.Framework.Spi; - -namespace Org.IdentityConnectors.Exchange -{ - /// - /// MS Exchange extension of Active Directory connector. - /// Full featured connector, see LegacyExchangeConnector for limited functionality connector. - /// LegacyExchangeConnector will be extension of this class, once ready. - /// - public class ExchangeConnector : ActiveDirectoryConnector - { - private static readonly string CLASS = typeof(ExchangeConnector).ToString(); - - //object class names - public const string MAILBOX_NAME = "mailbox"; - public const string MAILUSER_NAME = "mailuser"; - - //object classes - public static readonly ObjectClass MAILBOX = new ObjectClass(MAILBOX_NAME); - public static readonly ObjectClass MAILUSER = new ObjectClass(MAILUSER_NAME); - - //local vars - private ExchangeConfiguration _configuration = null; - private RunSpaceInstance _runspace = null; - private bool _disposed = false; - private IDictionary _mapOcInfo = null; - - - /// - /// Implementation of CreateOp.Create - /// - /// (oc - /// - /// - /// - public override Uid Create(ObjectClass oclass, - ICollection attributes, OperationOptions options) - { - const string METHOD = "Create"; - - Debug.WriteLine(METHOD + ":entry", CLASS); - - - //first create the object in AD - Uid uid = base.Create(oclass, attributes, options); - - try - { - if (oclass.Equals(MAILBOX)) - { - //enable mailbox for person - Command cmd = ExchangeUtils.GetCommand(CommandInfo.ENABLE_MAILBOX, attributes); - _runspace.InvokePipeline(cmd); - } else if (oclass.Equals(MAILUSER)) - { - //enable mailuser - Command cmd = ExchangeUtils.GetCommand(CommandInfo.ENABLE_MAILUSER, attributes); - _runspace.InvokePipeline(cmd); - - } - - Debug.WriteLine(METHOD + ":exit", CLASS); - - } - catch (Exception) - { - //do the rollback - delete the uid - base.Delete(oclass, uid, options); - throw; - } - - return uid; - } - - /// - /// Implementation of UpdateOp.Update - /// - /// - /// - /// - /// - /// - public override Uid Update(UpdateType type, ObjectClass oclass, - ICollection attributes, OperationOptions options) - { - //TODO: Implement Update - return base.Update(type, oclass, attributes, options); - } - - /// - /// Tests if the connector is properly configured and ready - /// - public override void Test() - { - //validate the configuration first, this will check AD configuration too - _configuration.Validate(); - //AD validation (includes configuration validation too) - base.Test(); - //runspace check - _runspace.Test(); - } - - /// - /// Implementation of SynOp.Sync - /// - /// - /// - /// - /// - public override void Sync(ObjectClass objClass, SyncToken token, - SyncResultsHandler handler, OperationOptions options) - { - //TODO: implement Sync - base.Sync(objClass, token, handler, options); - } - - /// - /// Implementation of SynOp.GetLatestSyncToken - /// - /// - public override SyncToken GetLatestSyncToken(ObjectClass oclass) - { - //TODO: Implement GetLatestSyncToken - return base.GetLatestSyncToken(oclass); - } - - /// - /// Implementation of SearchOp.ExecuteQuery - /// - /// - /// - /// - /// - public override void ExecuteQuery(ObjectClass oclass, string query, - ResultsHandler handler, OperationOptions options) - { - //TODO: Implement ExecuteQuery - base.ExecuteQuery(oclass, query, handler, options); - } - - - /// - /// Implementation of SearchOp.CreateFilterTranslator - /// - /// - /// - /// - public override Org.IdentityConnectors.Framework.Common.Objects.Filters.FilterTranslator CreateFilterTranslator(ObjectClass oclass, OperationOptions options) - { - //TODO: Implement CreateFilterTranslator - return base.CreateFilterTranslator(oclass, options); - } - - /// - /// Inits the connector with configuration injected - /// - /// - public override void Init(Configuration configuration) - { - base.Init(configuration); - _configuration = (ExchangeConfiguration)configuration; - _runspace = new RunSpaceInstance(RunSpaceInstance.SnapIn.Exchange); - _mapOcInfo = ExchangeUtils.GetOCInfo(); - } - - - /// - /// Dispose resources - /// - public override void Dispose() - { - //lock is probably not necessary - lock (this) - { - if (_disposed) - { - return; - } - if (_runspace != null) - { - _runspace.Dispose(); - } - base.Dispose(); - _disposed = true; - } - - } - - /// - /// Defines the supported object classes by the connector, used for schema building - /// - /// List of supported object classes - protected override ICollection GetSupportedObjectClasses() - { - ICollection ocList = base.GetSupportedObjectClasses(); - Assertions.NullCheck(ocList, "ocList"); - ocList.Add(MAILBOX); - ocList.Add(MAILUSER); - return ocList; - } - - /// - /// Gets the object class info for specified object class, used for schema building - /// - /// ObjectClass to get info for - /// ObjectClass' ObjectClassInfo - protected override ObjectClassInfo GetObjectClassInfo(ObjectClass oc) - { - ObjectClassInfo ret = CollectionUtil.GetValue(_mapOcInfo, oc, null) ?? base.GetObjectClassInfo(oc); - Assertions.NullCheck(ret, "ret"); - return ret; - } - - } - - - /// - /// Command definition object - /// - internal sealed class CommandInfo - { - private static IDictionary cinfos = null; - - internal static readonly CommandInfo ENABLE_MAILBOX = new CommandInfo("Enable-Mailbox"); - internal static readonly CommandInfo ENABLE_MAILUSER = new CommandInfo("Enable-MailUser"); - internal static readonly CommandInfo SET_MAILUSER = new CommandInfo("Set-MailUser"); - - - private CommandInfo(string name) - { - Name = name; - if (cinfos == null) - { - cinfos = ExchangeUtils.GetCommandInfo(); - } - - } - - /// - /// Comamnd Name - /// - internal string Name { get; private set; } - - /// - /// Comand Parameters - /// - internal string[] Parameters - { - get - { - var ret = CollectionUtil.GetValue(cinfos, Name, null); - Assertions.NullCheck(ret, "ret"); - - return ret.Parameter; - } - } - - /// - /// Name parameter - /// - internal string NameParameter - { - get - { - var ret = CollectionUtil.GetValue(cinfos, Name, null); - Assertions.NullCheck(ret, "ret"); - - return ret.NameParameter; - } - } - } -} - diff --git a/DotNetConnectors.sln/ExchangeConnector/ExchangeConnector.csproj b/DotNetConnectors.sln/ExchangeConnector/ExchangeConnector.csproj deleted file mode 100644 index 7ba60d80..00000000 --- a/DotNetConnectors.sln/ExchangeConnector/ExchangeConnector.csproj +++ /dev/null @@ -1,108 +0,0 @@ - - - - Debug - AnyCPU - 9.0.21022 - 2.0 - {F1CB12B6-0DD7-4DAB-9B21-630449B8610D} - Library - Properties - Org.IdentityConnectors.Exchange - Exchange.Connector - v3.5 - 512 - C:\Documents and Settings\Administrator\Application Data\ICSharpCode/SharpDevelop3.0\Settings.SourceAnalysis - - - true - full - false - bin\Debug\ - DEBUG;TRACE - prompt - 4 - bin\Debug\Exchange.Connector.xml - - - pdbonly - true - bin\Release\ - TRACE - prompt - 4 - - - - - 3.5 - - - False - ..\..\..\..\Program Files\Reference Assemblies\Microsoft\WindowsPowerShell\v1.0\System.Management.Automation.dll - - - - - - - - - - - - - - {BDF495CA-0FCD-4E51-A871-D467CDE3B43E} - ActiveDirectoryConnector - False - - - {F140E8DA-52B4-4159-992A-9DA10EA8EEFB} - Common - False - - - {8B24461B-456A-4032-89A1-CD418F7B5B62} - Framework - - - - - - - - - Designer - - - - - diff --git a/DotNetConnectors.sln/ExchangeConnector/ExchangeUtils.cs b/DotNetConnectors.sln/ExchangeConnector/ExchangeUtils.cs deleted file mode 100644 index 6f4bcaf8..00000000 --- a/DotNetConnectors.sln/ExchangeConnector/ExchangeUtils.cs +++ /dev/null @@ -1,375 +0,0 @@ -/* - * ==================== - * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER. - * - * Copyright 2007-2008 Sun Microsystems, Inc. All rights reserved. - * - * The contents of this file are subject to the terms of the Common Development - * and Distribution License("CDDL") (the "License"). You may not use this file - * except in compliance with the License. - * - * You can obtain a copy of the License at - * http://IdentityConnectors.dev.java.net/legal/license.txt - * See the License for the specific language governing permissions and limitations - * under the License. - * - * When distributing the Covered Code, include this CDDL Header Notice in each file - * and include the License file at identityconnectors/legal/license.txt. - * If applicable, add the following below this CDDL Header, with the fields - * enclosed by brackets [] replaced by your own identifying information: - * "Portions Copyrighted [year] [name of copyright owner]" - * ==================== - */ -using System; -using System.Collections; -using System.Collections.Generic; -using System.Diagnostics; -using System.IO; -using System.Management.Automation.Runspaces; -using System.Reflection; - -using Microsoft.Win32; -using System.Xml.Serialization; -using Org.IdentityConnectors.ActiveDirectory; -using Org.IdentityConnectors.Common; -using Org.IdentityConnectors.Framework.Common.Objects; - -namespace Org.IdentityConnectors.Exchange -{ - /// - /// Description of ExchangeUtils. - /// - public class ExchangeUtils : CommonUtils - { - private static readonly string CLASS = typeof(ExchangeUtils).ToString(); - - private const string OC_DEF_FILE = "Org.IdentityConnectors.Exchange.ObjectClasses.xml"; - private const string EXCHANGE_REG_KEY = "Software\\Microsoft\\Exchange\\v8.0\\Setup\\"; - private const string EXCHANGE_REG_VALUE = "MsiInstallPath"; - - /// use reflection to load the Exchange assembly - internal static Assembly AssemblyResolver(object p, ResolveEventArgs args) - { - //Add path for the Exchange 2007 DLLs - if (args.Name.Contains("Microsoft.Exchange")) - { - String installPath = GetRegistryStringValue(EXCHANGE_REG_KEY, EXCHANGE_REG_VALUE); - installPath += "\\bin\\" + args.Name.Split(',')[0] + ".dll"; - return Assembly.LoadFrom(installPath); - - } - - return null; - } - - /// - /// Get registry value, which is expected to be a string - /// - /// Registry Key Name - /// Registry Value Name - /// - internal static String GetRegistryStringValue(string keyName, string valName) - { - const string METHOD = "GetRegistryStringValue"; - Debug.WriteLine(METHOD + ":entry", CLASS); - //argument check - if (keyName == null) - { - keyName = ""; - } - if (valName == null) - { - throw new ArgumentNullException("valName"); - } - - RegistryKey regKey = Registry.LocalMachine.OpenSubKey(keyName, false); - try - { - Object val = regKey.GetValue(valName); - if (val != null) - { - RegistryValueKind regType = regKey.GetValueKind(valName); - if (!regType.Equals(RegistryValueKind.String)) - { - throw new InvalidDataException(String.Format("Invalid Registry data type, key name: {0} value name: {1} should be String", keyName, valName)); - } - return Convert.ToString(val); - } - else - { - throw new InvalidDataException(String.Format("Missing value for key name: {0} value name: {1}", keyName, valName)); - } - } - finally - { - if (regKey != null) - { - regKey.Close(); - } - Debug.WriteLine(METHOD + ":exit", CLASS); - } - } - - - - /// - /// reads the object class info definitions from xml - /// - ///Dictionary of object classes - internal static IDictionary GetOCInfo() - { - return GetOCInfo(OC_DEF_FILE); - } - - /// - /// - /// - internal static IDictionary GetCommandInfo () - { - Assembly assembly = Assembly.GetExecutingAssembly(); - Stream stream = assembly.GetManifestResourceStream("Org.IdentityConnectors.Exchange.CommandInfos.xml"); - - Assertions.NullCheck(stream, "stream"); - - //we just read - TextReader streamReader = new StreamReader(stream); - - XmlSerializer ser = new XmlSerializer(typeof(XCommandInfos)); - XCommandInfos cInfos = (XCommandInfos)ser.Deserialize(streamReader); - streamReader.Close(); - - Assertions.NullCheck(cInfos, "cInfos"); - - //create map of command infos - var map = new Dictionary(cInfos.XCommandInfo.Length); - foreach (XCommandInfo o in cInfos.XCommandInfo) - { - map.Add(o.Name, o); - } - - return map; - - } - - /// - /// creates command based on the commanf info, reading the calues from attributes - /// - /// Command defition - /// Attribute values - /// Ready to execute Command - internal static Command GetCommand(CommandInfo cmdInfo, ICollection attributes) - { - Assertions.NullCheck(cmdInfo, "cmdInfo"); - Assertions.NullCheck(attributes, "attributes"); - - //create command - Command cmd = new Command(cmdInfo.Name); - - //map name attribute, if mapping specified - if (!string.IsNullOrEmpty(cmdInfo.NameParameter)) - { - object val = GetAttValue(Name.NAME, attributes); - if (val != null) - { - cmd.Parameters.Add(cmdInfo.NameParameter, val); - } - } - - foreach (string attName in cmdInfo.Parameters) - { - object val = GetAttValue(attName, attributes); - if (val != null) - { - cmd.Parameters.Add(attName, val); - } - } - return cmd; - } - - /// - /// Helper method: Gets attribute value from the attribute collection - /// - /// attribute name - /// collection of attribute - /// attribute value as object, null if not found - internal static object GetAttValue(String attName, ICollection attributes) - { - Assertions.NullCheck(attName, "attName"); - Assertions.NullCheck(attributes, "attributes"); - - object value = null; - ConnectorAttribute attribute = ConnectorAttributeUtil.Find(attName, attributes); - - if (attribute != null) - { - value = ConnectorAttributeUtil.GetSingleValue(attribute); - } - - return value; - } - - /// - /// Helper method for filtering the specified attributes from collection of attributes - /// - /// Collection of attributes - /// Attribute names to be filtered out - /// Filtered collection of attributes - internal static ICollection FilterOut(ICollection attributes, params string[] attName) - { - Assertions.NullCheck(attributes, "attributes"); - if (attName == null || attName.Length == 0) - { - return attributes; - } - - IList names = new ArrayList(attName); - ICollection filtered = new List(); - foreach (ConnectorAttribute attribute in attributes) - { - if (!names.Contains(attribute.Name)) - { - filtered.Add(attribute); - } - } - return filtered; - } - - /// - /// Helper method - Replaces specified collection Items - /// - /// - /// - /// - internal static ArrayList FilterReplace(ArrayList col, string[,] replace) - { - Assertions.NullCheck(col, "col"); - Assertions.NullCheck(replace, "replace"); - - ArrayList newcol = (ArrayList) col.Clone(); - for (int i = 0; i < replace.GetLength(0); i++) - { - if (newcol.Contains(replace[i,0])) - { - newcol.Remove(replace[i, 0]); - newcol.Add(replace[i,1]); - } - } - return newcol; - } - - /// - /// finds the attributes in connector object and rename it according to input array of names, but only - /// if the aatribute name is in attributes to get - /// - /// - /// - /// - /// - internal static ConnectorObject ReplaceAttributes(ConnectorObject cobject, IList attsToGet, string[,] replace) - { - Assertions.NullCheck(cobject, "cobject"); - Assertions.NullCheck(attsToGet, "attsToGet"); - Assertions.NullCheck(replace, "replace"); - - var attributes = cobject.GetAttributes(); - var builder = new ConnectorObjectBuilder(); - foreach (ConnectorAttribute attribute in attributes) - { - for (int i = 0; i < replace.GetLength(0); i++) - { - string oldName = replace[i, 1]; - string newName = replace[i, 0]; - if (attsToGet.Contains(newName) && attribute.Name == oldName) - { - var newAttribute = RenameAttribute(attribute, replace[i, 0]); - builder.AddAttribute(newAttribute); - break; - } - } - - builder.AddAttribute(attribute); - } - builder.AddAttributes(attributes); - builder.ObjectClass = cobject.ObjectClass; - builder.SetName(cobject.Name); - builder.SetUid(cobject.Uid); - return builder.Build(); - } - - /// - /// Renames the connector attribute to new name - /// - /// - /// - /// - internal static ConnectorAttribute RenameAttribute(ConnectorAttribute cattribute, string newName) - { - Assertions.NullCheck(cattribute, "cattribute"); - Assertions.NullCheck(newName, "newName"); - - var caBuilder = new ConnectorAttributeBuilder(); - caBuilder.AddValue(cattribute.Value); - caBuilder.Name = newName; - return caBuilder.Build(); - } - - - } - - /// - /// DAO class for getting serialized data from xml - /// - [XmlRoot("CommandInfos")] - public class XCommandInfos - { - private readonly ArrayList lstCommandInfos = new ArrayList(); - - /// - /// Command info array - /// - [XmlElement("CommandInfo")] - public XCommandInfo[] XCommandInfo - { - get - { - var items = new XCommandInfo[lstCommandInfos.Count]; - lstCommandInfos.CopyTo(items); - return items; - - } - set - { - if (value == null) return; - var items = (XCommandInfo[])value; - lstCommandInfos.Clear(); - foreach (XCommandInfo item in items) - lstCommandInfos.Add(item); - - } - } - } - - /// - /// DO class for getting serialized data from XML - /// - public class XCommandInfo - { - - /// - /// Command name - /// - public string Name { get; set; } - - /// - /// Special parameter name used as id for this command - /// - public string NameParameter { get; set; } - - /// - /// Command parameters - /// - public string[] Parameter { get; set; } - } - - -} diff --git a/DotNetConnectors.sln/ExchangeConnector/LegacyExchangeConnector.cs b/DotNetConnectors.sln/ExchangeConnector/LegacyExchangeConnector.cs deleted file mode 100644 index 8b0aab56..00000000 --- a/DotNetConnectors.sln/ExchangeConnector/LegacyExchangeConnector.cs +++ /dev/null @@ -1,462 +0,0 @@ -/* - * ==================== - * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER. - * - * Copyright 2007-2008 Sun Microsystems, Inc. All rights reserved. - * - * The contents of this file are subject to the terms of the Common Development - * and Distribution License("CDDL") (the "License"). You may not use this file - * except in compliance with the License. - * - * You can obtain a copy of the License at - * http://IdentityConnectors.dev.java.net/legal/license.txt - * See the License for the specific language governing permissions and limitations - * under the License. - * - * When distributing the Covered Code, include this CDDL Header Notice in each file - * and include the License file at identityconnectors/legal/license.txt. - * If applicable, add the following below this CDDL Header, with the fields - * enclosed by brackets [] replaced by your own identifying information: - * "Portions Copyrighted [year] [name of copyright owner]" - * ==================== - */ -using System.Collections.Generic; -using System.Diagnostics; -using System.Management.Automation.Runspaces; -using Org.IdentityConnectors.ActiveDirectory; -using Org.IdentityConnectors.Common; -using Org.IdentityConnectors.Framework.Common.Exceptions; -using Org.IdentityConnectors.Framework.Common.Objects; -using Org.IdentityConnectors.Framework.Common.Objects.Filters; -using Org.IdentityConnectors.Framework.Spi; -using System.Collections; - -namespace Org.IdentityConnectors.Exchange -{ - /// - /// MS Exchange connector - build to have the same functionality as Exchange resource adapter - /// - /// - [ConnectorClass("connector_displayName", - typeof(ExchangeConfiguration), - MessageCatalogPaths = new[] { "Org.IdentityConnectors.Exchange.Messages", - "Org.IdentityConnectors.ActiveDirectory.Messages" } - )] - public class LegacyExchangeConnector : ActiveDirectoryConnector - { - private static readonly string CLASS = typeof(LegacyExchangeConnector).ToString(); - - //hardcoded stuff - internal const string ATT_RECIPIENT_TYPE = "RecipientType"; - internal const string ATT_EXTERNAL_MAIL = "ExternalEmailAddress"; - internal const string ATT_DATABASE = "Database"; - - internal const string ATT_EXTERNAL_MAIL_AD_NAME = "targetAddress"; - internal const string ATT_DATABASE_AD_NAME = "homeMDB"; - - internal static readonly string[,] ATT_MAPPING = new[,] - { - {ATT_DATABASE, ATT_DATABASE_AD_NAME}, - {ATT_EXTERNAL_MAIL, ATT_EXTERNAL_MAIL_AD_NAME} - }; - - private static readonly ConnectorAttributeInfo ATTINFO_RECIPIENT_TYPE = - ConnectorAttributeInfoBuilder.Build(ATT_RECIPIENT_TYPE, typeof(string), ConnectorAttributeInfo.Flags.REQUIRED | - ConnectorAttributeInfo.Flags.NOT_UPDATEABLE | ConnectorAttributeInfo.Flags.NOT_RETURNED_BY_DEFAULT); - - - - private static readonly ConnectorAttributeInfo ATTINFO_EXTERNAL_MAIL = - ConnectorAttributeInfoBuilder.Build(ATT_EXTERNAL_MAIL, typeof(string), ConnectorAttributeInfo.Flags.NOT_RETURNED_BY_DEFAULT | - ConnectorAttributeInfo.Flags.MULTIVALUED); - - private static readonly ConnectorAttributeInfo ATTINFO_DATABASE = - ConnectorAttributeInfoBuilder.Build(ATT_DATABASE, typeof(string), ConnectorAttributeInfo.Flags.NOT_RETURNED_BY_DEFAULT); - - private const string RCPT_TYPE_MAIL_BOX = "mailbox"; - private const string RCPT_TYPE_MAIL_USER = "mailuser"; - - - //local vars - private ExchangeConfiguration _configuration = null; - private RunSpaceInstance _runspace = null; - private bool _disposed = false; - - - #region CreateOp.Create implementation - /// - /// Implementation of CreateOp.Create - /// - /// (oc - /// - /// - /// - public override Uid Create(ObjectClass oclass, - ICollection attributes, OperationOptions options) - { - const string METHOD = "Create"; - Debug.WriteLine(METHOD + ":entry", CLASS); - - //get recipient type - string rcptType = ExchangeUtils.GetAttValue(ATT_RECIPIENT_TYPE, attributes) as string; - - if (rcptType != RCPT_TYPE_MAIL_BOX && rcptType != RCPT_TYPE_MAIL_USER) - { - //AD account only, we do nothing - return base.Create(oclass, attributes, options); - } - - //first create the object in AD - Uid uid = base.Create(oclass, FilterOut(attributes), options); - - //prepare the command - CommandInfo cmdInfo = rcptType == RCPT_TYPE_MAIL_BOX ? CommandInfo.ENABLE_MAILBOX : CommandInfo.ENABLE_MAILUSER; - Command cmd = ExchangeUtils.GetCommand(cmdInfo, attributes); - - try - { - //execute the command - _runspace.InvokePipeline(cmd); - } - catch - { - Trace.TraceWarning("Rolling back AD create for UID: " + uid.GetUidValue()); - //rollback AD create - try - { - base.Delete(oclass, uid, options); - } - catch - { - //ignore delete error - Trace.TraceWarning("Not able to rollback AD create for UID: " + uid.GetUidValue()); - } - //rethrow original exception - throw; - } - - Debug.WriteLine(METHOD + ":exit", CLASS); - return uid; - } - #endregion - - /// - /// Implementation of UpdateOp.Update - /// - /// - /// - /// - /// - /// - public override Uid Update(UpdateType type, ObjectClass oclass, - ICollection attributes, OperationOptions options) - { - const string METHOD = "Update"; - Debug.WriteLine(METHOD + ":entry", CLASS); - - Assertions.NullCheck(type, "updatetype"); - Assertions.NullCheck(oclass, "oclass"); - Assertions.NullCheck(attributes, "attributes"); - - //update in AD first - Uid uid = base.Update(type, oclass, FilterOut(attributes), options); - //get recipient type - string rcptType = ExchangeUtils.GetAttValue(ATT_RECIPIENT_TYPE, attributes) as string; - - //update is possible for mailuser's external email only - if (rcptType == RCPT_TYPE_MAIL_USER) - { - if (type == UpdateType.REPLACE) - { - //get name attribute - string name = ExchangeUtils.GetAttValue(Name.NAME, attributes) as string; - if (name == null) - { - //we don't know name, but we need it - NOTE: searching for all the default attributes, we need only Name here, it can be improved - ConnectorObject co = ADSearchByUid(uid, oclass, null); - Assertions.NullCheck(co, "co"); - //add to attributes - attributes.Add(co.Name); - } - - Command cmd = ExchangeUtils.GetCommand(CommandInfo.SET_MAILUSER, attributes); - _runspace.InvokePipeline(cmd); - } - else - { - throw new ConnectorException(string.Format("Update type [{0}] not supported", type)); - } - } - - Debug.WriteLine(METHOD + ":exit", CLASS); - return uid; - } - - /// - /// Tests if the connector is properly configured and ready - /// - public override void Test() - { - //validate the configuration first, this will check AD configuration too - _configuration.Validate(); - //AD validation (includes configuration validation too) - base.Test(); - //runspace check - _runspace.Test(); - } - - /// - /// Implementation of SynOp.Sync - /// - /// - /// - /// - /// - public override void Sync(ObjectClass objClass, SyncToken token, - SyncResultsHandler handler, OperationOptions options) - { - //TODO: implement Sync - base.Sync(objClass, token, handler, options); - } - - /// - /// Implementation of SynOp.GetLatestSyncToken - /// - /// - public override SyncToken GetLatestSyncToken(ObjectClass oclass) - { - //TODO: Implement GetLatestSyncToken - return base.GetLatestSyncToken(oclass); - } - - /// - /// Implementation of SearchOp.ExecuteQuery - /// - /// - /// - /// - /// - public override void ExecuteQuery(ObjectClass oclass, string query, - ResultsHandler handler, OperationOptions options) - { - ArrayList attsToGet = null; - if (options != null && options.AttributesToGet != null) - { - attsToGet = new ArrayList(options.AttributesToGet); - } - //delegate to get the exchange attributes if requested - ResultsHandler filter = delegate(ConnectorObject cobject) - { - ConnectorObject filtered = ExchangeUtils.ReplaceAttributes(cobject, attsToGet, ATT_MAPPING); - return handler(filtered); - }; - - ResultsHandler handler2use = handler; - OperationOptions options2use = options; - if (options != null && options.AttributesToGet != null) - { - if (attsToGet.Contains(ATT_DATABASE) || attsToGet.Contains(ATT_EXTERNAL_MAIL) || - attsToGet.Contains(ATT_RECIPIENT_TYPE)) - { - //replace Exchange attributes with AD names - var newAttsToGet = ExchangeUtils.FilterReplace(attsToGet, ATT_MAPPING); - //we have to remove recipient type, as it is unknown to AD - newAttsToGet.Remove(ATT_RECIPIENT_TYPE); - //build new op options - var builder = new OperationOptionsBuilder(options); - builder.AttributesToGet = (string[]) newAttsToGet.ToArray(typeof(string)); - options2use = builder.Build(); - handler2use = filter; - } - } - base.ExecuteQuery(oclass, query, handler2use, options2use); - } - - /// - /// Implementation of SearchOp.CreateFilterTranslator - /// - /// - /// - /// - public override FilterTranslator CreateFilterTranslator(ObjectClass oclass, OperationOptions options) - { - return new LegacyExchangeConnectorFilterTranslator(); - } - - /// - /// Inits the connector with configuration injected - /// - /// - public override void Init(Configuration configuration) - { - base.Init(configuration); - _configuration = (ExchangeConfiguration)configuration; - _runspace = new RunSpaceInstance(RunSpaceInstance.SnapIn.Exchange); - } - - - /// - /// Dispose resources - /// - public override void Dispose() - { - //lock is probably not necessary - lock (this) - { - if (_disposed) - { - return; - } - if (_runspace != null) - { - _runspace.Dispose(); - } - base.Dispose(); - _disposed = true; - } - - } - - /// - /// Gets the object class info for specified object class, used for schema building - /// - /// ObjectClass to get info for - /// ObjectClass' ObjectClassInfo - protected override ObjectClassInfo GetObjectClassInfo(ObjectClass oc) - { - //get the object class from base - ObjectClassInfo oinfo = base.GetObjectClassInfo(oc); - - //add additional attributes for ACCOUNT - if (oc == ObjectClass.ACCOUNT) - { - - var oiBuilder = new ObjectClassInfoBuilder - { - IsContainer = oinfo.IsContainer, - ObjectType = oinfo.ObjectType - }; - oiBuilder.AddAllAttributeInfo(oinfo.ConnectorAttributeInfos); - oiBuilder.AddAttributeInfo(ATTINFO_DATABASE); - oiBuilder.AddAttributeInfo(ATTINFO_RECIPIENT_TYPE); - oiBuilder.AddAttributeInfo(ATTINFO_EXTERNAL_MAIL); - } - - //return - return oinfo; - } - - /// - /// Attribute normalizer - /// - /// Object class - /// Attribute to be normalized - /// normalized attribute - public override ConnectorAttribute NormalizeAttribute(ObjectClass oclass, ConnectorAttribute attribute) - { - //normalize the attribute using AD connector first - attribute = base.NormalizeAttribute(oclass, attribute); - //normalize external mail value - if (attribute.Name == ATT_EXTERNAL_MAIL && attribute.Value != null) - { - IList normAttributes = new List(); - bool normalized = false; - foreach (object val in attribute.Value) - { - string strVal = val as string; - if (strVal != null) - { - string[] split = strVal.Split(':'); - if (split.Length == 2) - { - //it contains delimiter, use the second part - normAttributes.Add(split[1]); - normalized = true; - } else - { - //put the original value - normAttributes.Add(val); - } - } - } - if (normalized) - { - //build the attribute again - return ConnectorAttributeBuilder.Build(attribute.Name, normAttributes); - } - - } - //return the original attribute - return attribute; - } - - - /// - /// helper method to filter out all attributes used in LegacyExchangeConnector only - /// - /// - /// - private static ICollection FilterOut(ICollection attributes) - { - return ExchangeUtils.FilterOut(attributes, ATT_RECIPIENT_TYPE, ATT_DATABASE, ATT_EXTERNAL_MAIL); - } - - /// - /// helper method for searching object in AD by UID - /// - /// - /// - /// - /// - private ConnectorObject ADSearchByUid(Uid uid, ObjectClass oclass, OperationOptions options) - { - Assertions.NullCheck(uid, "uid"); - Assertions.NullCheck(oclass, "oclass"); - if (options == null) - { - options = new OperationOptionsBuilder().Build(); - } - - ConnectorObject ret = null; - Filter filter = FilterBuilder.EqualTo(uid); - var translator = - base.CreateFilterTranslator(oclass, options); - IList queries = translator.Translate(filter); - - if (queries.Count == 1) - { - ResultsHandler handler = delegate(ConnectorObject cobject) - { - ret = cobject; - return false; - }; - base.ExecuteQuery(oclass, queries[0], handler, options); - } - - - return ret; - } - - } - - /// - /// Filter translator which does MS Exchange specific things - /// - public class LegacyExchangeConnectorFilterTranslator : ActiveDirectoryFilterTranslator - { - protected override string[] GetLdapNamesForAttribute(ConnectorAttribute attr) - { - - if (attr.Is(LegacyExchangeConnector.ATT_DATABASE)) - { - return new string[] { LegacyExchangeConnector.ATT_DATABASE_AD_NAME }; - } - if (attr.Is(LegacyExchangeConnector.ATT_EXTERNAL_MAIL)) - { - return new string[] { LegacyExchangeConnector.ATT_EXTERNAL_MAIL_AD_NAME }; - } - return base.GetLdapNamesForAttribute(attr); - } - } - -} diff --git a/DotNetConnectors.sln/ExchangeConnector/Messages.resx b/DotNetConnectors.sln/ExchangeConnector/Messages.resx deleted file mode 100644 index 3960efa6..00000000 --- a/DotNetConnectors.sln/ExchangeConnector/Messages.resx +++ /dev/null @@ -1,123 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - text/microsoft-resx - - - 2.0 - - - System.Resources.ResXResourceReader, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 - - - System.Resources.ResXResourceWriter, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 - - - Exchange Connector - - \ No newline at end of file diff --git a/DotNetConnectors.sln/ExchangeConnector/ObjectClasses.xml b/DotNetConnectors.sln/ExchangeConnector/ObjectClasses.xml deleted file mode 100644 index f16c47ad..00000000 --- a/DotNetConnectors.sln/ExchangeConnector/ObjectClasses.xml +++ /dev/null @@ -1,114 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/DotNetConnectors.sln/ExchangeConnector/Properties/AssemblyInfo.cs b/DotNetConnectors.sln/ExchangeConnector/Properties/AssemblyInfo.cs deleted file mode 100644 index d1492654..00000000 --- a/DotNetConnectors.sln/ExchangeConnector/Properties/AssemblyInfo.cs +++ /dev/null @@ -1,58 +0,0 @@ -/* - * ==================== - * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER. - * - * Copyright 2007-2008 Sun Microsystems, Inc. All rights reserved. - * - * The contents of this file are subject to the terms of the Common Development - * and Distribution License("CDDL") (the "License"). You may not use this file - * except in compliance with the License. - * - * You can obtain a copy of the License at - * http://IdentityConnectors.dev.java.net/legal/license.txt - * See the License for the specific language governing permissions and limitations - * under the License. - * - * When distributing the Covered Code, include this CDDL Header Notice in each file - * and include the License file at identityconnectors/legal/license.txt. - * If applicable, add the following below this CDDL Header, with the fields - * enclosed by brackets [] replaced by your own identifying information: - * "Portions Copyrighted [year] [name of copyright owner]" - * ==================== - */ -using System.Reflection; -using System.Runtime.CompilerServices; -using System.Runtime.InteropServices; - -// General Information about an assembly is controlled through the following -// set of attributes. Change these attribute values to modify the information -// associated with an assembly. -[assembly: AssemblyTitle("ExchangeConnector")] -[assembly: AssemblyDescription("")] -[assembly: AssemblyConfiguration("")] -[assembly: AssemblyCompany("IDM")] -[assembly: AssemblyProduct("ExchangeConnector")] -[assembly: AssemblyCopyright("Copyright IDM 2008")] -[assembly: AssemblyTrademark("")] -[assembly: AssemblyCulture("")] - -// Setting ComVisible to false makes the types in this assembly not visible -// to COM components. If you need to access a type in this assembly from -// COM, set the ComVisible attribute to true on that type. -[assembly: ComVisible(false)] - -// The following GUID is for the ID of the typelib if this project is exposed to COM -[assembly: Guid("d23ec8a7-e491-4f91-bda5-8900423a410e")] - -// Version information for an assembly consists of the following four values: -// -// Major Version -// Minor Version -// Build Number -// Revision -// -// You can specify all the values or you can default the Build and Revision Numbers -// by using the '*' as shown below: -// [assembly: AssemblyVersion("1.0.*")] -[assembly: AssemblyVersion("1.0.0.0")] -[assembly: AssemblyFileVersion("1.0.0.0")] diff --git a/DotNetConnectors.sln/ExchangeConnector/RunSpaceInstance.cs b/DotNetConnectors.sln/ExchangeConnector/RunSpaceInstance.cs deleted file mode 100644 index 21a0ff76..00000000 --- a/DotNetConnectors.sln/ExchangeConnector/RunSpaceInstance.cs +++ /dev/null @@ -1,335 +0,0 @@ -/* - * ==================== - * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER. - * - * Copyright 2007-2008 Sun Microsystems, Inc. All rights reserved. - * - * The contents of this file are subject to the terms of the Common Development - * and Distribution License("CDDL") (the "License"). You may not use this file - * except in compliance with the License. - * - * You can obtain a copy of the License at - * http://IdentityConnectors.dev.java.net/legal/license.txt - * See the License for the specific language governing permissions and limitations - * under the License. - * - * When distributing the Covered Code, include this CDDL Header Notice in each file - * and include the License file at identityconnectors/legal/license.txt. - * If applicable, add the following below this CDDL Header, with the fields - * enclosed by brackets [] replaced by your own identifying information: - * "Portions Copyrighted [year] [name of copyright owner]" - * ==================== - */ -using System; -using System.Collections; -using System.Collections.Generic; -using System.Collections.ObjectModel; -using System.Diagnostics; -using System.Management.Automation; -using System.Management.Automation.Runspaces; -using System.Text; - -using Org.IdentityConnectors.Framework.Common.Exceptions; - -namespace Org.IdentityConnectors.Exchange -{ - - /// - /// The implementation of the run space. This wraps the real run space object - /// from powershell for use in the pool - /// First written for the exchange adapter, the snapin is not needed if you do - /// not use it for exchange - /// - /// Two possible ways of executing a command using different access point to - /// the Runspace: - /// - RunspaceInvoke: simple commands in string form, the command string can - /// contain multiple commands and is basically the same form - /// as what you use when typing a command in the exchange - /// shell - /// - PipelineInvoke: complex (multi) command structured pipelines which also - /// allow complex parameters, like objects, to be passed in. - /// - public sealed class RunSpaceInstance : IDisposable - { - private static readonly string CLASS = typeof(RunSpaceInstance).ToString(); - - // the exchange snap in which needs to be loaded - private const string EXCHANGE_SNAPIN = "Microsoft.Exchange.Management.PowerShell.Admin"; - - private RunspaceConfiguration _runSpaceConfig = null; - private Runspace _runSpace = null; - private RunspaceInvoke _runSpaceInvoke = null; - - //SnapIn type enum - /// - /// Snapin type to load - /// - public enum SnapIn - { - /// - /// None - /// - None, - /// - /// MS Exchange snapin - /// - Exchange - }; - - /// - /// RunSpaceInstance Constructor - /// - /// Type of snapin to be loaded - public RunSpaceInstance(SnapIn snapin) - { - // initialize this - InitRunSpace(snapin); - } - - /// - /// Implementation of IDisposable - /// - public void Dispose() - { - Dispose(true); - GC.SuppressFinalize(this); - } - - /// - /// Dispose/Finalize pattern - /// - /// - private void Dispose(bool disposing) - { - if (disposing) - { - // Free other state (managed objects). - // anything we should do? - } - // clean up the runspace with attached things: - // the API docs show that the RunspaceInvoke will call Dispose on - // the Runspace which in turn calls Close on the Runspace. - if (_runSpaceInvoke != null) - { - _runSpaceInvoke.Dispose(); - } - else - { - if (_runSpace != null) - { - _runSpace.Dispose(); - } - } - } - - /// - /// Finalize - /// - ~RunSpaceInstance() - { - // Simply call Dispose(false). - Dispose(false); - } - - - - /// - /// main initialisation routine for the Runspace - /// - private void InitRunSpace(SnapIn snapin) - { - string METHOD = "InitRunSpace with snapin " + snapin; - Debug.WriteLine(METHOD + ":entry", CLASS); - - - // create a new config from scratch - PSSnapInException snapOutput = null; - _runSpaceConfig = RunspaceConfiguration.Create(); - - switch (snapin) - { - case SnapIn.Exchange: - // used for force load of the exchange dll's - AppDomain.CurrentDomain.AssemblyResolve += - new ResolveEventHandler(ExchangeUtils.AssemblyResolver); - - PSSnapInInfo info = _runSpaceConfig.AddPSSnapIn(EXCHANGE_SNAPIN, - out snapOutput); - break; - } - - //check snapOutput - if (snapOutput != null) - { - throw snapOutput; - } - - // create the real Runspace and open it for processing - _runSpace = RunspaceFactory.CreateRunspace(_runSpaceConfig); - _runSpace.Open(); - _runSpaceInvoke = new RunspaceInvoke(_runSpace); - - Debug.WriteLine(METHOD + ":exit", CLASS); - } - - - /// - /// Test the state of this RunspaceInstance, throws InvalidRunspaceStateException if in incorrect state - /// - public void Test() - { - const string METHOD = "Test"; - Debug.WriteLine(METHOD + ":entry", CLASS); - - // compare the state against the passed in state - if (_runSpace != null - && _runSpace.RunspaceStateInfo.State == RunspaceState.Opened) - { - Debug.WriteLine(METHOD + ":exit", CLASS); - return ; - } - - throw new InvalidRunspaceStateException("Runspace is not in Opened state"); - } - - /// invoke the command - /// command string to execute - /// collection of objects with the result - /// if no command is passed in return null - /// if no output/errors from the invoke return an empty collection - public ICollection InvokeCommand(String commandString) - { - return InvokeCommand(commandString, null); - } - - /// - /// invoke the command - /// The input is passed in to the environment as the $input variable and - /// can be used in the script as follows: - /// invokeCommand("$input | Set-Mailbox", inputEnum) - /// inputEnum in the example could be the output of an earlier - /// invokeCommand call (and thus a complex set of objects) - /// - /// command string to execute - /// input passed in as $input in the execution - /// environment - /// collection of objects with the result - /// if no command is passed in return null - /// if no output from the invoke return an empty collection - public ICollection InvokeCommand(String commandString, - IEnumerable input) - { - const string METHOD = "InvokeCommand"; - Debug.WriteLine(METHOD + ":entry", CLASS); - - IList errors = null; - // trim the spaces and check the length - if (commandString == null || commandString.Trim().Length == 0) - { - Trace.TraceError("CommandString argument can't be null or empty"); - throw new ArgumentException("CommandString argument can't be null or empty"); - } - - // run the command - Collection returns = - _runSpaceInvoke.Invoke(commandString, input, out errors); - //check for errors - checkErrors(errors); - - // an empty collection instead of null when we have executed - if (returns == null) - { - Debug.WriteLine(METHOD + ":exit", CLASS); - returns = new Collection(); - } //if returns - Trace.WriteLine(String.Format("{0} results returned", returns.Count), CLASS); - Debug.WriteLine(METHOD + ":exit", CLASS); - return returns; - - } - - /// - /// invoke the pipeline - /// - /// a collection of commands to execute - /// collection of objects with the result - /// if no command is passed in return null - /// if no output from the invoke return an empty collection - public ICollection InvokePipeline(Collection commands) - { - const string METHOD = "InvokePipeline"; - Debug.WriteLine(METHOD + ":entry", CLASS); - - IList errors = null; - - if (commands == null || commands.Count == 0) - { - Trace.TraceInformation("Commands argument is null or empty", CLASS); - throw new ArgumentException("Commands argument is null or empty"); - } - - // make sure the output is set - errors = null; - Collection results; - - // create the pipeline - Pipeline pipe = _runSpace.CreatePipeline(); - // add the commands to the pipeline - foreach (Command item in commands) - { - pipe.Commands.Add(item); - } // foreach item - // run the pipeline if we have something to execute - results = pipe.Invoke(); - PipelineReader reader = pipe.Error; - errors = (IList)reader.ReadToEnd(); - //check for errors - checkErrors(errors); - // an empty collection instead of null when we have executed - if (results == null) - { - Trace.TraceInformation("NO result returned"); - results = new Collection(); - } //if results - Debug.WriteLine(METHOD + ":exit", CLASS); - return results; - - } - - /// - /// invoke the pipeline - /// - /// a command to execute - /// collection of objects with the result - /// if no command is passed in return null - /// if no output from the invoke return an empty collection - public ICollection InvokePipeline(Command item) - { - // create a new collection and add the command - // specifically not a CommandCollection: that will not work here - Collection commands = new Collection(); - commands.Add(item); - return InvokePipeline(commands); - } - - /// - /// Checks whether errors List contains some error, if so the errors are concatenated and exception is thrown - /// - /// List of error messages - private void checkErrors(IList errors) - { - StringBuilder builder = new StringBuilder(); - foreach (Object error in errors) - { - builder.Append(error.ToString()); - builder.Append("\n"); - } - - if (builder.Length > 0) - { - throw new ConnectorException("Exception when executing PowerShell: " + builder.ToString()); - } - } - - } // class RunSpaceInstance -} diff --git a/DotNetConnectors.sln/ExchangeConnector/build.xml b/DotNetConnectors.sln/ExchangeConnector/build.xml deleted file mode 100644 index f51519a7..00000000 --- a/DotNetConnectors.sln/ExchangeConnector/build.xml +++ /dev/null @@ -1,28 +0,0 @@ - - - - - - - diff --git a/DotNetConnectors.sln/Framework/Api.cs b/DotNetConnectors.sln/Framework/Api.cs deleted file mode 100644 index d8e0cf23..00000000 --- a/DotNetConnectors.sln/Framework/Api.cs +++ /dev/null @@ -1,535 +0,0 @@ -/* - * ==================== - * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER. - * - * Copyright 2007-2008 Sun Microsystems, Inc. All rights reserved. - * - * The contents of this file are subject to the terms of the Common Development - * and Distribution License("CDDL") (the "License"). You may not use this file - * except in compliance with the License. - * - * You can obtain a copy of the License at - * http://IdentityConnectors.dev.java.net/legal/license.txt - * See the License for the specific language governing permissions and limitations - * under the License. - * - * When distributing the Covered Code, include this CDDL Header Notice in each file - * and include the License file at identityconnectors/legal/license.txt. - * If applicable, add the following below this CDDL Header, with the fields - * enclosed by brackets [] replaced by your own identifying information: - * "Portions Copyrighted [year] [name of copyright owner]" - * ==================== - */ -using System; -using System.Reflection; -using System.Globalization; -using System.Collections.Generic; -using System.Net.Security; -using System.Security; -using System.Security.Cryptography.X509Certificates; -using System.Text; -using Org.IdentityConnectors.Common; -using Org.IdentityConnectors.Common.Pooling; -using Org.IdentityConnectors.Common.Security; -using Org.IdentityConnectors.Framework.Api.Operations; -using Org.IdentityConnectors.Framework.Common; -using Org.IdentityConnectors.Framework.Common.Objects; - -namespace Org.IdentityConnectors.Framework.Api -{ - public static class APIConstants { - public const int NO_TIMEOUT = -1; - } - - public interface APIConfiguration { - ConfigurationProperties ConfigurationProperties { get; } - bool IsConnectorPoolingSupported { get; } - ObjectPoolConfiguration ConnectorPoolConfiguration { get; } - ICollection> SupportedOperations { get; } - - int GetTimeout(SafeType operation); - void SetTimeout(SafeType operation, int timeout); - - int ProducerBufferSize { get; set; } - } - - /** - * Configuration properties encapsulates the {@link Configuration} and uses - * {@link Reflection} to determine the properties available for manipulation. - */ - public interface ConfigurationProperties { - - /** - * Get the list of properties names for this {@link Configuration}. - * - * @return get the list of properties names. - */ - IList PropertyNames { get; } - - /** - * Get a particular {@link ConfigurationProperty} by name. - * - * @param name - * the unique name of the property. - * @return a {@link ConfigurationProperty} if it exists otherwise null. - */ - ConfigurationProperty GetProperty(string name); - - /** - * Set the value of the {@link Configuration} property by name. - * - * @param name - * Name of the property to set the value against. - * @param value - * Value to set on the configuration property. - * @throws IllegalArgumentException - * iff the property name does not exist. - */ - void SetPropertyValue(string name, Object value); - - } - - /** - * Translation from {@link Configuration} at the SPI layer to the API. - */ - public interface ConfigurationProperty { - - int Order { get; } - - /** - * Get the unique name of the configuration property. - */ - string Name { get; } - - /** - * Get the help message from the message catalog. - */ - string GetHelpMessage(string def); - - /** - * Get the display name for the is configuration - */ - string GetDisplayName(string def); - - /** - * Get the value from the property. This should be the default value. - */ - object Value { get; set; } - - /** - * Get the type of the property. - */ - Type ValueType { get; } - - /** - * Is this a confidential property whose value should be encrypted by - * the application when persisted? - */ - bool IsConfidential { get; } - - /** - * Is this a required property - * @return True if the property is required - */ - bool IsRequired { get; } - - /** - * Set of operations for which this property must be specified. - * This is used for the case where a connector may or may not - * implement certain operations depending in the configuration. - * The default value of "empty array" is special in that - * it means that this property is applicable to all operations. - */ - ICollection> Operations { get; } - } - - /** - * Main interface for which consumers call the Connector API logic. - */ - public interface ConnectorFacade : CreateApiOp, DeleteApiOp, - SearchApiOp, UpdateApiOp, SchemaApiOp, AuthenticationApiOp, GetApiOp, - ValidateApiOp, TestApiOp, ScriptOnConnectorApiOp, ScriptOnResourceApiOp, - SyncApiOp { - - /** - * Get the set of operations that this {@link ConnectorFacade} will support. - */ - ICollection> SupportedOperations { get; } - - /** - * Get an instance of an operation that this facade supports. - */ - APIOperation GetOperation(SafeType type); - - } - - /** - * Manages a pool of connectors for use by a provisioner. - * - */ - public abstract class ConnectorFacadeFactory { - - // At some point we might make this pluggable, but for now, hard-code - private const string IMPL_NAME = - "Org.IdentityConnectors.Framework.Impl.Api.ConnectorFacadeFactoryImpl"; - private static ConnectorFacadeFactory _instance; - private static object LOCK = new Object(); - - /** - * Get the singleton instance of the {@link ConnectorFacadeFactory}. - */ - public static ConnectorFacadeFactory GetInstance() { - lock(LOCK) { - if (_instance == null) { - SafeType t = FrameworkInternalBridge.LoadType(IMPL_NAME); - _instance = t.CreateInstance(); - } - } - return _instance; - } - - /** - * Get a new instance of {@link ConnectorFacade}. - * - * @param config - * all the configuration that the framework, connector, and - * pooling needs. - * @return {@link ConnectorFacade} to call API operations against. - * @throws ClassNotFoundException - */ - public abstract ConnectorFacade NewInstance(APIConfiguration config); - - - /** - * Dispose of all connection pools, resources, etc. - */ - public abstract void Dispose(); - } - - /** - * The connector meta-data for a given connector. - */ - public interface ConnectorInfo { - /** - * Returns a friendly name suitable for display in the UI. - * - * @return The friendly name - */ - string GetConnectorDisplayName(); - - ConnectorMessages Messages { get; } - - ConnectorKey ConnectorKey { get; } - - /** - * Loads the {@link Connector} and {@link Configuration} class in order to - * determine the proper default configuration parameters. - */ - APIConfiguration CreateDefaultAPIConfiguration(); - } - /** - * Class responsible for maintaing a list of ConnectorInfo - * associated with a set of connector bundles. - */ - public interface ConnectorInfoManager { - /** - * Returns the list of ConnectorInfo - * @return the list of ConnectorInfo - */ - IList ConnectorInfos { get; } - - /** - * Given a connectorName and connectorVersion, returns the - * associated ConnectorInfo. - * @param key The connector key. - * @return The ConnectorInfo or null if it couldn't - * be found. - */ - ConnectorInfo FindConnectorInfo(ConnectorKey key); - } - /** - * The main entry point into connectors. This allows you - * to load the connector classes from a set of bundles. - */ - public abstract class ConnectorInfoManagerFactory { - //At some point we might make this pluggable, but for now, hard-code - private const string IMPL_NAME = - "Org.IdentityConnectors.Framework.Impl.Api.ConnectorInfoManagerFactoryImpl"; - private static ConnectorInfoManagerFactory _instance; - private static object LOCK = new Object(); - /// - /// Singleton pattern for getting an instance of the - /// ConnectorInfoManagerFactory. - /// - /// - public static ConnectorInfoManagerFactory GetInstance() { - lock(LOCK) { - if (_instance == null) { - SafeType t = - FrameworkInternalBridge.LoadType(IMPL_NAME); - _instance = t.CreateInstance(); - } - } - return _instance; - } - public abstract ConnectorInfoManager GetLocalManager(); - public abstract ConnectorInfoManager GetRemoteManager(RemoteFrameworkConnectionInfo info); - - /** - * Clears the bundle manager cache. Generally intended for unit testing - */ - public abstract void ClearRemoteCache(); - } - - /** - * Uniquely identifies a connector within an installation. - * Consists of the triple (bundleName, bundleVersion, connectorName) - */ - public sealed class ConnectorKey { - private readonly string _bundleName; - private readonly string _bundleVersion; - private readonly string _connectorName; - - public ConnectorKey(String bundleName, - String bundleVersion, - String connectorName) { - if (bundleName == null) { - throw new ArgumentException("bundleName may not be null"); - } - if (bundleVersion == null) { - throw new ArgumentException("bundleVersion may not be null"); - } - if (connectorName == null) { - throw new ArgumentException("connectorName may not be null"); - } - _bundleName = bundleName; - _bundleVersion = bundleVersion; - _connectorName = connectorName; - } - - public string BundleName { - get { - return _bundleName; - } - } - - public string BundleVersion { - get { - return _bundleVersion; - } - } - - public string ConnectorName { - get { - return _connectorName; - } - } - - public override bool Equals(object o) { - if ( o is ConnectorKey ) { - ConnectorKey other = (ConnectorKey)o; - if (!_bundleName.Equals(other._bundleName)) { - return false; - } - if (!_bundleVersion.Equals(other._bundleVersion)) { - return false; - } - if (!_connectorName.Equals(other._connectorName)) { - return false; - } - return true; - } - return false; - } - - public override int GetHashCode() { - int rv = 0; - rv ^= _connectorName.GetHashCode(); - return rv; - } - - public override string ToString() { - StringBuilder builder = new StringBuilder(); - builder.Append("ConnectorKey("); - builder.Append(" bundleName=").Append(_bundleName); - builder.Append(" bundleVersion=").Append(_bundleVersion); - builder.Append(" connectorName=").Append(_connectorName); - builder.Append(" )"); - return builder.ToString(); - } - } - - - - public sealed class RemoteFrameworkConnectionInfo { - private readonly String _host; - private readonly int _port; - private readonly GuardedString _key; - private readonly bool _useSSL; - private readonly RemoteCertificateValidationCallback _certificateValidationCallback; - private readonly int _timeout; - - /** - * Creates a new instance of RemoteFrameworkConnectionInfo, using - * a clear (non-ssl) connection and a 60-second timeout. - * @param host The host to connect to - * @param port The port to connect to - */ - public RemoteFrameworkConnectionInfo(String host, - int port, - GuardedString key) - :this(host,port,key,false,null,60*1000) { - } - - /** - * Creates a new instance of RemoteFrameworkConnectionInfo. - * @param host The host to connect to - * @param port The port to connect to - * @param useSSL Set to true if we are to connect via SSL. - * @param certificateValidationCallback to use - * for establising the SSL connection. May be null or empty, - * in which case the default installed providers for the JVM will - * be used. Ignored if 'useSSL' is false. - * @param timeout The timeout to use (in milliseconds). A value of 0 - * means infinite timeout; - */ - public RemoteFrameworkConnectionInfo(String host, - int port, - GuardedString key, - bool useSSL, - RemoteCertificateValidationCallback certificateValidationCallback, - int timeout) { - - if ( host == null ) { - throw new ArgumentException("Parameter 'host' is null."); - } - if ( key == null ) { - throw new ArgumentException("Parameter 'key' is null."); - } - - _host = host; - _port = port; - _key = key; - _useSSL = useSSL; - _certificateValidationCallback = certificateValidationCallback; - _timeout = timeout; - } - - /** - * Returns the host to connect to. - * @return The host to connect to. - */ - public String Host { - get { - return _host; - } - } - - /** - * Returns the port to connect to - * @return The port to connect to - */ - public int Port { - get { - return _port; - } - } - - public GuardedString Key { - get { - return _key; - } - } - - /** - * Returns true iff we are to use SSL to connect. - * @return true iff we are to use SSL to connect. - */ - public bool UseSSL { - get { - return _useSSL; - } - } - - /** - * Returns the list of {@link TrustManager}'s. to use when establishing - * the connection. - * @return The list of {@link TrustManager}'s. - */ - public RemoteCertificateValidationCallback CertificateValidationCallback { - get { - return _certificateValidationCallback; - } - } - - /** - * Returns the timeout (in milliseconds) to use for the connection. - * A value of zero means infinite timeout. - * @return the timeout (in milliseconds) to use for the connection. - */ - public int Timeout { - get { - return _timeout; - } - } - - /** - * {@inheritDoc} - */ - public override bool Equals(Object o) { - if ( o is RemoteFrameworkConnectionInfo ) { - RemoteFrameworkConnectionInfo other = - (RemoteFrameworkConnectionInfo)o; - if (!Object.Equals(Host,other.Host)) { - return false; - } - if (Port != other.Port) { - return false; - } - if (UseSSL != other.UseSSL) { - return false; - } - if (CertificateValidationCallback == null || - other.CertificateValidationCallback == null) { - if (CertificateValidationCallback != null || - other.CertificateValidationCallback != null) { - return false; - } - } - else { - if (!CertificateValidationCallback.Equals - (other.CertificateValidationCallback)) { - return false; - } - } - - if (!Key.Equals(other.Key)) { - return false; - } - - if (Timeout != other.Timeout) { - return false; - } - - return true; - } - return false; - } - - /** - * {@inheritDoc} - */ - public override int GetHashCode() { - return _host.GetHashCode() ^ _port; - } - - /** - * {@inheritDoc} - */ - public override String ToString() { - return "{host="+_host+", port="+_port+"}"; - } - - - } -} diff --git a/DotNetConnectors.sln/Framework/ApiOperations.cs b/DotNetConnectors.sln/Framework/ApiOperations.cs deleted file mode 100644 index bf1ce08f..00000000 --- a/DotNetConnectors.sln/Framework/ApiOperations.cs +++ /dev/null @@ -1,404 +0,0 @@ -/* - * ==================== - * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER. - * - * Copyright 2007-2008 Sun Microsystems, Inc. All rights reserved. - * - * The contents of this file are subject to the terms of the Common Development - * and Distribution License("CDDL") (the "License"). You may not use this file - * except in compliance with the License. - * - * You can obtain a copy of the License at - * http://IdentityConnectors.dev.java.net/legal/license.txt - * See the License for the specific language governing permissions and limitations - * under the License. - * - * When distributing the Covered Code, include this CDDL Header Notice in each file - * and include the License file at identityconnectors/legal/license.txt. - * If applicable, add the following below this CDDL Header, with the fields - * enclosed by brackets [] replaced by your own identifying information: - * "Portions Copyrighted [year] [name of copyright owner]" - * ==================== - */ -using System; -using System.Reflection; -using System.Globalization; -using System.Collections.Generic; - -using Org.IdentityConnectors.Common.Security; -using Org.IdentityConnectors.Framework.Common.Objects; -using Org.IdentityConnectors.Framework.Common.Objects.Filters; - -namespace Org.IdentityConnectors.Framework.Api.Operations -{ - /** - * Base interface for all API operations. - */ - public interface APIOperation { - } - - public interface AuthenticationApiOp : APIOperation { - - /** - * Most basic authentication available. - * - * @param username - * string that represents the account or user id. - * @param password - * string that represents the password for the account or user. - * @throws RuntimeException - * iff the credentials do not pass authentication otherwise - * nothing. - */ - Uid Authenticate(ObjectClass objectClass, string username, GuardedString password, OperationOptions options); - } - - /// - /// Operation to create connector objects based on the attributes provided. - /// - public interface CreateApiOp : APIOperation { - - /// - /// Creates a user based on the ConnectorAttributes provide. The only - /// required attribute is the ObjectClass and those required by the - /// Connector. The API will validate the existence of the - /// ObjectClass attribute and that there are no duplicate name'd - /// attributes. - /// - /// ConnectorAttribtes to create the object. - /// Unique id for the created object. - Uid Create(ObjectClass oclass, ICollection attrs, OperationOptions options); - } - - /// - /// Deletes an object with the specified Uid and ObjectClass on the - /// resource. - /// - public interface DeleteApiOp : APIOperation { - /// - /// Delete the object that the specified Uid identifies (if any). - /// - /// The type of object to delete. - /// The unique identitfier for the object to delete. - /// Throws UnknowUid if the object does not exist. - void Delete(ObjectClass objectClass, Uid uid, OperationOptions options); - } - - /** - * Get a particular {@link ConnectorObject} based on the {@link Uid}. - */ - public interface GetApiOp : APIOperation { - - /** - * Get a particular {@link ConnectorObject} based on the {@link Uid}. - * - * @param uid - * the unique id of the object that to get. - * @return {@link ConnectorObject} based on the {@link Uid} provided. - */ - ConnectorObject GetObject(ObjectClass objClass, Uid uid, OperationOptions options); - } - - /** - * Get the schema from the {@link Connector}. - */ - public interface SchemaApiOp : APIOperation { - /** - * Retrieve the basic schema of this {@link Connector}. - */ - Schema Schema(); - } - - - public interface SearchApiOp : APIOperation { - - /** - * Search the resource for all objects that match the filter. - * - * @param filter - * Reduces the number of entries to only those that match the - * {@link Filter} provided. - * @param handler - * class responsible for working with the objects returned from - * the search. - * @throws RuntimeException - * iff there is problem during the processing of the results. - */ - void Search(ObjectClass oclass, Filter filter, ResultsHandler handler, OperationOptions options); - } - - /** - * Runs a script in the same JVM or .Net Runtime as the Connector. - * That is, if you are using a local framework, the script will be - * run in your JVM. If you are connected to a remote framework, the - * script will be run in the remote JVM or .Net Runtime. - *

- * This API allows an application to run a script in the context - * of any connector. (A connector need not implement any particular interface - * in order to enable this.) The minimum contract to which each connector - * must adhere is as follows: - *

    - *
  1. Script will run in the same classloader/execution environment - * as the connector, so the script will have access to all the classes - * to which the connector has access. - *
  2. - *
  3. Script will have access to a "connector" variable - * that is equivalent to an initialized instance of a connector. - * Thus, at a minimum the script will be able to access - * {@link Connector#getConfiguration() the configuration of the connector}. - *
  4. - *
  5. Script will have access to any - * {@link ScriptContext#getScriptArguments() script-arguments} - * passed in by the application. - *
  6. - *
- *

- * A connector that implements {@link ScriptOnConnectorOp} - * may provide more variables than what is described above. - * A connector also may perform special processing - * for {@link OperationOptions} specific to that connector. - * Consult the javadoc of each particular connector to find out what - * additional capabilities, if any, that connector exposes for use in scripts. - *

- * NOTE: A caller who wants to execute scripts on a connector - * should assume that a script must not use any method of the connector - * beyond the minimum contract described above, - * unless the connector explicitly documents that method as - * "for use by connector script". The primary function of a connector - * is to implement the SPI in the context of the Connector framework. - * In general, no caller should invoke Connector methods directly - * --whether by a script or by other means. - */ - public interface ScriptOnConnectorApiOp : APIOperation { - - /** - * Runs the script. - * @param request - The script and arguments to run. - * @param options - Additional options that control how the script is - * run. The framework does not currently recognize any options - * but specific connectors might. Consult the documentation - * for each connector to identify supported options. - * @return The result of the script. The return type must be - * a type that the framework supports for serialization. - * @see ObjectSerializerFactory for a list of supported return types. - */ - Object RunScriptOnConnector(ScriptContext request, - OperationOptions options); - } - /** - * Runs a script on the target resource that a connector manages. - * This API operation is supported only for a connector that implements - * {@link ScriptOnResourceOp}. - *

- * The contract here at the API level is intentionally very loose. - * Each connector decides what script languages it supports, - * what running a script on a target resource actually means, - * and what script options (if any) that connector supports. - * Refer to the javadoc of each particular connector for more information. - */ - public interface ScriptOnResourceApiOp : APIOperation { - - /** - * Runs a script on a specific target resource. - * @param request The script and arguments to run. - * @param options Additional options which control how the script is - * run. Please refer to the connector documentation for supported - * options. - * @return The result of the script. The return type must be - * a type that the connector framework supports for serialization. - * See {@link ObjectSerializerFactory} for a list of supported return types. - */ - Object RunScriptOnResource(ScriptContext request, - OperationOptions options); - } - /** - * Receive synchronization events from the resource. This will be supported by - * connectors that implement {@link SyncOp}. - * - * @see SyncOp - */ - public interface SyncApiOp : APIOperation { - /** - * Perform a synchronization. - * - * @param objClass - * The object class to synchronize. Must not be null. - * @param token - * The token representing the last token from the previous sync. - * Should be null if this is the first sync for the given - * resource. - * @param handler - * The result handler Must not be null. - * @param options - * additional options that impact the way this operation is run. - * May be null. - */ - void Sync(ObjectClass objClass, SyncToken token, - SyncResultsHandler handler, - OperationOptions options); - /** - * Returns the token corresponding to the latest sync delta. - * This is to support applications that may wish to sync starting - * "now". - * @return The latest token or null if there is no sync data. - */ - SyncToken GetLatestSyncToken(ObjectClass objectClass); - } - - /** - * Updates a {@link ConnectorObject}. This operation - * is supported for those connectors that implement - * either {@link UpdateOp} or the more advanced - * {@link UpdateAttributeValuesOp}. - */ - public interface UpdateApiOp : APIOperation { - - /** - * Update the object specified by the {@link ObjectClass} and {@link Uid}, - * replacing the current values of each attribute with the values - * provided. - *

- * For each input attribute, replace - * all of the current values of that attribute in the target object with - * the values of that attribute. - *

- * If the target object does not currently contain an attribute that the - * input set contains, then add this - * attribute (along with the provided values) to the target object. - *

- * If the value of an attribute in the input set is - * {@code null}, then do one of the following, depending on - * which is most appropriate for the target: - *

    - *
  • If possible, remove that attribute from the target - * object entirely.
  • - *
  • Otherwise, replace all of the current values of that - * attribute in the target object with a single value of - * {@code null}.
  • - *
- * @param objclass - * the type of object to modify. Must not be null. - * @param uid - * the uid of the object to modify. Must not be null. - * @param replaceAttributes - * set of new {@link Attribute}. the values in this set - * represent the new, merged values to be applied to the object. - * This set may also include {@link OperationalAttributes operational attributes}. - * Must not be null. - * @param options - * additional options that impact the way this operation is run. - * May be null. - * @return the {@link Uid} of the updated object in case the update changes - * the formation of the unique identifier. - */ - Uid Update(ObjectClass objclass, - Uid uid, - ICollection replaceAttributes, - OperationOptions options); - - /** - * Update the object specified by the {@link ObjectClass} and {@link Uid}, - * adding to the current values of each attribute the values provided. - *

- * For each attribute that the input set contains, add to - * the current values of that attribute in the target object all of the - * values of that attribute in the input set. - *

- * NOTE that this does not specify how to handle duplicate values. - * The general assumption for an attribute of a {@code ConnectorObject} - * is that the values for an attribute may contain duplicates. - * Therefore, in general simply append the provided values - * to the current value for each attribute. - *

- * IMPLEMENTATION NOTE: for connectors that merely implement {@link UpdateOp} - * and not {@link UpdateAttributeValuesOp} this method will be simulated by - * fetching, merging, and calling - * {@link UpdateOp#update(ObjectClass, Uid, Set, OperationOptions)}. Therefore, - * connector implementations are encourage to implement {@link UpdateAttributeValuesOp} - * from a performance and atomicity standpoint. - * @param objclass - * the type of object to modify. Must not be null. - * @param uid - * the uid of the object to modify. Must not be null. - * @param valuesToAdd - * set of {@link Attribute} deltas. The values for the attributes - * in this set represent the values to add to attributes in the object. - * merged. This set must not include {@link OperationalAttributes operational attributes}. - * Must not be null. - * @param options - * additional options that impact the way this operation is run. - * May be null. - * @return the {@link Uid} of the updated object in case the update changes - * the formation of the unique identifier. - */ - Uid AddAttributeValues(ObjectClass objclass, - Uid uid, - ICollection valuesToAdd, - OperationOptions options); - - /** - * Update the object specified by the {@link ObjectClass} and {@link Uid}, - * removing from the current values of each attribute the values provided. - *

- * For each attribute that the input set contains, - * remove from the current values of that attribute in the target object - * any value that matches one of the values of the attribute from the input set. - *

- * NOTE that this does not specify how to handle unmatched values. - * The general assumption for an attribute of a {@code ConnectorObject} - * is that the values for an attribute are merely representational state. - * Therefore, the implementer should simply ignore any provided value - * that does not match a current value of that attribute in the target - * object. Deleting an unmatched value should always succeed. - *

- * IMPLEMENTATION NOTE: for connectors that merely implement {@link UpdateOp} - * and not {@link UpdateAttributeValuesOp} this method will be simulated by - * fetching, merging, and calling - * {@link UpdateOp#update(ObjectClass, Uid, Set, OperationOptions)}. Therefore, - * connector implementations are encourage to implement {@link UpdateAttributeValuesOp} - * from a performance and atomicity standpoint. - * @param objclass - * the type of object to modify. Must not be null. - * @param uid - * the uid of the object to modify. Must not be null. - * @param valuesToRemove - * set of {@link Attribute} deltas. The values for the attributes - * in this set represent the values to remove from attributes in the object. - * merged. This set must not include {@link OperationalAttributes operational attributes}. - * Must not be null. - * @param options - * additional options that impact the way this operation is run. - * May be null. - * @return the {@link Uid} of the updated object in case the update changes - * the formation of the unique identifier. - */ - Uid RemoveAttributeValues(ObjectClass objclass, - Uid uid, - ICollection valuesToRemove, - OperationOptions options); - - } - - - public interface ValidateApiOp : APIOperation { - /** - * Tests connectivity and validity of the {@link Configuration}. - * - * @throws RuntimeException - * iff the {@link Configuration} is not valid or a - * {@link Connection} to the resource could not be established. - */ - void Validate(); - } - - public interface TestApiOp : APIOperation { - /** - * Tests connectivity and validity of the {@link Configuration}. - * - * @throws RuntimeException - * iff the {@link Configuration} is not valid or a - * {@link Connection} to the resource could not be established. - */ - void Test(); - } -} diff --git a/DotNetConnectors.sln/Framework/AssemblyInfo.cs b/DotNetConnectors.sln/Framework/AssemblyInfo.cs deleted file mode 100644 index c33629ae..00000000 --- a/DotNetConnectors.sln/Framework/AssemblyInfo.cs +++ /dev/null @@ -1,53 +0,0 @@ -/* - * ==================== - * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER. - * - * Copyright 2007-2008 Sun Microsystems, Inc. All rights reserved. - * - * The contents of this file are subject to the terms of the Common Development - * and Distribution License("CDDL") (the "License"). You may not use this file - * except in compliance with the License. - * - * You can obtain a copy of the License at - * http://IdentityConnectors.dev.java.net/legal/license.txt - * See the License for the specific language governing permissions and limitations - * under the License. - * - * When distributing the Covered Code, include this CDDL Header Notice in each file - * and include the License file at identityconnectors/legal/license.txt. - * If applicable, add the following below this CDDL Header, with the fields - * enclosed by brackets [] replaced by your own identifying information: - * "Portions Copyrighted [year] [name of copyright owner]" - * ==================== - */ -#region Using directives - -using System.Reflection; -using System.Runtime.CompilerServices; -using System.Runtime.InteropServices; - -#endregion - -// General Information about an assembly is controlled through the following -// set of attributes. Change these attribute values to modify the information -// associated with an assembly. -[assembly: AssemblyTitle("Open Connectors Framework")] -[assembly: AssemblyDescription("")] -[assembly: AssemblyConfiguration("")] -[assembly: AssemblyCompany("")] -[assembly: AssemblyProduct("Open Connectors Framework")] -[assembly: AssemblyCopyright("")] -[assembly: AssemblyTrademark("")] -[assembly: AssemblyCulture("")] - -// This sets the default COM visibility of types in the assembly to invisible. -// If you need to expose a type to COM, use [ComVisible(true)] on that type. -[assembly: ComVisible(false)] - -// The assembly version has following format : -// -// Major.Minor.Build.Revision -// -// You can specify all the values or you can use the default the Revision and -// Build Numbers by using the '*' as shown below: -[assembly: AssemblyVersion("1.0.0.0")] diff --git a/DotNetConnectors.sln/Framework/Common.cs b/DotNetConnectors.sln/Framework/Common.cs deleted file mode 100644 index 2da8fa0d..00000000 --- a/DotNetConnectors.sln/Framework/Common.cs +++ /dev/null @@ -1,287 +0,0 @@ -/* - * ==================== - * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER. - * - * Copyright 2007-2008 Sun Microsystems, Inc. All rights reserved. - * - * The contents of this file are subject to the terms of the Common Development - * and Distribution License("CDDL") (the "License"). You may not use this file - * except in compliance with the License. - * - * You can obtain a copy of the License at - * http://IdentityConnectors.dev.java.net/legal/license.txt - * See the License for the specific language governing permissions and limitations - * under the License. - * - * When distributing the Covered Code, include this CDDL Header Notice in each file - * and include the License file at identityconnectors/legal/license.txt. - * If applicable, add the following below this CDDL Header, with the fields - * enclosed by brackets [] replaced by your own identifying information: - * "Portions Copyrighted [year] [name of copyright owner]" - * ==================== - */ -using System; -using System.Reflection; -using System.Collections.Generic; -using Org.IdentityConnectors.Common; -using Org.IdentityConnectors.Common.Security; -using Org.IdentityConnectors.Framework.Spi; -using Org.IdentityConnectors.Framework.Api.Operations; -using Org.IdentityConnectors.Framework.Spi.Operations; -using Org.IdentityConnectors.Framework.Common.Objects; -namespace Org.IdentityConnectors.Framework.Common -{ - internal static class FrameworkInternalBridge { - private static readonly Object LOCK = new Object(); - private static Assembly _assembly = null; - ///

- /// Loads a class from the FrameworkInternal module - /// - /// - /// - public static SafeType LoadType(String typeName) where T : class { - - Assembly assembly; - lock(LOCK) { - if (_assembly == null) { - AssemblyName assemName = new AssemblyName(); - assemName.Name = "FrameworkInternal"; - _assembly = Assembly.Load(assemName); - } - assembly = _assembly; - } - - return SafeType.ForRawType(assembly.GetType(typeName,true)); - - } - } - - public static class FrameworkUtil { - private static readonly IDictionary,SafeType> SPI_TO_API; - private static readonly ICollection CONFIG_SUPPORTED_TYPES; - private static readonly ICollection ATTR_SUPPORTED_TYPES; - - static FrameworkUtil() { - IDictionary,SafeType> temp = - new Dictionary,SafeType>(); - temp[SafeType.Get()]= - SafeType.Get(); - temp[SafeType.Get()]= - SafeType.Get(); - temp[SafeType.Get()]= - SafeType.Get(); - temp[SafeType.ForRawType(typeof(SearchOp<>))]= - SafeType.Get(); - temp[SafeType.Get()]= - SafeType.Get(); - temp[SafeType.Get()]= - SafeType.Get(); - temp[SafeType.Get()]= - SafeType.Get(); - temp[SafeType.Get()]= - SafeType.Get(); - temp[SafeType.Get()]= - SafeType.Get(); - temp[SafeType.Get()]= - SafeType.Get(); - temp[SafeType.Get()]= - SafeType.Get(); - SPI_TO_API = CollectionUtil.NewReadOnlyDictionary(temp); - - CONFIG_SUPPORTED_TYPES = CollectionUtil.NewReadOnlySet - ( - typeof(string), - typeof(long), - typeof(long?), - typeof(char), - typeof(char?), - typeof(double), - typeof(double?), - typeof(float), - typeof(float?), - typeof(int), - typeof(int?), - typeof(bool), - typeof(bool?), - typeof(Uri), - typeof(FileName), - typeof(GuardedString) - ); - ATTR_SUPPORTED_TYPES = CollectionUtil.NewReadOnlySet - ( - typeof(string), - typeof(long), - typeof(long?), - typeof(char), - typeof(char?), - typeof(double), - typeof(double?), - typeof(float), - typeof(float?), - typeof(int), - typeof(int?), - typeof(bool), - typeof(bool?), - typeof(byte[]), - typeof(BigDecimal), - typeof(BigInteger), - typeof(GuardedString) - ); - - } - - - /** - * Determines if the class is a supported attribute type. If not it throws - * an {@link IllegalArgumentException}. - * - *
    - *
  • string
  • - *
  • long
  • - *
  • long?
  • - *
  • char
  • - *
  • char?
  • - *
  • double
  • - *
  • double?
  • - *
  • float
  • - *
  • float?
  • - *
  • int
  • - *
  • int?
  • - *
  • bool
  • - *
  • bool?
  • - *
  • byte[]
  • - *
  • BigDecimal
  • - *
  • BigInteger
  • - *
- * - * @param clazz - * type to check against the support list of types. - * @throws IllegalArgumentException - * iff the type is not on the supported list. - */ - public static void CheckAttributeType(Type type) { - if (!FrameworkUtil.IsSupportedAttributeType(type)) { - String MSG = "Attribute type ''"+type+"'' is not supported."; - throw new ArgumentException(MSG); - } - } - public static void CheckAttributeValue(Object value) { - if ( value != null ) { - CheckAttributeType(value.GetType()); - } - } - public static ICollection> Spi2Apis(SafeType type) { - type = type.GetTypeErasure(); - HashSet> set = new HashSet>(); - set.Add(SPI_TO_API[type]); - // add GetApiOp if search is available.. - - if (type.RawType.Equals(typeof(SearchOp<>))) { - set.Add(SafeType.Get()); - } - return set; - } - public static ICollection> AllSPIOperations() { - return SPI_TO_API.Keys; - } - public static ICollection> AllAPIOperations() { - ICollection> set = - new HashSet>(); - CollectionUtil.AddAll(set, - SPI_TO_API.Values); - // add Get because it doesn't have a corresponding SPI. - set.Add(SafeType.Get()); - set.Add(SafeType.Get()); - return CollectionUtil.AsReadOnlySet(set); - } - public static ICollection> GetDefaultSupportedOperations(SafeType connector) { - ICollection> rv = - new HashSet>(); - ICollection interfaces = - ReflectionUtil.GetTypeErasure(ReflectionUtil.GetAllInterfaces(connector.RawType)); - foreach (SafeType spi in AllSPIOperations()) { - if (interfaces.Contains(spi.RawType)) { - CollectionUtil.AddAll(rv,Spi2Apis(spi)); - } - } - //finally add unconditionally supported ops - CollectionUtil.AddAll(rv,GetUnconditionallySupportedOperations()); - return CollectionUtil.AsReadOnlySet(rv); - } - public static ICollection> GetUnconditionallySupportedOperations() { - HashSet> ret; - ret = new HashSet>(); - //add validate api op always - ret.Add(SafeType.Get()); - //add ScriptOnConnectorApiOp always - ret.Add(SafeType.Get()); - return ret; - } - public static ICollection GetAllSupportedConfigTypes() { - return CONFIG_SUPPORTED_TYPES; - } - public static bool IsSupportedConfigurationType (Type type) { - if ( type.IsArray) { - return IsSupportedConfigurationType(type.GetElementType()); - } - else { - return CONFIG_SUPPORTED_TYPES.Contains(type); - } - } - public static ICollection GetAllSupportedAttributeTypes() { - return ATTR_SUPPORTED_TYPES; - } - public static bool IsSupportedAttributeType(Type clazz) { - return ATTR_SUPPORTED_TYPES.Contains(clazz); - } - - /** - * Determines if the class is a supported type for an OperationOption. If not it throws - * an {@link IllegalArgumentException}. - * - * @param clazz - * type to check against the support list of types. - * @throws IllegalArgumentException - * iff the type is not on the supported list. - */ - public static void CheckOperationOptionType(Type clazz) { - //the set of supported operation option types - //is the same as that for configuration beans plus Name, - //ObjectClass, Uid, and QualifiedUid - - if ( clazz.IsArray ) { - CheckOperationOptionType(clazz.GetElementType()); - return; - } - - if (FrameworkUtil.IsSupportedConfigurationType(clazz)) { - return; //ok - } - - if (typeof(ObjectClass).IsAssignableFrom(clazz)) { - return; //ok - } - - if (typeof(Uid).IsAssignableFrom(clazz)) { - return; //ok - } - - if (typeof(QualifiedUid).IsAssignableFrom(clazz)) { - return; //ok - } - - String MSG = "ConfigurationOption type '+"+clazz.Name+"+' is not supported."; - throw new ArgumentException(MSG); - } - /** - * Determines if the class of the object is a supported attribute type. - * If not it throws an {@link IllegalArgumentException}. - * @param value The value to check or null. - */ - public static void CheckOperationOptionValue(Object val) { - if ( val != null ) { - CheckOperationOptionType(val.GetType()); - } - } - } -} diff --git a/DotNetConnectors.sln/Framework/CommonExceptions.cs b/DotNetConnectors.sln/Framework/CommonExceptions.cs deleted file mode 100644 index 08043091..00000000 --- a/DotNetConnectors.sln/Framework/CommonExceptions.cs +++ /dev/null @@ -1,291 +0,0 @@ -/* - * ==================== - * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER. - * - * Copyright 2007-2008 Sun Microsystems, Inc. All rights reserved. - * - * The contents of this file are subject to the terms of the Common Development - * and Distribution License("CDDL") (the "License"). You may not use this file - * except in compliance with the License. - * - * You can obtain a copy of the License at - * http://IdentityConnectors.dev.java.net/legal/license.txt - * See the License for the specific language governing permissions and limitations - * under the License. - * - * When distributing the Covered Code, include this CDDL Header Notice in each file - * and include the License file at identityconnectors/legal/license.txt. - * If applicable, add the following below this CDDL Header, with the fields - * enclosed by brackets [] replaced by your own identifying information: - * "Portions Copyrighted [year] [name of copyright owner]" - * ==================== - */ -using System; -using Org.IdentityConnectors.Framework.Common.Objects; - -namespace Org.IdentityConnectors.Framework.Common.Exceptions -{ - #region AlreadyExistsException - public class AlreadyExistsException : ConnectorException { - - public AlreadyExistsException() : base() { - } - - public AlreadyExistsException(String message) : base(message) { - - } - - public AlreadyExistsException(Exception ex) : base(ex) { - } - - public AlreadyExistsException(String message, Exception ex) : base(message,ex) { - } - } - #endregion - - #region ConfigurationException - public class ConfigurationException : ConnectorException { - - public ConfigurationException() : base() { - } - - public ConfigurationException(String message) : base(message) { - } - - public ConfigurationException(Exception ex) : base(ex) { - } - - public ConfigurationException(String message, Exception ex) : base(message,ex) { - } - } - #endregion - - #region ConnectionBrokenException - public class ConnectionBrokenException : ConnectorIOException { - - - public ConnectionBrokenException() : base() { - } - - public ConnectionBrokenException(String msg) : base(msg) { - } - - public ConnectionBrokenException(Exception ex) : base(ex) { - } - - public ConnectionBrokenException(String message, Exception ex) : base(message,ex) { - } - } - #endregion - - #region ConnectionFailedException - public class ConnectionFailedException : ConnectorIOException { - - - public ConnectionFailedException() : base() { - } - - public ConnectionFailedException(String msg) : base(msg) { - } - - public ConnectionFailedException(Exception ex) : base(ex) { - } - - public ConnectionFailedException(String message, Exception ex) : base(message,ex) { - } - - } - #endregion - - #region ConnectorException - public class ConnectorException : ApplicationException { - - public ConnectorException() : base() { - } - - /** - * Sets a message for the {@link Exception}. - * - * @param message - * passed to the {@link RuntimeException} message. - */ - public ConnectorException(String message) :base(message) { - } - - /** - * Sets the stack trace to the original exception, so this exception can - * masquerade as the original only be a {@link RuntimeException}. - * - * @param originalException - * the original exception adapted to {@link RuntimeException}. - */ - public ConnectorException(Exception ex) : base(ex.Message,ex) { - } - - /** - * Sets the stack trace to the original exception, so this exception can - * masquerade as the original only be a {@link RuntimeException}. - * - * @param message - * @param originalException - * the original exception adapted to {@link RuntimeException}. - */ - public ConnectorException(String message, Exception originalException) : base(message,originalException) { - } - - } - #endregion - - #region ConnectorIOException - public class ConnectorIOException : ConnectorException { - - public ConnectorIOException() : base() { - } - - public ConnectorIOException(String msg) : base(msg) { - } - - public ConnectorIOException(Exception ex) : base(ex) { - } - - public ConnectorIOException(String message, Exception ex) : base(message,ex) { - } - } - #endregion - - #region ConnectorSecurityException - public class ConnectorSecurityException : ConnectorException { - - public ConnectorSecurityException() : base() { - } - - public ConnectorSecurityException(String message) : base(message) { - } - - public ConnectorSecurityException(Exception ex) : base(ex) { - } - - public ConnectorSecurityException(String message, Exception ex) : base(message,ex) { - } - } - #endregion - - #region InvalidCredentialException - public class InvalidCredentialException : ConnectorSecurityException { - - public InvalidCredentialException() : base() { - } - - public InvalidCredentialException(String message) : base(message) { - } - - public InvalidCredentialException(Exception ex) : base(ex) { - } - - public InvalidCredentialException(String message, Exception ex) : base(message,ex) { - } - } - #endregion - - #region InvalidPasswordException - public class InvalidPasswordException : InvalidCredentialException { - - public InvalidPasswordException() : base() { - } - - public InvalidPasswordException(String message) : base(message) { - } - - public InvalidPasswordException(Exception ex) : base(ex) { - } - - public InvalidPasswordException(String message, Exception ex) : base(message,ex) { - } - } - #endregion - - #region OperationTimeoutException - public class OperationTimeoutException : ConnectorException { - - public OperationTimeoutException() : base() { - } - - public OperationTimeoutException(String msg) : base(msg) { - } - - public OperationTimeoutException(Exception e) : base(e) { - } - - public OperationTimeoutException(String msg, Exception e) : base(msg,e) { - } - - } - #endregion - - #region PasswordExpiredException - public class PasswordExpiredException : InvalidPasswordException { - - private Uid _uid; - - public PasswordExpiredException() : base() { - } - - public PasswordExpiredException(String message) : base(message) { - } - - public PasswordExpiredException(Exception ex) : base(ex) { - } - - public PasswordExpiredException(String message, Exception ex) : base(message,ex) { - } - - public Uid Uid { - get { - return _uid; - } - set { - _uid = value; - } - } - } - #endregion - - #region PermissionDeniedException - public class PermissionDeniedException : ConnectorSecurityException { - - public PermissionDeniedException() : base() { - } - - public PermissionDeniedException(String message) : base(message) { - } - - public PermissionDeniedException(Exception ex) : base(ex) { - } - - public PermissionDeniedException(String message, Exception ex) : base(message,ex) { - } - } - #endregion - - #region UnknownUidException - public class UnknownUidException : InvalidCredentialException { - const string MSG = "Object with Uid '{0}' and ObjectClass '{1}' does not exist!"; - - public UnknownUidException() : base() { - } - - public UnknownUidException(Uid uid, ObjectClass objclass) : - base(String.Format(MSG, uid, objclass)){ - } - - public UnknownUidException(String message) : base(message) { - } - - public UnknownUidException(Exception ex) : base(ex) { - } - - public UnknownUidException(String message, Exception ex) : base(message,ex) { - } - } - #endregion -} diff --git a/DotNetConnectors.sln/Framework/CommonObjects.cs b/DotNetConnectors.sln/Framework/CommonObjects.cs deleted file mode 100644 index fa2cec1a..00000000 --- a/DotNetConnectors.sln/Framework/CommonObjects.cs +++ /dev/null @@ -1,3893 +0,0 @@ -/* - * ==================== - * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER. - * - * Copyright 2007-2008 Sun Microsystems, Inc. All rights reserved. - * - * The contents of this file are subject to the terms of the Common Development - * and Distribution License("CDDL") (the "License"). You may not use this file - * except in compliance with the License. - * - * You can obtain a copy of the License at - * http://IdentityConnectors.dev.java.net/legal/license.txt - * See the License for the specific language governing permissions and limitations - * under the License. - * - * When distributing the Covered Code, include this CDDL Header Notice in each file - * and include the License file at identityconnectors/legal/license.txt. - * If applicable, add the following below this CDDL Header, with the fields - * enclosed by brackets [] replaced by your own identifying information: - * "Portions Copyrighted [year] [name of copyright owner]" - * ==================== - */ -using System; -using System.Security; -using System.Collections; -using System.Collections.Generic; -using System.Globalization; -using System.Text; - -using Org.IdentityConnectors.Common; -using Org.IdentityConnectors.Common.Security; - -using Org.IdentityConnectors.Framework.Spi; -using Org.IdentityConnectors.Framework.Spi.Operations; -using Org.IdentityConnectors.Framework.Api.Operations; -using Org.IdentityConnectors.Framework.Common.Serializer; -using Org.IdentityConnectors.Framework.Common.Objects.Filters; -namespace Org.IdentityConnectors.Framework.Common.Objects -{ - #region ConnectorAttributeUtil - public static class ConnectorAttributeUtil { - - /** - * Gets the string value from the single value attribute. - * - * @param attr - * ConnectorAttribute to retrieve the string value from. - * @return null if the value is null otherwise the string value for the - * attribute. - * @throws ClassCastException - * iff the object in the attribute is not an string. - * @throws IllegalArgumentException - * iff the attribute is a multi valued instead of single valued. - */ - public static string GetStringValue(ConnectorAttribute attr) { - return (string)GetSingleValue(attr); - } - - /** - * Gets the string value from the single value attribute. - * - * @param attr - * ConnectorAttribute to retrieve the string value from. - * @return null if the value is null otherwise the string value for the - * attribute. - * @throws IllegalArgumentException - * iff the attribute is a multi valued instead of single valued. - */ - public static string GetAsStringValue(ConnectorAttribute attr) { - object obj = GetSingleValue(attr); - return obj != null ? obj.ToString() : null; - } - - public static GuardedString GetGuardedStringValue(ConnectorAttribute attr) { - object obj = GetSingleValue(attr); - return obj != null ? (GuardedString)obj : null; - } - /** - * Gets the integer value from the single value attribute. - * - * @param attr - * ConnectorAttribute to retrieve the integer value from. - * @return null if the value is null otherwise the integer value for the - * attribute. - * @throws ClassCastException - * iff the object in the attribute is not an integer. - * @throws IllegalArgumentException - * iff the attribute is a multi valued instead of single valued. - */ - public static int? GetIntegerValue(ConnectorAttribute attr) { - object obj = GetSingleValue(attr); - return obj != null ? (int?) obj : null; - } - - /** - * Gets the long value from the single value attribute. - * - * @param attr - * ConnectorAttribute to retrieve the long value from. - * @return null if the value is null otherwise the long value for the - * attribute. - * @throws ClassCastException - * iff the object in the attribute is not an long. - * @throws IllegalArgumentException - * iff the attribute is a multi valued instead of single valued. - */ - public static long? GetLongValue(ConnectorAttribute attr) { - Object obj = GetSingleValue(attr); - return obj != null ? (long?) obj : null; - } - - /** - * Gets the date value from the single value attribute. - * - * @param attr - * ConnectorAttribute to retrieve the date value from. - * @return null if the value is null otherwise the date value for the - * attribute. - * @throws ClassCastException - * iff the object in the attribute is not an long. - * @throws IllegalArgumentException - * iff the attribute is a multi valued instead of single valued. - */ - public static DateTime? GetDateTimeValue(ConnectorAttribute attr) { - long? val = GetLongValue(attr); - if (val != null) { - return DateTimeUtil.GetDateTimeFromUtcMillis(val.Value); - } - return null; - } - - /** - * Gets the integer value from the single value attribute. - * - * @param attr - * ConnectorAttribute to retrieve the integer value from. - * @return null if the value is null otherwise the integer value for the - * attribute. - * @throws ClassCastException - * iff the object in the attribute is not an integer. - * @throws IllegalArgumentException - * iff the attribute is a multi valued instead of single valued. - */ - public static double? GetDoubleValue(ConnectorAttribute attr) { - Object obj = GetSingleValue(attr); - return obj != null ? (double?) obj : null; - } - - public static bool? GetBooleanValue(ConnectorAttribute attr) { - object obj = GetSingleValue(attr); - return obj != null ? (bool?) obj : null; - } - - /** - * Get the single value from the ConnectorAttribute. - */ - public static object GetSingleValue(ConnectorAttribute attr) { - Object ret = null; - IList val = attr.Value; - if (val != null) { - // make sure this only called for single value.. - if (val.Count > 1) { - const string MSG = "The method is only for single value attributes."; - throw new ArgumentException(MSG); - } - ret = val[0]; - } - return ret; - } - - /// - /// Transform a Collection of ConnectorAttribute} instances into a {@link Map}. - /// The key to each element in the map is the name of an ConnectorAttribute. - /// The value of each element in the map is the ConnectorAttribute instance with that name. - /// - /// - /// - public static IDictionary ToMap( - ICollection attributes) { - IDictionary ret = - new Dictionary( - StringComparer.OrdinalIgnoreCase); - foreach (ConnectorAttribute attr in attributes) { - ret[attr.Name] = attr; - } - return ret; - } - - /** - * Get the {@link Uid} from the attribute set. - * - * @param attrs - * set of {@link ConnectorAttribute}s that may contain a {@link Uid}. - * @return null if the set does not contain a {@link Uid} object the first - * one found. - */ - public static Uid GetUidAttribute(ICollection attrs) { - return (Uid)Find(Uid.NAME, attrs); - } - - /** - * Filters out all special attributes from the set. These special attributes - * include {@link Password}, {@link Uid} etc.. - * - * @param attrs - * set of {@link ConnectorAttribute}s to filter out the operational and - * default attributes. - * @return a set that only contains plain attributes or empty. - */ - public static ICollection GetBasicAttributes(ICollection attrs) { - ICollection ret = new HashSet(); - foreach (ConnectorAttribute attr in attrs) { - // note this is dangerous because we need to be consistent - // in the naming of special attributes. - if (!IsSpecial(attr)) { - ret.Add(attr); - } - } - return ret; - } - /** - * Filter out any basic attributes from the specified set, leaving only - * special attributes. Special attributes include {@link Name}, {@link Uid}, - * and {@link OperationalAttributes}. - * - * @param attrs - * set of {@link Attribute}s to filter out the basic attributes - * @return a set that only contains special attributes or an empty set if - * there are none. - */ - public static ICollection GetSpecialAttributes(ICollection attrs) { - ICollection ret = new HashSet(); - foreach (ConnectorAttribute attr in attrs) { - if (IsSpecial(attr)) { - ret.Add(attr); - } - } - return ret; - } - - /** - * Returns a mutable copy of the original set with the uid attribute removed. - * @param attrs The original set. Must not be null. - * @return A mutable copy of the original set with the uid attribute removed. - */ - public static ICollection FilterUid(ICollection attrs) { - Assertions.NullCheck(attrs, "attrs"); - HashSet ret = new HashSet(); - foreach (ConnectorAttribute attr in attrs) { - if (!(attr is Uid)) { - ret.Add(attr); - } - } - return ret; - } - - /** - * Returns a mutable copy of the original set with the uid attribute added. - * @param attrs The original set. Must not be null. - * @param uid The uid. Must not be null. - * @return A mutable copy of the original set with the uid attribute added. - */ - public static ICollection AddUid(ICollection attrs, Uid uid) { - Assertions.NullCheck(attrs, "attrs"); - Assertions.NullCheck(uid, "uid"); - HashSet ret = new HashSet(attrs); - ret.Add(uid); - return ret; - } - - /** - * Determines if this attribute is a special attribute. - * - * @param attr - * {@link ConnectorAttribute} to test for against. - * @return true iff the attribute value is a {@link Uid}, - * {@link ObjectClass}, {@link Password}, or - * {@link OperationalAttributes}. - * @throws NullPointerException - * iff the attribute parameter is null. - */ - public static bool IsSpecial(ConnectorAttribute attr) { - // note this is dangerous because we need to be consistent - // in the naming of special attributes. - String name = attr.Name; - return IsSpecialName(name); - } - - /** - * Determines if this attribute is a special attribute. - * - * @param attr - * {@link ConnectorAttribute} to test for against. - * @return true iff the attribute value is a {@link Uid}, - * {@link ObjectClass}, {@link Password}, or - * {@link OperationalAttributes}. - * @throws NullPointerException - * iff the attribute parameter is null. - */ - public static bool IsSpecial(ConnectorAttributeInfo attr) { - // note this is dangerous because we need to be consistent - // in the naming of special attributes. - String name = attr.Name; - return IsSpecialName(name); - } - - private static bool IsSpecialName(String name) { - // note this is dangerous because we need to be consistent - // in the naming of special attributes. - return (name.StartsWith("@@") && name.EndsWith("@@")); - } - - /// - /// Creates the special naming for operational type attributes. - /// - /// string to make special - /// name constructed for use as an operational attribute. - public static string CreateSpecialName(string name) { - if (StringUtil.IsBlank(name)) { - const string ERR = "Name parameter must not be blank!"; - throw new ArgumentException(ERR); - } - return "@@" + name + "@@"; - } - - /// - /// Gets the 'Name' attribute from a set of ConnectorAttributes. - /// - /// set of attributes to search against. - /// the 'Name' attribute it if exsist otherwisenull - public static Name GetNameFromAttributes(ICollection attrs) { - return (Name)Find(Name.NAME, attrs); - } - - - /** - * Find the {@link ConnectorAttribute} of the given name in the {@link Set}. - * - * @param name - * {@link ConnectorAttribute}'s name to search for. - * @param attrs - * {@link Set} of attribute to search. - * @return {@link ConnectorAttribute} with the specified otherwise null. - */ - public static ConnectorAttribute Find(string name, ICollection attrs) { - Assertions.NullCheck(name, "name"); - ICollection attributes = CollectionUtil.NullAsEmpty(attrs); - foreach (ConnectorAttribute attr in attributes) { - if (attr.Is(name)) { - return attr; - } - } - return null; - } - /** - * Get the password value from the provided set of {@link ConnectorAttribute}s. - */ - public static GuardedString GetPasswordValue(ICollection attrs) { - ConnectorAttribute pwd = Find(OperationalAttributes.PASSWORD_NAME, attrs); - return (pwd == null) ? null : GetGuardedStringValue(pwd); - } - - /** - * Get the current password value from the provided set of {@link Attribute}s. - * - * @param attrs - * Set of {@link Attribute}s that may contain the current password - * {@link OperationalAttributes#CURRENT_PASSWORD_NAME} - * {@link Attribute}. - * @return null if it does not exist in the {@link Set} else - * the value. - */ - public static GuardedString GetCurrentPasswordValue(ICollection attrs) { - ConnectorAttribute pwd = Find(OperationalAttributes.CURRENT_PASSWORD_NAME, attrs); - return (pwd == null) ? null : GetGuardedStringValue(pwd); - } - /** - * Determine if the {@link ConnectorObject} is locked out. By getting the - * value of the {@link OperationalAttributes#LOCK_OUT_NAME}. - * - * @param obj - * {@link ConnectorObject} object to inspect. - * @throws NullPointerException - * iff the parameter 'obj' is null. - * @return null if the attribute does not exist otherwise to - * value of the {@link ConnectorAttribute}. - */ - public static bool? IsLockedOut(ConnectorObject obj) { - ConnectorAttribute attr = obj.GetAttributeByName(OperationalAttributes.LOCK_OUT_NAME); - return (attr == null) ? null : GetBooleanValue(attr); - } - - /** - * Determine if the {@link ConnectorObject} is enable. By getting the value - * of the {@link OperationalAttributes#ENABLE_NAME}. - * - * @param obj - * {@link ConnectorObject} object to inspect. - * @throws IllegalStateException - * if the object does not contain attribute in question. - * @throws NullPointerException - * iff the parameter 'obj' is null. - * @return null if the attribute does not exist otherwise to - * value of the {@link ConnectorAttribute}. - */ - public static bool? IsEnabled(ConnectorObject obj) { - ConnectorAttribute attr = obj.GetAttributeByName(OperationalAttributes.ENABLE_NAME); - return (attr == null) ? null : GetBooleanValue(attr); - } - - /** - * Retrieve the password expiration date from the {@link ConnectorObject}. - * - * @param obj - * {@link ConnectorObject} object to inspect. - * @throws IllegalStateException - * if the object does not contain attribute in question. - * @throws NullPointerException - * iff the parameter 'obj' is null. - * @return null if the {@link ConnectorAttribute} does not exist - * otherwise the value of the {@link ConnectorAttribute}. - */ - public static DateTime? GetPasswordExpirationDate(ConnectorObject obj) { - DateTime? ret = null; - ConnectorAttribute attr = obj.GetAttributeByName(OperationalAttributes.PASSWORD_EXPIRATION_DATE_NAME); - if (attr != null) { - long? date = GetLongValue(attr); - if (date != null) { - ret = DateTime.FromFileTimeUtc(date.Value); - } - } - return ret; - } - /** - * Get the password expired attribute from a {@link Collection} of - * {@link Attribute}s. - * - * @param attrs - * set of attribute to find the expired password - * {@link Attribute}. - * @return null if the attribute does not exist and the value - * of the {@link Attribute} if it does. - */ - public static bool? GetPasswordExpired(ICollection attrs) { - ConnectorAttribute pwd = Find(OperationalAttributes.PASSWORD_EXPIRED_NAME, attrs); - return (pwd == null) ? null : GetBooleanValue(pwd); - } - - /** - * Determine if the password is expired for this object. - * - * @param obj - * {@link ConnectorObject} that should contain a password expired - * attribute. - * @return null if the attribute does not exist and the value - * of the {@link Attribute} if it does. - */ - public static bool? IsPasswordExpired(ConnectorObject obj) { - ConnectorAttribute pwd = obj.GetAttributeByName(OperationalAttributes.PASSWORD_EXPIRED_NAME); - return (pwd == null) ? null : GetBooleanValue(pwd); - } - - /** - * Get the enable date from the set of attributes. - * - * @param attrs - * set of attribute to find the enable date - * {@link Attribute}. - * @return null if the attribute does not exist and the value - * of the {@link Attribute} if it does. - */ - public static DateTime? GetEnableDate(ICollection attrs) { - ConnectorAttribute attr = Find(OperationalAttributes.ENABLE_DATE_NAME, attrs); - return (attr == null) ? null : GetDateTimeValue(attr); - } - } - #endregion - - #region ConnectorAttributeInfoUtil - public static class ConnectorAttributeInfoUtil { - /** - * Transform a Collection of {@link AttributeInfo} instances into - * a {@link Map}. The key to each element in the map is the name of - * an AttributeInfo. The value of each element in the map is the - * AttributeInfo instance with that name. - * - * @param attributes - * set of AttributeInfo to transform to a map. - * @return a map of string and AttributeInfo. - * @throws NullPointerException - * iff the parameter attributes is - * null. - */ - public static IDictionary ToMap( - ICollection attributes) { - IDictionary - ret = new Dictionary( - StringComparer.OrdinalIgnoreCase); - foreach (ConnectorAttributeInfo attr in attributes) { - ret[attr.Name] = attr; - } - return ret; - } - - /** - * Find the {@link AttributeInfo} of the given name in the {@link Set}. - * - * @param name - * {@link AttributeInfo}'s name to search for. - * @param attrs - * {@link Set} of AttributeInfo to search. - * @return {@link AttributeInfo} with the specified otherwise null. - */ - public static ConnectorAttributeInfo Find(string name, ICollection attrs) { - Assertions.NullCheck(name, "name"); - ICollection attributes = CollectionUtil.NullAsEmpty(attrs); - foreach (ConnectorAttributeInfo attr in attributes) { - if (attr.Is(name)) { - return attr; - } - } - return null; - } - } - #endregion - - #region BigDecimal - /// - /// Placeholder since C# doesn't have a BigInteger - /// - public sealed class BigDecimal { - private BigInteger _unscaledVal; - private int _scale; - public BigDecimal(BigInteger unscaledVal, - int scale) { - if ( unscaledVal == null ) { - throw new ArgumentNullException(); - } - _unscaledVal = unscaledVal; - _scale = scale; - } - public BigInteger UnscaledValue { - get { - return _unscaledVal; - } - } - public int Scale { - get { - return _scale; - } - } - public override bool Equals(object o) { - BigDecimal other = o as BigDecimal; - if ( other != null ) { - return UnscaledValue.Equals(other.UnscaledValue) && - Scale == other.Scale; - } - return false; - } - public override int GetHashCode() { - return _unscaledVal.GetHashCode(); - } - - public override string ToString() - { - return UnscaledValue.ToString(); - } - } - #endregion - - #region BigInteger - /// - /// Placeholder since C# doesn't have a BigInteger - /// - public sealed class BigInteger { - private string _value; - public BigInteger(string val) { - if ( val == null ) { - throw new ArgumentNullException(); - } - _value = val; - } - public string Value { - get { - return _value; - } - } - public override bool Equals(object o) { - BigInteger other = o as BigInteger; - if ( other != null ) { - return Value.Equals(other.Value); - } - return false; - } - public override int GetHashCode() { - return _value.GetHashCode(); - } - public override string ToString() { - return _value; - } - } - #endregion - - #region ConnectorAttribute - /// - /// Represents a named collection of values within a resource object, - /// although the simplest case is a name-value pair (e.g., email, - /// employeeID). Values can be empty, null, or set with various types. - /// Empty and null are supported because it makes a difference on some - /// resources (in particular database resources). The developer of a - /// Connector will use an builder to construct an instance of - /// ConnectorAttribute. - /// - public class ConnectorAttribute { - private readonly string _name; - private readonly IList _value; - - internal ConnectorAttribute(string name, IList val) { - if (StringUtil.IsBlank(name)) { - throw new ArgumentException("Name must not be blank!"); - } - if (OperationalAttributes.PASSWORD_NAME.Equals(name) || - OperationalAttributes.CURRENT_PASSWORD_NAME.Equals(name)) { - // check the value.. - if (val == null || val.Count != 1) { - String MSG = "Must be a single value."; - throw new ArgumentException(MSG); - } - if (!(val[0] is GuardedString)) { - const string MSG = "Password value must be an instance of GuardedString."; - throw new ArgumentException(MSG); - } - } - _name = name; - // copy to prevent corruption preserve null - _value = (val == null) ? null : CollectionUtil.NewReadOnlyList(val); - } - - public string Name { - get { - return _name; - } - } - - public IList Value { - get { - return _value; - } - } - - public bool Is(string name) { - return Name.ToUpper().Equals(name.ToUpper()); - } - - public sealed override bool Equals(Object obj) { - // test identity - if (this == obj) { - return true; - } - // test for null.. - if (obj == null) { - return false; - } - // test that the exact class matches - if (!(GetType().Equals(obj.GetType()))) { - return false; - } - // test name field.. - ConnectorAttribute other = (ConnectorAttribute) obj; - if (!_name.ToUpper().Equals(other._name.ToUpper())) { - return false; - } - - if (!CollectionUtil.Equals(_value,other._value)) { - return false; - } - return true; - } - - public sealed override int GetHashCode() { - return _name.ToUpper().GetHashCode(); - } - - - public override string ToString() { - // poor man's consistent toString impl.. - StringBuilder bld = new StringBuilder(); - bld.Append("ConnectorAttribute: "); - IDictionary map = new Dictionary(); - map["Name"] = Name; - map["Value"] = Value; - bld.Append(map.ToString()); - return bld.ToString(); - } - } - #endregion - - #region ConnectorAttributeBuilder - public sealed class ConnectorAttributeBuilder { - private const String NAME_ERROR = "Name must not be blank!"; - - private string _name; - private IList _value; - - public ConnectorAttributeBuilder() { - } - public static ConnectorAttribute Build(String name) { - return new ConnectorAttributeBuilder(){ Name=name }.Build(); - } - public static ConnectorAttribute Build(String name, - params Object [] args) { - ConnectorAttributeBuilder bld = new ConnectorAttributeBuilder(); - bld.Name = name; - bld.AddValue(args); - return bld.Build(); - } - public static ConnectorAttribute Build(String name, - ICollection val) { - ConnectorAttributeBuilder bld = new ConnectorAttributeBuilder(); - bld.Name = name; - bld.AddValue(val); - return bld.Build(); - } - - public string Name { - get { - return _name; - } - set { - if (StringUtil.IsBlank(value)) { - throw new ArgumentException(NAME_ERROR); - } - _name = value; - } - } - - public IList Value { - get { - return _value == null ? null : CollectionUtil.AsReadOnlyList(_value); - } - } - - public ConnectorAttributeBuilder AddValue(params Object [] args) { - AddValuesInternal(args); - return this; - } - public ConnectorAttributeBuilder AddValue(ICollection values) { - AddValuesInternal(values); - return this; - } - - public ConnectorAttribute Build() { - if (StringUtil.IsBlank(Name)) { - throw new ArgumentException(NAME_ERROR); - } - if (Uid.NAME.Equals(_name)) { - return new Uid(GetSingleStringValue()); - } else if (Org.IdentityConnectors.Framework.Common.Objects.Name.NAME.Equals(_name)) { - return new Name(GetSingleStringValue()); - } - return new ConnectorAttribute(Name, _value); - } - private void CheckSingleValue() { - if (_value == null || _value.Count != 1) { - const String MSG = "Must be a single value."; - throw new ArgumentException(MSG); - } - } - private String GetSingleStringValue() { - CheckSingleValue(); - if (!(_value[0] is String)) { - const String MSG = "Must be single string value."; - throw new ArgumentException(MSG); - } - return (String) _value[0]; - } - private void AddValuesInternal(IEnumerable values) { - if (values != null) { - // make sure the list is ready to receive values. - if (_value == null) { - _value = new List(); - } - // add each value checking to make sure its correct - foreach (Object v in values) { - FrameworkUtil.CheckAttributeValue(v); - _value.Add(v); - } - } - } - - // ======================================================================= - // Operational Attributes - // ======================================================================= - /** - * Builds an password expiration date {@link ConnectorAttribute}. This - * {@link ConnectorAttribute} represents the date/time a password will expire on a - * resource. - * - * @param dateTime - * UTC time in milliseconds. - * @return an {@link ConnectorAttribute} built with the pre-defined name for password - * expiration date. - */ - public static ConnectorAttribute BuildPasswordExpirationDate(DateTime dateTime) { - return BuildPasswordExpirationDate(DateTimeUtil.GetUtcTimeMillis(dateTime)); - } - - /** - * Builds an password expiration date {@link ConnectorAttribute}. This - * {@link ConnectorAttribute} represents the date/time a password will expire on a - * resource. - * - * @param dateTime - * UTC time in milliseconds. - * @return an {@link ConnectorAttribute} built with the pre-defined name for password - * expiration date. - */ - public static ConnectorAttribute BuildPasswordExpirationDate(long dateTime) { - return Build(OperationalAttributes.PASSWORD_EXPIRATION_DATE_NAME, - dateTime); - } - - /** - * Builds the operational attribute password. - * - * @param password - * the string that represents a password. - * @return an attribute that represents a password. - */ - public static ConnectorAttribute BuildPassword(GuardedString password) { - return Build(OperationalAttributes.PASSWORD_NAME, password); - } - - /** - * Builds the operational attribute current password. The current password - * indicates this a password change by the account owner and not an - * administrator. The use case is that an administrator password change may - * not keep history or validate against policy. - * - * @param password - * the string that represents a password. - * @return an attribute that represents a password. - */ - public static ConnectorAttribute BuildCurrentPassword(GuardedString password) { - return Build(OperationalAttributes.CURRENT_PASSWORD_NAME, password); - } - - public static ConnectorAttribute BuildPassword(SecureString password) { - return Build(OperationalAttributes.PASSWORD_NAME, new GuardedString(password)); - } - public static ConnectorAttribute BuildCurrentPassword(SecureString password) { - return Build(OperationalAttributes.CURRENT_PASSWORD_NAME, new GuardedString(password)); - } - /** - * Builds ant operational attribute that either represents the object is - * enabled or sets in disabled depending on where its used for instance on - * {@link CreateApiOp} it could be used to create a disabled account. In - * {@link SearchApiOp} it would show the object is enabled or disabled. - * - * @param value - * true indicates the object is enabled otherwise false. - * @return {@link ConnectorAttribute} that determines the enable/disable state of an - * object. - */ - public static ConnectorAttribute BuildEnabled(bool val) { - return Build(OperationalAttributes.ENABLE_NAME, val); - } - - /** - * Builds out an operational {@link ConnectorAttribute} that determines the enable - * date for an object. - * - * @param date - * The date and time to enable a particular object, or the date - * time an object will be enabled. - * @return {@link ConnectorAttribute} - */ - public static ConnectorAttribute BuildEnableDate(DateTime date) { - return BuildEnableDate(DateTimeUtil.GetUtcTimeMillis(date)); - } - - /** - * Builds out an operational {@link ConnectorAttribute} that determines the enable - * date for an object. The time parameter is UTC in milliseconds. - * - * @param date - * The date and time to enable a particular object, or the date - * time an object will be enabled. - * @return {@link ConnectorAttribute} - */ - public static ConnectorAttribute BuildEnableDate(long date) { - return Build(OperationalAttributes.ENABLE_DATE_NAME, date); - } - - /** - * Builds out an operational {@link ConnectorAttribute} that determines the disable - * date for an object. - * - * @param date - * The date and time to enable a particular object, or the date - * time an object will be enabled. - * @return {@link ConnectorAttribute} - */ - public static ConnectorAttribute BuildDisableDate(DateTime date) { - return BuildDisableDate(DateTimeUtil.GetUtcTimeMillis(date)); - } - - /** - * Builds out an operational {@link ConnectorAttribute} that determines the disable - * date for an object. The time parameter is UTC in milliseconds. - * - * @param date - * The date and time to enable a particular object, or the date - * time an object will be enabled. - * @return {@link ConnectorAttribute} - */ - public static ConnectorAttribute BuildDisableDate(long date) { - return Build(OperationalAttributes.DISABLE_DATE_NAME, date); - } - - /** - * Builds the lock attribute that determines if an object is locked out. - * - * @param lock - * true if the object is locked otherwise false. - * @return {@link ConnectorAttribute} that represents the lock state of an object. - */ - public static ConnectorAttribute BuildLockOut(bool lck) { - return Build(OperationalAttributes.LOCK_OUT_NAME, lck); - } - - /** - * Builds out an operational {@link Attribute} that determines if a password - * is expired or expires a password. - * - * @param value - * from the API true expires and from the SPI its shows its - * either expired or not. - * @return {@link Attribute} - */ - public static ConnectorAttribute BuildPasswordExpired(bool expired) { - return Build(OperationalAttributes.PASSWORD_EXPIRED_NAME, expired); - } - - // ======================================================================= - // Pre-defined Attributes - // ======================================================================= - - /** - * Builds out a pre-defined {@link ConnectorAttribute} that determines the last login - * date for an object. - * - * @param date - * The date and time of the last login. - * @return {@link ConnectorAttribute} - */ - public static ConnectorAttribute BuildLastLoginDate(DateTime date) { - return BuildLastLoginDate(DateTimeUtil.GetUtcTimeMillis(date)); - } - - /** - * Builds out a pre-defined {@link ConnectorAttribute} that determines the last login - * date for an object. The time parameter is UTC in milliseconds. - * - * @param date - * The date and time of the last login. - * @return {@link ConnectorAttribute} - */ - public static ConnectorAttribute BuildLastLoginDate(long date) { - return Build(PredefinedAttributes.LAST_LOGIN_DATE_NAME, date); - } - - /** - * Builds out a pre-defined {@link ConnectorAttribute} that determines the last - * password change date for an object. - * - * @param date - * The date and time the password was changed. - * @return {@link ConnectorAttribute} - */ - public static ConnectorAttribute BuildLastPasswordChangeDate(DateTime date) { - return BuildLastPasswordChangeDate(DateTimeUtil.GetUtcTimeMillis(date)); - } - - /** - * Builds out a pre-defined {@link ConnectorAttribute} that determines the last - * password change date for an object. - * - * @param date - * The date and time the password was changed. - * @return {@link ConnectorAttribute} - */ - public static ConnectorAttribute BuildLastPasswordChangeDate(long date) { - return Build(PredefinedAttributes.LAST_PASSWORD_CHANGE_DATE_NAME, date); - } - - /** - * Common password policy attribute where the password must be changed every - * so often. The value for this attribute is milliseconds since its the - * lowest common denominator. - */ - public static ConnectorAttribute BuildPasswordChangeInterval(long val) { - return Build(PredefinedAttributes.PASSWORD_CHANGE_INTERVAL_NAME, val); - } - } - #endregion - - #region ConnectorMessages - /** - * Message catalog for a given connector. - */ - public interface ConnectorMessages { - /** - * Formats the given message key in the current UI culture. - * @param key The message key to format. - * @param dflt The default message if key is not found. If null, defaults - * to key. - * @param args Parameters with which to format the message. - * @return The formatted string. - */ - String Format(String key, String dflt, params object [] args); - } - #endregion - - #region ConnectorObject - public sealed class ConnectorObject { - private readonly ObjectClass _objectClass; - private readonly IDictionary _attrs; - public ConnectorObject(ObjectClass objectClass, ICollection attrs) { - if (objectClass == null) { - throw new ArgumentException("ObjectClass may not be null"); - } - if ( attrs == null || attrs.Count == 0 ) { - throw new ArgumentException("attrs cannot be empty or null."); - } - _objectClass = objectClass; - _attrs = - CollectionUtil.NewReadOnlyDictionary(attrs, - value => { return value.Name;}); - if (!_attrs.ContainsKey(Uid.NAME)) { - const String MSG = "The ConnectorAttribute set must contain a Uid."; - throw new ArgumentException(MSG); - } - if (!_attrs.ContainsKey(Name.NAME)) { - const string MSG = "The ConnectorAttribute set must contain a Name."; - throw new ArgumentException(MSG); - } - } - public ICollection GetAttributes() { - return _attrs.Values; - } - public ConnectorAttribute GetAttributeByName(string name) { - return CollectionUtil.GetValue(_attrs,name,null); - } - public Uid Uid { - get { - return (Uid)GetAttributeByName(Uid.NAME); - } - } - public Name Name { - get { - return (Name)GetAttributeByName(Name.NAME); - } - } - public ObjectClass ObjectClass { - get { - return _objectClass; - } - } - public override int GetHashCode() { - return CollectionUtil.GetHashCode(_attrs); - } - public override bool Equals(Object o) { - ConnectorObject other = o as ConnectorObject; - if ( other != null ) { - if (!_objectClass.Equals(other.ObjectClass)) { - return false; - } - return CollectionUtil.Equals(_attrs,other._attrs); - } - return false; - } - } - #endregion - - #region ConnectorObjectBuilder - public sealed class ConnectorObjectBuilder { - private IDictionary _attributes; - public ConnectorObjectBuilder() { - _attributes = new Dictionary(); - // default always add the account object class.. - ObjectClass = ObjectClass.ACCOUNT; - } - - public void SetUid(string uid) { - AddAttribute(new Uid(uid)); - } - - public void SetUid(Uid uid) { - AddAttribute(uid); - } - - public void SetName(string name) { - AddAttribute(new Name(name)); - } - - public void SetName(Name name) { - AddAttribute(name); - } - - public ObjectClass ObjectClass { get; set; } - - // ======================================================================= - // Clone basically.. - // ======================================================================= - /** - * Takes all the attribute from a {@link ConnectorObject} and add/overwrite - * the current attributes. - */ - public ConnectorObjectBuilder Add(ConnectorObject obj) { - // simply add all the attributes it will include (Uid, ObjectClass..) - foreach (ConnectorAttribute attr in obj.GetAttributes()) { - AddAttribute(attr); - } - ObjectClass = obj.ObjectClass; - return this; - } - - public ConnectorObjectBuilder AddAttribute(params ConnectorAttribute [] attrs) { - ValidateParameter(attrs, "attrs"); - foreach (ConnectorAttribute a in attrs) { - //DONT use Add - it throws exceptions if already there - _attributes[a.Name] = a; - } - return this; - } - public ConnectorObjectBuilder AddAttributes(ICollection attrs) { - ValidateParameter(attrs, "attrs"); - foreach (ConnectorAttribute a in attrs) { - _attributes[a.Name] = a; - } - return this; - } - /** - * Adds values to the attribute. - */ - public ConnectorObjectBuilder AddAttribute(String name, params object [] objs) { - AddAttribute(ConnectorAttributeBuilder.Build(name, objs)); - return this; - } - - /** - * Adds each object in the collection. - */ - public ConnectorObjectBuilder AddAttribute(String name, ICollection obj) { - AddAttribute(ConnectorAttributeBuilder.Build(name, obj)); - return this; - } - public ConnectorObject Build() { - // check that there are attributes to return.. - if (_attributes.Count == 0) { - throw new InvalidOperationException("No attributes set!"); - } - return new ConnectorObject(ObjectClass,_attributes.Values); - } - private static void ValidateParameter(Object param, String paramName) { - if (param == null) { - String FORMAT = "Parameter "+param+" must not be null!"; - throw new NullReferenceException(FORMAT); - } - } - } - #endregion - - #region ConnectorAttributeInfo - public sealed class ConnectorAttributeInfo { - private readonly string _name; - private readonly Type _type; - private readonly Flags _flags; - - /** - * Enum of modifier flags to use for attributes. Note that - * this enum is designed for configuration by exception such that - * an empty set of flags are the defaults: - *
    - *
  • updateable
  • - *
  • creatable
  • - *
  • returned by default
  • - *
  • readable
  • - *
  • single-valued
  • - *
  • optional
  • - *
- */ - [FlagsAttribute] - public enum Flags { - NONE = 0, - REQUIRED = 1, - MULTIVALUED = 2, - NOT_CREATABLE = 4, - NOT_UPDATEABLE = 8, - NOT_READABLE = 16, - NOT_RETURNED_BY_DEFAULT = 32 - } - - internal ConnectorAttributeInfo(string name, Type type, - Flags flags) { - if (StringUtil.IsBlank(name)) { - throw new ArgumentException("Name must not be blank!"); - } - if ((OperationalAttributes.PASSWORD_NAME.Equals(name) || - OperationalAttributes.CURRENT_PASSWORD_NAME.Equals(name)) && - !typeof(GuardedString).Equals(type)) { - String MSG = "Password based attributes must be of type GuardedString."; - throw new ArgumentException(MSG); - } - // check the type.. - FrameworkUtil.CheckAttributeType(type); - _name = name; - _type = type; - _flags = flags; - if (!IsReadable && IsReturnedByDefault) { - throw new ArgumentException("Attribute "+name+" is flagged as not-readable, so it should also be as not-returned-by-default."); - } - } - - - /** - * The native name of the attribute. - * - * @return the native name of the attribute its describing. - */ - public string Name { - get { - return _name; - } - } - - /** - * The basic type associated with this attribute. All primitives are - * supported. - * - * @return the native type if uses. - */ - public Type ValueType { - get { - return _type; - } - } - - /** - * Returns the set of flags associated with the attribute. - * @return the set of flags associated with the attribute - */ - public Flags InfoFlags { - get { - return _flags; - } - } - - - public bool Is(string name) { - return Name.ToUpper().Equals(name.ToUpper()); - } - - /** - * Determines if the attribute is readable. - * - * @return true if the attribute is readable else false. - */ - public bool IsReadable { - get { - return (_flags & Flags.NOT_READABLE) == 0; - } - } - - /** - * Determines if the attribute is writable on create. - * - * @return true if the attribute is writable on create else false. - */ - public bool IsCreatable { - get { - return (_flags & Flags.NOT_CREATABLE) == 0; - } - } - - /** - * Determines if the attribute is writable on update. - * - * @return true if the attribute is writable on update else false. - */ - public bool IsUpdateable { - get { - return (_flags & Flags.NOT_UPDATEABLE) == 0; - } - } - - /** - * Determines whether this attribute is required for creates. - * - * @return true if the attribute is required for an object else false. - */ - public bool IsRequired { - get { - return (_flags & Flags.REQUIRED) != 0; - } - } - - /** - * Determines if this attribute can handle multiple values. There is a - * special case with byte[] since in most instances this denotes a single - * object. - * - * @return true if the attribute is multi-value otherwise false. - */ - public bool IsMultiValued { - get { - return (_flags & Flags.MULTIVALUED) != 0; - } - } - - /** - * Determines if the attribute is returned by default. Indicates if an - * {@link ConnectorAttribute} will be returned during {@link SearchApiOp} or - * {@link GetApiOp} inside a {@link ConnectorObject} by default. The default - * value is true. - * - * @return false iff the attribute should not be returned by default. - */ - public bool IsReturnedByDefault { - get { - return (_flags & Flags.NOT_RETURNED_BY_DEFAULT) == 0; - } - } - - public override bool Equals(Object o) { - ConnectorAttributeInfo other = o as ConnectorAttributeInfo; - if ( other != null ) { - if (!Name.ToUpper().Equals(other.Name.ToUpper())) { - return false; - } - if (!ValueType.Equals(other.ValueType)) { - return false; - } - if (_flags != other._flags) { - return false; - } - return true; - } - return false; - } - - public override int GetHashCode() { - return _name.ToUpper().GetHashCode(); - } - - public override string ToString() { - return SerializerUtil.SerializeXmlObject(this,false); - } - } - #endregion - - #region ConnectorAttributeInfoBuilder - /** - * Simplifies the process of building 'AttributeInfo' objects. This class is - * responsible for providing a default implementation of {@link AttributeInfo}. - * - * - * AttributeInfoBuilder bld = new AttributeInfoBuilder("email"); - * bld.setRequired(true); - * AttributeInfo info = bld.build(); - * - * - * @author Will Droste - * @version $Revision: 1.9 $ - * @since 1.0 - */ - public sealed class ConnectorAttributeInfoBuilder { - - private String _name; - private Type _type; - private ConnectorAttributeInfo.Flags _flags; - - /** - * Creates an builder with all the defaults set. The name must be set before - * the 'build' method is called otherwise an {@link IllegalStateException} - * is thrown. - * - *
-         * Name: <not set>
-         * Readable: true
-         * Writeable: true
-         * Required: false
-         * Type: string
-         * MultiValue: false
-         * 
- */ - public ConnectorAttributeInfoBuilder() { - ValueType=(typeof(String)); - _flags = ConnectorAttributeInfo.Flags.NONE; - } - - /** - * Creates an builder with all the defaults set. The name must be set before - * the 'build' method is called otherwise an {@link IllegalStateException} - * is thrown. - * - *
-         * Name: <not set>
-         * Readable: true
-         * Writeable: true
-         * Required: false
-         * Type: string
-         * MultiValue: false
-         * 
- */ - public ConnectorAttributeInfoBuilder(String name) : this(name,typeof(String)){ - } - - /** - * Creates an builder with all the defaults set. The name must be set before - * the 'build' method is called otherwise an {@link IllegalStateException} - * is thrown. - * - *
-    	 * Name: <not set>
-    	 * Readable: true
-    	 * Writeable: true
-    	 * Required: false
-    	 * Type: string
-    	 * MultiValue: false
-    	 * 
- */ - public ConnectorAttributeInfoBuilder(String name, Type type) { - Name=(name); - ValueType=(type); - //noneOf means the defaults - _flags = ConnectorAttributeInfo.Flags.NONE; - } - - /** - * Builds an {@link AttributeInfo} object based on the properties set. - * - * @return {@link AttributeInfo} based on the properties set. - */ - public ConnectorAttributeInfo Build() { - return new ConnectorAttributeInfo(_name, _type, _flags); - } - - /** - * Sets the unique name of the {@link AttributeInfo} object. - * - * @param name - * unique name of the {@link AttributeInfo} object. - */ - public String Name { - set { - if (StringUtil.IsBlank(value)) { - throw new ArgumentException("Argument must not be blank."); - } - _name = value; - } - } - - /** - * Please see {@link FrameworkUtil#checkAttributeType(Class)} for the - * definitive list of supported types. - * - * @param value - * type for an {@link Attribute}'s value. - * @throws IllegalArgumentException - * if the Class is not a supported type. - */ - public Type ValueType { - set { - FrameworkUtil.CheckAttributeType(value); - _type = value; - } - } - - /** - * Determines if the attribute is readable. - */ - public bool Readable { - set { - SetFlag(ConnectorAttributeInfo.Flags.NOT_READABLE,!value); - } - } - - /** - * Determines if the attribute is writable. - */ - public bool Creatable { - set { - SetFlag(ConnectorAttributeInfo.Flags.NOT_CREATABLE,!value); - } - } - - /** - * Determines if this attribute is required. - */ - public bool Required { - set { - SetFlag(ConnectorAttributeInfo.Flags.REQUIRED,value); - } - } - - /** - * Determines if this attribute supports multivalue. - */ - public bool MultiValued { - set { - SetFlag(ConnectorAttributeInfo.Flags.MULTIVALUED,value); - } - } - - /** - * Determines if this attribute writable during update. - */ - public bool Updateable { - set { - SetFlag(ConnectorAttributeInfo.Flags.NOT_UPDATEABLE,!value); - } - } - - public bool ReturnedByDefault { - set { - SetFlag(ConnectorAttributeInfo.Flags.NOT_RETURNED_BY_DEFAULT,!value); - } - } - - /** - * Sets all of the flags for this builder. - * @param flags The set of attribute info flags. Null means clear all flags. - *

- * NOTE: EnumSet.noneOf(AttributeInfo.Flags.class) results in - * an attribute with the default behavior: - *

    - *
  • updateable
  • - *
  • creatable
  • - *
  • returned by default
  • - *
  • readable
  • - *
  • single-valued
  • - *
  • optional
  • - *
- */ - public ConnectorAttributeInfo.Flags InfoFlags { - set { - _flags = value; - } - } - - private void SetFlag(ConnectorAttributeInfo.Flags flag, bool value) { - if (value) { - _flags = _flags | flag; - } - else { - _flags = _flags & ~flag; - } - } - - /** - * Convenience method to create an AttributeInfo. Equivalent to - * - * new AttributeInfoBuilder(name,type).setFlags(flags).build() - * - * @param name The name of the attribute - * @param type The type of the attribute - * @param flags The flags for the attribute. Null means clear all flags - * @return The attribute info - */ - public static ConnectorAttributeInfo Build(String name, Type type, - ConnectorAttributeInfo.Flags flags) { - return new ConnectorAttributeInfoBuilder(name,type){InfoFlags=flags}.Build(); - } - /** - * Convenience method to create an AttributeInfo. Equivalent to - * - * AttributeInfoBuilder.build(name,type,null) - * - * @param name The name of the attribute - * @param type The type of the attribute - * @param flags The flags for the attribute - * @return The attribute info - */ - public static ConnectorAttributeInfo Build(String name, Type type) { - return Build(name,type,ConnectorAttributeInfo.Flags.NONE); - } - - /** - * Convenience method to create an AttributeInfo. Equivalent to - * - * AttributeInfoBuilder.build(name,type) - * - * @param name The name of the attribute - * @return The attribute info - */ - public static ConnectorAttributeInfo Build(String name) { - return Build(name,typeof(String)); - } - } - #endregion - - #region FileName - /// - /// Placeholder for java.io.File since C#'s - /// FileInfo class throws exceptions if the - /// file doesn't exist. - /// - public sealed class FileName { - private string _path; - public FileName(string path) { - if ( path == null ) { - throw new ArgumentNullException(); - } - _path = path; - } - public string Path { - get { - return _path; - } - } - public override bool Equals(object o) { - FileName other = o as FileName; - if ( other != null ) { - return Path.Equals(other.Path); - } - return false; - } - public override int GetHashCode() { - return _path.GetHashCode(); - } - public override string ToString() { - return _path; - } - } - #endregion - - #region Name - public sealed class Name : ConnectorAttribute { - - public readonly static string NAME = ConnectorAttributeUtil.CreateSpecialName("NAME"); - public readonly static ConnectorAttributeInfo INFO = - new ConnectorAttributeInfoBuilder(NAME) {Required=true}.Build(); - - public Name(String value) : base(NAME, CollectionUtil.NewReadOnlyList(value)) { - } - - /** - * The single value of the attribute that is the unique id of an object. - * - * @return value that identifies an object. - */ - public String GetNameValue() { - return ConnectorAttributeUtil.GetStringValue(this); - } - } - #endregion - - #region ObjectClass - public sealed class ObjectClass { - public const String ACCOUNT_NAME = "account"; - public const String PERSON_NAME = "person"; - public const String GROUP_NAME = "group"; - public const String ORGANIZATION_NAME = "organization"; - /** - * Denotes an account based object. - */ - public static readonly ObjectClass ACCOUNT = new ObjectClass(ACCOUNT_NAME); - /** - * Denotes a person based object. - */ - public static readonly ObjectClass PERSON = new ObjectClass(PERSON_NAME); - /** - * Denotes a group based object. - */ - public static readonly ObjectClass GROUP = new ObjectClass(GROUP_NAME); - /** - * Denotes a organization based object. - */ - public static readonly ObjectClass ORGANIZATION = new ObjectClass(ORGANIZATION_NAME); - - private readonly String _type; - - public ObjectClass(String type) { - if ( type == null ) { - throw new ArgumentException("Type cannot be null."); - } - _type = type; - } - public String GetObjectClassValue() { - return _type; - } - - /** - * Convenience method to build the display name key for - * an object class. - * - * @return The display name key. - */ - public String GetDisplayNameKey() { - return "MESSAGE_OBJECT_CLASS_"+_type.ToUpper(); - } - - public override int GetHashCode() { - return _type.GetHashCode(); - } - - public override bool Equals(object o) { - if ( o is ObjectClass ) { - ObjectClass other = (ObjectClass)o; - return _type.Equals(other._type); - } - return false; - } - - public override string ToString() - { - return "ObjectClass: " + _type; - } - } - #endregion - - #region ObjectClassInfo - public sealed class ObjectClassInfo { - - private readonly String _type; - private readonly ICollection _info; - private readonly bool _isContainer; - - public ObjectClassInfo(String type, - ICollection attrInfo, - bool isContainer) { - _type = type; - _info = CollectionUtil.NewReadOnlySet(attrInfo); - _isContainer = isContainer; - // check to make sure name exists - IDictionary dict - = ConnectorAttributeInfoUtil.ToMap(attrInfo); - if (!dict.ContainsKey(Name.NAME)) { - const string MSG = "Missing 'Name' connector attribute info."; - throw new ArgumentException(MSG); - } - } - - public ICollection ConnectorAttributeInfos { - get { - return this._info; - } - } - - public String ObjectType { - get { - return this._type; - } - } - - public bool IsContainer { - get { - return this._isContainer; - } - } - - public override int GetHashCode() { - return _type.GetHashCode(); - } - - public override bool Equals(Object o) { - ObjectClassInfo other = o as ObjectClassInfo; - if ( other != null ) { - if (!ObjectType.Equals(other.ObjectType)) { - return false; - } - if (!CollectionUtil.Equals(ConnectorAttributeInfos, - other.ConnectorAttributeInfos)) { - return false; - } - if (_isContainer != other._isContainer) { - return false; - } - return true; - } - return false; - } - - public override string ToString() - { - return SerializerUtil.SerializeXmlObject(this,false); - } - } - #endregion - - #region ObjectClassInfoBuilder - /** - * Used to help facilitate the building of {@link ObjectClassInfo} objects. - */ - public sealed class ObjectClassInfoBuilder { - private bool _isContainer; - private IDictionary _info; - - public ObjectClassInfoBuilder() { - _info = new Dictionary(); - ObjectType = ObjectClass.ACCOUNT_NAME; - } - - public string ObjectType { get; set; } - - /** - * Add each {@link AttributeInfo} object to the {@link ObjectClassInfo}. - */ - public ObjectClassInfoBuilder AddAttributeInfo(ConnectorAttributeInfo info) { - if (_info.ContainsKey(info.Name)) { - const string MSG = "ConnectorAttributeInfo of name ''{0}'' already exists!"; - throw new ArgumentException(String.Format(MSG, info.Name)); - } - _info[info.Name] = info; - return this; - } - - public ObjectClassInfoBuilder AddAllAttributeInfo(ICollection info) { - foreach (ConnectorAttributeInfo cainfo in info) { - AddAttributeInfo(cainfo); - } - return this; - } - - public bool IsContainer { - get { - return _isContainer; - } - set { - _isContainer = value; - } - } - - public ObjectClassInfo Build() { - // determine if name is missing and add it by default - if (!_info.ContainsKey(Name.NAME)) { - _info[Name.NAME] = Name.INFO; - } - return new ObjectClassInfo(ObjectType, _info.Values, _isContainer); - } - } - #endregion - - #region OperationalAttributeInfos - /** - * {@link AttributeInfo} for each operational attribute. - */ - public static class OperationalAttributeInfos { - /** - * Gets/sets the enable status of an object. - */ - public static readonly ConnectorAttributeInfo ENABLE = - ConnectorAttributeInfoBuilder.Build( - OperationalAttributes.ENABLE_NAME, typeof(bool)); - - /** - * Gets/sets the enable date for an object. - */ - public static readonly ConnectorAttributeInfo ENABLE_DATE = - ConnectorAttributeInfoBuilder.Build( - OperationalAttributes.ENABLE_DATE_NAME, typeof(long)); - - /** - * Gets/sets the disable date for an object. - */ - public static readonly ConnectorAttributeInfo DISABLE_DATE = - ConnectorAttributeInfoBuilder.Build( - OperationalAttributes.DISABLE_DATE_NAME, typeof(long)); - - /** - * Gets/sets the lock out attribute for an object. - */ - public static readonly ConnectorAttributeInfo LOCK_OUT = - ConnectorAttributeInfoBuilder.Build( - OperationalAttributes.LOCK_OUT_NAME, typeof(bool)); - - /** - * Gets/sets the password expiration date for an object. - */ - public static readonly ConnectorAttributeInfo PASSWORD_EXPIRATION_DATE = - ConnectorAttributeInfoBuilder.Build( - OperationalAttributes.PASSWORD_EXPIRATION_DATE_NAME, typeof(long)); - - /** - * Normally this is a write-only attribute. Sets the password for an object. - */ - public static readonly ConnectorAttributeInfo PASSWORD = - ConnectorAttributeInfoBuilder.Build( - OperationalAttributes.PASSWORD_NAME, typeof(GuardedString), - ConnectorAttributeInfo.Flags.NOT_READABLE | - ConnectorAttributeInfo.Flags.NOT_RETURNED_BY_DEFAULT); - - /** - * Used in conjunction with password to do an account level password change. - * This is for a non-administrator change of the password and therefore - * requires the current password. - */ - public static readonly ConnectorAttributeInfo CURRENT_PASSWORD = - ConnectorAttributeInfoBuilder.Build( - OperationalAttributes.CURRENT_PASSWORD_NAME, typeof(GuardedString), - ConnectorAttributeInfo.Flags.NOT_READABLE | - ConnectorAttributeInfo.Flags.NOT_RETURNED_BY_DEFAULT); - - /** - * Used to determine if a password is expired or to expire a password. - */ - public static readonly ConnectorAttributeInfo PASSWORD_EXPIRED = - ConnectorAttributeInfoBuilder.Build( - OperationalAttributes.PASSWORD_EXPIRED_NAME, typeof(bool)); - - } - #endregion - - #region OperationalAttributes - /** - * Operational attributes have special meaning and cannot be represented by pure - * operations. For instance some administrators would like to create an account - * in the disabled state. The do not want this to be a two operation process - * since this can leave the door open to abuse. Therefore special attributes - * that can perform operations were introduced. The - * {@link OperationalAttributes#DISABLED} attribute could be added to the set of - * attribute sent to a Connector for the {@link CreateOp} operation. To tell the - * {@link Connector} to create the account with it in the disabled state whether - * the target resource itself has an attribute or an additional method must be - * called. - */ - public static class OperationalAttributes { - /** - * Gets/sets the enable status of an object. - */ - public static readonly string ENABLE_NAME = ConnectorAttributeUtil.CreateSpecialName("ENABLE"); - /** - * Gets/sets the enable date for an object. - */ - public static readonly string ENABLE_DATE_NAME = ConnectorAttributeUtil.CreateSpecialName("ENABLE_DATE"); - /** - * Gets/sets the disable date for an object. - */ - public static readonly string DISABLE_DATE_NAME = ConnectorAttributeUtil.CreateSpecialName("DISABLE_DATE"); - /** - * Gets/sets the lock out attribute for an object. - */ - public static readonly string LOCK_OUT_NAME = ConnectorAttributeUtil.CreateSpecialName("LOCK_OUT"); - /** - * Gets/sets the password expiration date for an object. - */ - public static readonly string PASSWORD_EXPIRATION_DATE_NAME = ConnectorAttributeUtil.CreateSpecialName("PASSWORD_EXPIRATION_DATE"); - /** - * Gets/sets the password expired for an object. - */ - public static readonly string PASSWORD_EXPIRED_NAME = ConnectorAttributeUtil.CreateSpecialName("PASSWORD_EXPIRED"); - /** - * Normally this is a write-only attribute. Sets the password for an object. - */ - public static readonly string PASSWORD_NAME = ConnectorAttributeUtil.CreateSpecialName("PASSWORD"); - /** - * Used in conjunction with password to do an account level password change. - * This is for a non-administrator change of the password and therefore - * requires the current password. - */ - public static readonly string CURRENT_PASSWORD_NAME = ConnectorAttributeUtil.CreateSpecialName("CURRENT_PASSWORD"); - - // ======================================================================= - // Helper Methods.. - // ======================================================================= - public static readonly ICollection OPERATIONAL_ATTRIBUTE_NAMES = - CollectionUtil.NewReadOnlySet ( - LOCK_OUT_NAME, - ENABLE_NAME, - ENABLE_DATE_NAME, - DISABLE_DATE_NAME, - PASSWORD_EXPIRATION_DATE_NAME, - PASSWORD_NAME, - CURRENT_PASSWORD_NAME, - PASSWORD_EXPIRED_NAME - ); - - public static ICollection GetOperationalAttributeNames() { - return CollectionUtil.NewReadOnlySet(OPERATIONAL_ATTRIBUTE_NAMES); - } - public static bool IsOperationalAttribute(ConnectorAttribute attr) { - string name = (attr != null) ? attr.Name : null; - return OPERATIONAL_ATTRIBUTE_NAMES.Contains(name); - } - } - #endregion - - #region PredefinedAttributes - /** - * List of well known or pre-defined attributes. Common attributes that most - * resources have that are not operational in nature. - */ - public static class PredefinedAttributes { - /** - * Attribute that should hold a reasonable value to - * display for the value of an object. If this is not present, then the - * application will have to use the NAME to show the value. - */ - public static readonly String SHORT_NAME = ConnectorAttributeUtil.CreateSpecialName("SHORT_NAME"); - - /** - * Attribute that should hold the value of the object's description, - * if one is available. - */ - public static readonly String DESCRIPTION = ConnectorAttributeUtil.CreateSpecialName("DESCRIPTION"); - - /** - * Read-only attribute that shows the last date/time the password was - * changed. - */ - public static readonly string LAST_PASSWORD_CHANGE_DATE_NAME = ConnectorAttributeUtil.CreateSpecialName("LAST_PASSWORD_CHANGE_DATE"); - - /** - * Common password policy attribute where the password must be changed every - * so often. The value for this attribute is milliseconds since its the - * lowest common denominator. - */ - public static readonly string PASSWORD_CHANGE_INTERVAL_NAME = ConnectorAttributeUtil.CreateSpecialName("PASSWORD_CHANGE_INTERVAL"); - - /** - * Last login date for an account. This is usually used to determine inactivity. - */ - public static readonly string LAST_LOGIN_DATE_NAME = ConnectorAttributeUtil.CreateSpecialName("LAST_LOGIN_DATE"); - - /** - * Groups an account object belongs to. - */ - public static readonly string GROUPS_NAME = ConnectorAttributeUtil.CreateSpecialName("GROUPS"); - - /** - * Accounts that belong to a group or organization. - */ - public static readonly string ACCOUNTS_NAME = ConnectorAttributeUtil.CreateSpecialName("ACCOUNTS"); - - /** - * An organization that that an account/person belongs to. - */ - public static readonly string ORGANIZATION_NAME = ConnectorAttributeUtil.CreateSpecialName("ORGANIZATION"); - } - #endregion - - #region PredefinedAttributeInfos - public static class PredefinedAttributeInfos { - /** - * Attribute that should hold a reasonable value to - * display for the value of an object. If this is not present, then the - * application will have to use the NAME to show the value. - */ - public static readonly ConnectorAttributeInfo SHORT_NAME = - ConnectorAttributeInfoBuilder.Build(PredefinedAttributes.SHORT_NAME); - - /** - * Attribute that should hold the value of the object's description, - * if one is available. - */ - public static readonly ConnectorAttributeInfo DESCRIPTION = - ConnectorAttributeInfoBuilder.Build(PredefinedAttributes.DESCRIPTION); - /** - * Read-only attribute that shows the last date/time the password was - * changed. - */ - public static readonly ConnectorAttributeInfo LAST_PASSWORD_CHANGE_DATE = - ConnectorAttributeInfoBuilder.Build( - PredefinedAttributes.LAST_PASSWORD_CHANGE_DATE_NAME, - typeof(long), - ConnectorAttributeInfo.Flags.NOT_CREATABLE | - ConnectorAttributeInfo.Flags.NOT_UPDATEABLE); - - /** - * Common password policy attribute where the password must be changed every - * so often. The value for this attribute is milliseconds since its the - * lowest common denominator. - */ - public static readonly ConnectorAttributeInfo PASSWORD_CHANGE_INTERVAL = - ConnectorAttributeInfoBuilder.Build( - PredefinedAttributes.PASSWORD_CHANGE_INTERVAL_NAME, typeof(long)); - - /** - * Last login date for an account. This is usually used to determine - * inactivity. - */ - public static readonly ConnectorAttributeInfo LAST_LOGIN_DATE = - ConnectorAttributeInfoBuilder.Build( - PredefinedAttributes.LAST_LOGIN_DATE_NAME, - typeof(long), - ConnectorAttributeInfo.Flags.NOT_CREATABLE | - ConnectorAttributeInfo.Flags.NOT_UPDATEABLE); - - /** - * Groups that an account or person belong to. The Attribute values are the - * UID value of each group that an account has membership in. - */ - public static readonly ConnectorAttributeInfo GROUPS = - ConnectorAttributeInfoBuilder.Build(PredefinedAttributes.GROUPS_NAME, - typeof(String), - ConnectorAttributeInfo.Flags.MULTIVALUED | - ConnectorAttributeInfo.Flags.NOT_RETURNED_BY_DEFAULT); - - - /** - * Accounts that are members of a group or organization. The Attribute - * values are the UID value of each account the has a group or organization - * membership. - */ - public static readonly ConnectorAttributeInfo ACCOUNTS = - ConnectorAttributeInfoBuilder.Build(PredefinedAttributes.ACCOUNTS_NAME, - typeof(String), - ConnectorAttributeInfo.Flags.MULTIVALUED| - ConnectorAttributeInfo.Flags.NOT_RETURNED_BY_DEFAULT); - - - /** - * Organizations that an account or person is a member of. The Attribute - * values are the UID value of each organization that an account or person is - * a member of. - */ - public static readonly ConnectorAttributeInfo ORGANIZATION = - ConnectorAttributeInfoBuilder.Build(PredefinedAttributes.ORGANIZATION_NAME, - typeof(String), - ConnectorAttributeInfo.Flags.NOT_RETURNED_BY_DEFAULT); - } - #endregion - - #region OperationOptions - /** - * Arbitrary options to be passed into various operations. This serves - * as a catch-all for extra options. - */ - public sealed class OperationOptions { - - /** - * An option to use with {@link SearchApiOp} that specified the scope - * under which to perform the search. To be used in conjunction with - * {@link #OP_CONTAINER}. Must be one of the following values - *
    - *
  1. {@link #SCOPE_OBJECT}
  2. - *
  3. {@link #SCOPE_ONE_LEVEL}
  4. - *
  5. {@link #SCOPE_SUBTREE}
  6. - *
- */ - public const String OP_SCOPE = "SCOPE"; - public const String SCOPE_OBJECT = "object"; - public const String SCOPE_ONE_LEVEL = "onelevel"; - public const String SCOPE_SUBTREE = "subtree"; - - /** - * An option to use with {@link SearchApiOp} that specified the container - * under which to perform the search. Must be of type {@link QualifiedUid}. - * Should be implemented for those object classes whose {@link ObjectClassInfo#isContainer()} - * returns true. - */ - public const String OP_CONTAINER = "CONTAINER"; - - /** - * An option to use with {@link ScriptOnResourceApiOp} and possibly others - * that specifies an account under which to execute the script/operation. - * The specified account will appear to have performed any action that the - * script/operation performs. - *

- * Check the javadoc for a particular connector to see whether that - * connector supports this option. - */ - public static readonly string OP_RUN_AS_USER = "RUN_AS_USER"; - - /** - * An option to use with {@link ScriptOnResourceApiOp} and possibly others - * that specifies a password under which to execute the script/operation. - */ - public static readonly string OP_RUN_WITH_PASSWORD = "RUN_WITH_PASSWORD"; - - /** - * Determines the attributes to retrieve during {@link SearchApiOp} and - * {@link SyncApiOp}. - */ - public static readonly string OP_ATTRIBUTES_TO_GET = "ATTRS_TO_GET"; - - private readonly IDictionary _operationOptions; - - /** - * Public only for serialization; please use {@link OperationOptionsBuilder}. - * @param operationOptions The options. - */ - public OperationOptions(IDictionary operationOptions) { - foreach (Object val in operationOptions.Values) { - FrameworkUtil.CheckOperationOptionValue(val); - } - //clone options to do a deep copy in case anything - //is an array - IDictionary operationOptionsClone = (IDictionary)SerializerUtil.CloneObject(operationOptions); - _operationOptions = CollectionUtil.NewReadOnlyDictionary(operationOptionsClone); - } - - /** - * Returns a map of options. Each value in the map - * must be of a type that the framework can serialize. - * See {@link ObjectSerializerFactory} for a list of supported types. - * - * @return A map of options. - */ - public IDictionary Options { - get { - return _operationOptions; - } - } - - /** - * Convenience method that returns {@link #OP_SCOPE}. - * @return The value for {@link #OP_SCOPE}. - */ - public String Scope { - get { - return (String) CollectionUtil.GetValue(_operationOptions,OP_SCOPE,null); - } - } - - /** - * Convenience method that returns {@link #OP_CONTAINER}. - * @return The value for {@link #OP_CONTAINER}. - */ - public QualifiedUid getContainer { - get { - return (QualifiedUid) CollectionUtil.GetValue(_operationOptions,OP_CONTAINER,null); - } - } - - /** - * Get the string array of attribute names to return in the object. - */ - public string[] AttributesToGet { - get { - return (string[]) CollectionUtil.GetValue( - _operationOptions, OP_ATTRIBUTES_TO_GET, null); - } - } - - /** - * Get the account to run the operation as.. - */ - public string RunAsUser { - get { - return (string) CollectionUtil.GetValue( - _operationOptions, OP_RUN_AS_USER, null); - } - } - - /** - * Get the password to run the operation as.. - */ - public GuardedString RunWithPassword { - get { - return (GuardedString) CollectionUtil.GetValue( - _operationOptions, OP_RUN_WITH_PASSWORD, null); - } - } - - public override string ToString() - { - StringBuilder bld = new StringBuilder(); - bld.Append("OperationOptions: ").Append(Options); - return bld.ToString(); - } - } - #endregion - - #region OperationOptionsBuilder - /** - * Builder for {@link OperationOptions}. - */ - public sealed class OperationOptionsBuilder { - private readonly IDictionary _options; - - /** - * Create a builder with an empty set of options. - */ - public OperationOptionsBuilder() { - _options = new Dictionary(); - } - - /** - * Create a builder from an existing set of options. - * @param options The existing set of options. Must not be null. - */ - public OperationOptionsBuilder(OperationOptions options) { - Assertions.NullCheck(options, "options"); - // clone options to do a deep copy in case anything - // is an array - IDictionary operationOptionsClone = (IDictionary) SerializerUtil - .CloneObject(options.Options); - _options = CollectionUtil.NewDictionary(operationOptionsClone); - } - - /** - * Sets a given option and a value for that option. - * @param name The name of the option - * @param value The value of the option. Must be one of the types that - * we can serialize. - * See {@link ObjectSerializerFactory} for a list of supported types. - */ - public void SetOption(String name, Object value) { - if ( name == null ) { - throw new ArgumentException("Argument 'value' cannot be null."); - } - //don't validate value here - we do that implicitly when - //we clone in the constructor of OperationOptions - _options[name] = value; - } - - /** - * Returns a mutable reference of the options map. - * @return A mutable reference of the options map. - */ - public IDictionary Options { - get { - //might as well be mutable since it's the builder and - //we don't want to deep copy anyway - return _options; - } - } - - /** - * Creates the OperationOptions. - * @return The newly-created OperationOptions - */ - public OperationOptions Build() { - return new OperationOptions(_options); - } - - /** - * Sets the {@link OperationOptions#OP_ATTRIBUTES_TO_GET} option. - * - * @param attrNames - * list of {@link Attribute} names. - */ - public string[] AttributesToGet { - set { - Assertions.NullCheck(value, "AttributesToGet"); - // don't validate value here - we do that in - // the constructor of OperationOptions - that's - // really the only place we can truly enforce this - _options[OperationOptions.OP_ATTRIBUTES_TO_GET] = value; - } - } - - /** - * Set the run with password option. - */ - public GuardedString RunWithPassword { - set { - Assertions.NullCheck(value, "RunWithPassword"); - _options[OperationOptions.OP_RUN_WITH_PASSWORD] = value; - } - } - - /** - * Set the run as user option. - */ - public string RunAsUser { - set { - Assertions.NullCheck(value, "RunAsUser"); - _options[OperationOptions.OP_RUN_AS_USER] = value; - } - } - /** - * Convenience method to set {@link OperationOptions#OP_SCOPE} - * @param scope The scope. May not be null. - * @return A this reference to allow chaining - */ - public string Scope { - set { - Assertions.NullCheck(value, "scope"); - _options[OperationOptions.OP_SCOPE] = value; - } - } - - /** - * Convenience method to set {@link OperationOptions#OP_CONTAINER} - * @param container The container. May not be null. - * @return A this reference to allow chaining - */ - public QualifiedUid Container { - set { - Assertions.NullCheck(value, "container"); - _options[OperationOptions.OP_CONTAINER] = value; - } - } - - } - #endregion - - #region OperationOptionInfo - public sealed class OperationOptionInfo { - private String _name; - private Type _type; - - public OperationOptionInfo(String name, - Type type) { - Assertions.NullCheck(name, "name"); - Assertions.NullCheck(type, "type"); - FrameworkUtil.CheckOperationOptionType(type); - _name = name; - _type = type; - } - - public String Name { - get { - return _name; - } - } - - public Type OptionType { - get { - return _type; - } - } - - public override bool Equals(Object o) { - if (o is OperationOptionInfo) { - OperationOptionInfo other = - (OperationOptionInfo)o; - if (!_name.Equals(other._name)) { - return false; - } - if (!_type.Equals(other._type)) { - return false; - } - return true; - } - return false; - } - - public override int GetHashCode() { - return _name.GetHashCode(); - } - - public override string ToString() - { - StringBuilder bld = new StringBuilder(); - bld.Append("OperationOptionInfo("); - bld.Append(_name); - bld.Append(_type.ToString()); - bld.Append(')'); - return bld.ToString(); - } - - } - #endregion - - #region OperationOptionInfoBuilder - public sealed class OperationOptionInfoBuilder { - private String _name; - private Type _type; - - public OperationOptionInfoBuilder() { - } - - public OperationOptionInfoBuilder(String name, - Type type) { - _name = name; - _type = type; - } - - public String Name { - get { - return _name; - } - set { - _name = value; - } - } - - public Type OptionType { - get { - return _type; - } - set { - _type = value; - } - } - - public OperationOptionInfo Build() { - return new OperationOptionInfo(_name,_type); - } - - public static OperationOptionInfo Build(String name, Type type) { - return new OperationOptionInfoBuilder(name,type).Build(); - } - - public static OperationOptionInfo Build(String name) { - return Build(name, typeof(string)); - } - - public static OperationOptionInfo BuildAttributesToGet() { - return Build(OperationOptions.OP_ATTRIBUTES_TO_GET, typeof(string[])); - } - - public static OperationOptionInfo BuildRunWithPassword() { - return Build(OperationOptions.OP_RUN_WITH_PASSWORD); - } - - public static OperationOptionInfo BuildRunAsUser() { - return Build(OperationOptions.OP_RUN_AS_USER); - } - public static OperationOptionInfo BuildScope() { - return Build(OperationOptions.OP_SCOPE); - } - - public static OperationOptionInfo BuildContainer() { - return Build(OperationOptions.OP_CONTAINER,typeof(QualifiedUid)); - } - } - #endregion - - #region QualifiedUid - /** - * A fully-qualified uid. That is, a pair of {@link ObjectClass} and - * {@link Uid}. - */ - public sealed class QualifiedUid { - private readonly ObjectClass _objectClass; - private readonly Uid _uid; - - /** - * Create a QualifiedUid. - * @param objectClass The object class. May not be null. - * @param uid The uid. May not be null. - */ - public QualifiedUid(ObjectClass objectClass, - Uid uid) { - Assertions.NullCheck(objectClass,"objectClass"); - Assertions.NullCheck(uid,"uid"); - _objectClass = objectClass; - _uid = uid; - } - - /** - * Returns the object class. - * @return The object class. - */ - public ObjectClass ObjectClass { - get { - return _objectClass; - } - } - - /** - * Returns the uid. - * @return The uid. - */ - public Uid Uid { - get { - return _uid; - } - } - - /** - * Returns true iff o is a QualifiedUid and the object class and uid match. - */ - public override bool Equals(Object o) { - if ( o is QualifiedUid ) { - QualifiedUid other = (QualifiedUid)o; - return ( _objectClass.Equals(other._objectClass) && - _uid.Equals(other._uid) ); - } - return false; - } - - /** - * Returns a hash code based on uid - */ - public override int GetHashCode() { - return _uid.GetHashCode(); - } - - /** - * Returns a string representation acceptible for debugging. - */ - public override String ToString() { - return SerializerUtil.SerializeXmlObject(this, false); - } - - } - #endregion - - #region ResultsHandler - /** - * Encapsulate the handling of each object returned by the search. - */ - public delegate bool ResultsHandler(ConnectorObject obj); - #endregion - - #region Schema - /** - * Determines the objects supported by a - * {@link com.sun.openconnectors.framework.spi.Connector}. - * The {@link Schema} object is used to represent the basic objects that a - * connector supports. This does not prevent a connector from supporting more. - * Rather, this is informational for the caller of the connector to understand - * a minimum support level. - * The schema defines 4 primary data structures - *

    - *
  1. Declared ObjectClasses ({@link #getObjectClassInfo()}).
  2. - *
  3. Declared OperationOptionInfo ({@link #getOperationOptionInfo()}).
  4. - *
  5. Supported ObjectClasses by operation ({@link #getSupportedObjectClassesByOperation()}).
  6. - *
  7. Supported OperationOptionInfo by operation({@link #getSupportedOptionsByOperation()()}).
  8. - *
- * - * TODO: add more to describe and what is expected from this call and how it is - * used.. based on OperationalAttribute etc.. - */ - public sealed class Schema { - private readonly ICollection _declaredObjectClasses; - private readonly ICollection _declaredOperationOptions; - private readonly IDictionary,ICollection> - _supportedObjectClassesByOperation; - private readonly IDictionary,ICollection> - _supportedOptionsByOperation; - - /** - * Public only for serialization; please use - * SchemaBuilder instead. - * @param info - * @param supportedObjectClassesByOperation - */ - public Schema(ICollection info, - ICollection options, - IDictionary,ICollection> supportedObjectClassesByOperation, - IDictionary,ICollection> supportedOptionsByOperation) { - _declaredObjectClasses = CollectionUtil.NewReadOnlySet(info); - _declaredOperationOptions = CollectionUtil.NewReadOnlySet(options); - - //make read-only - { - IDictionary,ICollection> temp = - new Dictionary,ICollection>(); - foreach (KeyValuePair,ICollection> entry in - supportedObjectClassesByOperation) { - SafeType op = - entry.Key; - ICollection resolvedClasses = - CollectionUtil.NewReadOnlySet(entry.Value); - temp[op] = resolvedClasses; - } - _supportedObjectClassesByOperation = CollectionUtil.AsReadOnlyDictionary(temp); - } - //make read-only - { - IDictionary,ICollection> temp = - new Dictionary,ICollection>(); - foreach (KeyValuePair,ICollection> entry in - supportedOptionsByOperation) { - SafeType op = - entry.Key; - ICollection resolvedClasses = - CollectionUtil.NewReadOnlySet(entry.Value); - temp[op] = resolvedClasses; - } - _supportedOptionsByOperation = CollectionUtil.AsReadOnlyDictionary(temp); - } - } - - /** - * Returns the set of object classes that are defined in the schema, regardless - * of which operations support them. - */ - public ICollection ObjectClassInfo { - get { - return _declaredObjectClasses; - } - } - - /** - * Returns the ObjectClassInfo for the given type. - * @param type The type to find. - * @return the ObjectClassInfo for the given type or null if not found. - */ - public ObjectClassInfo FindObjectClassInfo(String type) { - foreach (ObjectClassInfo info in _declaredObjectClasses) { - if ( info.ObjectType.Equals(type) ) { - return info; - } - } - return null; - } - - /** - * Returns the set of operation options that are defined in the schema, regardless - * of which operations support them. - * @return The options defined in this schema. - */ - public ICollection OperationOptionInfo { - get { - return _declaredOperationOptions; - } - } - - /** - * Returns the OperationOptionInfo for the given name. - * @param name The name to find. - * @return the OperationOptionInfo for the given name or null if not found. - */ - public OperationOptionInfo FindOperationOptionInfo(String name) { - Assertions.NullCheck(name, "name"); - foreach (OperationOptionInfo info in _declaredOperationOptions) { - if ( info.Name.Equals(name) ) { - return info; - } - } - return null; - } - - /** - * Returns the supported object classes for the given operation. - * @param apiop The operation. - * @return the supported object classes for the given operation. - */ - public ICollection GetSupportedObjectClassesByOperation(SafeType apiop) { - ICollection rv = - CollectionUtil.GetValue(_supportedObjectClassesByOperation,apiop,null); - if ( rv == null ) { - ICollection empty = - CollectionUtil.NewReadOnlySet(); - - return empty; - } - else { - return rv; - } - } - - /** - * Returns the supported options for the given operation. - * @param apiop The operation. - * @return the supported options for the given operation. - */ - public ICollection GetSupportedOptionsByOperation(SafeType apiop) { - ICollection rv = - CollectionUtil.GetValue(_supportedOptionsByOperation,apiop,null); - if ( rv == null ) { - ICollection empty = - CollectionUtil.NewReadOnlySet(); - return empty; - } - else { - return rv; - } - } - - /** - * Returns the set of object classes that apply to a particular operation. - * @return the set of object classes that apply to a particular operation. - */ - public IDictionary,ICollection> SupportedObjectClassesByOperation { - get { - return _supportedObjectClassesByOperation; - } - } - /** - * Returns the set of operation options that apply to a particular operation. - * @return the set of operation options that apply to a particular operation. - */ - public IDictionary,ICollection> SupportedOptionsByOperation { - get { - return _supportedOptionsByOperation; - } - } - - - - public override int GetHashCode() { - return CollectionUtil.GetHashCode(_declaredObjectClasses); - } - - public override bool Equals(object o) { - Schema other = o as Schema; - if ( other != null ) { - if (!CollectionUtil.Equals(ObjectClassInfo,other.ObjectClassInfo)) { - return false; - } - if (!CollectionUtil.Equals(OperationOptionInfo,other.OperationOptionInfo)) { - return false; - } - if (!CollectionUtil.Equals(_supportedObjectClassesByOperation, - other._supportedObjectClassesByOperation)) { - return false; - } - if (!CollectionUtil.Equals(_supportedOptionsByOperation, - other._supportedOptionsByOperation)) { - return false; - } - return true; - } - return false; - } - - public override string ToString() - { - return SerializerUtil.SerializeXmlObject(this, false); - } - } - #endregion - - #region SchemaBuilder - /** - * Simple builder class to help facilitate creating a {@link Schema} object. - */ - public sealed class SchemaBuilder { - private readonly SafeType _connectorClass; - private readonly ICollection _declaredObjectClasses - = new HashSet(); - private readonly ICollection _declaredOperationOptions - = new HashSet(); - - private readonly IDictionary,ICollection> - _supportedObjectClassesByOperation = - new Dictionary,ICollection>(); - private readonly IDictionary,ICollection> - _supportedOptionsByOperation = - new Dictionary,ICollection>(); - - - /** - * - */ - public SchemaBuilder(SafeType connectorClass) { - Assertions.NullCheck(connectorClass, "connectorClass"); - _connectorClass = connectorClass; - } - - /** - * Adds another ObjectClassInfo to the schema. Also, adds this - * to the set of supported classes for every operation defined by - * the Connector. - * - * @param info - * @throws IllegalStateException If already defined - */ - public void DefineObjectClass(ObjectClassInfo info) { - Assertions.NullCheck(info, "info"); - if (_declaredObjectClasses.Contains(info)) { - throw new InvalidOperationException("ObjectClass already defined: "+ - info.ObjectType); - } - _declaredObjectClasses.Add(info); - foreach (SafeType op in - FrameworkUtil.GetDefaultSupportedOperations(_connectorClass)) { - ICollection oclasses = - CollectionUtil.GetValue(_supportedObjectClassesByOperation,op,null); - if (oclasses == null) { - oclasses = new HashSet(); - _supportedObjectClassesByOperation[op] = oclasses; - } - oclasses.Add(info); - } - } - /** - * Adds another OperationOptionInfo to the schema. Also, adds this - * to the set of supported options for every operation defined by - * the Connector. - */ - public void DefineOperationOption(OperationOptionInfo info) { - Assertions.NullCheck(info, "info"); - if (_declaredOperationOptions.Contains(info)) { - throw new InvalidOperationException("OperationOption already defined: "+ - info.Name); - } - _declaredOperationOptions.Add(info); - foreach (SafeType op in - FrameworkUtil.GetDefaultSupportedOperations(_connectorClass)) { - ICollection oclasses = - CollectionUtil.GetValue(_supportedOptionsByOperation,op,null); - if (oclasses == null) { - oclasses = new HashSet(); - _supportedOptionsByOperation[op] = oclasses; - } - oclasses.Add(info); - } - } - - /** - * Adds another ObjectClassInfo to the schema. Also, adds this - * to the set of supported classes for every operation defined by - * the Connector. - * @throws IllegalStateException If already defined - */ - public void DefineObjectClass(String type, ICollection attrInfo) { - ObjectClassInfoBuilder bld = new ObjectClassInfoBuilder(); - bld.ObjectType = type; - bld.AddAllAttributeInfo(attrInfo); - ObjectClassInfo obj = bld.Build(); - DefineObjectClass(obj); - } - - /** - * Adds another OperationOptionInfo to the schema. Also, adds this - * to the set of supported options for every operation defined by - * the Connector. - * @throws IllegalStateException If already defined - */ - public void DefineOperationOption(String optionName, Type type) { - OperationOptionInfoBuilder bld = new OperationOptionInfoBuilder(); - bld.Name=(optionName); - bld.OptionType=(type); - OperationOptionInfo info = bld.Build(); - DefineOperationOption(info); - } - - /** - * Adds the given ObjectClassInfo as a supported ObjectClass for - * the given operation. - * @param op The SPI operation - * @param def The ObjectClassInfo - * @throws IllegalArgumentException If the given ObjectClassInfo was - * not already defined using {@link #defineObjectClass(ObjectClassInfo)}. - */ - public void AddSupportedObjectClass(SafeType op, - ObjectClassInfo def) - { - Assertions.NullCheck(op, "op"); - Assertions.NullCheck(def, "def"); - ICollection> apis = - FrameworkUtil.Spi2Apis(op); - if (!_declaredObjectClasses.Contains(def)) { - throw new ArgumentException("ObjectClass "+def.ObjectType+ - " not defined in schema."); - } - foreach (SafeType api in apis) { - ICollection infos = - CollectionUtil.GetValue(_supportedObjectClassesByOperation,api,null); - if ( infos == null ) { - throw new ArgumentException("Operation "+op+ - " not implement by connector."); - } - if ( infos.Contains(def)) { - throw new ArgumentException("ObjectClass "+def.ObjectType+ - " already supported for operation "+op); - } - infos.Add(def); - } - } - - /** - * Removes the given ObjectClassInfo as a supported ObjectClass for - * the given operation. - * @param op The SPI operation - * @param def The ObjectClassInfo - * @throws IllegalArgumentException If the given ObjectClassInfo was - * not already defined using {@link #defineObjectClass(ObjectClassInfo)}. - */ - public void RemoveSupportedObjectClass(SafeType op, - ObjectClassInfo def) - { - Assertions.NullCheck(op, "op"); - Assertions.NullCheck(def, "def"); - ICollection> apis = - FrameworkUtil.Spi2Apis(op); - if (!_declaredObjectClasses.Contains(def)) { - throw new ArgumentException("ObjectClass "+def.ObjectType+ - " not defined in schema."); - } - foreach (SafeType api in apis) { - ICollection infos = - CollectionUtil.GetValue(_supportedObjectClassesByOperation,api,null); - if ( infos == null ) { - throw new ArgumentException("Operation "+op+ - " not implement by connector."); - } - if ( !infos.Contains(def)) { - throw new ArgumentException("ObjectClass "+def.ObjectType - +" already removed for operation "+op); - } - infos.Remove(def); - } - } - /** - * Adds the given OperationOptionInfo as a supported option for - * the given operation. - * @param op The SPI operation - * @param def The OperationOptionInfo - * @throws IllegalArgumentException If the given OperationOptionInfo was - * not already defined using {@link #defineOperationOption(OperationOptionInfo)}. - */ - public void AddSupportedOperationOption(SafeType op, - OperationOptionInfo def) { - Assertions.NullCheck(op, "op"); - Assertions.NullCheck(def, "def"); - ICollection> apis = - FrameworkUtil.Spi2Apis(op); - if (!_declaredOperationOptions.Contains(def)) { - throw new ArgumentException("OperationOption "+def.Name+ - " not defined in schema."); - } - foreach (SafeType api in apis) { - ICollection infos = - CollectionUtil.GetValue(_supportedOptionsByOperation,api,null); - if ( infos == null ) { - throw new ArgumentException("Operation "+op+ - " not implement by connector."); - } - if ( infos.Contains(def) ) { - throw new ArgumentException("OperationOption "+def.Name+ - " already supported for operation "+op); - } - infos.Add(def); - } - } - - /** - * Removes the given OperationOptionInfo as a supported option for - * the given operation. - * @param op The SPI operation - * @param def The OperationOptionInfo - * @throws IllegalArgumentException If the given OperationOptionInfo was - * not already defined using {@link #defineOperationOption(OperationOptionInfo)}. - */ - public void RemoveSupportedOperationOption(SafeType op, - OperationOptionInfo def) { - Assertions.NullCheck(op, "op"); - Assertions.NullCheck(def, "def"); - ICollection> apis = - FrameworkUtil.Spi2Apis(op); - if (!_declaredOperationOptions.Contains(def)) { - throw new ArgumentException("OperationOption "+def.Name+ - " not defined in schema."); - } - foreach (SafeType api in apis) { - ICollection infos = - CollectionUtil.GetValue(_supportedOptionsByOperation,api,null); - if ( infos == null ) { - throw new ArgumentException("Operation "+op+ - " not implement by connector."); - } - if ( !infos.Contains(def) ) { - throw new ArgumentException("OperationOption "+def.Name+ - " already removed for operation "+op); - } - infos.Remove(def); - } - } - - /** - * Clears the operation-specific supported classes. Normally, when - * you add an ObjectClass, using {@link #defineObjectClass(ObjectClassInfo)}, - * it is added to all operations. You may then remove those that you need - * using {@link #removeSupportedObjectClass(Class, ObjectClassInfo)}. You - * may wish, as an alternative to clear everything out and instead add using - * {@link #addSupportedObjectClass(Class, ObjectClassInfo)}. - */ - public void ClearSupportedObjectClassesByOperation() { - foreach (ICollection values in - _supportedObjectClassesByOperation.Values) - { - values.Clear(); - } - } - /** - * Clears the operation-specific supported options. Normally, when - * you add an OperationOptionInfo, using {@link #defineOperationOption(OperationOptionInfo)(ObjectClassInfo)}, - * it is added to all operations. You may then remove those that you need - * using {@link #removeSupportedOperationOption(Class, OperationOptionInfo)}. You - * may wish, as an alternative to clear everything out and instead add using - * {@link #addSupportedOperationOption(Class, OperationOptionInfo)}. - */ - public void ClearSupportedOptionsByOperation() { - foreach (ICollection values in - _supportedOptionsByOperation.Values) - { - values.Clear(); - } - } - - /** - * Builds the {@link Schema} object based on the {@link ObjectClassInfo}s - * added so far. - * - * @return new Schema object based on the info provided. - */ - public Schema Build() { - if (_declaredObjectClasses.Count == 0) { - String ERR = "Must be at least one ObjectClassInfo object!"; - throw new InvalidOperationException(ERR); - } - return new Schema(_declaredObjectClasses, - _declaredOperationOptions, - _supportedObjectClassesByOperation, - _supportedOptionsByOperation); - } - } - #endregion - - #region ScriptContext - /** - * Encapsulates a script and all of its parameters. - * @see com.sun.openconnectors.framework.api.operations.ScriptOnResourceApiOp - * @see com.sun.openconnectors.framework.api.operations.ScriptOnConnectorApiOp - */ - public sealed class ScriptContext { - - private readonly String _scriptLanguage; - private readonly String _scriptText; - private readonly IDictionary _scriptArguments; - - /** - * Public only for serialization; please use {@link ScriptContextBuilder}. - * @param scriptLanguage The script language. Must not be null. - * @param scriptText The script text. Must not be null. - * @param scriptArguments The script arguments. May be null. - */ - public ScriptContext(String scriptLanguage, - String scriptText, - IDictionary scriptArguments) { - - if (scriptLanguage == null) { - throw new ArgumentException("Argument 'scriptLanguage' must be specified"); - } - if (scriptText == null) { - throw new ArgumentException("Argument 'scriptText' must be specified"); - } - //clone script arguments and options - this serves two purposes - //1)makes sure everthing is serializable - //2)does a deep copy - IDictionary scriptArgumentsClone = (IDictionary)SerializerUtil.CloneObject(scriptArguments); - _scriptLanguage = scriptLanguage; - _scriptText = scriptText; - _scriptArguments = CollectionUtil.NewReadOnlyDictionary(scriptArgumentsClone); - } - - /** - * Identifies the language in which the script is written - * (e.g., bash, csh, - * Perl4 or Python). - * @return The script language. - */ - public String ScriptLanguage { - get { - return _scriptLanguage; - } - } - - /** - * Returns the text (i.e., actual characters) of the script. - * @return The text of the script. - */ - public String ScriptText { - get { - return _scriptText; - } - } - - /** - * Returns a map of arguments to be passed to the script. - * Values must be types that the framework can serialize. - * See {@link ObjectSerializerFactory} for a list of supported types. - * @return A map of arguments to be passed to the script. - */ - public IDictionary ScriptArguments { - get { - return _scriptArguments; - } - } - - public override string ToString() - { - StringBuilder bld = new StringBuilder(); - bld.Append("ScriptContext: "); - // poor man's to string method. - IDictionary map = new Dictionary(); - map["Language"] = ScriptLanguage; - map["Text"] = ScriptText; - map["Arguments"] = ScriptArguments; - bld.Append(map.ToString()); - return bld.ToString(); - } - - } - #endregion - - #region ScriptContextBuilder - /** - * Builds an {@link ScriptContext}. - */ - public sealed class ScriptContextBuilder { - private String _scriptLanguage; - private String _scriptText; - private readonly IDictionary _scriptArguments = new - Dictionary(); - - /** - * Creates an empty builder. - */ - public ScriptContextBuilder() { - - } - - /** - * Creates a builder with the required parameters specified. - * @param scriptLanguage a string that identifies the language - * in which the script is written - * (e.g., bash, csh, - * Perl4 or Python). - * @param scriptText The text (i.e., actual characters) of the script. - */ - public ScriptContextBuilder(String scriptLanguage, - String scriptText) { - _scriptLanguage = scriptLanguage; - _scriptText = scriptText; - } - - /** - * Identifies the language in which the script is written - * (e.g., bash, csh, - * Perl4 or Python). - * @return The script language. - */ - public String ScriptLanguage { - get { - return _scriptLanguage; - } - set { - _scriptLanguage = value; - } - } - - /** - * Returns the actual characters of the script. - * @return the actual characters of the script. - */ - public String ScriptText { - get { - return _scriptText; - } - set { - _scriptText = value; - } - } - - /** - * Adds or sets an argument to pass to the script. - * @param name The name of the argument. Must not be null. - * @param value The value of the argument. Must be one of - * type types that the framework can serialize. - * @see ObjectSerializerFactory for a list of supported types. - */ - public ScriptContextBuilder AddScriptArgument(String name, Object value) { - if ( name == null ) { - throw new ArgumentException("Argument 'name' cannot be null."); - } - //don't validate value here - we do that implicitly when - //we clone in the constructor of ScriptRequest - _scriptArguments[name] = value; - return this; - } - - /** - * Removes the given script argument. - * @param name The name of the argument. Must not be null. - */ - public ScriptContextBuilder RemoveScriptArgument(String name) { - if ( name == null ) { - throw new ArgumentException("Argument 'name' cannot be null."); - } - _scriptArguments.Remove(name); - return this; - } - - /** - * Returns a mutable reference of the script arguments map. - * @return A mutable reference of the script arguments map. - */ - public IDictionary ScriptArguments { - get { - //might as well be mutable since it's the builder and - //we don't want to deep copy anyway - return _scriptArguments; - } - } - - /** - * Creates a ScriptContext. - * The scriptLanguage and scriptText - * must be set prior to calling this. - * @return The ScriptContext. - */ - public ScriptContext Build() { - return new ScriptContext(_scriptLanguage, - _scriptText, - _scriptArguments); - } - } - #endregion - - #region SyncDelta - /** - * Represents a change to an object in a resource. - * - * @see SyncApiOp - * @see SyncOp - */ - public sealed class SyncDelta { - private readonly SyncToken _token; - private readonly SyncDeltaType _deltaType; - private readonly Uid _uid; - private readonly ConnectorObject _object; - - /** - * Creates a SyncDelata - * @param token - * The token. Must not be null. - * @param deltaType - * The delta. Must not be null. - * @param uid - * The uid. Must not be null. - * @param object - * The object that has changed. May be null for delete. - */ - internal SyncDelta(SyncToken token, SyncDeltaType deltaType, - Uid uid, - ConnectorObject obj) { - Assertions.NullCheck(token, "token"); - Assertions.NullCheck(deltaType, "deltaType"); - Assertions.NullCheck(uid, "uid"); - - //only allow null object for delete - if ( obj == null && - deltaType != SyncDeltaType.DELETE) { - throw new ArgumentException("ConnectorObject must be specified for anything other than delete."); - } - - //if object not null, make sure its Uid - //matches - if ( obj != null ) { - if (!uid.Equals(obj.Uid)) { - throw new ArgumentException("Uid does not match that of the object."); - } - } - - _token = token; - _deltaType = deltaType; - _uid = uid; - _object = obj; - - } - - /** - * Returns the Uid of the object that changed. - * - * @return the Uid of the object that changed. - */ - public Uid Uid { - get { - return _uid; - } - } - - /** - * Returns the connector object that changed. This - * may be null in the case of delete. - * @return The object or possibly null if this - * represents a delete. - */ - public ConnectorObject Object { - get { - return _object; - } - } - - /** - * Returns the SyncToken of the object that changed. - * - * @return the SyncToken of the object that changed. - */ - public SyncToken Token { - get { - return _token; - } - } - - /** - * Returns the type of the change the occured. - * - * @return The type of change that occured. - */ - public SyncDeltaType DeltaType { - get { - return _deltaType; - } - } - - - public override String ToString() { - IDictionary values = new Dictionary(); - values["Token"] = _token; - values["DeltaType"] = _deltaType; - values["Uid"] = _uid; - values["Object"] = _object; - return values.ToString(); - } - - public override int GetHashCode() { - return _uid.GetHashCode(); - } - - public override bool Equals(Object o) { - if ( o is SyncDelta ) { - SyncDelta other = (SyncDelta)o; - if (!_token.Equals(other._token)) { - return false; - } - if (!_deltaType.Equals(other._deltaType)) { - return false; - } - if (!_uid.Equals(other._uid)) { - return false; - } - if (_object == null) { - if ( other._object != null ) { - return false; - } - } - else if (!_object.Equals(other._object)) { - return false; - } - return true; - } - return false; - } - } - #endregion - - #region SyncDeltaBuilder - /** - * Builder for {@link SyncDelta}. - */ - public sealed class SyncDeltaBuilder { - private SyncToken _token; - private SyncDeltaType _deltaType; - private Uid _uid; - private ConnectorObject _object; - - /** - * Create a new SyncDeltaBuilder - */ - public SyncDeltaBuilder() { - - } - - /** - * Creates a new SyncDeltaBuilder whose - * values are initialized to those of the delta. - * @param delta The original delta. - */ - public SyncDeltaBuilder(SyncDelta delta) { - _token = delta.Token; - _deltaType = delta.DeltaType; - _object = delta.Object; - _uid = delta.Uid; - } - - /** - * Returns the SyncToken of the object that changed. - * - * @return the SyncToken of the object that changed. - */ - public SyncToken Token { - get { - return _token; - } - set { - _token = value; - } - } - - /** - * Returns the type of the change that occurred. - * - * @return The type of change that occurred. - */ - public SyncDeltaType DeltaType { - get { - return _deltaType; - } - set { - _deltaType = value; - } - } - - /** - * Returns the Uid of the object that changed. - * Note that this is implicitly set when you call - * {@link #setObject(ConnectorObject)}. - * - * @return the Uid of the object that changed. - */ - public Uid Uid { - get { - return _uid; - } - set { - _uid = value; - } - } - - /** - * Returns the object that changed. - * Sets the object that changed and implicitly - * sets Uid if object is not null. - * @return The object that changed. May be null for - * deletes. - */ - public ConnectorObject Object { - get { - return _object; - } - set { - _object = value; - if ( value != null ) { - _uid = value.Uid; - } - } - } - - /** - * Creates a SyncDelta. Prior to calling the following must be specified: - *
    - *
  1. {@link #setObject(ConnectorObject) Object} (for anything other than delete)
  2. - *
  3. {@link #setUid(Uid) Uid} (this is implictly set when calling {@link #setObject(ConnectorObject)})
  4. - *
  5. {@link #setToken(SyncToken) Token}
  6. - *
  7. {@link #setDeltaType(SyncDeltaType) DeltaType}
  8. - *
- */ - public SyncDelta Build() { - return new SyncDelta(_token, _deltaType, _uid, _object); - } - } - #endregion - - #region SyncDeltaType - /** - * The type of change. - */ - public enum SyncDeltaType { - /** - * The change represents either a create or an update in - * the resource. These are combined into a single value because: - *
    - *
  1. Many resources will not be able to distinguish a create from an update. - * Those that have an audit log will be able to. However, many implementations - * will only have the current record and a modification timestamp.
  2. - *
  3. Regardless of whether or not the resource can distinguish the two cases, - * the application needs to distinguish.
  4. - *
- */ - CREATE_OR_UPDATE, - - /** - * The change represents a DELETE in the resource - */ - DELETE - } - #endregion - - #region SyncResultsHandler - /** - * Called to handle a delta in the stream. Will be called multiple times, - * once for each result. Although a callback, this is still invoked - * synchronously. That is, it is guaranteed that following a call to - * {@link SyncApiOp#sync(ObjectClass, SyncToken, SyncResultsHandler)} no - * more invocations to {@link #handle(SyncDelta)} will be performed. - * - * @param delta - * The change - * @return True iff the application wants to continue processing more - * results. - * @throws RuntimeException - * If the application encounters an exception. This will stop - * the interation and the exception will be propogated back to - * the application. - */ - public delegate bool SyncResultsHandler(SyncDelta delta); - #endregion - - #region SyncToken - /** - * Abstract "place-holder" for synchronization. The application must not make - * any attempt to interpret the value of the token. From the standpoint of the - * application the token is merely a black-box. The application may only persist - * the value of the token for use on subsequent synchronization requests. - *

- * What this token represents is entirely connector-specific. On some connectors - * this might be a last-modified value. On others, it might be a unique ID of a - * log table entry. On others such as JMS, this might be a dummy value since - * JMS itself keeps track of the state of the sync. - */ - public sealed class SyncToken { - - private Object _value; - - /** - * Creates a new - * - * @param value - * May not be null. TODO: define set of allowed value types - * (currently same as set of allowed attribute values). - */ - public SyncToken(Object value) { - Assertions.NullCheck(value, "value"); - FrameworkUtil.CheckAttributeValue(value); - _value = value; - } - - /** - * Returns the value for the token. - * - * @return The value for the token. - */ - public Object Value { - get { - return _value; - } - } - - public override String ToString() { - return "SyncToken: " + _value.ToString(); - } - - public override int GetHashCode() { - return CollectionUtil.GetHashCode(_value); - } - - public override bool Equals(Object o) { - if ( o is SyncToken ) { - SyncToken other = (SyncToken)o; - return CollectionUtil.Equals(_value, other._value); - } - return false; - } - - - } - #endregion - - #region Uid - public sealed class Uid : ConnectorAttribute { - - public static readonly string NAME = ConnectorAttributeUtil.CreateSpecialName("UID"); - - public Uid(String val) : base(NAME, CollectionUtil.NewReadOnlyList(Check(val))) { - } - private static String Check(String value) { - if (StringUtil.IsBlank(value)) { - String ERR = "Uid value must not be blank!"; - throw new ArgumentException(ERR); - } - return value; - } - /** - * The single value of the attribute that is the unique id of an object. - * - * @return value that identifies an object. - */ - public String GetUidValue() { - return ConnectorAttributeUtil.GetStringValue(this); - } - } - #endregion - - #region ConnectorAttributesAccessor - /** - * Attributes Accessor convenience methods for accessing attributes. - * - * This class wraps a set of attributes to make lookup faster than the - * {@link AttributeUtil#find(String, Set)} method, since that method must - * re-create the map each time. - * - * @author Warren Strange - */ - public class ConnectorAttributesAccessor { - - ICollection _attrs; - IDictionary _attrMap; - - public ConnectorAttributesAccessor(ICollection attrs) { - _attrs = attrs; - _attrMap = ConnectorAttributeUtil.ToMap(attrs); - } - - /** - * Find the named attribute - * - * @param name - - * the attribute name to search for - * @return the Attribute, or null if not found. - */ - public ConnectorAttribute Find(String name) { - return CollectionUtil.GetValue(_attrMap, name, null); - } - - /** - * Get the {@link Name} attribute from the set of attributes. - * - * @return the {@link Name} attribute in the set. - */ - public Name GetName() { - return (Name) Find(Name.NAME); - } - - /** - * Return the enabled status of the account. - * - * If the ENABLE operational attribute is present, it's value takes - * precedence over the current value. If it is missing, the currentlyEnabled - * status is returned instead. - * - * @param dflt - * the default state if enable is not found. - * @return true if the account is enabled, false otherwise - */ - public bool GetEnabled(bool dflt) { - bool e = dflt; - ConnectorAttribute enable = Find(OperationalAttributes.ENABLE_NAME); - if (enable != null) { - e = ConnectorAttributeUtil.GetBooleanValue(enable).Value; - } - return e; - } - - /** - * Get the password as a GuardeString - * - * @return the password as a guarded String - */ - public GuardedString GetPassword() { - ConnectorAttribute a = Find(OperationalAttributes.PASSWORD_NAME); - return a == null ? null : ConnectorAttributeUtil.GetGuardedStringValue(a); - } - - /** - * Return a list of attributes - * - * @param name - - * name of attribute to search for. - * - * @return The List (generic object) iff it exists otherwise null. - */ - public IList FindList(String name) { - ConnectorAttribute a = Find(name); - return (a == null) ? null : a.Value; - } - - /** - * Return the multivalued attribute as a list of strings. This will throw a - * ClassCastException if the underlying attribute list is not of type - * String. - * - * @param name - * the name of the attribute to search for - * @return a List of String values for the attribute - */ - public IList FindStringList(String name) { - IList l = FindList(name); - if (l != null) { - IList ret = new List(l.Count); - foreach (object o in l) { - ret.Add((String) o); - } - return ret; - } - return null; - } - - /** - * Determines if the set as the attribute specified. - * - * @param name - * attribute name - * @return true if the named attribute exists, false otherwise - */ - public bool HasAttribute(String name) { - return Find(name) != null; - } - - /** - * Get the string value from the specified (single-valued) attribute. - * - * @param name - * Attribute from which to retrieve the long value. - * @return null if the value is null otherwise the long value for the - * attribute. - * @throws ClassCastException - * iff the object in the attribute is not an long. - * @throws IllegalArgumentException - * iff the attribute is a multi-valued (rather than - * single-valued). - */ - public String FindString(String name) { - ConnectorAttribute a = Find(name); - return a == null ? null : ConnectorAttributeUtil.GetStringValue(a); - } - - /** - * Get the integer value from the specified (single-valued) attribute. - * - * @param name - * Attribute from which to retrieve the long value. - * @return null if the value is null otherwise the long value for the - * attribute. - * @throws ClassCastException - * iff the object in the attribute is not an long. - * @throws IllegalArgumentException - * iff the attribute is a multi-valued (rather than - * single-valued). - */ - public int? FindInteger(String name) { - ConnectorAttribute a = Find(name); - return (a == null) ? null : ConnectorAttributeUtil.GetIntegerValue(a); - } - - /** - * Get the long value from the specified (single-valued) attribute. - * - * @param name - * Attribute from which to retrieve the long value. - * @return null if the value is null otherwise the long value for the - * attribute. - * @throws ClassCastException - * iff the object in the attribute is not an long. - * @throws IllegalArgumentException - * iff the attribute is a multi-valued (rather than - * single-valued). - */ - public long? FindLong(String name) { - ConnectorAttribute a = Find(name); - return a == null ? null : ConnectorAttributeUtil.GetLongValue(a); - } - - /** - * Get the date value from the specified (single-valued) attribute that - * contains a long. - * - * @param name - * Attribute from which to retrieve the date value. - * @return null if the value is null otherwise the date value for the - * attribute. - * @throws ClassCastException - * iff the object in the attribute is not an long. - * @throws IllegalArgumentException - * iff the attribute is a multi-valued (rather than - * single-valued). - */ - public DateTime? FindDateTime(String name) { - ConnectorAttribute a = Find(name); - return a == null ? null : ConnectorAttributeUtil.GetDateTimeValue(a); - } - - /** - * Get the integer value from the specified (single-valued) attribute. - * - * @param name - * Attribute from which to retrieve the integer value. - * @return null if the value is null otherwise the integer value for the - * attribute. - * @throws ClassCastException - * iff the object in the attribute is not an integer. - * @throws IllegalArgumentException - * iff the attribute is a multi-valued (rather than - * single-valued).. - */ - public double? FindDouble(String name) { - ConnectorAttribute a = Find(name); - return a == null ? null : ConnectorAttributeUtil.GetDoubleValue(a); - } - - /** - * Get the boolean value from the specified (single-valued) attribute. - * - * @param name - * Attribute from which to retrieve the boolean value. - * @return null if the value is null otherwise the boolean value for the - * attribute. - * @throws ClassCastException - * iff the object in the attribute is not an {@link Boolean}. - * @throws IllegalArgumentException - * iff the attribute is a multi-valued (rather than - * single-valued). - */ - public bool? FindBoolean(String name) { - ConnectorAttribute a = Find(name); - return a == null ? null : ConnectorAttributeUtil.GetBooleanValue(a); - } - } - #endregion -} diff --git a/DotNetConnectors.sln/Framework/CommonObjectsFilter.cs b/DotNetConnectors.sln/Framework/CommonObjectsFilter.cs deleted file mode 100644 index 2e32a68f..00000000 --- a/DotNetConnectors.sln/Framework/CommonObjectsFilter.cs +++ /dev/null @@ -1,1314 +0,0 @@ -/* - * ==================== - * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER. - * - * Copyright 2007-2008 Sun Microsystems, Inc. All rights reserved. - * - * The contents of this file are subject to the terms of the Common Development - * and Distribution License("CDDL") (the "License"). You may not use this file - * except in compliance with the License. - * - * You can obtain a copy of the License at - * http://IdentityConnectors.dev.java.net/legal/license.txt - * See the License for the specific language governing permissions and limitations - * under the License. - * - * When distributing the Covered Code, include this CDDL Header Notice in each file - * and include the License file at identityconnectors/legal/license.txt. - * If applicable, add the following below this CDDL Header, with the fields - * enclosed by brackets [] replaced by your own identifying information: - * "Portions Copyrighted [year] [name of copyright owner]" - * ==================== - */ -using System; -using System.Text; -using System.Collections.Generic; -using Org.IdentityConnectors.Common; -using Org.IdentityConnectors.Framework.Common.Objects; -namespace Org.IdentityConnectors.Framework.Common.Objects.Filters -{ - #region AbstractFilterTranslator - /** - * Base class to make it easier to implement Search. - * A search filter may contain operators (such as 'contains' or 'in') - * or may contain logical operators (such as 'AND', 'OR' or 'NOT') - * that a connector cannot implement using the native API - * of the target system or application. - * A connector developer should subclass AbstractFilterTranslator - * in order to declare which filter operations the connector does support. - * This allows the FilterTranslator instance to analyze - * a specified search filter and reduce the filter to its most efficient form. - * The default (and worst-case) behavior is to return a null expression, - * which means that the connector should return "everything" - * (that is, should return all values for every requested attribute) - * and rely on the common code in the framework to perform filtering. - * This "fallback" behavior is good (in that it ensures consistency - * of search behavior across connector implementations) but it is - * obviously better for performance and scalability if each connector - * performs as much filtering as the native API of the target can support. - *

- * A subclass should override each of the following methods where possible: - *

    - *
  1. {@link #createAndExpression}
  2. - *
  3. {@link #createOrExpression}
  4. - *
  5. {@link #createContainsExpression(ContainsFilter, boolean)}
  6. - *
  7. {@link #createEndsWithExpression(EndsWithFilter, boolean)}
  8. - *
  9. {@link #createEqualsExpression(EqualsFilter, boolean)}
  10. - *
  11. {@link #createGreaterThanExpression(GreaterThanFilter, boolean)}
  12. - *
  13. {@link #createGreaterThanOrEqualExpression(GreaterThanOrEqualFilter, boolean)}
  14. - *
  15. {@link #createStartsWithExpression(StartsWithFilter, boolean)}
  16. - *
- *

- * Translation can then be performed using {@link #translate(Filter)}. - *

- * @param The result type of the translator. Commonly this will - * be a string, but there are cases where you might need to return - * a more complex data structure. For example if you are building a SQL - * query, you will need not *just* the base WHERE clause but a list - * of tables that need to be joined together. - */ - abstract public class AbstractFilterTranslator : FilterTranslator - where T : class - { - - /** - * Main method to be called to translate a filter - * @param filter The filter to translate. - * @return The list of queries to be performed. The list - * size() may be one of the following: - *

    - *
  1. 0 - This - * signifies fetch everything. This may occur if your filter - * was null or one of your create* methods returned null.
  2. - *
  3. 1 - List contains a single query that will return the results from the filter. - * Note that the results may be a superset of those specified by - * the filter in the case that one of your create* methods returned null. - * That is OK from a behavior standpoint since ConnectorFacade performs - * a second level of filtering. However it is undesirable from a performance standpoint.
  4. - *
  5. >1 - List contains multiple queries that must be performed in order to - * meet the filter that was passed in. Note that this only occurs if your - * {@link #createOrExpression} method can return null. If this happens, it - * is the responsibility of the connector implementor to perform each query - * and combine the results. In order to eliminate duplicates, the connector - * implementation must keep an in-memory HashSet of those UID - * that have been visited thus far. This will not scale well if your - * result sets are large. Therefore it is recommended that if - * at all possible you implement {@link #createOrExpression}
  6. - *
- */ - public IList Translate(Filter filter) { - if ( filter == null ) { - return new List(); - } - //this must come first - filter = NormalizeNot(filter); - filter = SimplifyAndDistribute(filter); - //might have simplified it to the everything filter - if ( filter == null ) { - return new List(); - } - IList result = TranslateInternal(filter); - //now "optimize" - we can eliminate exact matches at least - HashSet set = new HashSet(); - IList optimized = new List(result.Count); - foreach (T obj in result) { - if ( set.Add(obj) ) { - optimized.Add(obj); - } - } - return optimized; - } - - /** - * Pushes Not's so that they are just before the leaves of the tree - */ - private Filter NormalizeNot(Filter filter) { - if ( filter is AndFilter ) { - AndFilter af = (AndFilter)filter; - return new AndFilter(NormalizeNot(af.Left), - NormalizeNot(af.Right)); - } - else if ( filter is OrFilter ) { - OrFilter of = (OrFilter)filter; - return new OrFilter(NormalizeNot(of.Left), - NormalizeNot(of.Right)); - } - else if ( filter is NotFilter ) { - NotFilter nf = (NotFilter)filter; - return Negate(NormalizeNot(nf.Filter)); - } - else { - return filter; - } - } - - /** - * Given a filter, create a filter representing its negative. - * This is used by normalizeNot. - */ - private Filter Negate(Filter filter) - { - if ( filter is AndFilter ) { - AndFilter af = (AndFilter)filter; - return new OrFilter(Negate(af.Left), - Negate(af.Right)); - } - else if ( filter is OrFilter ) { - OrFilter of = (OrFilter)filter; - return new AndFilter(Negate(of.Left), - Negate(of.Right)); - } - else if ( filter is NotFilter ) { - NotFilter nf = (NotFilter)filter; - return nf.Filter; - } - else { - return new NotFilter(filter); - } - } - - /** - * Simultaneously prunes those portions of the - * filter than cannot be implemented and distributes - * Ands over Ors where needed if the resource does not - * implement Or. - * - * @param filter Nots must already be normalized - * @return a simplified filter or null to represent the - * "everything" filter. - */ - private Filter SimplifyAndDistribute(Filter filter) { - if ( filter is AndFilter ) { - AndFilter af = (AndFilter)filter; - Filter simplifiedLeft = - SimplifyAndDistribute(af.Left); - Filter simplifiedRight = - SimplifyAndDistribute(af.Right); - if ( simplifiedLeft == null ) { - //left is "everything" - just return the right - return simplifiedRight; - } - else if ( simplifiedRight == null ) { - //right is "everything" - just return the left - return simplifiedLeft; - } - else { - //simulate translation of the left and right - //to see where we end up - IList leftExprs = - TranslateInternal(simplifiedLeft); - IList rightExprs = - TranslateInternal(simplifiedRight); - if (leftExprs.Count == 0) { - //This can happen only when one of the create* methods - //is inconsistent from one invocation to the next - //(simplifiedLeft should have been null - //in the previous 'if' above). - throw new InvalidOperationException("Translation method is inconsistent: "+leftExprs); - } - if (rightExprs.Count == 0) { - //This can happen only when one of the create* methods - //is inconsistent from one invocation to the next - //(simplifiedRight should have been null - //in the previous 'if' above). - throw new InvalidOperationException("Translation method is inconsistent: "+rightExprs); - } - - //Simulate ANDing each pair(left,right). - //If all of them return null (i.e., "everything"), - //then the request cannot be filtered. - bool anyAndsPossible = false; - foreach ( T leftExpr in leftExprs ) { - foreach ( T rightExpr in rightExprs ) { - T test = CreateAndExpression( - leftExpr, - rightExpr); - if ( test != null ) { - anyAndsPossible = true; - break; - } - } - if ( anyAndsPossible ) { - break; - } - } - - //If no AND filtering is possible, - //return whichever of left or right - //contains the fewest expressions. - if (!anyAndsPossible) { - if ( leftExprs.Count <= rightExprs.Count ) { - return simplifiedLeft; - } - else { - return simplifiedRight; - } - } - - //Since AND filtering is possible for at least - //one expression, let's distribute. - if ( leftExprs.Count > 1 ) { - //The left can contain more than one expression - //only if the left-hand side is an unimplemented OR. - //Distribute our AND to the left. - OrFilter left = (OrFilter)simplifiedLeft; - OrFilter newFilter = - new OrFilter(new AndFilter(left.Left, - simplifiedRight), - new AndFilter(left.Right, - simplifiedRight)); - return SimplifyAndDistribute(newFilter); - } - else if ( rightExprs.Count > 1 ) { - //The right can contain more than one expression - //only if the right-hand side is an unimplemented OR. - //Distribute our AND to the right. - OrFilter right = (OrFilter)simplifiedRight; - OrFilter newFilter = - new OrFilter(new AndFilter(simplifiedLeft, - right.Left), - new AndFilter(simplifiedLeft, - right.Right)); - return SimplifyAndDistribute(newFilter); - } - else { - //Each side contains exactly one expression - //and the translator does implement AND - //(anyAndsPossible must be true - //for them to have hit this branch). - if (!anyAndsPossible) { - throw new Exception("expected anyAndsPossible"); - } - return new AndFilter(simplifiedLeft,simplifiedRight); - } - } - } - else if ( filter is OrFilter ) { - OrFilter of = (OrFilter)filter; - Filter simplifiedLeft = - SimplifyAndDistribute(of.Left); - Filter simplifiedRight = - SimplifyAndDistribute(of.Right); - //If either left or right reduces to "everything", - //then simplify the OR to "everything". - if ( simplifiedLeft == null || simplifiedRight == null ) { - return null; - } - //otherwise - return new OrFilter(simplifiedLeft, - simplifiedRight); - } - else { - //Otherwise, it's a NOT(LEAF) or a LEAF. - //Simulate creating it. - T expr = CreateLeafExpression(filter); - if ( expr == null ) { - //If the expression cannot be implemented, - //return the "everything" filter. - return null; - } - else { - //Otherwise, return the filter. - return filter; - } - } - } - - /** - * Translates the filter into a list of expressions. - * The filter must have already been transformed - * using normalizeNot followed by a simplifyAndDistribute. - * @param filter A filter (normalized, simplified, and distibuted) - * @return A list of expressions or empty list for everything. - */ - private IList TranslateInternal(Filter filter) { - if ( filter is AndFilter ) { - T result = TranslateAnd((AndFilter)filter); - IList rv = new List(); - if ( result != null ) { - rv.Add(result); - } - return rv; - } - else if ( filter is OrFilter ) { - return TranslateOr((OrFilter)filter); - } - else { - //otherwise it's either a leaf or a NOT (leaf) - T expr = CreateLeafExpression(filter); - IList exprs = new List(); - if ( expr != null ) { - exprs.Add(expr); - } - return exprs; - } - } - - private T TranslateAnd( AndFilter filter ) { - IList leftExprs = TranslateInternal(filter.Left); - IList rightExprs = TranslateInternal(filter.Right); - if ( leftExprs.Count != 1 ) { - //this can happen only if one of the create* methods - //is inconsistent from one invocation to the next - //(at this point we've already been simplified and - //distributed). - throw new InvalidOperationException("Translation method is inconsistent: "+leftExprs); - } - if ( rightExprs.Count != 1 ) { - //this can happen only if one of the create* methods - //is inconsistent from one invocation to the next - //(at this point we've already been simplified and - //distributed). - throw new InvalidOperationException("Translation method is inconsistent: "+rightExprs); - } - T rv = CreateAndExpression(leftExprs[0], rightExprs[0]); - if ( rv == null ) { - //This could happen only if we're inconsistent - //(since the simplify logic already should have removed - //any expression that cannot be filtered). - throw new InvalidOperationException("createAndExpression is inconsistent"); - } - return rv; - } - - private IList TranslateOr( OrFilter filter ) { - IList leftExprs = TranslateInternal(filter.Left); - IList rightExprs = TranslateInternal(filter.Right); - if ( leftExprs.Count == 0 ) { - //This can happen only if one of the create* methods - //is inconsistent from one invocation to the next. - throw new InvalidOperationException("Translation method is inconsistent"); - } - if ( rightExprs.Count == 0 ) { - //This can happen only if one of the create* methods - //methods is inconsistent from on invocation to the next. - throw new InvalidOperationException("Translation method is inconsistent"); - } - if ( leftExprs.Count == 1 && rightExprs.Count == 1 ) { - //If each side contains exactly one expression, - //try to create a combined expression. - T val = CreateOrExpression(leftExprs[0], rightExprs[0]); - if ( val != null ) { - IList rv = new List(); - rv.Add(val); - return rv; - } - //Otherwise, fall through - } - - //Return a list of queries from the left and from the right - IList rv2 = new List(leftExprs.Count+rightExprs.Count); - CollectionUtil.AddAll(rv2,leftExprs); - CollectionUtil.AddAll(rv2,rightExprs); - return rv2; - } - - /** - * Creates an expression for a LEAF or a NOT(leaf) - * @param filter Must be either a leaf or a NOT(leaf) - * @return The expression - */ - private T CreateLeafExpression(Filter filter) { - Filter leafFilter; - bool not; - if ( filter is NotFilter ) { - NotFilter nf = (NotFilter)filter; - leafFilter = nf.Filter; - not = true; - } - else { - leafFilter = filter; - not = false; - } - T expr = CreateLeafExpression(leafFilter,not); - return expr; - } - - /** - * Creates a Leaf expression - * @param filter Must be a leaf expression - * @param not Is ! to be applied to the leaf expression - * @return The expression or null (for everything) - */ - private T CreateLeafExpression(Filter filter, bool not) { - if ( filter is ContainsFilter ) { - return CreateContainsExpression((ContainsFilter)filter, not); - } - else if (filter is EndsWithFilter) { - return CreateEndsWithExpression((EndsWithFilter)filter,not); - } - else if ( filter is EqualsFilter ) { - return CreateEqualsExpression((EqualsFilter)filter, not); - } - else if ( filter is GreaterThanFilter ) { - return CreateGreaterThanExpression((GreaterThanFilter)filter, not); - } - else if ( filter is GreaterThanOrEqualFilter ) { - return CreateGreaterThanOrEqualExpression((GreaterThanOrEqualFilter)filter, not); - } - else if ( filter is LessThanFilter ) { - return CreateLessThanExpression((LessThanFilter)filter, not); - } - else if ( filter is LessThanOrEqualFilter ) { - return CreateLessThanOrEqualExpression((LessThanOrEqualFilter)filter, not); - } - else if (filter is StartsWithFilter) { - return CreateStartsWithExpression((StartsWithFilter)filter,not); - } - else if (filter is ContainsAllValuesFilter) { - return CreateContainsAllValuesExpression((ContainsAllValuesFilter)filter, not); - } - else { - //unrecognized expression - nothing we can do - return null; - } - } - - /** - * Should be overridden by subclasses to create an AND expression - * if the native resource supports AND. - * @param leftExpression The left expression. Will never be null. - * @param rightExpression The right expression. Will never be null. - * @return The AND expression. A return value of null means - * a native AND query cannot be created for the given expressions. - * In this case, the resulting query will consist of the - * leftExpression only. - */ - protected virtual T CreateAndExpression(T leftExpression, T rightExpression) { - return null; - } - - /** - * Should be overridden by subclasses to create an OR expression - * if the native resource supports OR. - * @param leftExpression The left expression. Will never be null. - * @param rightExpression The right expression. Will never be null. - * @return The OR expression. A return value of null means - * a native OR query cannot be created for the given expressions. - * In this case, {@link #translate} may return multiple queries, each - * of which must be run and results combined. - */ - protected virtual T CreateOrExpression(T leftExpression, T rightExpression) { - return null; - } - - /** - * Should be overridden by subclasses to create a CONTAINS expression - * if the native resource supports CONTAINS. - * @param filter The contains filter. Will never be null. - * @param not True if this should be a NOT CONTAINS - * @return The CONTAINS expression. A return value of null means - * a native CONTAINS query cannot be created for the given filter. - * In this case, {@link #translate} may return an empty query set, meaning - * fetch everything. The filter will be re-applied in memory - * to the resulting object stream. This does not scale well, so - * if possible, you should implement this method. - */ - protected virtual T CreateContainsExpression(ContainsFilter filter, bool not) { - return null; - } - - /** - * Should be overridden by subclasses to create a ENDS-WITH expression - * if the native resource supports ENDS-WITH. - * @param filter The contains filter. Will never be null. - * @param not True if this should be a NOT ENDS-WITH - * @return The ENDS-WITH expression. A return value of null means - * a native ENDS-WITH query cannot be created for the given filter. - * In this case, {@link #translate} may return an empty query set, meaning - * fetch everything. The filter will be re-applied in memory - * to the resulting object stream. This does not scale well, so - * if possible, you should implement this method. - */ - protected virtual T CreateEndsWithExpression(EndsWithFilter filter, bool not) { - return null; - } - - /** - * Should be overridden by subclasses to create a EQUALS expression - * if the native resource supports EQUALS. - * @param filter The contains filter. Will never be null. - * @param not True if this should be a NOT EQUALS - * @return The EQUALS expression. A return value of null means - * a native EQUALS query cannot be created for the given filter. - * In this case, {@link #translate} may return an empty query set, meaning - * fetch everything. The filter will be re-applied in memory - * to the resulting object stream. This does not scale well, so - * if possible, you should implement this method. - */ - protected virtual T CreateEqualsExpression(EqualsFilter filter, bool not) { - return null; - } - - /** - * Should be overridden by subclasses to create a GREATER-THAN expression - * if the native resource supports GREATER-THAN. - * @param filter The contains filter. Will never be null. - * @param not True if this should be a NOT GREATER-THAN - * @return The GREATER-THAN expression. A return value of null means - * a native GREATER-THAN query cannot be created for the given filter. - * In this case, {@link #translate} may return an empty query set, meaning - * fetch everything. The filter will be re-applied in memory - * to the resulting object stream. This does not scale well, so - * if possible, you should implement this method. - */ - protected virtual T CreateGreaterThanExpression(GreaterThanFilter filter, bool not) { - return null; - } - - /** - * Should be overridden by subclasses to create a GREATER-THAN-EQUAL expression - * if the native resource supports GREATER-THAN-EQUAL. - * @param filter The contains filter. Will never be null. - * @param not True if this should be a NOT GREATER-THAN-EQUAL - * @return The GREATER-THAN-EQUAL expression. A return value of null means - * a native GREATER-THAN-EQUAL query cannot be created for the given filter. - * In this case, {@link #translate} may return an empty query set, meaning - * fetch everything. The filter will be re-applied in memory - * to the resulting object stream. This does not scale well, so - * if possible, you should implement this method. - */ - protected virtual T CreateGreaterThanOrEqualExpression(GreaterThanOrEqualFilter filter, bool not) { - return null; - } - - /** - * Should be overridden by subclasses to create a LESS-THAN expression - * if the native resource supports LESS-THAN. - * @param filter The contains filter. Will never be null. - * @param not True if this should be a NOT LESS-THAN - * @return The LESS-THAN expression. A return value of null means - * a native LESS-THAN query cannot be created for the given filter. - * In this case, {@link #translate} may return an empty query set, meaning - * fetch everything. The filter will be re-applied in memory - * to the resulting object stream. This does not scale well, so - * if possible, you should implement this method. - */ - protected virtual T CreateLessThanExpression(LessThanFilter filter, bool not) { - return null; - } - - /** - * Should be overridden by subclasses to create a LESS-THAN-EQUAL expression - * if the native resource supports LESS-THAN-EQUAL. - * @param filter The contains filter. Will never be null. - * @param not True if this should be a NOT LESS-THAN-EQUAL - * @return The LESS-THAN-EQUAL expression. A return value of null means - * a native LESS-THAN-EQUAL query cannot be created for the given filter. - * In this case, {@link #translate} may return an empty query set, meaning - * fetch everything. The filter will be re-applied in memory - * to the resulting object stream. This does not scale well, so - * if possible, you should implement this method. - */ - protected virtual T CreateLessThanOrEqualExpression(LessThanOrEqualFilter filter, bool not) { - return null; - } - - /** - * Should be overridden by subclasses to create a STARTS-WITH expression - * if the native resource supports STARTS-WITH. - * @param filter The contains filter. Will never be null. - * @param not True if this should be a NOT STARTS-WITH - * @return The STARTS-WITH expression. A return value of null means - * a native STARTS-WITH query cannot be created for the given filter. - * In this case, {@link #translate} may return an empty query set, meaning - * fetch everything. The filter will be re-applied in memory - * to the resulting object stream. This does not scale well, so - * if possible, you should implement this method. - */ - protected virtual T CreateStartsWithExpression(StartsWithFilter filter, bool not) { - return null; - } - - protected virtual T CreateContainsAllValuesExpression(ContainsAllValuesFilter filter, bool not) { - return null; - } - } - #endregion - - #region AndFilter - public sealed class AndFilter : CompositeFilter { - - /** - * And the the left and right filters. - */ - public AndFilter(Filter left, Filter right) - : base(left, right) { - } - - /** - * Ands the left and right filters. - * - * @see Filter#accept(ConnectorObject) - */ - public override bool Accept(ConnectorObject obj) { - return Left.Accept(obj) && Right.Accept(obj); - } - - public override string ToString() - { - StringBuilder bld = new StringBuilder(); - bld.Append("AND: ").Append(Left).Append(", ").Append(Right); - return bld.ToString(); - } - } - #endregion - - #region AttributeFilter - public abstract class AttributeFilter : Filter { - private readonly ConnectorAttribute _attribute; - - /** - * Root filter for Attribute testing.. - */ - internal AttributeFilter(ConnectorAttribute attribute) { - _attribute = attribute; - if (attribute == null) { - throw new ArgumentException("Attribute not be null!"); - } - } - - /** - * Get the internal attribute. - */ - public ConnectorAttribute GetAttribute() { - return _attribute; - } - - /** - * Determines if the attribute provided is present in the - * {@link ConnectorObject}. - */ - public bool IsPresent(ConnectorObject obj) { - return obj.GetAttributeByName(_attribute.Name) != null; - } - public abstract bool Accept(ConnectorObject obj); - } - #endregion - - #region ComparableAttributeFilter - /** - * Filter for an attribute value that is comparable. - */ - public abstract class ComparableAttributeFilter : - SingleValueAttributeFilter { - - /** - * Attempt compare attribute values. - */ - internal ComparableAttributeFilter(ConnectorAttribute attr) - : base(attr) { - // determine if this attribute value is comparable.. - if (!(GetValue() is IComparable)) { - String ERR = "Must be a comparable value!"; - throw new ArgumentException(ERR); - } - } - - /** - * Call compareTo on the attribute values. If the attribute is not present - * in the {@link ConnectorObject} return -1. - */ - public int Compare(ConnectorObject obj) { - int ret = -1; - ConnectorAttribute attr = obj.GetAttributeByName(GetName()); - if (attr != null && attr.Value.Count == 1) { - // it must be a comparable because that's were testing against - if (!(attr.Value[0] is IComparable)) { - String ERR = "Attribute value must be comparable!"; - throw new ArgumentException(ERR); - } - // grab this value and the on from the attribute an compare.. - IComparable o1 = (IComparable)attr.Value[0]; - IComparable o2 = (IComparable)GetValue(); - ret = o1.CompareTo(o1); - } - return ret; - } - } - #endregion - - #region CompositeFilter - public abstract class CompositeFilter : Filter { - - public Filter Left { get; set; } - - public Filter Right { get; set; } - - internal CompositeFilter(Filter left, Filter right) { - Left = left; - Right = right; - } - public abstract bool Accept(ConnectorObject obj); - } - #endregion - - #region ContainsFilter - public sealed class ContainsFilter : StringFilter { - - public ContainsFilter(ConnectorAttribute attr) - : base(attr) { - } - - public override bool Accept(String value) { - return value.Contains(GetValue()); - } - - public override string ToString() - { - StringBuilder bld = new StringBuilder(); - bld.Append("CONTAINS: ").Append(GetAttribute()); - return bld.ToString(); - } - } - #endregion - - #region EndsWithFilter - public sealed class EndsWithFilter : StringFilter { - - public EndsWithFilter(ConnectorAttribute attr) - : base(attr) { - } - - public override bool Accept(String value) { - return value.EndsWith(GetValue()); - } - - public override string ToString() { - StringBuilder bld = new StringBuilder(); - bld.Append("ENDSWITH: ").Append(GetAttribute()); - return bld.ToString(); - } - } - #endregion - - #region EqualsFilter - public sealed class EqualsFilter : AttributeFilter { - - /** - * Determines if the attribute inside the {@link ConnectorObject} is equal - * to the {@link Attribute} provided. - */ - public EqualsFilter(ConnectorAttribute attr) - :base(attr) { - } - - /** - * Determines if the attribute exists in the {@link ConnectorObject} and if - * its equal to the one provided. - * - * @see Filter#accept(ConnectorObject) - */ - public override bool Accept(ConnectorObject obj) { - bool ret = false; - ConnectorAttribute thisAttr = GetAttribute(); - ConnectorAttribute attr = obj.GetAttributeByName(thisAttr.Name); - if (attr != null) { - ret = thisAttr.Equals(attr); - } - return ret; - } - - public override string ToString() { - StringBuilder bld = new StringBuilder(); - bld.Append("EQUALS: ").Append(GetAttribute()); - return bld.ToString(); - } - - } - #endregion - - #region Filter - public interface Filter { - bool Accept(ConnectorObject obj); - } - #endregion - - #region FilterBuilder - /** - * FilterBuilder creates a {@link Filter} object, that can determine if a - * ConnectorObject will be filtered or not. - * - * @author Will Droste - * @version $Revision: 1.7 $ - * @since 1.0 - */ - public static class FilterBuilder { - - /** - * Determine if the {@link ConnectorObject} {@link ConnectorAttribute} value ends - * with the {@link ConnectorAttribute} value provided. - * - * @param attr - * {@link ConnectorAttribute} value to test against the - * {@link ConnectorObject} attribute value. - * @return true if the {@link ConnectorObject} attribute value contains the - * attribute value provided. - */ - public static Filter EndsWith(ConnectorAttribute attr) { - return new EndsWithFilter(attr); - } - - /** - * Determine if the {@link ConnectorObject} {@link ConnectorAttribute} value starts - * with the {@link ConnectorAttribute} value provided. - * - * @param attr - * {@link ConnectorAttribute} value to test against the - * {@link ConnectorObject} attribute value. - * @return true if the {@link ConnectorObject} attribute value contains the - * attribute value provided. - */ - public static Filter StartsWith(ConnectorAttribute attr) { - return new StartsWithFilter(attr); - } - - public static Filter ContainsAllValues(ConnectorAttribute attr) { - return new ContainsAllValuesFilter(attr); - } - - /** - * Determine if the {@link ConnectorObject} {@link ConnectorAttribute} value contains - * the {@link ConnectorAttribute} value provided. - * - * @param attr - * {@link ConnectorAttribute} value to test against the - * {@link ConnectorObject} attribute value. - * @return true if the {@link ConnectorObject} attribute value contains the - * attribute value provided. - */ - public static Filter Contains(ConnectorAttribute attr) { - return new ContainsFilter(attr); - } - - /** - * The {@link ConnectorAttribute} value provided is less than or equal to the - * {@link ConnectorObject} attribute value. - * - * @param attr - * ConnectorAttribute to do the comparison. - * @return true if attribute provided is greater than or equal to the one - * provided by the {@link ConnectorObject}. - */ - public static Filter GreaterThanOrEqualTo(ConnectorAttribute attr) { - return new GreaterThanOrEqualFilter(attr); - } - - /** - * The {@link ConnectorAttribute} value provided is less than or equal to the - * {@link ConnectorObject} attribute value. - * - * @param attr - * ConnectorAttribute to do the comparison. - * @return true if attribute provided is less than or equal to the one - * provided by the {@link ConnectorObject}. - */ - public static Filter LessThanOrEqualTo(ConnectorAttribute attr) { - return new LessThanOrEqualFilter(attr); - } - - /** - * The {@link ConnectorAttribute} value provided is less than the - * {@link ConnectorObject} attribute value. - * - * @param attr - * ConnectorAttribute to do the comparison. - * @return true if attribute provided is less than the one provided by the - * {@link ConnectorObject}. - */ - public static Filter LessThan(ConnectorAttribute attr) { - return new LessThanFilter(attr); - } - - /** - * ConnectorAttribute value is greater than the {@link ConnectorObject} attribute - * value. - * - * @param attr - * ConnectorAttribute to do the comparison. - * @return true if attribute provided is greater than the one provided by - * the {@link ConnectorObject}. - */ - public static Filter GreaterThan(ConnectorAttribute attr) { - return new GreaterThanFilter(attr); - } - - /** - * Determines if the {@link ConnectorAttribute} provided exists in the - * {@link ConnectorObject} and is equal. - */ - public static Filter EqualTo(ConnectorAttribute attr) { - return new EqualsFilter(attr); - } - - /** - * Ands the two {@link Filter}. - * - * @param leftOperand - * left side operand. - * @param rightOperand - * right side operand. - * @return the result of leftOperand && rightOperand - */ - public static Filter And(Filter leftOperand, Filter rightOperand) { - return new AndFilter(leftOperand, rightOperand); - } - - /** - * ORs the two {@link Filter}. - * - * @param leftOperand - * left side operand. - * @param rightOperand - * right side operand. - * @return the result of leftOperand || rightOperand - */ - public static Filter Or(Filter leftOperand, Filter rightOperand) { - return new OrFilter(leftOperand, rightOperand); - } - - /** - * NOT the {@link Filter}. - * - * @param filter - * negate the result of {@link Filter}. - * @return the result of not {@link Filter}. - */ - public static Filter Not(Filter filter) { - return new NotFilter(filter); - } - } - #endregion - - #region FilterTranslator - public interface FilterTranslator { - IList Translate(Filter filter); - } - #endregion - - #region GreaterThanFilter - public sealed class GreaterThanFilter : ComparableAttributeFilter { - - /** - * Determine if the {@link ConnectorObject} {@link Attribute} value is - * greater than the one provided in the filter. - */ - public GreaterThanFilter(ConnectorAttribute attr) - : base (attr) { - } - - /** - * Determine if the {@link ConnectorObject} {@link Attribute} value is - * greater than the one provided in the filter. - * - * @see com.sun.openconnectors.framework.common.objects.Filter#accept(ConnectorObject) - */ - public override bool Accept(ConnectorObject obj) { - return IsPresent(obj) && this.Compare(obj) > 0; - } - - public override string ToString() - { - StringBuilder bld = new StringBuilder(); - bld.Append("GREATERTHAN: ").Append(GetAttribute()); - return bld.ToString(); - } - } - #endregion - - #region GreaterThanOrEqualFilter - public sealed class GreaterThanOrEqualFilter : ComparableAttributeFilter { - - /** - * Determine if the {@link ConnectorObject} {@link Attribute} value is - * greater than the one provided in the filter. - */ - public GreaterThanOrEqualFilter(ConnectorAttribute attr) - : base (attr) { - } - - /** - * Determine if the {@link ConnectorObject} {@link Attribute} value is - * greater than the one provided in the filter. - * - * @see com.sun.openconnectors.framework.common.objects.Filter#accept(ConnectorObject) - */ - public override bool Accept(ConnectorObject obj) { - return IsPresent(obj) && this.Compare(obj) >= 0; - } - - public override string ToString() - { - StringBuilder bld = new StringBuilder(); - bld.Append("GREATERTHANOREQUAL: ").Append(GetAttribute()); - return bld.ToString(); - } - } - #endregion - - #region LessThanFilter - public sealed class LessThanFilter : ComparableAttributeFilter { - - /** - * Determine if the {@link ConnectorObject} {@link Attribute} value is - * greater than the one provided in the filter. - */ - public LessThanFilter(ConnectorAttribute attr) - : base (attr) { - } - - /** - * Determine if the {@link ConnectorObject} {@link Attribute} value is - * greater than the one provided in the filter. - * - * @see com.sun.openconnectors.framework.common.objects.Filter#accept(ConnectorObject) - */ - public override bool Accept(ConnectorObject obj) { - return IsPresent(obj) && this.Compare(obj) < 0; - } - - public override string ToString() - { - StringBuilder bld = new StringBuilder(); - bld.Append("LESSTHAN: ").Append(GetAttribute()); - return bld.ToString(); - } - } - #endregion - - #region LessThanOrEqualFilter - public sealed class LessThanOrEqualFilter : ComparableAttributeFilter { - - /** - * Determine if the {@link ConnectorObject} {@link Attribute} value is - * greater than the one provided in the filter. - */ - public LessThanOrEqualFilter(ConnectorAttribute attr) - : base (attr) { - } - - /** - * Determine if the {@link ConnectorObject} {@link Attribute} value is - * greater than the one provided in the filter. - * - * @see com.sun.openconnectors.framework.common.objects.Filter#accept(ConnectorObject) - */ - public override bool Accept(ConnectorObject obj) { - return IsPresent(obj) && this.Compare(obj) <= 0; - } - - public override string ToString() - { - StringBuilder bld = new StringBuilder(); - bld.Append("LESSTHANOREQUAL: ").Append(GetAttribute()); - return bld.ToString(); - } - } - #endregion - - #region NotFilter - /** - * Proxy the filter to return the negative of the value. - */ - public sealed class NotFilter : Filter { - - private readonly Filter _filter; - - /** - * Take the value returned from the internal filter and NOT it. - */ - public NotFilter(Filter filter) { - _filter = filter; - } - - /** - * Get the internal filter that is being negated. - */ - public Filter Filter { - get { - return _filter; - } - } - - /** - * Return the opposite the internal filters return value. - * - * @see Filter#accept(ConnectorObject) - */ - public bool Accept(ConnectorObject obj) { - return !_filter.Accept(obj); - } - - public override string ToString() - { - StringBuilder bld = new StringBuilder(); - bld.Append("NOT: ").Append(Filter); - return bld.ToString(); - } - } - #endregion - - #region OrFilter - public sealed class OrFilter : CompositeFilter { - - /** - * Or the left and right filters. - */ - public OrFilter(Filter left, Filter right) - : base(left, right) { - } - - /** - * ORs the left and right filters. - * - * @see Filter#accept(ConnectorObject) - */ - public override bool Accept(ConnectorObject obj) { - return Left.Accept(obj) || Right.Accept(obj); - } - - public override string ToString() { - StringBuilder bld = new StringBuilder(); - bld.Append("OR: ").Append(Left).Append(", ").Append(Right); - return bld.ToString(); - } - } - #endregion - - #region SingleValueAttributeFilter - /** - * Get a single value out of the attribute to test w/. - */ - public abstract class SingleValueAttributeFilter : AttributeFilter { - - /** - * Attempt to single out the value for comparison. - */ - internal SingleValueAttributeFilter(ConnectorAttribute attr) : - base(attr) { - // make sure this is not a Uid.. - if (Uid.NAME.Equals(attr.Name)) { - String MSG = "Uid can only be used for equals comparison."; - throw new ArgumentException(MSG); - } - // actual runtime.. - if (attr.Value.Count != 1) { - String ERR = "Must only be one value!"; - throw new ArgumentException(ERR); - } - } - - /** - * Value to test against. - */ - public Object GetValue() { - return GetAttribute().Value[0]; - } - /** - * Name of the attribute to find in the {@link ConnectorObject}. - */ - public String GetName() { - return GetAttribute().Name; - } - } - #endregion - - #region StartsWithFilter - public sealed class StartsWithFilter : StringFilter { - - public StartsWithFilter(ConnectorAttribute attr) - : base(attr) { - } - - public override bool Accept(String value) { - return value.StartsWith(GetValue()); - } - - public override string ToString() - { - StringBuilder bld = new StringBuilder(); - bld.Append("STARTSWITH: ").Append(GetAttribute()); - return bld.ToString(); - } - } - #endregion - - #region StringFilter - /** - * Filter based on strings. - */ - public abstract class StringFilter : SingleValueAttributeFilter { - - /** - * Attempts to get a string from the attribute. - */ - internal StringFilter(ConnectorAttribute attr) - : base(attr) { - Object val = base.GetValue(); - if (!(val is string)) { - String MSG = "Value must be a string!"; - throw new ArgumentException(MSG); - } - } - - /** - * Get the string value from the afore mentioned attribute. - * - * @see SingleValueAttributeFilter#getValue() - */ - public new String GetValue() { - return (String) base.GetValue(); - } - - /** - * @throws ClassCastException - * iff the value from the {@link ConnectorObject}'s attribute - * of the same name as provided is not a string. - * @see com.sun.openconnectors.framework.common.objects.Filter#accept(ConnectorObject) - */ - public override bool Accept(ConnectorObject obj) { - bool ret = false; - ConnectorAttribute attr = obj.GetAttributeByName(GetName()); - if (attr != null) { - ret = Accept((string)attr.Value[0]); - } - return ret; - } - - public abstract bool Accept(String value); - } - #endregion - - #region ContainsAllValues Filter - public class ContainsAllValuesFilter : AttributeFilter { - private readonly string _name; - private readonly ICollection _values; - - public ContainsAllValuesFilter(ConnectorAttribute attr) : base(attr) { - _name = attr.Name; - _values = attr.Value; - } - /** - * Determine if the {@link ConnectorObject} contains an {@link Attribute} - * which contains all the values provided in the {@link Attribute} passed - * into the filter. - * - * {@inheritDoc} - */ - public override bool Accept(ConnectorObject obj) { - bool rv = false; - ConnectorAttribute found = obj.GetAttributeByName(_name); - if (found != null) { - // TODO: possible optimization using 'Set' - foreach (object o in _values) { - if (!(rv = found.Value.Contains(o))) { - break; - } - } - } - return rv; - } - } - #endregion - -} diff --git a/DotNetConnectors.sln/Framework/CommonSerializer.cs b/DotNetConnectors.sln/Framework/CommonSerializer.cs deleted file mode 100644 index 261429cb..00000000 --- a/DotNetConnectors.sln/Framework/CommonSerializer.cs +++ /dev/null @@ -1,289 +0,0 @@ -/* - * ==================== - * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER. - * - * Copyright 2007-2008 Sun Microsystems, Inc. All rights reserved. - * - * The contents of this file are subject to the terms of the Common Development - * and Distribution License("CDDL") (the "License"). You may not use this file - * except in compliance with the License. - * - * You can obtain a copy of the License at - * http://IdentityConnectors.dev.java.net/legal/license.txt - * See the License for the specific language governing permissions and limitations - * under the License. - * - * When distributing the Covered Code, include this CDDL Header Notice in each file - * and include the License file at identityconnectors/legal/license.txt. - * If applicable, add the following below this CDDL Header, with the fields - * enclosed by brackets [] replaced by your own identifying information: - * "Portions Copyrighted [year] [name of copyright owner]" - * ==================== - */ -using System; -using System.IO; -using System.Collections.Generic; -using Org.IdentityConnectors.Common; -namespace Org.IdentityConnectors.Framework.Common.Serializer -{ - /** - * Interface for reading objects from a stream. - */ - public interface BinaryObjectDeserializer { - /** - * Reads the next object from the stream. Throws - * a wrapped {@link EOFException} if end of stream is reached. - * @return The next object from the stream. - */ - object ReadObject(); - - /** - * Closes the underlying stream - */ - void Close(); - } - /** - * Interface for writing objects to a stream. - */ - public interface BinaryObjectSerializer { - /** - * Writes the next object to the stream. - * @param obj The object to write. - * @see ObjectSerializerFactory for a list of supported types. - */ - void WriteObject(object obj); - - /** - * Flushes the underlying stream. - */ - void Flush(); - - /** - * Closes the underylying stream after first flushing it. - */ - void Close(); - } - - /** - * Serializer factory for serializing connector objects. The list of - * supported types are as follows: - * TODO: list supported types - *
    - *
- * @see SerializerUtil - */ - public abstract class ObjectSerializerFactory { - // At some point we might make this pluggable, but for now, hard-code - private const string IMPL_NAME = "Org.IdentityConnectors.Framework.Impl.Serializer.ObjectSerializerFactoryImpl"; - - private static ObjectSerializerFactory _instance; - - private static readonly Object LOCK = new Object(); - - - /** - * Get the singleton instance of the {@link ObjectSerializerFactory}. - */ - public static ObjectSerializerFactory GetInstance() { - lock (LOCK) { - if (_instance == null) { - SafeType t = - FrameworkInternalBridge.LoadType(IMPL_NAME); - - _instance = t.CreateInstance(); - } - return _instance; - } - } - - /** - * Creates a BinaryObjectSerializer for writing objects to - * the given stream. - * - * NOTE: consider using {@link SerializerUtil#serializeBinaryObject(Object)} - * for convenience serializing a single object. - * - * NOTE2: do not mix and match {@link SerializerUtil#serializeBinaryObject(Object)} - * with {{@link #newBinaryDeserializer(InputStream)}. This is unsafe since there - * is header information and state associated with the stream. Objects written - * using one method must be read using the proper corresponding method. - * - * @param os The stream - * @return The serializer - */ - public abstract BinaryObjectSerializer NewBinarySerializer(Stream os); - - /** - * Creates a BinaryObjectDeserializer for reading objects from - * the given stream. - * - * NOTE: consider using {@link SerializerUtil#deserializeBinaryObject(byte[])} - * for convenience deserializing a single object. - * - * NOTE2: do not mix and match {@link SerializerUtil#deserializeBinaryObject(Object)} - * with {{@link #newBinarySerializer(OutputStream)}. This is unsafe since there - * is header information and state associated with the stream. Objects written - * using one method must be read using the proper corresponding method. - * - * @param os The stream - * @return The deserializer - */ - public abstract BinaryObjectDeserializer NewBinaryDeserializer(Stream i); - - /** - * Creates a BinaryObjectSerializer for writing objects to - * the given stream. - * - * NOTE: consider using {@link SerializerUtil#serializeXmlObject(Object,boolean)} - * for convenience serializing a single object. - * - * NOTE2: do not mix and match {@link SerializerUtil#serializeXmlObject(Object,boolean)} - * with {{@link #deserializeXmlStream(InputSource, XmlObjectResultsHandler, boolean)}. - * - * @param w The writer - * @param includeHeader True to include the xml header - * @param multiObject Is this to produce a multi-object document. If false, only - * a single object may be written. - * @return The serializer - */ - public abstract XmlObjectSerializer NewXmlSerializer(TextWriter w, - bool includeHeader, - bool multiObject); - - /** - * Deserializes XML objects from a stream - * - * NOTE: Consider using {@link SerializerUtil#deserializeXmlObject(String,boolean)} - * for convenience deserializing a single object. - * - * NOTE2: Do not mix and match {@link SerializerUtil#deserializeXmlObject(Object,boolean)} - * with {{@link #newXmlSerializer(Writer, boolean, boolean)}. - * - * @param is The input source - * @param handler The callback to receive objects from the stream - * @param validate True iff we are to validate - */ - public abstract void DeserializeXmlStream(TextReader reader, - XmlObjectResultsHandler handler, - bool validate); - - } - - /** - * Bag of utilities for serialization - */ - public static class SerializerUtil { - - - /** - * Serializes the given object to bytes - * @param object The object to serialize - * @return The bytes - * @see ObjectSerializerFactory for a list of supported types - */ - public static byte [] SerializeBinaryObject(object obj) { - ObjectSerializerFactory fact = ObjectSerializerFactory.GetInstance(); - MemoryStream mem = new MemoryStream(); - BinaryObjectSerializer ser = fact.NewBinarySerializer(mem); - ser.WriteObject(obj); - ser.Close(); - return mem.ToArray(); - } - - /** - * Deserializes the given object from bytes - * @param bytes The bytes to deserialize - * @return The object - * @see ObjectSerializerFactory for a list of supported types - */ - public static object DeserializeBinaryObject(byte [] bytes) { - ObjectSerializerFactory fact = ObjectSerializerFactory.GetInstance(); - MemoryStream mem = new MemoryStream(bytes); - BinaryObjectDeserializer des = fact.NewBinaryDeserializer(mem); - return des.ReadObject(); - } - - /** - * Serializes the given object to xml - * @param object The object to serialize - * @param includeHeader True if we are to include the xml header. - * @return The xml - * @see ObjectSerializerFactory for a list of supported types - */ - public static String SerializeXmlObject(Object obj, bool includeHeader) { - ObjectSerializerFactory fact = ObjectSerializerFactory.GetInstance(); - StringWriter w = new StringWriter(); - XmlObjectSerializer ser = fact.NewXmlSerializer(w, includeHeader, false); - ser.WriteObject(obj); - ser.Close(true); - return w.ToString(); - } - - /** - * Deserializes the given object from xml - * @param bytes The xml to deserialize - * @param validate True if we are to validate the xml - * @return The object - * @see ObjectSerializerFactory for a list of supported types - */ - public static Object DeserializeXmlObject(String str, bool validate) { - ObjectSerializerFactory fact = ObjectSerializerFactory.GetInstance(); - StringReader source = new StringReader(str); - IList rv = new List(); - fact.DeserializeXmlStream(source, - obj => { - rv.Add(obj); - return true; - },validate); - if ( rv.Count > 0 ) { - return rv[0]; - } - else { - return null; - } - } - - /** - * Clones the given object by serializing it to bytes and then - * deserializing it. - * @param object The object. - * @return A clone of the object - */ - public static object CloneObject(Object obj) { - byte [] bytes = SerializeBinaryObject(obj); - return DeserializeBinaryObject(bytes); - } - - } - - /** - * Callback interface to receive xml objects from a stream of objects. - */ - public delegate bool XmlObjectResultsHandler(Object obj); - - /** - * Interface for writing objects to a stream. - */ - public interface XmlObjectSerializer { - /** - * Writes the next object to the stream. - * @param object The object to write. - * @see ObjectSerializerFactory for a list of supported types. - * @throws ConnectorException if there is more than one object - * and this is not configured for multi-object document. - */ - void WriteObject(Object obj); - - /** - * Flushes the underlying stream. - */ - void Flush(); - - /** - * Adds document end tag and optinally closes the underlying stream - */ - void Close(bool closeUnderlyingStream); - } - - -} diff --git a/DotNetConnectors.sln/Framework/Framework.csproj b/DotNetConnectors.sln/Framework/Framework.csproj deleted file mode 100644 index 4cecaa9d..00000000 --- a/DotNetConnectors.sln/Framework/Framework.csproj +++ /dev/null @@ -1,99 +0,0 @@ - - - - {8B24461B-456A-4032-89A1-CD418F7B5B62} - Debug - AnyCPU - Library - Org.IdentityConnectors.Framework - Framework - v3.5 - False - False - 4 - false - - - prompt - 4 - AnyCPU - bin\Debug\ - true - Full - False - True - DEBUG;TRACE - - - pdbonly - bin\Release\ - False - prompt - 4 - True - False - TRACE - - - False - Auto - 4194304 - AnyCPU - 4096 - - - - - - 3.5 - - - 3.5 - - - - - - - - - - - - - - - - - - - {F140E8DA-52B4-4159-992A-9DA10EA8EEFB} - Common - - - - - - - diff --git a/DotNetConnectors.sln/Framework/Spi.cs b/DotNetConnectors.sln/Framework/Spi.cs deleted file mode 100644 index 974246d0..00000000 --- a/DotNetConnectors.sln/Framework/Spi.cs +++ /dev/null @@ -1,233 +0,0 @@ -/* - * ==================== - * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER. - * - * Copyright 2007-2008 Sun Microsystems, Inc. All rights reserved. - * - * The contents of this file are subject to the terms of the Common Development - * and Distribution License("CDDL") (the "License"). You may not use this file - * except in compliance with the License. - * - * You can obtain a copy of the License at - * http://IdentityConnectors.dev.java.net/legal/license.txt - * See the License for the specific language governing permissions and limitations - * under the License. - * - * When distributing the Covered Code, include this CDDL Header Notice in each file - * and include the License file at identityconnectors/legal/license.txt. - * If applicable, add the following below this CDDL Header, with the fields - * enclosed by brackets [] replaced by your own identifying information: - * "Portions Copyrighted [year] [name of copyright owner]" - * ==================== - */ -using System; -using System.Globalization; -using System.Collections.Generic; -using Org.IdentityConnectors.Common; -using Org.IdentityConnectors.Common.Pooling; -using Org.IdentityConnectors.Framework.Common.Objects; -using Org.IdentityConnectors.Framework.Spi.Operations; -namespace Org.IdentityConnectors.Framework.Spi -{ - #region AttributeNormalizer - /** - * Interface to be implemented by connectors that need - * to normalize certain attributes. This might, for - * example, be used to normalize whitespace within - * DN's to ensure consistent filtering whether that - * filtering is natively on the resource or by the - * connector framework. For connectors implementing - * this interface, the method {@link #normalizeAttribute(ObjectClass, Attribute)} - * will be applied to each of the following: - *
    - *
  1. The filter passed to {@link SearchOp}.
  2. - *
  3. The results returned from {@link SearchOp}.
  4. - *
  5. The results returned from {@link SyncOp}.
  6. - *
  7. The attributes passed to {@link AdvancedUpdateOp}.
  8. - *
  9. The Uid returned from {@link AdvancedUpdateOp}.
  10. - *
  11. The attributes passed to {@link UpdateOp}.
  12. - *
  13. The Uid returned from {@link UpdateOp}.
  14. - *
  15. The attributes passed to {@link CreateOp}.
  16. - *
  17. The Uid returned from {@link CreateOp}.
  18. - *
  19. The Uid passed to {@link DeleteOp}.
  20. - *
- */ - public interface AttributeNormalizer - { - ConnectorAttribute NormalizeAttribute(ObjectClass oclass, ConnectorAttribute attribute); - } - #endregion - - #region Configuration - /// - /// Configuration information for the Connector. - /// - public interface Configuration { - - ConnectorMessages ConnectorMessages{get;set;} - - /** - * Determine if the configuration is valid based on the values set. - */ - void Validate(); - } - #endregion - - #region AbstractConfiguration - public abstract class AbstractConfiguration : Configuration { - - public ConnectorMessages ConnectorMessages{get;set;} - - public abstract void Validate(); - } - #endregion - - #region ConnectorClassAttribute - [AttributeUsage(AttributeTargets.Class,AllowMultiple=false)] - public class ConnectorClassAttribute : System.Attribute { - - private readonly String _connectorDisplayNameKey; - private readonly SafeType _connectorConfigurationClass; - - public ConnectorClassAttribute(String connectorDisplayNameKey, - Type connectorConfigurationClass) { - _connectorDisplayNameKey = connectorDisplayNameKey; - _connectorConfigurationClass = SafeType.ForRawType(connectorConfigurationClass); - } - - public string ConnectorDisplayNameKey { - get { - return _connectorDisplayNameKey; - } - } - - public SafeType ConnectorConfigurationType { - get { - return _connectorConfigurationClass; - } - } - - public string [] MessageCatalogPaths {get;set;} - - } - #endregion - - #region ConfigurationPropertyAttribute - /// - /// The interface is traversed through reflection. This - /// annotation provides a way to override the default configuration operation for - /// each property. - /// - /// - /// - /// public class MyClass : IConfiguration { - /// [ConfigurationPropertionOptions(Confidential=true)] - /// public string MyProperty {get ; set;} - /// } - /// - /// - [AttributeUsage(AttributeTargets.Property)] - public class ConfigurationPropertyAttribute : System.Attribute { - - /// - /// Order in which this property is displayed. - /// - public int Order {get;set;} - /// - /// Is this a confidential property whose value should be - /// encrypted by the application when persisted? - /// - public bool Confidential {get;set;} - /// - /// Is this a required property? - /// - public bool Required {get;set;} - /// - /// Change the default help message key. - /// - public string HelpMessageKey {get;set;} - /// - /// Change the default display message key. - /// - public string DisplayMessageKey {get;set;} - - /** - * List of operations for which this property must be specified. - * This is used for the case where a connector may or may not - * implement certain operations depending in the configuration. - * The default value of "empty array" is special in that - * it means that this property is applicable to all operations. - * MUST be SPI operations - */ - public Type [] OperationTypes {get;set;} - - /** - * List of operations for which this property must be specified. - * This is used for the case where a connector may or may not - * implement certain operations depending in the configuration. - * The default value of "empty array" is special in that - * it means that this property is applicable to all operations. - */ - public SafeType [] Operations { - get { - Type [] types = OperationTypes; - SafeType [] rv = new SafeType[types.Length]; - for ( int i = 0; i < types.Length; i++ ) { - rv[i] = - SafeType.ForRawType(types[i]); - } - return rv; - } - } - - /// - /// Default constructor - /// - public ConfigurationPropertyAttribute() { - Order = 1; - Confidential = false; - Required = false; - HelpMessageKey = null; - DisplayMessageKey = null; - OperationTypes = new Type[0]; - } - } - #endregion - - #region Connector - /// - /// This is the main interface to declare a connector. Developers must implement - /// this interface. The life-cycle for a is as follows - /// is called then any of the operations implemented - /// in the Connector and finally dispose. The and - /// allow for block operations. For instance bulk creates or - /// deletes and the use of before and after actions. Once is - /// called the object is discarded. - /// - public interface Connector : IDisposable { - - /// - /// Initialize the connector with its configuration. For instance in a JDBC - /// Connector this would include the database URL, password, and user. - /// - /// instance of the object implemented by - /// the developer and populated with information - /// in order to initialize the . - void Init(Configuration configuration); - } - #endregion - - #region PoolableConnector - /** - * To be implemented by Connectors that wish to be pooled. - */ - public interface PoolableConnector : Connector { - /** - * Checks to see if the connector is still alive. - * @throws RuntimeException If no longer alive. - */ - void CheckAlive(); - } - #endregion - -} diff --git a/DotNetConnectors.sln/Framework/SpiOperations.cs b/DotNetConnectors.sln/Framework/SpiOperations.cs deleted file mode 100644 index 236f1124..00000000 --- a/DotNetConnectors.sln/Framework/SpiOperations.cs +++ /dev/null @@ -1,412 +0,0 @@ -/* - * ==================== - * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER. - * - * Copyright 2007-2008 Sun Microsystems, Inc. All rights reserved. - * - * The contents of this file are subject to the terms of the Common Development - * and Distribution License("CDDL") (the "License"). You may not use this file - * except in compliance with the License. - * - * You can obtain a copy of the License at - * http://IdentityConnectors.dev.java.net/legal/license.txt - * See the License for the specific language governing permissions and limitations - * under the License. - * - * When distributing the Covered Code, include this CDDL Header Notice in each file - * and include the License file at identityconnectors/legal/license.txt. - * If applicable, add the following below this CDDL Header, with the fields - * enclosed by brackets [] replaced by your own identifying information: - * "Portions Copyrighted [year] [name of copyright owner]" - * ==================== - */ -using System; -using System.Collections.Generic; - -using Org.IdentityConnectors.Common.Security; -using Org.IdentityConnectors.Framework.Common.Objects; -using Org.IdentityConnectors.Framework.Common.Objects.Filters; - -namespace Org.IdentityConnectors.Framework.Spi.Operations -{ - - - /** - * Authenticate an object based on their unique identifier and password. - */ - public interface AuthenticateOp : SPIOperation { - - /** - * Simple authentication with two parameters presumed to be user name and - * password. The {@link Connector} developer is expected to attempt to - * authenticate these credentials natively. If the authentication fails the - * developer should throw a type of {@link RuntimeException} either - * {@link IllegalArgumentException} or if a native exception is available - * and if its of type {@link RuntimeException} simple throw it. If the - * native exception is not a {@link RuntimeException} wrap it in one and - * throw it. This will provide the most detail for logging problem and - * failed attempts. - *

- * The developer is of course encourage to try and throw the most - * informative exception as possible. In that regards there are several - * exceptions provided in the exceptions package. For instance one of the - * most common is {@link InvalidPassword}. - * - * @param username - * the name based credential for authentication. - * @param password - * the password based credential for authentication. - * @throws RuntimeException - * iff native authentication fails. If a native exception if - * available attempt to throw it. - */ - Uid Authenticate(ObjectClass objectClass, String username, GuardedString password, OperationOptions options); - } - - /** - * The {@link Connector} developer is responsible for taking the attributes - * given (which always includes the {@link ObjectClass}) and create an object - * and its {@link Uid}. The {@link Connector} developer must return the - * {@link Uid} so that the caller can refer to the created object. - *

- * The {@link Connector} developer should make a best effort to create the - * object otherwise throw an informative {@link RuntimeException} telling the - * caller why the operation could not be completed. It reasonable to use - * defaults for required {@link Attribute}s as long as they are documented. - * - * @author Will Droste - * @version $Revision $ - * @since 1.0 - */ - public interface CreateOp : SPIOperation { - /** - * The {@link Connector} developer is responsible for taking the attributes - * given (which always includes the {@link ObjectClass}) and create an - * object and its {@link Uid}. The {@link Connector} developer must return - * the {@link Uid} so that the caller can refer to the created object. - * - * @param name - * specifies the name of the object to create. - * @param attrs - * includes all the attributes necessary to create the resource - * object including the {@link ObjectClass} attribute. - * @return the unique id for the object that is created. For instance in - * LDAP this would be the 'dn', for a database this would be the - * primary key, and for 'ActiveDirectory' this would be the GUID. - */ - Uid Create(ObjectClass oclass, ICollection attrs, OperationOptions options); - } - - ///

- /// Deletes an object with the specified Uid and ObjectClass on the - /// resource. - /// - public interface DeleteOp : SPIOperation { - /// - /// Delete the object that the specified Uid identifies (if any). - /// - /// The type of object to delete. - /// The unique identitfier for the object to delete. - /// Throws UnknowUid if the object does not exist. - void Delete(ObjectClass objClass, Uid uid, OperationOptions options); - } - - public interface SchemaOp : SPIOperation { - - /** - * Determines what types of objects this {@link Connector} supports. This - * method is considered an operation since determining supported objects may - * require configuration information and allows this determination to be - * dynamic. - * - * @return basic schema supported by this {@link Connector}. - */ - Schema Schema(); - } - /** - * Operation that runs a script in the environment of the connector. - * (Compare to {@link ScriptOnResourceOp}, which runs a script - * on the target resource that the connector manages.) - * A connector that intends to provide to scripts - * more than is required by the basic contract - * specified in the javadoc for {@link ScriptOnConnectorApiOp} - * should implement this interface. - *

- * Each connector that implements this interface must support - * at least the behavior specified by {@link ScriptOnConnectorApiOp}. - * A connector also may expose additional variables for use by scripts - * and may respond to specific {@link OperationOptions options}. - * Each connector that implements this interface - * must describe in its javadoc as available "for use by connector scripts" - * any such additional variables or supported options. - */ - public interface ScriptOnConnectorOp : SPIOperation { - - /** - * Runs the script request. - * @param request The script and arguments to run. - * @param options Additional options that control how the script is - * run. - * @return The result of the script. The return type must be - * a type that the framework supports for serialization. - * See {@link ObjectSerializerFactory} for a list of supported types. - */ - Object RunScriptOnConnector(ScriptContext request, - OperationOptions options); - } - /** - * Operation that runs a script directly on a target resource. - * (Compare to {@link ScriptOnConnectorOp}, which runs a script - * in the context of a particular connector.) - *

- * A connector that intends to support - * {@link ScriptOnResourceApiOp} - * should implement this interface. Each connector that implements - * this interface must document which script languages the connector supports, - * as well as any supported {@link OperationOptions}. - */ - public interface ScriptOnResourceOp : SPIOperation { - /** - * Run the specified script on the target resource - * that this connector manages. - * @param request The script and arguments to run. - * @param options Additional options that control - * how the script is run. - * @return The result of the script. The return type must be - * a type that the framework supports for serialization. - * See {@link ObjectSerializerFactory} for a list of supported types. - */ - Object RunScriptOnResource(ScriptContext request, - OperationOptions options); - } - - /** - * Implement this interface to allow the Connector to search for resource - * objects. - */ - public interface SearchOp : SPIOperation where T : class { - /** - * Creates a filter translator that will translate - * a specified filter to the native filter. The - * translated filters will be subsequently passed to - * {@link #search(ObjectClass, Object, ResultsHandler)} - * @param oclass The object class for the search. Will never be null. - * @param options - * additional options that impact the way this operation is run. - * If the caller passes null, the framework will convert this into - * an empty set of options, so SPI need not worry - * about this ever being null. - * @return A filter translator. - */ - FilterTranslator CreateFilterTranslator(ObjectClass oclass, OperationOptions options); - /** - * This will be called by ConnectorFacade, once for each native query produced - * by the FilterTranslator. If there is more than one query the results will - * automatically be merged together and duplicates eliminated. NOTE - * that this implies an in-memory data structure that holds a set of - * Uids, so memory usage in the event of multiple queries will be O(N) - * where N is the number of results. That is why it is important that - * the FilterTranslator implement OR if possible. - * @param oclass The object class for the search. Will never be null. - * @param query The native query to run. A value of null means 'return everything for the given object class'. - * @param handler - * Results should be returned to this handler - * @param options - * additional options that impact the way this operation is run. - * If the caller passes null, the framework will convert this into - * an empty set of options, so SPI need not worry - * about this ever being null. - */ - void ExecuteQuery(ObjectClass oclass, T query, ResultsHandler handler, OperationOptions options); - } - /** - * Receive synchronization events from the resource. - * - * @see SyncApiOp - */ - public interface SyncOp : SPIOperation { - /** - * Perform a synchronization. - * - * @param objClass - * The object class to synchronize. Must not be null. - * @param token - * The token representing the last token from the previous sync. - * Should be null if this is the first sync for the given - * resource. - * @param handler - * The result handler Must not be null. - * @param options - * additional options that impact the way this operation is run. - * If the caller passes null, the framework will convert this into - * an empty set of options, so SPI need not worry - * about this ever being null. - */ - void Sync(ObjectClass objClass, SyncToken token, - SyncResultsHandler handler, - OperationOptions options); - /** - * Returns the token corresponding to the latest sync delta. - * This is to support applications that may wish to sync starting - * "now". - * @return The latest token or null if there is no sync data. - */ - SyncToken GetLatestSyncToken(ObjectClass objectClass); - } - - /** - * The developer of a Connector should implement either this interface or the - * {@link UpdateAttributeValuesOp} interface if the Connector will allow an authorized - * caller to update (i.e., modify or replace) objects on the target resource. - *

- * This update method is simpler to implement than {link UpdateAttributeValuesOp}, - * which must handle any of several different types of update that the caller - * may specify. However a true implementation of {link UpdateAttributeValuesOp} - * offers better performance and atomicity semantics. - * - * @author Will Droste - * @version $Revision $ - * @since 1.0 - */ - public interface UpdateOp : SPIOperation { - /** - * Update the object specified by the {@link ObjectClass} and {@link Uid}, - * replacing the current values of each attribute with the values - * provided. - *

- * For each input attribute, replace - * all of the current values of that attribute in the target object with - * the values of that attribute. - *

- * If the target object does not currently contain an attribute that the - * input set contains, then add this - * attribute (along with the provided values) to the target object. - *

- * If the value of an attribute in the input set is - * {@code null}, then do one of the following, depending on - * which is most appropriate for the target: - *

    - *
  • If possible, remove that attribute from the target - * object entirely.
  • - *
  • Otherwise, replace all of the current values of that - * attribute in the target object with a single value of - * {@code null}.
  • - *
- * @param objclass - * the type of object to modify. Will never be null. - * @param uid - * the uid of the object to modify. Will never be null. - * @param replaceAttributes - * set of new {@link Attribute}. the values in this set - * represent the new, merged values to be applied to the object. - * This set may also include {@link OperationalAttributes operational attributes}. - * Will never be null. - * @param options - * additional options that impact the way this operation is run. - * Will never be null. - * @return the {@link Uid} of the updated object in case the update changes - * the formation of the unique identifier. - */ - Uid Update(ObjectClass objclass, - Uid uid, - ICollection replaceAttributes, - OperationOptions options); - } - - /** - * More advanced implementation of {@link UpdateOp} to be implemented by - * connectors that wish to offer better performance and atomicity semantics - * for the methods {@link UpdateApiOp#addAttributeValues(ObjectClass, Uid, Set, OperationOptions)} - * and {@link UpdateApiOp#removeAttributeValues(ObjectClass, Uid, Set, OperationOptions)}. - */ - public interface UpdateAttributeValuesOp : UpdateOp { - - /** - * Update the object specified by the {@link ObjectClass} and {@link Uid}, - * adding to the current values of each attribute the values provided. - *

- * For each attribute that the input set contains, add to - * the current values of that attribute in the target object all of the - * values of that attribute in the input set. - *

- * NOTE that this does not specify how to handle duplicate values. - * The general assumption for an attribute of a {@code ConnectorObject} - * is that the values for an attribute may contain duplicates. - * Therefore, in general simply append the provided values - * to the current value for each attribute. - *

- * @param objclass - * the type of object to modify. Will never be null. - * @param uid - * the uid of the object to modify. Will never be null. - * @param valuesToAdd - * set of {@link Attribute} deltas. The values for the attributes - * in this set represent the values to add to attributes in the object. - * merged. This set will never include {@link OperationalAttributes operational attributes}. - * Will never be null. - * @param options - * additional options that impact the way this operation is run. - * Will never be null. - * @return the {@link Uid} of the updated object in case the update changes - * the formation of the unique identifier. - */ - Uid AddAttributeValues(ObjectClass objclass, - Uid uid, - ICollection valuesToAdd, - OperationOptions options); - - /** - * Update the object specified by the {@link ObjectClass} and {@link Uid}, - * removing from the current values of each attribute the values provided. - *

- * For each attribute that the input set contains, - * remove from the current values of that attribute in the target object - * any value that matches one of the values of the attribute from the input set. - *

- * NOTE that this does not specify how to handle unmatched values. - * The general assumption for an attribute of a {@code ConnectorObject} - * is that the values for an attribute are merely representational state. - * Therefore, the implementer should simply ignore any provided value - * that does not match a current value of that attribute in the target - * object. Deleting an unmatched value should always succeed. - * @param objclass - * the type of object to modify. Will never be null. - * @param uid - * the uid of the object to modify. Will never be null. - * @param valuesToRemove - * set of {@link Attribute} deltas. The values for the attributes - * in this set represent the values to remove from attributes in the object. - * merged. This set will never include {@link OperationalAttributes operational attributes}. - * Will never be null. - * @param options - * additional options that impact the way this operation is run. - * Will never be null.. - * @return the {@link Uid} of the updated object in case the update changes - * the formation of the unique identifier. - */ - Uid RemoveAttributeValues(ObjectClass objclass, - Uid uid, - ICollection valuesToRemove, - OperationOptions options); - - } - - public interface TestOp : SPIOperation { - - /** - * Tests connectivity and validity of the {@link Configuration}. - * - * @throws RuntimeException - * iff the {@link Configuration} is not valid or a - * {@link Connection} to the resource could not be established. - */ - void Test(); - } - - /** - * Tagging interface for the {@link Connector} SPI. - */ - public interface SPIOperation { - - } -} diff --git a/DotNetConnectors.sln/Framework/Test.cs b/DotNetConnectors.sln/Framework/Test.cs deleted file mode 100644 index ea50962a..00000000 --- a/DotNetConnectors.sln/Framework/Test.cs +++ /dev/null @@ -1,296 +0,0 @@ -/* - * ==================== - * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER. - * - * Copyright 2007-2008 Sun Microsystems, Inc. All rights reserved. - * - * The contents of this file are subject to the terms of the Common Development - * and Distribution License("CDDL") (the "License"). You may not use this file - * except in compliance with the License. - * - * You can obtain a copy of the License at - * http://IdentityConnectors.dev.java.net/legal/license.txt - * See the License for the specific language governing permissions and limitations - * under the License. - * - * When distributing the Covered Code, include this CDDL Header Notice in each file - * and include the License file at identityconnectors/legal/license.txt. - * If applicable, add the following below this CDDL Header, with the fields - * enclosed by brackets [] replaced by your own identifying information: - * "Portions Copyrighted [year] [name of copyright owner]" - * ==================== - */ -using System; - -using System.IO; -using System.Xml; -using System.Collections; -using System.Collections.Generic; -using System.Reflection; -using System.Runtime.CompilerServices; -using Org.IdentityConnectors.Common; -using Org.IdentityConnectors.Framework.Api; -using Org.IdentityConnectors.Framework.Api.Operations; -using Org.IdentityConnectors.Framework.Common; -using Org.IdentityConnectors.Framework.Common.Objects; -using Org.IdentityConnectors.Framework.Common.Objects.Filters; -using Org.IdentityConnectors.Framework.Spi; -using Org.IdentityConnectors.Framework.Spi.Operations; - -namespace Org.IdentityConnectors.Framework.Test -{ - public sealed class ToListResultsHandler { - private IList _objects - = new List(); - public bool Handle(ConnectorObject obj) { - _objects.Add(obj); - return true; - } - - public IList Objects { - get { - return _objects; - } - } - } - public abstract class TestHelpers { - - /** - * Method for convenient testing of local connectors. - */ - public static APIConfiguration CreateTestConfiguration(SafeType clazz, - Configuration config) { - return GetInstance().CreateTestConfigurationImpl(clazz, config); - } - - /** - * Creates an dummy message catalog ideal for unit testing. - * All messages are formatted as follows: - *

- * message-key: arg0.toString(), ..., argn.toString - * @return A dummy message catalog. - */ - public static ConnectorMessages CreateDummyMessages() { - return GetInstance().CreateDummyMessagesImpl(); - } - - public static IList SearchToList(SearchApiOp search, - ObjectClass oclass, - Filter filter) { - return SearchToList(search, oclass, filter, null); - } - - public static IList SearchToList(SearchApiOp search, - ObjectClass oclass, - Filter filter, - OperationOptions options) { - ToListResultsHandler handler = new - ToListResultsHandler(); - search.Search(oclass,filter, handler.Handle,options); - return handler.Objects; - } - /** - * Performs a raw, unfiltered search at the SPI level, - * eliminating duplicates from the result set. - * @param search The search SPI - * @param oclass The object class - passed through to - * connector so it may be null if the connecor - * allowing it to be null. (This is convenient for - * unit tests, but will not be the case in general) - * @param filter The filter to search on - * @param options The options - may be null - will - * be cast to an empty OperationOptions - * @return The list of results. - */ - public static IList SearchToList(SearchOp search, - ObjectClass oclass, - Filter filter) where T : class{ - return SearchToList(search,oclass,filter,null); - } - /** - * Performs a raw, unfiltered search at the SPI level, - * eliminating duplicates from the result set. - * @param search The search SPI - * @param oclass The object class - passed through to - * connector so it may be null if the connecor - * allowing it to be null. (This is convenient for - * unit tests, but will not be the case in general) - * @param filter The filter to search on - * @param options The options - may be null - will - * be cast to an empty OperationOptions - * @return The list of results. - */ - public static IList SearchToList(SearchOp search, - ObjectClass oclass, - Filter filter, - OperationOptions options) where T : class{ - ToListResultsHandler handler = new - ToListResultsHandler(); - Search(search,oclass,filter, handler.Handle, options); - return handler.Objects; - } - - /** - * Performs a raw, unfiltered search at the SPI level, - * eliminating duplicates from the result set. - * @param search The search SPI - * @param oclass The object class - passed through to - * connector so it may be null if the connecor - * allowing it to be null. (This is convenient for - * unit tests, but will not be the case in general) - * @param filter The filter to search on - * @param handler The result handler - * @param options The options - may be null - will - * be cast to an empty OperationOptions - */ - public static void Search(SearchOp search, - ObjectClass oclass, - Filter filter, - ResultsHandler handler, - OperationOptions options) where T : class { - GetInstance().SearchImpl(search, oclass, filter, handler, options); - } - - - //At some point we might make this pluggable, but for now, hard-code - private const String IMPL_NAME = - "Org.IdentityConnectors.Framework.Impl.Test.TestHelpersImpl"; - private static readonly object LOCK = new object(); - private static TestHelpers _instance; - - /** - * Returns the instance of this factory. - * @return The instance of this factory - */ - private static TestHelpers GetInstance() { - lock(LOCK) { - if (_instance == null) { - SafeType type = FrameworkInternalBridge.LoadType(IMPL_NAME); - _instance = type.CreateInstance(); - } - return _instance; - } - } - - - abstract protected APIConfiguration CreateTestConfigurationImpl(SafeType clazz, - Configuration config); - abstract protected void SearchImpl(SearchOp search, - ObjectClass oclass, - Filter filter, - ResultsHandler handler, - OperationOptions options) where T : class; - - - private static IDictionary _properties = null; - private static readonly string PREFIX = Environment.GetEnvironmentVariable("USERPROFILE") + "/.connectors/"; - public static readonly string GLOBAL_PROPS = "connectors.xml"; - - [MethodImpl(MethodImplOptions.NoInlining)] - public static string GetProperty(string key, string def) { - Assembly asm = Assembly.GetCallingAssembly(); - return CollectionUtil.GetValue(GetProperties(asm), key, def); - } - - private static IDictionary GetProperties(Assembly asm) { - lock(LOCK) { - if (_properties == null) { - _properties = LoadProperties(asm); - } - } - // create a new instance so its not mutable - return CollectionUtil.NewReadOnlyDictionary(_properties); - } - - private static IDictionary LoadProperties(Assembly asm) { - const string ERR = "Unable to load optional XML properties file: "; - string fn = null; - IDictionary props = null; - IDictionary ret = new Dictionary(); - - //load global properties file - try { - fn = Path.Combine(PREFIX, GLOBAL_PROPS); - props = LoadPropertiesFile(fn); - CollectionUtil.AddAll(ret, props); - } catch (Exception e) { - TraceUtil.TraceException(ERR + fn, e); - } - - // load the project properties file - try { - fn = Path.Combine(Environment.CurrentDirectory, "project.xml"); - props = LoadPropertiesFile(fn); - CollectionUtil.AddAll(ret, props); - } catch (Exception e) { - TraceUtil.TraceException(ERR + fn, e); - } - - // private settings are in the "assembly name" folder, as defined in the assembly - string prjName = asm.GetName().Name; - if (!StringUtil.IsBlank(prjName)) { - //load private project properties file - try { - fn = Path.Combine(PREFIX, prjName + "/project.xml"); - props = LoadPropertiesFile(fn); - CollectionUtil.AddAll(ret, props); - } catch (IOException e) { - TraceUtil.TraceException(ERR + fn, e); - } - - string cfg = Environment.GetEnvironmentVariable("configuration"); - if(!StringUtil.IsBlank(cfg)) { - try { - // load a config-specific properties file - fn = Path.Combine(PREFIX, prjName + "/" + cfg + "/project.xml"); - props = LoadPropertiesFile(fn); - CollectionUtil.AddAll(ret, props); - } catch (IOException e) { - TraceUtil.TraceException(ERR + fn, e); - } - } - } else { - TraceUtil.TraceException("Could not infer assembly name.", new Exception()); - } - - // load the environment variables - foreach (DictionaryEntry entry in Environment.GetEnvironmentVariables()) { - ret[entry.Key.ToString()] = entry.Value.ToString(); - } - return ret; - } - - ///

- /// Format for the xml file is simple <property name='' value=''/> - /// - /// - /// - public static IDictionary LoadPropertiesFile(string filename) { - IDictionary ret = new Dictionary(); - //Environment. - XmlTextReader reader = null; - try { - // Load the reader with the data file and ignore all white space nodes. - reader = new XmlTextReader(filename); - reader.WhitespaceHandling = WhitespaceHandling.None; - // Parse the file and display each of the nodes. - while (reader.Read()) { - if (reader.NodeType == XmlNodeType.Element && - reader.Name.Equals("property")) { - string name = reader.GetAttribute("name"); - string xmlValue = reader.GetAttribute("value"); - if (!StringUtil.IsBlank(name) && xmlValue != null) { - ret[name] = xmlValue; - } - } - } - } finally { - if (reader!=null) - reader.Close(); - } - return ret; - } - - abstract protected ConnectorMessages CreateDummyMessagesImpl(); - } -} diff --git a/DotNetConnectors.sln/FrameworkInternal/Api.cs b/DotNetConnectors.sln/FrameworkInternal/Api.cs deleted file mode 100644 index 09f7f320..00000000 --- a/DotNetConnectors.sln/FrameworkInternal/Api.cs +++ /dev/null @@ -1,889 +0,0 @@ -/* - * ==================== - * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER. - * - * Copyright 2007-2008 Sun Microsystems, Inc. All rights reserved. - * - * The contents of this file are subject to the terms of the Common Development - * and Distribution License("CDDL") (the "License"). You may not use this file - * except in compliance with the License. - * - * You can obtain a copy of the License at - * http://IdentityConnectors.dev.java.net/legal/license.txt - * See the License for the specific language governing permissions and limitations - * under the License. - * - * When distributing the Covered Code, include this CDDL Header Notice in each file - * and include the License file at identityconnectors/legal/license.txt. - * If applicable, add the following below this CDDL Header, with the fields - * enclosed by brackets [] replaced by your own identifying information: - * "Portions Copyrighted [year] [name of copyright owner]" - * ==================== - */ -using System; - -using Org.IdentityConnectors.Framework.Api; -using Org.IdentityConnectors.Framework.Api.Operations; -using Org.IdentityConnectors.Framework.Common; -using Org.IdentityConnectors.Framework.Common.Objects; -using Org.IdentityConnectors.Framework.Common.Objects.Filters; -using Org.IdentityConnectors.Framework.Common.Serializer; -using Org.IdentityConnectors.Framework.Impl.Api.Local; -using Org.IdentityConnectors.Framework.Impl.Api.Remote; -using Org.IdentityConnectors.Framework.Impl.Serializer.Binary; -using Org.IdentityConnectors.Common; -using Org.IdentityConnectors.Common.Pooling; -using Org.IdentityConnectors.Common.Proxy; -using Org.IdentityConnectors.Common.Security; -using System.Linq; -using System.Collections.Generic; -using System.Globalization; -using System.Resources; -using System.Reflection; -using System.Reflection.Emit; -using System.Diagnostics; -using System.Text; - -namespace Org.IdentityConnectors.Framework.Impl.Api -{ - #region ConfigurationPropertyImpl - /// - /// Internal class, public only for unit tests - /// - public class ConfigurationPropertyImpl : ConfigurationProperty { - - private ICollection> _operations; - - public ICollection> Operations { - get { - return _operations; - } - set { - _operations = CollectionUtil.NewReadOnlySet(value); - } - } - - public ConfigurationPropertiesImpl Parent { get; set; } - - public int Order { get; set; } - - public string Name { get; set; } - - public string HelpMessageKey { get; set; } - - public string DisplayMessageKey { get; set; } - - public string GetHelpMessage(string def) { - return FormatMessage(HelpMessageKey,def); - } - - public string GetDisplayName(string def) { - return FormatMessage(DisplayMessageKey,def); - } - - public object Value { get; set; } - - public Type ValueType { get; set; } - - public bool IsConfidential { get; set; } - - public bool IsRequired { get; set; } - - public override int GetHashCode() - { - return Name.GetHashCode(); - } - - public override bool Equals(Object o) { - ConfigurationPropertyImpl other = o as ConfigurationPropertyImpl; - if ( other != null ) { - if (!Name.Equals(other.Name)) { - return false; - } - if (!CollectionUtil.Equals(Value,other.Value)) { - return false; - } - if (Order != other.Order) { - return false; - } - if (!CollectionUtil.Equals(HelpMessageKey,other.HelpMessageKey)) { - return false; - } - if (!CollectionUtil.Equals(DisplayMessageKey,other.DisplayMessageKey)) { - return false; - } - if (IsConfidential != other.IsConfidential) { - return false; - } - if (IsRequired != other.IsRequired) { - return false; - } - if (!CollectionUtil.Equals(ValueType,other.ValueType)) { - return false; - } - if (!CollectionUtil.Equals(_operations,other._operations)) { - return false; - } - - return true; - } - return false; - } - private String FormatMessage(String key, String dflt, params object [] args) { - APIConfigurationImpl apiConfig = Parent.Parent; - ConnectorMessages messages = - apiConfig.ConnectorInfo.Messages; - return messages.Format(key, dflt, args); - } - } - #endregion - - #region ConfigurationPropertiesImpl - /// - /// Internal class, public only for unit tests - /// - public class ConfigurationPropertiesImpl : ConfigurationProperties { - //properties, sorted by "order" - private IList _properties; - //property names, sorted by "order - private IList _propertyNames; - private IDictionary _propertiesByName; - - public IList Properties { - get { - return _properties; - } - set { - ConfigurationPropertyImpl [] arr = - value == null ? new ConfigurationPropertyImpl[0] : value.ToArray(); - - Array.Sort(arr, - (p1,p2)=> {return p1.Order.CompareTo(p2.Order);}); - _properties = - CollectionUtil.NewReadOnlyList(arr); - IList propertyNames = new List(); - IDictionary propertiesByName = - new Dictionary(); - foreach (ConfigurationPropertyImpl property in _properties) { - propertyNames.Add(property.Name); - propertiesByName[property.Name] = property; - property.Parent = this; - } - _propertyNames = CollectionUtil.AsReadOnlyList(propertyNames); - _propertiesByName = CollectionUtil.AsReadOnlyDictionary(propertiesByName); - } - } - public ConfigurationProperty GetProperty(String name) { - return CollectionUtil.GetValue(_propertiesByName,name,null); - } - public IList PropertyNames { - get { - return _propertyNames; - } - } - public APIConfigurationImpl Parent { get; set; } - public void SetPropertyValue(String name, object val) { - ConfigurationProperty property = GetProperty(name); - if ( property == null ) { - String MSG = "Property '"+name+"' does not exist."; - throw new ArgumentException(MSG); - } - property.Value = val; - } - - public override int GetHashCode() - { - return CollectionUtil.GetHashCode(_properties); - } - - public override bool Equals(object o) { - ConfigurationPropertiesImpl other = o as ConfigurationPropertiesImpl; - if ( other != null ) { - return CollectionUtil.SetsEqual(_properties, - other._properties); - } - return false; - } - } - #endregion - - #region APIConfigurationImpl - /// - /// Internal class, public only for unit tests - /// - public class APIConfigurationImpl : APIConfiguration { - - private ObjectPoolConfiguration _connectorPooling; - - private ConfigurationPropertiesImpl _configurationProperties; - private ICollection> _supportedOperations = - CollectionUtil.NewReadOnlySet>(new SafeType[0]); - - private IDictionary, int> _timeoutMap = - new Dictionary, int>(); - - public ConfigurationProperties ConfigurationProperties { - get { - return _configurationProperties; - } - set { - if (_configurationProperties != null) { - _configurationProperties.Parent = null; - } - _configurationProperties = (ConfigurationPropertiesImpl)value; - if (_configurationProperties != null) { - _configurationProperties.Parent = this; - } - } - } - public IDictionary, int> TimeoutMap { - get { - return _timeoutMap; - } - set { - _timeoutMap = value; - } - } - public bool IsConnectorPoolingSupported { get; set;} - public ObjectPoolConfiguration ConnectorPoolConfiguration - { - get { - if (_connectorPooling == null) { - _connectorPooling = new ObjectPoolConfiguration(); - } - return _connectorPooling; - } - set { - _connectorPooling = value; - } - } - public ICollection> SupportedOperations { - get { - return _supportedOperations; - } - set { - _supportedOperations = CollectionUtil.NewReadOnlySet>(value); - } - } - - public int GetTimeout(SafeType operation) { - return CollectionUtil.GetValue(_timeoutMap,operation, - APIConstants.NO_TIMEOUT); - } - public void SetTimeout(SafeType operation, int timeout) { - _timeoutMap[operation] = timeout; - } - - public AbstractConnectorInfo ConnectorInfo { get; set; } - - public int ProducerBufferSize { get; set; } - public APIConfigurationImpl() { - ProducerBufferSize = 100; - } - } - #endregion - - #region AbstractConnectorInfo - /// - /// internal class, public only for unit tests - /// - public class AbstractConnectorInfo : ConnectorInfo { - - private APIConfigurationImpl _defaultAPIConfiguration; - - - public string GetConnectorDisplayName() { - return Messages.Format(ConnectorDisplayNameKey, - ConnectorKey.ConnectorName); - } - - public ConnectorKey ConnectorKey { get; set; } - - public string ConnectorDisplayNameKey { get; set; } - - public ConnectorMessages Messages { get; set; } - - public APIConfigurationImpl DefaultAPIConfiguration { - get { - return _defaultAPIConfiguration; - } - set { - if ( value != null ) { - value.ConnectorInfo = this; - } - _defaultAPIConfiguration = value; - } - } - - public APIConfiguration CreateDefaultAPIConfiguration() { - APIConfigurationImpl rv = - (APIConfigurationImpl) - SerializerUtil.CloneObject(_defaultAPIConfiguration); - rv.ConnectorInfo = this; - return rv; - } - } - #endregion - - #region ConnectorMessagesImpl - /// - /// internal class, public only for unit tests - /// - public class ConnectorMessagesImpl : ConnectorMessages { - - private IDictionary> - _catalogs = new Dictionary>(); - - public String Format(String key, String dflt, params object [] args) { - if ( key == null ) { - return dflt; - } - CultureInfo locale = CultureInfo.CurrentUICulture; - if ( locale == null ) { - locale = CultureInfo.CurrentCulture; - } - if ( dflt == null ) { - dflt = key; - } - CultureInfo foundCulture = locale; - IDictionary - catalog = CollectionUtil.GetValue(_catalogs,foundCulture,null); - //check neutral culture - if ( catalog == null ) { - foundCulture = foundCulture.Parent; - catalog = CollectionUtil.GetValue(_catalogs,foundCulture,null); - } - //check invariant culture - if ( catalog == null ) { - foundCulture = foundCulture.Parent; - catalog = CollectionUtil.GetValue(_catalogs,foundCulture,null); - } - String message = null; - if ( catalog != null ) { - message = CollectionUtil.GetValue(catalog,key,null); - } - if ( message == null ) { - message = GetFrameworkMessage(locale,key); - } - if ( message == null ) { - return dflt; - } - else { - //TODO: think more about this since the formatting - //is slightly different than Java - return String.Format(foundCulture,message,args); - } - } - - private String GetFrameworkMessage(CultureInfo culture, String key) - { - ResourceManager manager = - new ResourceManager("Org.IdentityConnectors.Resources", - typeof(ConnectorMessagesImpl).Assembly); - String contents = (String)manager.GetObject(key,culture); - return contents; - } - - public IDictionary> Catalogs { - get { - return _catalogs; - } - set { - if ( value == null ) { - _catalogs = - new Dictionary>(); - } - else { - _catalogs = value; - } - } - } - } - #endregion - - #region ConnectorInfoManagerFactoryImpl - public sealed class ConnectorInfoManagerFactoryImpl : - ConnectorInfoManagerFactory { - private class RemoteManagerKey { - private readonly String _host; - private readonly int _port; - - public RemoteManagerKey(RemoteFrameworkConnectionInfo info) { - _host = info.Host; - _port = info.Port; - } - - public override bool Equals(Object o) { - if ( o is RemoteManagerKey ) { - RemoteManagerKey other = (RemoteManagerKey)o; - if (!_host.Equals(other._host)) { - return false; - } - if (_port != other._port) { - return false; - } - return true; - } - return false; - } - - public override int GetHashCode() { - return _host.GetHashCode()^_port; - } - - } - - private Object LOCAL_LOCK = new Object(); - private Object REMOTE_LOCK = new Object(); - - private ConnectorInfoManager - _localManagerCache; - - private IDictionary - _remoteManagerCache = new Dictionary(); - - public ConnectorInfoManagerFactoryImpl() { - - } - - - - public override void ClearRemoteCache() { - lock (REMOTE_LOCK) { - _remoteManagerCache.Clear(); - } - } - - - public override ConnectorInfoManager GetLocalManager() - { - lock (LOCAL_LOCK) { - ConnectorInfoManager rv = _localManagerCache; - if ( rv == null ) { - rv = new LocalConnectorInfoManagerImpl(); - } - _localManagerCache = rv; - return rv; - } - } - - public override ConnectorInfoManager GetRemoteManager(RemoteFrameworkConnectionInfo info) - { - RemoteManagerKey key = new RemoteManagerKey(info); - lock (REMOTE_LOCK) { - RemoteConnectorInfoManagerImpl rv = CollectionUtil.GetValue(_remoteManagerCache,key,null); - if ( rv == null ) { - rv = new RemoteConnectorInfoManagerImpl(info); - } - _remoteManagerCache[key] = rv; - return rv.Derive(info); - } - } - - } - #endregion - - #region AbstractConnectorFacade - internal abstract class AbstractConnectorFacade : ConnectorFacade { - - private readonly APIConfigurationImpl _configuration; - - - /** - * Builds up the maps of supported operations and calls. - */ - public AbstractConnectorFacade(APIConfigurationImpl configuration) { - Assertions.NullCheck(configuration,"configuration"); - //clone in case application tries to modify - //after the fact. this is necessary to - //ensure thread-safety of a ConnectorFacade - //also, configuration is used as a key in the - //pool, so it is important that it not be modified. - _configuration = (APIConfigurationImpl)SerializerUtil.CloneObject(configuration); - //parent ref not included in the clone - _configuration.ConnectorInfo=(configuration.ConnectorInfo); - } - - /** - * Return an instance of an API operation. - * - * @return null if the operation is not support otherwise - * return an instance of the operation. - * @see com.sun.openconnectors.framework.api.ConnectorFacade#getOperation(java.lang.Class) - */ - public APIOperation GetOperation(SafeType api) { - if (!SupportedOperations.Contains(api)) { - return null; - } - return GetOperationImplementation(api); - } - - /** - * {@inheritDoc} - */ - public ICollection> SupportedOperations { - get { - return _configuration.SupportedOperations; - } - } - - // ======================================================================= - // Operation API Methods - // ======================================================================= - /** - * {@inheritDoc} - */ - public Schema Schema() { - return ((SchemaApiOp) this.GetOperationCheckSupported(SafeType.Get())) - .Schema(); - } - - /** - * {@inheritDoc} - */ - public Uid Create(ObjectClass oclass, ICollection attrs, OperationOptions options) { - CreateApiOp op = ((CreateApiOp) GetOperationCheckSupported(SafeType.Get())); - return op.Create(oclass,attrs,options); - } - - /** - * {@inheritDoc} - */ - public void Delete(ObjectClass objClass, Uid uid, OperationOptions options) { - ((DeleteApiOp) - this.GetOperationCheckSupported(SafeType.Get())) - .Delete(objClass, uid, options); - } - - /** - * {@inheritDoc} - */ - public void Search(ObjectClass oclass,Filter filter, ResultsHandler handler, OperationOptions options) { - ((SearchApiOp) this.GetOperationCheckSupported(SafeType.Get())).Search( - oclass,filter, handler, options); - } - - /** - * {@inheritDoc} - */ - public Uid Update(ObjectClass objclass, Uid uid, ICollection attrs, OperationOptions options) { - return ((UpdateApiOp) this.GetOperationCheckSupported(SafeType.Get())) - .Update(objclass, uid, attrs, options); - } - - /** - * {@inheritDoc} - */ - public Uid AddAttributeValues( - ObjectClass objclass, - Uid uid, - ICollection attrs, - OperationOptions options) { - return ((UpdateApiOp) this.GetOperationCheckSupported(SafeType.Get())) - .AddAttributeValues(objclass, uid, attrs, options); - } - - /** - * {@inheritDoc} - */ - public Uid RemoveAttributeValues( - ObjectClass objclass, - Uid uid, - ICollection attrs, - OperationOptions options) { - return ((UpdateApiOp) this.GetOperationCheckSupported(SafeType.Get())) - .RemoveAttributeValues(objclass, uid, attrs, options); - } - - /** - * {@inheritDoc} - */ - public Uid Authenticate(ObjectClass objectClass,String username, GuardedString password, OperationOptions options) { - return ((AuthenticationApiOp) this - .GetOperationCheckSupported(SafeType.Get())).Authenticate( - objectClass,username, password, options); - } - - /** - * {@inheritDoc} - */ - public ConnectorObject GetObject(ObjectClass objClass, Uid uid, OperationOptions options) { - return ((GetApiOp) this.GetOperationCheckSupported(SafeType.Get())) - .GetObject(objClass, uid, options); - } - /** - * {@inheritDoc} - */ - public Object RunScriptOnConnector(ScriptContext request, - OperationOptions options) { - return ((ScriptOnConnectorApiOp) this - .GetOperationCheckSupported(SafeType.Get())) - .RunScriptOnConnector(request, options); - } - - /** - * {@inheritDoc} - */ - public Object RunScriptOnResource(ScriptContext request, - OperationOptions options) { - return ((ScriptOnResourceApiOp) this - .GetOperationCheckSupported(SafeType.Get())) - .RunScriptOnResource(request, options); - } - - /** - * {@inheritDoc} - */ - public void Test() { - ((TestApiOp) this.GetOperationCheckSupported(SafeType.Get())).Test(); - } - - /** - * {@inheritDoc} - */ - public void Validate() { - ((ValidateApiOp) this.GetOperationCheckSupported(SafeType.Get())).Validate(); - } - - public void Sync(ObjectClass objClass, SyncToken token, - SyncResultsHandler handler, - OperationOptions options) { - ((SyncApiOp)this.GetOperationCheckSupported(SafeType.Get())) - .Sync(objClass, token, handler, options); - } - - public SyncToken GetLatestSyncToken(ObjectClass objectClass) { - return ((SyncApiOp)this.GetOperationCheckSupported(SafeType.Get())) - .GetLatestSyncToken(objectClass); - } - - private APIOperation GetOperationCheckSupported(SafeType api) { - // check if this operation is supported. - if (!SupportedOperations.Contains(api)) { - String MSG = "Operation ''{0}'' not supported."; - String str = String.Format(MSG, api); - throw new InvalidOperationException(str); - } - return GetOperationImplementation(api); - } - - /** - * Gets the implementation of the given operation - * @param api The operation to implement. - * @return The implementation - */ - protected abstract APIOperation GetOperationImplementation(SafeType api); - - protected APIConfigurationImpl GetAPIConfiguration() { - return _configuration; - } - - /** - * Creates a new {@link APIOperation} proxy given a handler. - */ - protected APIOperation NewAPIOperationProxy(SafeType api, InvocationHandler handler) { - return (APIOperation) Proxy.NewProxyInstance(api.RawType, handler); - } - - private static bool LOGGINGPROXY_ENABLED; - static AbstractConnectorFacade() { - string enabled = System.Configuration. - ConfigurationManager.AppSettings.Get("logging.proxy"); - LOGGINGPROXY_ENABLED = StringUtil.IsTrue(enabled); - } - - protected APIOperation CreateLoggingProxy(SafeType api, APIOperation target) { - APIOperation ret = target; - if (LOGGINGPROXY_ENABLED) { - LoggingProxy logging = new LoggingProxy(api, target); - ret = NewAPIOperationProxy(api, logging); - } - return ret; - } - } - #endregion - - #region ConnectorFacadeFactoryImpl - public class ConnectorFacadeFactoryImpl : ConnectorFacadeFactory { - - - - - /** - * {@inheritDoc} - */ - public override ConnectorFacade NewInstance(APIConfiguration config) { - ConnectorFacade ret = null; - APIConfigurationImpl impl = (APIConfigurationImpl) config; - AbstractConnectorInfo connectorInfo = impl.ConnectorInfo; - if ( connectorInfo is LocalConnectorInfoImpl ) { - LocalConnectorInfoImpl localInfo = - (LocalConnectorInfoImpl)connectorInfo; - // create a new Provisioner.. - ret = new LocalConnectorFacadeImpl(localInfo,impl); - } - else { - ret = new RemoteConnectorFacadeImpl(impl); - } - return ret; - } - - - /** - * Dispose of all object pools and other resources associated with this - * class. - */ - public override void Dispose() { - ConnectorPoolManager.Dispose(); - } - - } - #endregion - - #region ObjectStreamHandler - internal interface ObjectStreamHandler { - bool Handle(Object obj); - } - #endregion - - #region StreamHandlerUtil - internal static class StreamHandlerUtil { - - /** - * Adapts from a ObjectStreamHandler to a ResultsHandler - */ - private class ResultsHandlerAdapter { - private readonly ObjectStreamHandler _target; - public ResultsHandlerAdapter(ObjectStreamHandler target) { - _target = target; - } - public bool Handle(ConnectorObject obj) { - return _target.Handle(obj); - } - } - - /** - * Adapts from a ObjectStreamHandler to a SyncResultsHandler - */ - private class SyncResultsHandlerAdapter { - private readonly ObjectStreamHandler _target; - public SyncResultsHandlerAdapter(ObjectStreamHandler target) { - _target = target; - } - public bool Handle(SyncDelta obj) { - return _target.Handle(obj); - } - } - - /** - * Adapts from a ObjectStreamHandler to a SyncResultsHandler - */ - private class ObjectStreamHandlerAdapter : ObjectStreamHandler { - private readonly Type _targetInterface; - private readonly Object _target; - public ObjectStreamHandlerAdapter(Type targetInterface, Object target) { - Assertions.NullCheck(targetInterface, "targetInterface"); - Assertions.NullCheck(target, "target"); - if (!targetInterface.IsAssignableFrom(target.GetType())) { - throw new ArgumentException("Target" +targetInterface+" "+target); - } - if (!IsAdaptableToObjectStreamHandler(targetInterface)) { - throw new ArgumentException("Target interface not supported: "+targetInterface); - } - _targetInterface = targetInterface; - _target = target; - } - public bool Handle(Object obj) { - if (_targetInterface.Equals( typeof(ResultsHandler) )) { - return ((ResultsHandler)_target)((ConnectorObject)obj); - } - else if (_targetInterface.Equals(typeof(SyncResultsHandler))) { - return ((SyncResultsHandler)_target)((SyncDelta)obj); - } - else { - throw new InvalidOperationException("Unhandled case: "+_targetInterface); - } - } - } - - public static bool IsAdaptableToObjectStreamHandler(Type clazz) { - return ( typeof(ResultsHandler).IsAssignableFrom(clazz) || - typeof(SyncResultsHandler).IsAssignableFrom(clazz)); - } - - public static ObjectStreamHandler AdaptToObjectStreamHandler(Type interfaceType, - Object target) { - return new ObjectStreamHandlerAdapter(interfaceType,target); - } - public static Object AdaptFromObjectStreamHandler(Type interfaceType, - ObjectStreamHandler target) { - if (interfaceType.Equals( typeof(ResultsHandler) ) ){ - return new ResultsHandler(new ResultsHandlerAdapter(target).Handle); - } - else if (interfaceType.Equals( typeof(SyncResultsHandler) ) ) { - return new SyncResultsHandler(new SyncResultsHandlerAdapter(target).Handle); - } - else { - throw new InvalidOperationException("Unhandled case: "+interfaceType); - } - } - - } - #endregion - - #region LoggingProxy - public class LoggingProxy : InvocationHandler { - - private readonly SafeType _op; - private readonly object _target; - - public LoggingProxy(SafeType api, object target) { - _op = api; - _target = target; - } - /// - /// Log all operations. - /// - public Object Invoke(Object proxy, MethodInfo method, Object [] args) { - //do not proxy equals, hashCode, toString - if (method.DeclaringType.Equals(typeof(object))) { - return method.Invoke(this, args); - } - StringBuilder bld = new StringBuilder(); - bld.Append("Enter: "); - AddMethodName(bld, method); - bld.Append('('); - for (int i=0; args != null && i < args.Length; i++) { - if (i != 0) { - bld.Append(", "); - } - bld.Append('{').Append(i).Append('}'); - } - bld.Append(')'); - // write out trace header - Trace.TraceInformation(bld.ToString(), args); - // invoke the method - try { - object ret = method.Invoke(_target, args); - // clear out buffer. - bld.Length = 0; - bld.Append("Return: "); - AddMethodName(bld, method); - bld.Append("({0})"); - Trace.TraceInformation(bld.ToString(), ret); - return ret; - } catch (TargetInvocationException e) { - Exception root = e.InnerException; - throw root; - } - } - - private void AddMethodName(StringBuilder bld, MethodInfo method) { - bld.Append(_op.RawType.Name); - bld.Append('.'); - bld.Append(method.Name); - } - } - #endregion - -} diff --git a/DotNetConnectors.sln/FrameworkInternal/ApiLocal.cs b/DotNetConnectors.sln/FrameworkInternal/ApiLocal.cs deleted file mode 100644 index 6f78e65e..00000000 --- a/DotNetConnectors.sln/FrameworkInternal/ApiLocal.cs +++ /dev/null @@ -1,1034 +0,0 @@ -/* - * ==================== - * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER. - * - * Copyright 2007-2008 Sun Microsystems, Inc. All rights reserved. - * - * The contents of this file are subject to the terms of the Common Development - * and Distribution License("CDDL") (the "License"). You may not use this file - * except in compliance with the License. - * - * You can obtain a copy of the License at - * http://IdentityConnectors.dev.java.net/legal/license.txt - * See the License for the specific language governing permissions and limitations - * under the License. - * - * When distributing the Covered Code, include this CDDL Header Notice in each file - * and include the License file at identityconnectors/legal/license.txt. - * If applicable, add the following below this CDDL Header, with the fields - * enclosed by brackets [] replaced by your own identifying information: - * "Portions Copyrighted [year] [name of copyright owner]" - * ==================== - */ -using System; -using System.Diagnostics; -using Org.IdentityConnectors.Common; -using Org.IdentityConnectors.Common.Pooling; -using Org.IdentityConnectors.Common.Proxy; -using Org.IdentityConnectors.Framework.Api; -using Org.IdentityConnectors.Framework.Api.Operations; -using Org.IdentityConnectors.Framework.Common; -using Org.IdentityConnectors.Framework.Common.Objects; -using Org.IdentityConnectors.Framework.Common.Exceptions; -using Org.IdentityConnectors.Framework.Common.Serializer; -using Org.IdentityConnectors.Framework.Impl.Api.Remote; -using Org.IdentityConnectors.Framework.Impl.Api.Local.Operations; -using Org.IdentityConnectors.Framework.Spi; -using Org.IdentityConnectors.Framework.Spi.Operations; -using System.Collections.Generic; -using System.Globalization; -using System.Reflection; -using System.Resources; -using System.Threading; -using System.IO; -using System.Linq; -namespace Org.IdentityConnectors.Framework.Impl.Api.Local -{ - #region ConnectorPoolManager - public class ConnectorPoolManager { - - - private class ConnectorPoolKey { - private readonly ConnectorKey _connectorKey; - private readonly ConfigurationPropertiesImpl _configProperties; - private readonly ObjectPoolConfiguration _poolingConfig; - public ConnectorPoolKey(ConnectorKey connectorKey, - ConfigurationPropertiesImpl configProperties, - ObjectPoolConfiguration poolingConfig) { - _connectorKey = connectorKey; - _configProperties = configProperties; - _poolingConfig = poolingConfig; - } - public override int GetHashCode() { - return _connectorKey.GetHashCode(); - } - public override bool Equals(Object o) { - if ( o is ConnectorPoolKey ) { - ConnectorPoolKey other = (ConnectorPoolKey)o; - if (!_connectorKey.Equals(other._connectorKey)) { - return false; - } - if (!_configProperties.Equals(other._configProperties)) { - return false; - } - if (!_poolingConfig.Equals(other._poolingConfig)) { - return false; - } - return true; - } - return false; - } - } - - private class ConnectorPoolHandler : ObjectPoolHandler { - private readonly APIConfigurationImpl _apiConfiguration; - private readonly LocalConnectorInfoImpl _localInfo; - public ConnectorPoolHandler(APIConfigurationImpl apiConfiguration, - LocalConnectorInfoImpl localInfo) { - _apiConfiguration = apiConfiguration; - _localInfo = localInfo; - } - public PoolableConnector NewObject() { - Configuration config = - CSharpClassProperties.CreateBean((ConfigurationPropertiesImpl)_apiConfiguration.ConfigurationProperties, - _localInfo.ConnectorConfigurationClass); - PoolableConnector connector = - (PoolableConnector)_localInfo.ConnectorClass.CreateInstance(); - connector.Init(config); - return connector; - } - public void TestObject(PoolableConnector obj) { - obj.CheckAlive(); - } - public void DisposeObject(PoolableConnector obj) { - obj.Dispose(); - } - } - - /** - * Cache of the various _pools.. - */ - private static readonly IDictionary> - _pools = new Dictionary>(); - - - /** - * Get a object pool for this connector if it supports connector pooling. - */ - public static ObjectPool GetPool(APIConfigurationImpl impl, - LocalConnectorInfoImpl localInfo) { - ObjectPool pool = null; - // determine if this connector wants generic connector pooling.. - if (impl.IsConnectorPoolingSupported) { - ConnectorPoolKey key = - new ConnectorPoolKey( - impl.ConnectorInfo.ConnectorKey, - (ConfigurationPropertiesImpl)impl.ConfigurationProperties, - impl.ConnectorPoolConfiguration); - - lock (_pools) { - // get the pool associated.. - pool = CollectionUtil.GetValue(_pools,key,null); - // create a new pool if it doesn't exist.. - if (pool == null) { - Trace.TraceInformation("Creating new pool: "+ - impl.ConnectorInfo.ConnectorKey); - // this instance is strictly used for the pool.. - pool = new ObjectPool( - new ConnectorPoolHandler(impl,localInfo), - impl.ConnectorPoolConfiguration); - // add back to the map of _pools.. - _pools[key] = pool; - } - } - } - return pool; - } - - public static void Dispose() { - lock (_pools) { - // close each pool.. - foreach (ObjectPool pool in _pools.Values) { - try { - pool.Shutdown(); - } catch (Exception e) { - TraceUtil.TraceException("Failed to close pool", e); - } - } - // clear the map of all _pools.. - _pools.Clear(); - } - } - - } - #endregion - - #region CSharpClassProperties - internal static class CSharpClassProperties - { - public static ConfigurationPropertiesImpl - CreateConfigurationProperties(Configuration defaultObject) - { - SafeType config = SafeType.Get(defaultObject); - ConfigurationPropertiesImpl properties = - new ConfigurationPropertiesImpl(); - IList temp = - new List(); - IDictionary descs = GetFilteredProperties(config); - - foreach (PropertyInfo desc in descs.Values) { - - String name = desc.Name; - - // get the configuration options.. - ConfigurationPropertyAttribute options = - GetPropertyOptions(desc); - // use the options to set internal properties.. - int order = 0; - String helpKey = name + ".help"; - String displKey = name + ".display"; - bool confidential = false; - bool required = false; - if (options != null) { - // determine the display and help keys.. - if (!StringUtil.IsBlank(options.HelpMessageKey)) { - helpKey = options.HelpMessageKey; - } - if (!StringUtil.IsBlank(options.DisplayMessageKey)) { - displKey = options.DisplayMessageKey; - } - // determine the order.. - order = options.Order; - required = options.Required; - confidential = options.Confidential; - } - Type type = desc.PropertyType; - if (!FrameworkUtil.IsSupportedConfigurationType(type)) { - const String MSG = "Property type ''{0}'' is not supported."; - throw new ArgumentException(String.Format(MSG, type)); - } - - Object value = desc.GetValue(defaultObject,null); - - ConfigurationPropertyImpl prop = new ConfigurationPropertyImpl(); - prop.IsConfidential=confidential; - prop.IsRequired=required; - prop.DisplayMessageKey=displKey; - prop.HelpMessageKey=helpKey; - prop.Name=name; - prop.Order=order; - prop.Value=value; - prop.ValueType=type; - prop.Operations = options == null ? null : TranslateOperations(options.Operations); - - temp.Add(prop); - - } - properties.Properties=(temp); - return properties; - } - - private static ICollection> TranslateOperations(SafeType [] ops) - { - ICollection> set = - new HashSet>(); - foreach (SafeType spi in ops) { - CollectionUtil.AddAll(set,FrameworkUtil.Spi2Apis(spi)); - } - return set; - } - - public static Configuration - CreateBean(ConfigurationPropertiesImpl properties, - SafeType config) { - Configuration rv = config.CreateInstance(); - rv.ConnectorMessages=properties.Parent.ConnectorInfo.Messages; - IDictionary descriptors = - GetFilteredProperties(config); - foreach (ConfigurationPropertyImpl property in properties.Properties) { - String name = property.Name; - PropertyInfo desc = - CollectionUtil.GetValue(descriptors,name,null); - if ( desc == null ) { - String FMT = - "Class ''{0}'' does not have a property ''{1}''."; - String MSG = String.Format(FMT, - config.RawType.Name, - name); - throw new ArgumentException(MSG); - } - object val = property.Value; - //some value types such as arrays - //are mutable. make sure the config object - //has its own copy - val = SerializerUtil.CloneObject(val); - desc.SetValue(rv,val,null); - } - return rv; - } - - private static IDictionary - GetFilteredProperties(SafeType config) - { - IDictionary rv = - new Dictionary(); - PropertyInfo [] descriptors = config.RawType.GetProperties(); - foreach (PropertyInfo descriptor in descriptors) { - String propName = descriptor.Name; - if ( !descriptor.CanWrite ) { - //if there's no setter, ignore it - continue; - } - if ("ConnectorMessages".Equals(propName)) { - continue; - } - if ( !descriptor.CanRead ) { - const String FMT = - "Found setter ''{0}'' but not the corresponding getter."; - String MSG = String.Format(FMT,propName); - throw new ArgumentException(MSG); - } - rv[propName] = descriptor; - } - return rv; - } - - - - /** - * Get the option from the property. - */ - private static ConfigurationPropertyAttribute GetPropertyOptions( - PropertyInfo propertyInfo) { - Object [] objs = - propertyInfo.GetCustomAttributes( - typeof(ConfigurationPropertyAttribute),true); - if ( objs.Length == 0 ) { - return null; - } - else { - return (ConfigurationPropertyAttribute)objs[0]; - } - } - - } - #endregion - - #region LocalConnectorInfoManagerImpl - internal class LocalConnectorInfoManagerImpl : ConnectorInfoManager - { - private IList _connectorInfo; - - public LocalConnectorInfoManagerImpl() - { - _connectorInfo = new List(); - Assembly assembly = Assembly.GetExecutingAssembly(); - FileInfo thisAssemblyFile = - new FileInfo(assembly.Location); - DirectoryInfo directory = - thisAssemblyFile.Directory; - FileInfo [] files = - directory.GetFiles("*.Connector.dll"); - foreach (FileInfo file in files) { - Assembly lib = - Assembly.LoadFrom(file.ToString()); - CollectionUtil.AddAll(_connectorInfo, - ProcessAssembly(lib)); - } - } - - private IList ProcessAssembly(Assembly assembly) { - IList rv = new List(); - - Type [] types = null; - try { - types = assembly.GetTypes(); - } - catch (Exception e) { - TraceUtil.TraceException("Unable to load assembly: "+assembly.FullName+". Assembly will be ignored.",e); - } - - foreach (Type type in types) { - Object [] attributes = type.GetCustomAttributes( - typeof(ConnectorClassAttribute), - false); - if ( attributes.Length > 0 ) { - ConnectorClassAttribute attribute = - (ConnectorClassAttribute)attributes[0]; - LocalConnectorInfoImpl info = - CreateConnectorInfo(assembly,type,attribute); - rv.Add(info); - } - } - return rv; - } - - private LocalConnectorInfoImpl CreateConnectorInfo(Assembly assembly, - Type rawConnectorClass, - ConnectorClassAttribute attribute) { - String fileName = assembly.Location; - if (!typeof(Connector).IsAssignableFrom(rawConnectorClass)) { - String MSG = ( "File "+fileName+ - " declares a connector "+rawConnectorClass+ - " that does not implement Connector."); - throw new ConfigurationException(MSG); - } - SafeType connectorClass = - SafeType.ForRawType(rawConnectorClass); - SafeType connectorConfigurationClass = attribute.ConnectorConfigurationType; - if ( connectorConfigurationClass == null ) { - String MSG = ( "File "+fileName+ - " contains a ConnectorInfo attribute "+ - "with no connector configuration class."); - throw new ConfigurationException(MSG); - } - String connectorDisplayNameKey = - attribute.ConnectorDisplayNameKey; - if ( connectorDisplayNameKey == null ) { - String MSG = ( "File "+fileName+ - " contains a ConnectorInfo attribute "+ - "with no connector display name."); - throw new ConfigurationException(MSG); - } - ConnectorKey key = - new ConnectorKey(assembly.GetName().Name, - assembly.GetName().Version.ToString(), - connectorClass.RawType.Namespace+"."+connectorClass.RawType.Name); - LocalConnectorInfoImpl rv = new LocalConnectorInfoImpl(); - rv.ConnectorClass = connectorClass; - rv.ConnectorConfigurationClass = connectorConfigurationClass; - rv.ConnectorDisplayNameKey = connectorDisplayNameKey; - rv.ConnectorKey = key; - rv.DefaultAPIConfiguration = CreateDefaultAPIConfiguration(rv); - rv.Messages = LoadMessages(assembly,rv,attribute.MessageCatalogPaths); - return rv; - } - - private APIConfigurationImpl - CreateDefaultAPIConfiguration(LocalConnectorInfoImpl localInfo) { - SafeType connectorClass = - localInfo.ConnectorClass; - APIConfigurationImpl rv = new APIConfigurationImpl(); - Configuration config = - localInfo.ConnectorConfigurationClass.CreateInstance(); - bool pooling = IsPoolingSupported(connectorClass); - rv.IsConnectorPoolingSupported=pooling; - rv.ConfigurationProperties=(CSharpClassProperties.CreateConfigurationProperties(config)); - rv.ConnectorInfo=(localInfo); - rv.SupportedOperations=(FrameworkUtil.GetDefaultSupportedOperations(connectorClass)); - return rv; - } - - private static bool IsPoolingSupported(SafeType clazz) { - return ReflectionUtil.IsParentTypeOf(typeof(PoolableConnector),clazz.RawType); - } - /// - /// Given an assembly, returns the list of cultures that - /// it is localized for - /// - /// - /// - private CultureInfo [] GetLocalizedCultures(Assembly assembly) { - FileInfo assemblyFile = - new FileInfo(assembly.Location); - DirectoryInfo directory = - assemblyFile.Directory; - IList temp = new List(); - DirectoryInfo [] subdirs = directory.GetDirectories(); - foreach (DirectoryInfo subdir in subdirs) { - String name = subdir.Name; - CultureInfo cultureInfo; - //get the culture if the directory is the name of the - //culture - try { - cultureInfo = new CultureInfo(name); - } - catch (ArgumentException) { - //invalid culture - continue; - } - //see if there's a satellite assembly for this - try { - assembly.GetSatelliteAssembly(cultureInfo); - } - catch (Exception) { - //invalid assembly - continue; - } - temp.Add(cultureInfo); - } - temp.Add(CultureInfo.InvariantCulture); - return temp.ToArray(); - } - - private ConnectorMessagesImpl LoadMessages(Assembly assembly, - LocalConnectorInfoImpl info, - String [] nameBases) { - if ( nameBases == null || nameBases.Length == 0 ) { - String pkage = - info.ConnectorClass.RawType.Namespace; - nameBases = new String[]{pkage+".Messages"}; - } - ConnectorMessagesImpl rv = new ConnectorMessagesImpl(); - CultureInfo [] cultures = GetLocalizedCultures(assembly); - for ( int i = nameBases.Length - 1; i >= 0; i-- ) { - String nameBase = nameBases[i]; - ResourceManager manager = new ResourceManager(nameBase,assembly); - foreach (CultureInfo culture in cultures) { - ResourceSet resourceSet = manager.GetResourceSet(culture,true,false); - if ( resourceSet != null ) { - IDictionary temp = - CollectionUtil.GetValue(rv.Catalogs,culture,null); - if ( temp == null ) { - temp = new Dictionary(); - rv.Catalogs[culture] = temp; - } - foreach (System.Collections.DictionaryEntry entry in resourceSet) { - String key = ""+entry.Key; - String val = ""+entry.Value; - temp[key] = val; - } - } - } - } - - return rv; - } - - public ConnectorInfo FindConnectorInfo(ConnectorKey key) { - foreach (ConnectorInfo info in _connectorInfo) { - if ( info.ConnectorKey.Equals(key) ) { - return info; - } - } - return null; - } - public IList ConnectorInfos { - get { - return CollectionUtil.AsReadOnlyList(_connectorInfo); - } - } - } - #endregion - - #region LocalConnectorInfoImpl - /// - /// Internal class, public only for unit tests - /// - public class LocalConnectorInfoImpl : AbstractConnectorInfo - { - public RemoteConnectorInfoImpl ToRemote() { - RemoteConnectorInfoImpl rv = new RemoteConnectorInfoImpl(); - rv.ConnectorDisplayNameKey=ConnectorDisplayNameKey; - rv.ConnectorKey=ConnectorKey; - rv.DefaultAPIConfiguration=DefaultAPIConfiguration; - rv.Messages=Messages; - return rv; - } - public SafeType ConnectorClass {get;set;} - public SafeType ConnectorConfigurationClass {get;set;} - } - #endregion - - #region LocalConnectorFacadeImpl - internal class LocalConnectorFacadeImpl : AbstractConnectorFacade { - - // ======================================================================= - // Constants - // ======================================================================= - /** - * Map the API interfaces to their implementation counterparts. - */ - private static readonly IDictionary,ConstructorInfo> API_TO_IMPL= - new Dictionary,ConstructorInfo>(); - - private static void AddImplementation(SafeType inter, - SafeType impl) { - ConstructorInfo info = - impl.RawType.GetConstructor(new Type[]{typeof(ConnectorOperationalContext), - typeof(Connector)}); - if ( info == null ) { - throw new ArgumentException(impl+" does not define the proper constructor"); - } - API_TO_IMPL[inter]= info; - } - - static LocalConnectorFacadeImpl() { - AddImplementation(SafeType.Get(), - SafeType.Get()); - AddImplementation(SafeType.Get(), - SafeType.Get()); - AddImplementation(SafeType.Get(), - SafeType.Get()); - AddImplementation(SafeType.Get(), - SafeType.Get()); - AddImplementation(SafeType.Get(), - SafeType.Get()); - AddImplementation(SafeType.Get(), - SafeType.Get()); - AddImplementation(SafeType.Get(), - SafeType.Get()); - AddImplementation(SafeType.Get(), - SafeType.Get()); - AddImplementation(SafeType.Get(), - SafeType.Get()); - AddImplementation(SafeType.Get(), - SafeType.Get()); - } - - - - // ======================================================================= - // Fields - // ======================================================================= - /** - * Pool used to acquire connection from to use during operations. - */ - - /** - * The connector info - */ - private readonly LocalConnectorInfoImpl connectorInfo; - - /** - * Builds up the maps of supported operations and calls. - */ - public LocalConnectorFacadeImpl(LocalConnectorInfoImpl connectorInfo, - APIConfigurationImpl apiConfiguration) - :base(apiConfiguration) { - this.connectorInfo = connectorInfo; - } - - // ======================================================================= - // ConnectorFacade Interface - // ======================================================================= - - protected override APIOperation GetOperationImplementation(SafeType api) { - APIOperation proxy; - //first create the inner proxy - this is the proxy that obtaining - //a connection from the pool, etc - //NOTE: we want to skip this part of the proxy for - //validate op, but we will want the timeout proxy - if ( api.RawType.Equals(typeof(ValidateApiOp))) { - OperationalContext context = - new OperationalContext(connectorInfo,GetAPIConfiguration()); - proxy = new ValidateImpl(context); - } - else if ( api.RawType.Equals( typeof(GetApiOp) ) ) { - ConstructorInfo constructor = - API_TO_IMPL[SafeType.Get()]; - ConnectorOperationalContext context = - new ConnectorOperationalContext(connectorInfo, - GetAPIConfiguration(), - GetPool()); - - ConnectorAPIOperationRunnerProxy handler = - new ConnectorAPIOperationRunnerProxy(context,constructor); - proxy = - new GetImpl((SearchApiOp)NewAPIOperationProxy(SafeType.Get(),handler)); - } - else { - ConstructorInfo constructor = - API_TO_IMPL[api]; - ConnectorOperationalContext context = - new ConnectorOperationalContext(connectorInfo, - GetAPIConfiguration(), - GetPool()); - - ConnectorAPIOperationRunnerProxy handler = - new ConnectorAPIOperationRunnerProxy(context,constructor); - proxy = - NewAPIOperationProxy(api,handler); - } - - //TODO: timeout - - // add logging proxy.. - proxy = CreateLoggingProxy(api, proxy); - return proxy; - } - private ObjectPool GetPool() - { - return ConnectorPoolManager.GetPool(GetAPIConfiguration(),connectorInfo); - } - } - #endregion - - #region ObjectPool - public class ObjectPool where T : class { - - /** - * Statistics bean - */ - public sealed class Statistics { - private readonly int _numIdle; - private readonly int _numActive; - - internal Statistics(int numIdle, int numActive) { - _numIdle = numIdle; - _numActive = numActive; - } - - /** - * Returns the number of idle objects - */ - public int NumIdle { - get { - return _numIdle; - } - } - - /** - * Returns the number of active objects - */ - public int NumActive { - get { - return _numActive; - } - } - } - - /** - * An object plus additional book-keeping - * information about the object - */ - private class PooledObject where T2 : class { - /** - * The underlying object - */ - private readonly T2 _object; - - /** - * True if this is currently active, false if - * it is idle - */ - private bool _isActive; - - /** - * Last state change (change from active to - * idle or vice-versa) - */ - private long _lastStateChangeTimestamp; - - /** - * Is this a freshly created object (never been pooled)? - */ - private bool _isNew; - - public PooledObject(T2 obj) { - _object = obj; - _isNew = true; - Touch(); - } - - public T2 Object { - get { - return _object; - } - } - - public bool IsActive { - get { - return _isActive; - } - set { - if (_isActive != value) { - Touch(); - _isActive = value; - } - } - } - - public bool IsNew { - get { - return _isNew; - } - set { - _isNew = value; - } - } - - - private void Touch() { - _lastStateChangeTimestamp = DateTimeUtil.GetCurrentUtcTimeMillis(); - } - - public long LastStateChangeTimestamp { - get { - return _lastStateChangeTimestamp; - } - } - } - - /** - * The lock object we use for everything - */ - private readonly Object LOCK = new Object(); - - /** - * Map from the object to the - * PooledObject (use IdentityHashMap so it's - * always object equality) - */ - private readonly IDictionary> - _activeObjects = CollectionUtil.NewIdentityDictionary>(); - - /** - * Queue of idle objects. The one that has - * been idle for the longest comes first in the queue - */ - private readonly LinkedList> - _idleObjects = new LinkedList>(); - - /** - * ObjectPoolHandler we use for managing object lifecycle - */ - private readonly ObjectPoolHandler _handler; - - /** - * Configuration for this pool. - */ - private readonly ObjectPoolConfiguration _config; - - /** - * Is the pool shutdown - */ - private bool _isShutdown; - - /** - * Create a new ObjectPool - * @param handler Handler for objects - * @param config Configuration for the pool - */ - public ObjectPool(ObjectPoolHandler handler, - ObjectPoolConfiguration config) { - - Assertions.NullCheck(handler, "handler"); - Assertions.NullCheck(config, "config"); - - _handler = handler; - //clone it - _config = - (ObjectPoolConfiguration)SerializerUtil.CloneObject(config); - //validate it - _config.Validate(); - - } - - /** - * Return an object to the pool - * @param object - */ - public void ReturnObject(T obj) { - Assertions.NullCheck(obj, "object"); - lock (LOCK) { - //remove it from the active list - PooledObject pooled = - CollectionUtil.GetValue(_activeObjects,obj,null); - - //they are attempting to return something - //we haven't allocated (or that they've - //already returned) - if ( pooled == null ) { - throw new InvalidOperationException("Attempt to return an object not in the pool: "+obj); - } - _activeObjects.Remove(obj); - - //set it to idle and add to idle list - //(this might get evicted right away - //by evictIdleObjects if we're over the - //limit or if we're shutdown) - pooled.IsActive=(false); - pooled.IsNew=(false); - _idleObjects.AddLast(pooled); - - //finally evict idle objects - EvictIdleObjects(); - - //wake anyone up who was waiting on a object - Monitor.PulseAll(LOCK); - } - } - - /** - * Borrow an object from the pool. - * @return An object - */ - public T BorrowObject() { - while ( true ) { - PooledObject rv = BorrowObjectNoTest(); - try { - //make sure we are testing it outside - //of synchronization. otherwise this - //can create an IO bottleneck - _handler.TestObject(rv.Object); - return rv.Object; - } - catch (Exception e) { - //it's bad - remove from active objects - lock (LOCK) { - _activeObjects.Remove(rv.Object); - } - DisposeNoException(rv.Object); - //if it's a new object, break out of the loop - //immediately - if ( rv.IsNew ) { - throw e; - } - } - } - } - - /** - * Borrow an object from the pool, but don't test - * it (it gets tested by the caller *outside* of - * synchronization) - * @return the object - */ - private PooledObject BorrowObjectNoTest() { - //time when the call began - long startTime = DateTimeUtil.GetCurrentUtcTimeMillis(); - - lock (LOCK) { - EvictIdleObjects(); - while ( true ) { - if (_isShutdown) { - throw new InvalidOperationException("Object pool already shutdown"); - } - - PooledObject pooledConn = null; - - //first try to recycle an idle object - if (_idleObjects.Count > 0) { - pooledConn = _idleObjects.First(); - _idleObjects.RemoveFirst(); - } - //otherwise, allocate a new object if - //below the limit - else if (_activeObjects.Count < _config.MaxObjects) { - pooledConn = - new PooledObject(_handler.NewObject()); - } - - //if there's an object available, return it - //and break out of the loop - if ( pooledConn != null ) { - pooledConn.IsActive=(true); - _activeObjects[pooledConn.Object] = - pooledConn; - return pooledConn; - } - - //see if we haven't timed-out yet - long elapsed = - DateTimeUtil.GetCurrentUtcTimeMillis() - startTime; - long remaining = _config.MaxWait - elapsed; - - //wait if we haven't timed out - if (remaining > 0) { - Monitor.Wait(LOCK,(int)remaining); - } - else { - //otherwise throw - throw new ConnectorException("Max objects exceeded"); - } - } - } - } - - /** - * Closes any idle objects in the pool. - * Existing active objects will remain alive and - * be allowed to shutdown gracefully, but no more - * objects will be allocated. - */ - public void Shutdown() { - lock(LOCK) { - _isShutdown = true; - //just evict idle objects - //if there are any active objects still - //going, leave them alone so they can return - //gracefully - EvictIdleObjects(); - //wake anyone up who was waiting on an object - Monitor.PulseAll(LOCK); - } - } - - /** - * Gets a snapshot of the pool's stats at a point in time. - * @return The statistics - */ - public Statistics GetStatistics() { - lock(LOCK) { - return new Statistics(_idleObjects.Count, - _activeObjects.Count); - } - } - - /** - * Evicts idle objects as needed (evicts - * all idle objects if we're shutdown) - */ - private void EvictIdleObjects() { - while (TooManyIdleObjects()) { - PooledObject conn = _idleObjects.First(); - _idleObjects.RemoveFirst(); - DisposeNoException(conn.Object); - } - } - - /** - * Returns true if any of the following are true: - *
    - *
  1. We're shutdown and there are idle objects
  2. - *
  3. Max idle objects exceeded
  4. - *
  5. Min idle objects exceeded and there are old objects
  6. - *
- */ - private bool TooManyIdleObjects() { - - if (_isShutdown && _idleObjects.Count > 0) { - return true; - } - - if (_config.MaxIdle < _idleObjects.Count) { - return true; - } - if (_config.MinIdle >= _idleObjects.Count) { - return false; - } - - PooledObject oldest = - _idleObjects.First(); - - long age = - ( DateTimeUtil.GetCurrentUtcTimeMillis()-oldest.LastStateChangeTimestamp ); - - - return age > _config.MinEvictableIdleTimeMillis; - } - - /** - * Dispose of an object, but don't throw any exceptions - * @param object - */ - private void DisposeNoException(T obj) { - try { - _handler.DisposeObject(obj); - } - catch (Exception e) { - TraceUtil.TraceException("disposeObject() is not supposed to throw",e); - } - } - } - #endregion - - #region ObjectPoolHandler - public interface ObjectPoolHandler where T : class { - T NewObject(); - void TestObject(T obj); - void DisposeObject(T obj); - } - #endregion -} diff --git a/DotNetConnectors.sln/FrameworkInternal/ApiLocalOperations.cs b/DotNetConnectors.sln/FrameworkInternal/ApiLocalOperations.cs deleted file mode 100644 index 6ed4c594..00000000 --- a/DotNetConnectors.sln/FrameworkInternal/ApiLocalOperations.cs +++ /dev/null @@ -1,1416 +0,0 @@ -/* - * ==================== - * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER. - * - * Copyright 2007-2008 Sun Microsystems, Inc. All rights reserved. - * - * The contents of this file are subject to the terms of the Common Development - * and Distribution License("CDDL") (the "License"). You may not use this file - * except in compliance with the License. - * - * You can obtain a copy of the License at - * http://IdentityConnectors.dev.java.net/legal/license.txt - * See the License for the specific language governing permissions and limitations - * under the License. - * - * When distributing the Covered Code, include this CDDL Header Notice in each file - * and include the License file at identityconnectors/legal/license.txt. - * If applicable, add the following below this CDDL Header, with the fields - * enclosed by brackets [] replaced by your own identifying information: - * "Portions Copyrighted [year] [name of copyright owner]" - * ==================== - */ -using System; - -using Org.IdentityConnectors.Common; -using Org.IdentityConnectors.Common.Pooling; -using Org.IdentityConnectors.Common.Proxy; -using Org.IdentityConnectors.Common.Script; -using Org.IdentityConnectors.Common.Security; -using Org.IdentityConnectors.Framework.Api.Operations; -using Org.IdentityConnectors.Framework.Common.Objects; -using Org.IdentityConnectors.Framework.Common.Objects.Filters; -using Org.IdentityConnectors.Framework.Common.Exceptions; -using Org.IdentityConnectors.Framework.Common.Serializer; -using Org.IdentityConnectors.Framework.Spi; -using Org.IdentityConnectors.Framework.Spi.Operations; -using System.Reflection; -using System.Collections.Generic; -using System.Linq; - -namespace Org.IdentityConnectors.Framework.Impl.Api.Local.Operations -{ - #region APIOperationRunner - /** - * NOTE: internal class, public only for unit tests - * Base class for API operation runners. - */ - public abstract class APIOperationRunner { - - /** - * Context that has all the information required to execute an operation. - */ - private readonly OperationalContext _context; - - - /** - * Creates the API operation so it can called multiple times. - */ - public APIOperationRunner(OperationalContext context) { - _context = context; - //TODO: verify this - // get the APIOperation that this class implements.. - //List> apiOps = getInterfaces(this - //.getClass(), APIOperation.class); - // there should be only one.. - //if (apiOps.size() > 1) { - // final String MSG = "Must only implement one operation."; - // throw new IllegalStateException(MSG); - //} - } - - /** - * Get the current operational context. - */ - public OperationalContext GetOperationalContext() { - return _context; - } - - } - #endregion - - #region ConnectorAPIOperationRunner - /** - * NOTE: internal class, public only for unit tests - * Subclass of APIOperationRunner for operations that require a connector. - */ - public abstract class ConnectorAPIOperationRunner : APIOperationRunner { - - /** - * The connector instance - */ - private readonly Connector _connector; - - /** - * Creates the API operation so it can called multiple times. - */ - public ConnectorAPIOperationRunner(ConnectorOperationalContext context, - Connector connector) : base(context) { - _connector = connector; - } - - public Connector GetConnector() { - return _connector; - } - - public ObjectNormalizerFacade GetNormalizer(ObjectClass oclass) { - AttributeNormalizer norm = null; - Connector connector = GetConnector(); - if ( connector is AttributeNormalizer ) { - norm = (AttributeNormalizer)connector; - } - return new ObjectNormalizerFacade(oclass,norm); - } - } - #endregion - - #region ConnectorAPIOperationRunnerProxy - /** - * Proxy for APIOperationRunner that takes care of setting up underlying - * connector and creating the implementation of APIOperationRunner. - * The implementation of APIOperationRunner gets created whenever the - * actual method is invoked. - */ - internal class ConnectorAPIOperationRunnerProxy : InvocationHandler { - - - /** - * The operational context - */ - private readonly ConnectorOperationalContext _context; - - /** - * The implementation constructor. The instance is lazily created upon - * invocation - */ - private readonly ConstructorInfo _runnerImplConstructor; - - /** - * Create an APIOperationRunnerProxy - * @param context The operational context - * @param runnerImplConstructor The implementation constructor. Implementation - * must define a two-argument constructor(OperationalContext,Connector) - */ - public ConnectorAPIOperationRunnerProxy(ConnectorOperationalContext context, - ConstructorInfo runnerImplConstructor) { - _context = context; - _runnerImplConstructor = runnerImplConstructor; - } - - public Object Invoke(Object proxy, MethodInfo method, object[] args) - { - //do not proxy equals, hashCode, toString - if (method.DeclaringType.Equals(typeof(object))) { - return method.Invoke(this, args); - } - object ret = null; - Connector connector = null; - ObjectPool pool = _context.GetPool(); - // get the connector class.. - SafeType connectorClazz = _context.GetConnectorClass(); - try { - // pooling is implemented get one.. - if (pool != null) { - connector = pool.BorrowObject(); - } - else { - // get a new instance of the connector.. - connector = connectorClazz.CreateInstance(); - // initialize the connector.. - connector.Init(_context.GetConfiguration()); - } - APIOperationRunner runner = - (APIOperationRunner)_runnerImplConstructor.Invoke(new object[]{ - _context, - connector}); - ret = method.Invoke(runner, args); - // call out to the operation.. - } catch (TargetInvocationException e) { - Exception root = e.InnerException; - throw root; - } finally { - // make sure dispose of the connector properly - if (connector != null) { - // determine if there was a pool.. - if (pool != null) { - try { - //try to return it to the pool even though an - //exception may have happened that leaves it in - //a bad state. The contract of checkAlive - //is that it will tell you if the connector is - //still valid and so we leave it up to the pool - //and connector to work it out. - pool.ReturnObject((PoolableConnector)connector); - } catch (Exception e) { - //don't let pool exceptions propogate or mask - //other exceptions. do log it though. - TraceUtil.TraceException(null,e); - } - } - //not pooled - just dispose - else { - //dispose it not supposed to throw, but just in case, - //catch the exception and log it so we know about it - //but don't let the exception prevent additional - //cleanup that needs to happen - try { - connector.Dispose(); - } catch (Exception e) { - //log this though - TraceUtil.TraceException(null,e); - } - } - } - } - return ret; - } - } - #endregion - - #region ConnectorOperationalContext - /** - * NOTE: internal class, public only for unit tests - * Simple structure to pass more variables through the constructor of - * {@link APIOperationRunner}. - */ - public class ConnectorOperationalContext : OperationalContext { - - private readonly ObjectPool _pool; - - public ConnectorOperationalContext(LocalConnectorInfoImpl connectorInfo, - APIConfigurationImpl apiConfiguration, - ObjectPool pool) - :base(connectorInfo,apiConfiguration) { - _pool = pool; - } - - public ObjectPool GetPool() { - return _pool; - } - - - public SafeType GetConnectorClass() { - return GetConnectorInfo().ConnectorClass; - } - - } - #endregion - - #region AuthenticationImpl - internal class AuthenticationImpl : ConnectorAPIOperationRunner, - AuthenticationApiOp { - /** - * Pass the configuration etc to the abstract class. - */ - public AuthenticationImpl(ConnectorOperationalContext context, - Connector connector) - :base(context,connector) { - } - - /** - * Authenticate using the basic credentials. - * - * @see Authentication#authenticate(String, String) - */ - public Uid Authenticate(ObjectClass objectClass, String username, GuardedString password, OperationOptions options) { - Assertions.NullCheck(objectClass,"objectClass"); - Assertions.NullCheck(username, "username"); - Assertions.NullCheck(password, "password"); - //convert null into empty - if ( options == null ) { - options = new OperationOptionsBuilder().Build(); - } - return ((AuthenticateOp) GetConnector()).Authenticate(objectClass,username, password,options); - } - } - #endregion - - #region CreateImpl - internal class CreateImpl : ConnectorAPIOperationRunner, - CreateApiOp { - - /** - * Initializes the operation works. - */ - public CreateImpl(ConnectorOperationalContext context, - Connector connector) - :base(context,connector) { - } - - /** - * Calls the create method on the Connector side. - * - * @see CreateApiOp#create(Set) - */ - public Uid Create(ObjectClass oclass, ICollection attributes, OperationOptions options) { - Assertions.NullCheck(oclass, "oclass"); - Assertions.NullCheck(attributes, "attributes"); - //convert null into empty - if ( options == null ) { - options = new OperationOptionsBuilder().Build(); - } - HashSet dups = new HashSet(); - foreach (ConnectorAttribute attr in attributes) { - if (dups.Contains(attr.Name)) { - throw new ArgumentException("Duplicate attribute name exists: " + attr.Name); - } - dups.Add(attr.Name); - } - if (oclass == null) { - throw new ArgumentException("Required attribute ObjectClass not found!"); - } - Connector connector = GetConnector(); - ObjectNormalizerFacade normalizer = GetNormalizer(oclass); - ICollection normalizedAttributes = - normalizer.NormalizeAttributes(attributes); - // create the object.. - Uid ret = ((CreateOp) connector).Create(oclass,attributes,options); - return (Uid)normalizer.NormalizeAttribute(ret); - } - } - #endregion - - #region DeleteImpl - internal class DeleteImpl : ConnectorAPIOperationRunner , - DeleteApiOp { - - /** - * Initializes the operation works. - */ - public DeleteImpl(ConnectorOperationalContext context, - Connector connector) - :base(context,connector) { - } - /** - * Calls the delete method on the Connector side. - * - * @see com.sun.openconnectors.framework.api.operations.CreateApiOp#create(java.util.Set) - */ - public void Delete(ObjectClass objClass, Uid uid, OperationOptions options) { - Assertions.NullCheck(objClass, "objClass"); - Assertions.NullCheck(uid, "uid"); - //convert null into empty - if ( options == null ) { - options = new OperationOptionsBuilder().Build(); - } - Connector connector = GetConnector(); - ObjectNormalizerFacade normalizer = GetNormalizer(objClass); - // delete the object.. - ((DeleteOp) connector).Delete(objClass, - (Uid)normalizer.NormalizeAttribute(uid), - options); - } - } - #endregion - - #region AttributesToGetResultsHandler - public abstract class AttributesToGetResultsHandler { - // ======================================================================= - // Fields - // ======================================================================= - private readonly string[] _attrsToGet; - - // ======================================================================= - // Constructors - // ======================================================================= - /** - * Keep the attribute to get.. - */ - public AttributesToGetResultsHandler(string[] attrsToGet) { - Assertions.NullCheck(attrsToGet, "attrsToGet"); - _attrsToGet = attrsToGet; - } - - /** - * Simple method that clones the object and remove the attribute thats are - * not in the {@link OperationOptions#OP_ATTRIBUTES_TO_GET} set. - * - * @param attrsToGet - * case insensitive set of attribute names. - */ - public ICollection ReduceToAttrsToGet( - ICollection attrs) { - ICollection ret = new HashSet(); - IDictionary map = ConnectorAttributeUtil.ToMap(attrs); - foreach (string attrName in _attrsToGet) { - ConnectorAttribute attr = CollectionUtil.GetValue(map, attrName, null); - // TODO: Should we throw if the attribute is not yet it was - // requested?? Or do we ignore because the API maybe asking - // for what the resource doesn't have?? - if (attr != null) { - ret.Add(attr); - } - } - return ret; - } - public ConnectorObject ReduceToAttrsToGet(ConnectorObject obj) { - // clone the object and reduce the attributes only the set of - // attributes. - ConnectorObjectBuilder bld = new ConnectorObjectBuilder(); - bld.SetUid(obj.Uid); - bld.SetName(obj.Name); - bld.ObjectClass = obj.ObjectClass; - ICollection objAttrs = obj.GetAttributes(); - ICollection attrs = ReduceToAttrsToGet(objAttrs); - bld.AddAttributes(attrs); - return bld.Build(); - } - } - #endregion - - #region SearchAttributesToGetResultsHandler - public sealed class SearchAttributesToGetResultsHandler : - AttributesToGetResultsHandler { - - // ======================================================================= - // Fields - // ======================================================================= - private readonly ResultsHandler _handler; - - // ======================================================================= - // Constructors - // ======================================================================= - public SearchAttributesToGetResultsHandler( - ResultsHandler handler, string[] attrsToGet) : base(attrsToGet) { - Assertions.NullCheck(handler, "handler"); - this._handler = handler; - } - - public bool Handle(ConnectorObject obj) { - // clone the object and reduce the attributes only the set of - // attributes. - return _handler(ReduceToAttrsToGet(obj)); - } - } - #endregion - - #region SearchAttributesToGetResultsHandler - public sealed class SyncAttributesToGetResultsHandler : - AttributesToGetResultsHandler { - - // ======================================================================= - // Fields - // ======================================================================= - private readonly SyncResultsHandler _handler; - - // ======================================================================= - // Constructors - // ======================================================================= - public SyncAttributesToGetResultsHandler( - SyncResultsHandler handler, string[] attrsToGet) : base(attrsToGet) { - Assertions.NullCheck(handler, "handler"); - this._handler = handler; - } - - public bool Handle(SyncDelta delta) { - SyncDeltaBuilder bld = new SyncDeltaBuilder(); - bld.Uid = delta.Uid; - bld.Token = delta.Token; - bld.DeltaType = delta.DeltaType; - if ( delta.Object != null ) { - bld.Object=ReduceToAttrsToGet(delta.Object); - } - return _handler(bld.Build()); - } - } - #endregion - - #region DuplicateFilteringResultsHandler - public sealed class DuplicateFilteringResultsHandler { - - // ======================================================================= - // Fields - // ======================================================================= - private readonly ResultsHandler _handler; - private readonly HashSet _visitedUIDs = new HashSet(); - - private bool _stillHandling; - - // ======================================================================= - // Constructors - // ======================================================================= - /** - * Filter chain for producers. - * - * @param producer - * Producer to filter. - * - */ - public DuplicateFilteringResultsHandler(ResultsHandler handler) { - // there must be a producer.. - if (handler == null) { - throw new ArgumentException("Handler must not be null!"); - } - this._handler = handler; - } - - public bool Handle(ConnectorObject obj) { - String uid = - obj.Uid.GetUidValue(); - if (!_visitedUIDs.Add(uid)) { - //we've already seen this - don't pass it - //throw - return true; - } - _stillHandling = _handler(obj); - return _stillHandling; - } - - public bool IsStillHandling { - get { - return _stillHandling; - } - } - - } - #endregion - - #region FilteredResultsHandler - public sealed class FilteredResultsHandler { - - // ======================================================================= - // Fields - // ======================================================================= - readonly ResultsHandler handler; - readonly Filter filter; - - // ======================================================================= - // Constructors - // ======================================================================= - /** - * Filter chain for producers. - * - * @param producer - * Producer to filter. - * @param filter - * Filter to use to accept objects. - */ - public FilteredResultsHandler(ResultsHandler handler, Filter filter) { - // there must be a producer.. - if (handler == null) { - throw new ArgumentException("Producer must not be null!"); - } - this.handler = handler; - // use a default pass through filter.. - this.filter = filter == null ? new PassThruFilter() : filter; - } - - public bool Handle(ConnectorObject obj) { - if ( filter.Accept(obj) ) { - return handler(obj); - } - else { - return true; - } - } - - /** - * Use a pass thru filter to use if a null filter is provided. - */ - class PassThruFilter : Filter { - public bool Accept(ConnectorObject obj) { - return true; - } - } - - } - #endregion - - #region GetImpl - /** - * Uses {@link SearchOp} to find the object that is referenced by the - * {@link Uid} provided. - */ - public class GetImpl : GetApiOp { - - readonly SearchApiOp op; - - private class ResultAdapter { - private IList _list = new List(); - public bool Handle(ConnectorObject obj) { - _list.Add(obj); - return false; - } - public ConnectorObject GetResult() { - return _list.Count == 0 ? null : _list[0]; - } - } - - public GetImpl(SearchApiOp search) { - this.op = search; - } - - public ConnectorObject GetObject(ObjectClass objClass, Uid uid, OperationOptions options) { - Assertions.NullCheck(objClass, "objClass"); - Assertions.NullCheck(uid, "uid"); - //convert null into empty - if ( options == null ) { - options = new OperationOptionsBuilder().Build(); - } - Filter filter = FilterBuilder.EqualTo(uid); - ResultAdapter adapter = new ResultAdapter(); - op.Search(objClass,filter,new ResultsHandler(adapter.Handle),options); - return adapter.GetResult(); - } - } - #endregion - - #region OperationalContext - /** - * NOTE: internal class, public only for unit tests - * OperationalContext - base class for operations that do not - * require a connection. - */ - public class OperationalContext { - - /** - * ConnectorInfo - */ - private readonly LocalConnectorInfoImpl connectorInfo; - - /** - * Contains the {@link Connector} {@link Configuration}. - */ - private readonly APIConfigurationImpl apiConfiguration; - - - public OperationalContext(LocalConnectorInfoImpl connectorInfo, - APIConfigurationImpl apiConfiguration) { - this.connectorInfo = connectorInfo; - this.apiConfiguration = apiConfiguration; - } - - public Configuration GetConfiguration() { - return CSharpClassProperties.CreateBean((ConfigurationPropertiesImpl)this.apiConfiguration.ConfigurationProperties, - connectorInfo.ConnectorConfigurationClass); - } - - protected LocalConnectorInfoImpl GetConnectorInfo() { - return connectorInfo; - } - } - #endregion - - #region NormalizingResultsHandler - public class NormalizingResultsHandler { - - private readonly ResultsHandler _target; - private readonly ObjectNormalizerFacade _normalizer; - - public NormalizingResultsHandler(ResultsHandler target, - ObjectNormalizerFacade normalizer) { - Assertions.NullCheck(target, "target"); - Assertions.NullCheck(normalizer, "normalizer"); - _target = target; - _normalizer = normalizer; - } - - - public bool Handle(ConnectorObject obj) { - ConnectorObject normalized = _normalizer.NormalizeObject(obj); - return _target(normalized); - } - - } - #endregion - - #region NormalizingSyncResultsHandler - public class NormalizingSyncResultsHandler { - - private readonly SyncResultsHandler _target; - private readonly ObjectNormalizerFacade _normalizer; - - public NormalizingSyncResultsHandler(SyncResultsHandler target, - ObjectNormalizerFacade normalizer) { - Assertions.NullCheck(target, "target"); - Assertions.NullCheck(normalizer, "normalizer"); - _target = target; - _normalizer = normalizer; - } - - - public bool Handle(SyncDelta delta) { - SyncDelta normalized = _normalizer.NormalizeSyncDelta(delta); - return _target(normalized); - } - - } - #endregion - - #region ObjectNormalizerFacade - public sealed class ObjectNormalizerFacade { - /** - * The (non-null) object class - */ - private readonly ObjectClass _objectClass; - /** - * The (possibly null) attribute normalizer - */ - private readonly AttributeNormalizer _normalizer; - - /** - * Create a new ObjectNormalizer - * @param objectClass The object class - * @param normalizer The normalizer. May be null. - */ - public ObjectNormalizerFacade(ObjectClass objectClass, - AttributeNormalizer normalizer) { - Assertions.NullCheck(objectClass, "objectClass"); - _objectClass = objectClass; - _normalizer = normalizer; - } - - /** - * Returns the normalized value of the attribute. - * If no normalizer is specified, returns the original - * attribute. - * @param attribute The attribute to normalize. - * @return The normalized attribute - */ - public ConnectorAttribute NormalizeAttribute(ConnectorAttribute attribute) { - if ( attribute == null ) { - return null; - } - else if (_normalizer != null) { - return _normalizer.NormalizeAttribute(_objectClass, attribute); - } - else { - return attribute; - } - } - - /** - * Returns the normalized set of attributes or null - * if the original set is null. - * @param attributes The original attributes. - * @return The normalized attributes or null if - * the original set is null. - */ - public ICollection NormalizeAttributes(ICollection attributes) { - if ( attributes == null ) { - return null; - } - ICollection temp = new HashSet(); - foreach (ConnectorAttribute attribute in attributes ) { - temp.Add(NormalizeAttribute(attribute)); - } - return CollectionUtil.AsReadOnlySet(temp); - } - - /** - * Returns the normalized object. - * @param orig The original object - * @return The normalized object. - */ - public ConnectorObject NormalizeObject(ConnectorObject orig) { - return new ConnectorObject(orig.ObjectClass, - NormalizeAttributes(orig.GetAttributes())); - } - - /** - * Returns the normalized sync delta. - * @param delta The original delta. - * @return The normalized delta. - */ - public SyncDelta NormalizeSyncDelta(SyncDelta delta) { - SyncDeltaBuilder builder = new - SyncDeltaBuilder(delta); - if ( delta.Object != null ) { - builder.Object=NormalizeObject(delta.Object); - } - return builder.Build(); - } - - /** - * Returns a filter consisting of the original with - * all attributes normalized. - * @param filter The original. - * @return The normalized filter. - */ - public Filter NormalizeFilter(Filter filter) { - if ( filter is ContainsFilter ) { - AttributeFilter afilter = - (AttributeFilter)filter; - return new ContainsFilter(NormalizeAttribute(afilter.GetAttribute())); - } - else if (filter is EndsWithFilter) { - AttributeFilter afilter = - (AttributeFilter)filter; - return new EndsWithFilter(NormalizeAttribute(afilter.GetAttribute())); - } - else if ( filter is EqualsFilter ) { - AttributeFilter afilter = - (AttributeFilter)filter; - return new EqualsFilter(NormalizeAttribute(afilter.GetAttribute())); - } - else if ( filter is GreaterThanFilter ) { - AttributeFilter afilter = - (AttributeFilter)filter; - return new GreaterThanFilter(NormalizeAttribute(afilter.GetAttribute())); - } - else if ( filter is GreaterThanOrEqualFilter ) { - AttributeFilter afilter = - (AttributeFilter)filter; - return new GreaterThanOrEqualFilter(NormalizeAttribute(afilter.GetAttribute())); - } - else if ( filter is LessThanFilter ) { - AttributeFilter afilter = - (AttributeFilter)filter; - return new LessThanFilter(NormalizeAttribute(afilter.GetAttribute())); - } - else if ( filter is LessThanOrEqualFilter ) { - AttributeFilter afilter = - (AttributeFilter)filter; - return new LessThanOrEqualFilter(NormalizeAttribute(afilter.GetAttribute())); - } - else if (filter is StartsWithFilter) { - AttributeFilter afilter = - (AttributeFilter)filter; - return new StartsWithFilter(NormalizeAttribute(afilter.GetAttribute())); - } - else if (filter is ContainsAllValuesFilter) { - AttributeFilter afilter = - (AttributeFilter)filter; - return new ContainsAllValuesFilter(NormalizeAttribute(afilter.GetAttribute())); - } - else if ( filter is NotFilter ) { - NotFilter notFilter = - (NotFilter)filter; - return new NotFilter(NormalizeFilter(notFilter.Filter)); - } - else if ( filter is AndFilter ) { - AndFilter andFilter = - (AndFilter)filter; - return new AndFilter(NormalizeFilter(andFilter.Left), - NormalizeFilter(andFilter.Right)); - } - else if ( filter is OrFilter ) { - OrFilter orFilter = - (OrFilter)filter; - return new OrFilter(NormalizeFilter(orFilter.Left), - NormalizeFilter(orFilter.Right)); - } - else { - return filter; - } - } - - - } - #endregion - - #region SchemaImpl - internal class SchemaImpl : ConnectorAPIOperationRunner , SchemaApiOp { - /** - * Initializes the operation works. - */ - public SchemaImpl(ConnectorOperationalContext context, - Connector connector) - :base(context,connector) { - } - - /** - * Retrieve the schema from the {@link Connector}. - * - * @see com.sun.openconnectors.framework.api.operations.SchemaApiOp#schema() - */ - public Schema Schema() { - return ((SchemaOp)GetConnector()).Schema(); - } - } - #endregion - - #region ScriptOnConnectorImpl - public class ScriptOnConnectorImpl : ConnectorAPIOperationRunner, - ScriptOnConnectorApiOp { - - public ScriptOnConnectorImpl(ConnectorOperationalContext context, - Connector connector) : - base(context,connector) { - } - - public Object RunScriptOnConnector(ScriptContext request, - OperationOptions options) { - Assertions.NullCheck(request, "request"); - //convert null into empty - if ( options == null ) { - options = new OperationOptionsBuilder().Build(); - } - Object rv; - if ( GetConnector() is ScriptOnConnectorOp ) { - rv = ((ScriptOnConnectorOp)GetConnector()).RunScriptOnConnector(request, options); - } - else { - String language = request.ScriptLanguage; - Assembly assembly = GetConnector().GetType().Assembly; - - ScriptExecutor executor = - ScriptExecutorFactory.NewInstance(language).NewScriptExecutor( - BuildReferenceList(assembly), - request.ScriptText, - false); - IDictionary scriptArgs = - new Dictionary(request.ScriptArguments); - scriptArgs["connector"]=GetConnector(); //add the connector instance itself - rv = executor.Execute(scriptArgs); - } - return SerializerUtil.CloneObject(rv); - } - - private Assembly [] BuildReferenceList(Assembly assembly) - { - List list = new List(); - BuildReferenceList2(assembly,list,new HashSet()); - return list.ToArray(); - } - - private void BuildReferenceList2(Assembly assembly, - List list, - HashSet visited) { - bool notThere = visited.Add(assembly.GetName().FullName); - if (notThere) - { - list.Add(assembly); - foreach (AssemblyName referenced in assembly.GetReferencedAssemblies()) { - Assembly assembly2 = Assembly.Load(referenced); - BuildReferenceList2(assembly2,list,visited); - } - } - } - - } - #endregion - - #region ScriptOnResourceImpl - public class ScriptOnResourceImpl : ConnectorAPIOperationRunner, - ScriptOnResourceApiOp { - - public ScriptOnResourceImpl(ConnectorOperationalContext context, - Connector connector) : - base(context,connector) { - } - - public Object RunScriptOnResource(ScriptContext request, - OperationOptions options) { - Assertions.NullCheck(request, "request"); - //convert null into empty - if ( options == null ) { - options = new OperationOptionsBuilder().Build(); - } - Object rv - = ((ScriptOnResourceOp)GetConnector()).RunScriptOnResource(request, options); - return SerializerUtil.CloneObject(rv); - } - - } - #endregion - - #region SearchImpl - internal class SearchImpl : ConnectorAPIOperationRunner , SearchApiOp { - - /** - * Initializes the operation works. - */ - public SearchImpl(ConnectorOperationalContext context, - Connector connector) - :base(context,connector) { - } - - /** - * Call the SPI search routines to return the results to the - * {@link ResultsHandler}. - * - * @see SearchApiOp#search(Filter, long, long, SearchApiOp.ResultsHandler) - */ - public void Search(ObjectClass oclass, Filter originalFilter, ResultsHandler handler, OperationOptions options) { - Assertions.NullCheck(oclass, "oclass"); - Assertions.NullCheck(handler, "handler"); - //convert null into empty - if ( options == null ) { - options = new OperationOptionsBuilder().Build(); - } - ObjectNormalizerFacade normalizer = - GetNormalizer(oclass); - //chain a normalizing handler (must come before - //filter handler) - handler = - new NormalizingResultsHandler(handler,normalizer).Handle; - Filter normalizedFilter = - normalizer.NormalizeFilter(originalFilter); - - //get the IList interface that this type implements - Type interfaceType = ReflectionUtil.FindInHierarchyOf - (typeof(SearchOp<>),GetConnector().GetType()); - Type [] val = interfaceType.GetGenericArguments(); - if (val.Length != 1) { - throw new Exception("Unexpected type: "+interfaceType); - } - Type queryType = val[0]; - Type searcherRawType = typeof(RawSearcherImpl<>); - Type searcherType = - searcherRawType.MakeGenericType(queryType); - RawSearcher searcher = (RawSearcher)Activator.CreateInstance(searcherType); - // add filtering handler - handler = new FilteredResultsHandler(handler, normalizedFilter).Handle; - // add attributes to get handler - string[] attrsToGet = options.AttributesToGet; - if (attrsToGet != null && attrsToGet.Length > 0) { - handler = new SearchAttributesToGetResultsHandler( - handler, attrsToGet).Handle; - } - searcher.RawSearch(GetConnector(),oclass,normalizedFilter,handler,options); - } - } - #endregion - - #region RawSearcher - internal interface RawSearcher { - /** - * Public because it is used by TestHelpers. Raw, - * SPI-level search. - * @param search The underlying implementation of search - * (generally the connector itself) - * @param oclass The object class - * @param filter The filter - * @param handler The handler - * @param options The options - */ - void RawSearch(Object search, - ObjectClass oclass, - Filter filter, - ResultsHandler handler, - OperationOptions options); - } - #endregion - - #region RawSearcherImpl - internal class RawSearcherImpl : RawSearcher where T : class { - public void RawSearch(Object search, - ObjectClass oclass, - Filter filter, - ResultsHandler handler, - OperationOptions options) { - RawSearch((SearchOp)search,oclass,filter,handler,options); - } - - /** - * Public because it is used by TestHelpers. Raw, - * SPI-level search. - * @param search The underlying implementation of search - * (generally the connector itself) - * @param oclass The object class - * @param filter The filter - * @param handler The handler - * @param options The options - */ - public static void RawSearch(SearchOp search, - ObjectClass oclass, - Filter filter, - ResultsHandler handler, - OperationOptions options) { - - FilterTranslator translator = - search.CreateFilterTranslator(oclass, options); - IList queries = - (IList)translator.Translate(filter); - if ( queries.Count == 0) { - search.ExecuteQuery(oclass, - null, handler, options); - } - else { - //eliminate dups if more than one - bool eliminateDups = - queries.Count > 1; - DuplicateFilteringResultsHandler dups = null; - if (eliminateDups) { - dups = new DuplicateFilteringResultsHandler(handler); - handler = dups.Handle; - } - foreach( T query in queries ) { - search.ExecuteQuery(oclass, - query, handler, options); - //don't run any more queries if the consumer - //has stopped - if (dups != null ) { - if (!dups.IsStillHandling) { - break; - } - } - } - } - } - - } - #endregion - - #region SyncImpl - public class SyncImpl : ConnectorAPIOperationRunner, - SyncApiOp { - - public SyncImpl(ConnectorOperationalContext context, - Connector connector) - : base(context,connector) { - } - - public void Sync(ObjectClass objClass, SyncToken token, - SyncResultsHandler handler, - OperationOptions options) { - //token is allowed to be null, objClass and handler must not be null - Assertions.NullCheck(objClass, "objClass"); - Assertions.NullCheck(handler, "handler"); - //convert null into empty - if ( options == null ) { - options = new OperationOptionsBuilder().Build(); - } - // add a handler in the chain to remove attributes - string[] attrsToGet = options.AttributesToGet; - if (attrsToGet != null && attrsToGet.Length > 0) { - handler = new SyncAttributesToGetResultsHandler( - handler, attrsToGet).Handle; - } - //chain a normalizing results handler - ObjectNormalizerFacade normalizer = - GetNormalizer(objClass); - handler = new NormalizingSyncResultsHandler(handler,normalizer).Handle; - ((SyncOp)GetConnector()).Sync(objClass, token, handler, options); - } - public SyncToken GetLatestSyncToken(ObjectClass objectClass) - { - return ((SyncOp) GetConnector()).GetLatestSyncToken(objectClass); - } - } - #endregion - - #region TestImpl - /** - * Provides a method for the API to call the SPI's test method on the - * connector. The test method is intended to determine if the {@link Connector} - * is ready to perform the various operations it supports. - * - * @author Will Droste - * - */ - internal class TestImpl : ConnectorAPIOperationRunner , TestApiOp { - - public TestImpl(ConnectorOperationalContext context, Connector connector) - :base(context,connector) { - } - - /** - * {@inheritDoc} - */ - public void Test() { - ((TestOp) GetConnector()).Test(); - } - - } - #endregion - - #region UpdateImpl - /** - * NOTE: internal class, public only for unit tests - * Handles both version of update this include simple replace and the advance - * update. - */ - public class UpdateImpl : ConnectorAPIOperationRunner , UpdateApiOp { - /** - * All the operational attributes that can not be added or deleted. - */ - static readonly HashSet OPERATIONAL_ATTRIBUTE_NAMES = new HashSet(); - - const String OPERATIONAL_ATTRIBUTE_ERR = - "Operational attribute '{0}' can not be added or deleted only replaced."; - - static UpdateImpl() { - OPERATIONAL_ATTRIBUTE_NAMES.Add(Name.NAME); - CollectionUtil.AddAll(OPERATIONAL_ATTRIBUTE_NAMES, - OperationalAttributes.OPERATIONAL_ATTRIBUTE_NAMES); - } - - /** - * Determines which type of update a connector supports and then uses that - * handler. - */ - public UpdateImpl(ConnectorOperationalContext context, - Connector connector) - :base(context,connector){ - } - - public Uid Update(ObjectClass objclass, - Uid uid, - ICollection replaceAttributes, - OperationOptions options) { - // validate all the parameters.. - ValidateInput(objclass,uid,replaceAttributes,false); - //cast null as empty - if ( options == null ) { - options = new OperationOptionsBuilder().Build(); - } - - ObjectNormalizerFacade normalizer = - GetNormalizer(objclass); - uid = (Uid)normalizer.NormalizeAttribute(uid); - replaceAttributes = - normalizer.NormalizeAttributes(replaceAttributes); - UpdateOp op = (UpdateOp)GetConnector(); - Uid ret = op.Update(objclass, uid, replaceAttributes, options); - return (Uid)normalizer.NormalizeAttribute(ret); - } - - public Uid AddAttributeValues(ObjectClass objclass, - Uid uid, - ICollection valuesToAdd, - OperationOptions options) { - // validate all the parameters.. - ValidateInput(objclass,uid,valuesToAdd,true); - //cast null as empty - if ( options == null ) { - options = new OperationOptionsBuilder().Build(); - } - - ObjectNormalizerFacade normalizer = - GetNormalizer(objclass); - uid = (Uid)normalizer.NormalizeAttribute(uid); - valuesToAdd = - normalizer.NormalizeAttributes(valuesToAdd); - UpdateOp op = (UpdateOp)GetConnector(); - Uid ret; - if ( op is UpdateAttributeValuesOp ) { - UpdateAttributeValuesOp valueOp = - (UpdateAttributeValuesOp)op; - ret = valueOp.AddAttributeValues(objclass, uid, valuesToAdd, options); - } - else { - ICollection replaceAttributes = - FetchAndMerge(objclass,uid,valuesToAdd,true,options); - ret = op.Update(objclass, uid, replaceAttributes, options); - } - return (Uid)normalizer.NormalizeAttribute(ret); - } - - public Uid RemoveAttributeValues(ObjectClass objclass, - Uid uid, - ICollection valuesToRemove, - OperationOptions options) { - // validate all the parameters.. - ValidateInput(objclass,uid,valuesToRemove,true); - //cast null as empty - if ( options == null ) { - options = new OperationOptionsBuilder().Build(); - } - - ObjectNormalizerFacade normalizer = - GetNormalizer(objclass); - uid = (Uid)normalizer.NormalizeAttribute(uid); - valuesToRemove = - normalizer.NormalizeAttributes(valuesToRemove); - UpdateOp op = (UpdateOp)GetConnector(); - Uid ret; - if ( op is UpdateAttributeValuesOp ) { - UpdateAttributeValuesOp valueOp = - (UpdateAttributeValuesOp)op; - ret = valueOp.RemoveAttributeValues(objclass, uid, valuesToRemove, options); - } - else { - ICollection replaceAttributes = - FetchAndMerge(objclass,uid,valuesToRemove,false,options); - ret = op.Update(objclass, uid, replaceAttributes, options); - } - return (Uid)normalizer.NormalizeAttribute(ret); - } - - private ICollection FetchAndMerge(ObjectClass objclass, Uid uid, - ICollection valuesToChange, - bool add, - OperationOptions options) - { - // check that this connector supports Search.. - if (ReflectionUtil.FindInHierarchyOf(typeof(SearchOp<>),GetConnector().GetType()) != null) { - String MSG = "Connector must support search"; - throw new InvalidOperationException(MSG); - } - - //add attrs to get to operation options, so that the - //object we fetch has exactly the set of attributes we require - //(there may be ones that are not in the default set) - OperationOptionsBuilder builder = new OperationOptionsBuilder(options); - ICollection attrNames = new HashSet(); - foreach (ConnectorAttribute attribute in valuesToChange) { - attrNames.Add(attribute.Name); - } - builder.AttributesToGet=(attrNames.ToArray()); - options = builder.Build(); - - // get the connector object from the resource... - ConnectorObject o = GetConnectorObject(objclass, uid, options); - if (o == null) { - throw new UnknownUidException(uid, objclass); - } - // merge the update data.. - ICollection mergeAttrs = Merge(valuesToChange, o.GetAttributes(),add); - return mergeAttrs; - } - - /** - * Merges two connector objects into a single updated object. - */ - public ICollection Merge(ICollection updateAttrs, - ICollection baseAttrs, bool add) { - // return the merged attributes - ICollection ret = new HashSet(); - // create map that can be modified to get the subset of changes - IDictionary baseAttrMap = ConnectorAttributeUtil.ToMap(baseAttrs); - // run through attributes of the current object.. - foreach (ConnectorAttribute updateAttr in updateAttrs) { - // get the name of the update attributes - String name = updateAttr.Name; - // remove each attribute that is an update attribute.. - ConnectorAttribute baseAttr = CollectionUtil.GetValue(baseAttrMap,name,null); - IList values; - ConnectorAttribute modifiedAttr; - if (add) { - if (baseAttr == null) { - modifiedAttr = updateAttr; - } else { - // create a new list with the base attribute to add to.. - values = CollectionUtil.NewList(baseAttr.Value); - CollectionUtil.AddAll(values,updateAttr.Value); - modifiedAttr = ConnectorAttributeBuilder.Build(name, values); - } - } - else { - if (baseAttr == null) { - // nothing to actually do the attribute do not exist - continue; - } else { - // create a list with the base attribute to remove from.. - values = CollectionUtil.NewList(baseAttr.Value); - foreach (Object val in updateAttr.Value) { - values.Remove(val); - } - // if the values are empty send a null to the connector.. - if (values.Count == 0) { - modifiedAttr = ConnectorAttributeBuilder.Build(name); - } else { - modifiedAttr = ConnectorAttributeBuilder.Build(name, values); - } - } - } - ret.Add(modifiedAttr); - } - return ret; - } - - /** - * Get the {@link ConnectorObject} to modify. - */ - private ConnectorObject GetConnectorObject(ObjectClass oclass, Uid uid, OperationOptions options) { - // attempt to get the connector object.. - GetApiOp get = new GetImpl(new SearchImpl((ConnectorOperationalContext)GetOperationalContext(), - GetConnector())); - return get.GetObject(oclass, uid, options); - } - - /** - * Makes things easier if you can trust the input. - */ - public static void ValidateInput(ObjectClass objclass, - Uid uid, - ICollection attrs, bool isDelta) { - Assertions.NullCheck(uid, "uid"); - Assertions.NullCheck(objclass, "objclass"); - Assertions.NullCheck(attrs, "attrs"); - // check to make sure there's not a uid.. - if (ConnectorAttributeUtil.GetUidAttribute(attrs) != null) { - throw new ArgumentException( - "Parameter 'attrs' contains a uid."); - } - // check for things only valid during ADD/DELETE - if (isDelta) { - foreach (ConnectorAttribute attr in attrs) { - Assertions.NullCheck(attr, "attr"); - // make sure that none of the values are null.. - if (attr.Value == null) { - throw new ArgumentException( - "Can not add or remove a 'null' value."); - } - // make sure that if this an delete/add that it doesn't include - // certain attributes because it doesn't make any sense.. - String name = attr.Name; - if (OPERATIONAL_ATTRIBUTE_NAMES.Contains(name)) { - String msg = String.Format(OPERATIONAL_ATTRIBUTE_ERR, name); - throw new ArgumentException(msg); - } - } - } - } - } - #endregion - - #region ValidateImpl - internal class ValidateImpl : APIOperationRunner, ValidateApiOp { - - public ValidateImpl(OperationalContext context) - :base(context) { - } - - /** - * {@inheritDoc} - */ - public void Validate() { - GetOperationalContext().GetConfiguration().Validate(); - } - } - #endregion - -} diff --git a/DotNetConnectors.sln/FrameworkInternal/ApiRemote.cs b/DotNetConnectors.sln/FrameworkInternal/ApiRemote.cs deleted file mode 100644 index b9ba8dbe..00000000 --- a/DotNetConnectors.sln/FrameworkInternal/ApiRemote.cs +++ /dev/null @@ -1,383 +0,0 @@ -/* - * ==================== - * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER. - * - * Copyright 2007-2008 Sun Microsystems, Inc. All rights reserved. - * - * The contents of this file are subject to the terms of the Common Development - * and Distribution License("CDDL") (the "License"). You may not use this file - * except in compliance with the License. - * - * You can obtain a copy of the License at - * http://IdentityConnectors.dev.java.net/legal/license.txt - * See the License for the specific language governing permissions and limitations - * under the License. - * - * When distributing the Covered Code, include this CDDL Header Notice in each file - * and include the License file at identityconnectors/legal/license.txt. - * If applicable, add the following below this CDDL Header, with the fields - * enclosed by brackets [] replaced by your own identifying information: - * "Portions Copyrighted [year] [name of copyright owner]" - * ==================== - */ -using System; -using System.Collections.Generic; -using System.IO; -using System.Net; -using System.Reflection; -using System.Security.Authentication; -using System.Globalization; -using System.Net.Security; -using System.Net.Sockets; -using System.Security.Cryptography.X509Certificates; -using System.Threading; -using Org.IdentityConnectors.Common; -using Org.IdentityConnectors.Common.Proxy; -using Org.IdentityConnectors.Framework.Api; -using Org.IdentityConnectors.Framework.Api.Operations; -using Org.IdentityConnectors.Framework.Common.Exceptions; -using Org.IdentityConnectors.Framework.Common.Objects; -using Org.IdentityConnectors.Framework.Common.Objects.Filters; -using Org.IdentityConnectors.Framework.Common.Serializer; -using Org.IdentityConnectors.Framework.Impl.Api; -using Org.IdentityConnectors.Framework.Impl.Api.Local.Operations; -using Org.IdentityConnectors.Framework.Impl.Api.Remote.Messages; -using System.Diagnostics; -namespace Org.IdentityConnectors.Framework.Impl.Api.Remote -{ - public class RemoteFrameworkConnection : IDisposable { - - private TcpClient _socket; - private Stream _stream; - - private BinaryObjectSerializer _encoder; - private BinaryObjectDeserializer _decoder; - - public RemoteFrameworkConnection(RemoteFrameworkConnectionInfo info) { - Init(info); - } - - public RemoteFrameworkConnection(TcpClient socket, Stream stream) { - Init(socket,stream); - } - - private void Init(RemoteFrameworkConnectionInfo connectionInfo) - { - IPAddress [] addresses = - Dns.GetHostAddresses(connectionInfo.Host); - TcpClient client = new TcpClient(addresses[0].AddressFamily); - client.SendTimeout = connectionInfo.Timeout; - client.ReceiveTimeout = connectionInfo.Timeout; - client.Connect(addresses[0],connectionInfo.Port); - Stream stream; - try { - stream = client.GetStream(); - } - catch (Exception) { - try { client.Close(); } catch (Exception) {} - throw; - } - try { - if (connectionInfo.UseSSL) { - if (connectionInfo.CertificateValidationCallback != null) { - RemoteCertificateValidationCallback callback = - connectionInfo.CertificateValidationCallback; - - stream = new SslStream( - stream,false,callback); - } - else { - stream = new SslStream(stream, - false); - } - ((SslStream)stream).AuthenticateAsClient(connectionInfo.Host, - new X509CertificateCollection(new X509Certificate[0]), - SslProtocols.Tls, - false); - } - } - catch (Exception) { - try { stream.Close(); } catch (Exception) {} - try { client.Close(); } catch (Exception) {} - throw; - } - Init(client,stream); - } - - private void Init(TcpClient socket, Stream stream) - { - _socket = socket; - _stream = stream; - ObjectSerializerFactory fact = - ObjectSerializerFactory.GetInstance(); - _encoder = fact.NewBinarySerializer(_stream); - _decoder = fact.NewBinaryDeserializer(_stream); - } - - public void Dispose() { - Flush(); - _stream.Close(); - _socket.Close(); - } - - public void Flush() { - _encoder.Flush(); - } - - public void WriteObject(object obj) { - _encoder.WriteObject(obj); - } - - public object ReadObject() { - //flush first in case there is any data in the - //output buffer - Flush(); - return _decoder.ReadObject(); - } - } - - - /// - /// internal class, public only for unit tests - /// - public sealed class RemoteConnectorInfoImpl : AbstractConnectorInfo { - - - public RemoteConnectorInfoImpl() { - - } - - //transient field, not serialized - public RemoteFrameworkConnectionInfo RemoteConnectionInfo {get;set;} - - } - - public class RemoteConnectorInfoManagerImpl : ConnectorInfoManager { - - - private IList _connectorInfo; - - private RemoteConnectorInfoManagerImpl() { - - } - - public RemoteConnectorInfoManagerImpl(RemoteFrameworkConnectionInfo info) - { - using (RemoteFrameworkConnection connection = - new RemoteFrameworkConnection(info)) { - connection.WriteObject(CultureInfo.CurrentUICulture); - connection.WriteObject(info.Key); - connection.WriteObject(new HelloRequest()); - HelloResponse response = (HelloResponse)connection.ReadObject(); - if ( response.Exception != null ) { - throw response.Exception; - } - IList remoteInfos = - response.ConnectorInfos; - //populate transient fields not serialized - foreach (RemoteConnectorInfoImpl remoteInfo in remoteInfos ) { - remoteInfo.RemoteConnectionInfo=info; - } - _connectorInfo = - CollectionUtil.NewReadOnlyList(remoteInfos); - } - - } - - /** - * Derives another RemoteConnectorInfoManagerImpl with - * a different RemoteFrameworkConnectionInfo but with the - * same metadata - * @param info - * @return - */ - public RemoteConnectorInfoManagerImpl Derive(RemoteFrameworkConnectionInfo info) { - RemoteConnectorInfoManagerImpl rv = new RemoteConnectorInfoManagerImpl(); - IList remoteInfosObj = - (IList)SerializerUtil.CloneObject(_connectorInfo); - IList remoteInfos = - CollectionUtil.NewList(remoteInfosObj); - foreach (ConnectorInfo remoteInfo in remoteInfos) { - ((RemoteConnectorInfoImpl)remoteInfo).RemoteConnectionInfo=(info); - } - rv._connectorInfo = - CollectionUtil.AsReadOnlyList(remoteInfos); - return rv; - } - - - public ConnectorInfo FindConnectorInfo(ConnectorKey key) { - foreach (ConnectorInfo info in _connectorInfo) { - if ( info.ConnectorKey.Equals(key) ) { - return info; - } - } - return null; - } - - public IList ConnectorInfos { - get { - return _connectorInfo; - } - } - - } - - internal class RemoteConnectorFacadeImpl : AbstractConnectorFacade { - - /** - * Builds up the maps of supported operations and calls. - */ - public RemoteConnectorFacadeImpl(APIConfigurationImpl configuration) - :base(configuration) { - } - - protected override APIOperation GetOperationImplementation(SafeType api) { - InvocationHandler handler = new RemoteOperationInvocationHandler( - GetAPIConfiguration(), - api); - APIOperation proxy = NewAPIOperationProxy(api, handler); - // add logging.. - proxy = CreateLoggingProxy(api, proxy); - return proxy; - } - } - - - /** - * Invocation handler for all of our operations - */ - public class RemoteOperationInvocationHandler : InvocationHandler { - private readonly APIConfigurationImpl _configuration; - private readonly SafeType _operation; - - public RemoteOperationInvocationHandler(APIConfigurationImpl configuration, - SafeType operation) { - _configuration = configuration; - _operation = operation; - } - - - public Object Invoke(Object proxy, MethodInfo method, Object[] args) - { - //don't proxy toString, hashCode, or equals - if ( method.DeclaringType.Equals(typeof(object))) { - return method.Invoke(this, args); - } - - //partition arguments into arguments that can - //be simply marshalled as part of the request and - //those that are response handlers - IList simpleMarshallArgs = - CollectionUtil.NewList(args); - ObjectStreamHandler streamHandlerArg = - ExtractStreamHandler(ReflectionUtil.GetParameterTypes(method),simpleMarshallArgs); - - //build the request object - RemoteConnectorInfoImpl connectorInfo = - (RemoteConnectorInfoImpl)_configuration.ConnectorInfo; - RemoteFrameworkConnectionInfo connectionInfo = - connectorInfo.RemoteConnectionInfo; - OperationRequest request = new OperationRequest( - connectorInfo.ConnectorKey, - _configuration, - _operation, - method.Name, - simpleMarshallArgs); - - //create the connection - RemoteFrameworkConnection connection = - new RemoteFrameworkConnection(connectionInfo); - try { - connection.WriteObject(CultureInfo.CurrentUICulture); - connection.WriteObject(connectionInfo.Key); - //send the request - connection.WriteObject(request); - - //now process each response stream (if any) - if ( streamHandlerArg != null ) { - HandleStreamResponse(connection,streamHandlerArg); - } - - //finally return the actual return value - OperationResponsePart response = - (OperationResponsePart)connection.ReadObject(); - if ( response.Exception != null ) { - throw response.Exception; - } - return response.Result; - } - finally { - connection.Dispose(); - } - } - /** - * Handles a stream response until the end of the stream - */ - private static void HandleStreamResponse(RemoteFrameworkConnection connection, ObjectStreamHandler streamHandler) { - Object response; - bool handleMore = true; - while ( true ) { - response = connection.ReadObject(); - if ( response is OperationResponsePart ) { - OperationResponsePart part = (OperationResponsePart)response; - if ( part.Exception != null ) { - throw part.Exception; - } - object obj = - part.Result; - if ( handleMore ) { - handleMore = streamHandler.Handle(obj); - } - } - else if ( response is OperationResponsePause ) { - if ( handleMore ) { - connection.WriteObject(new OperationRequestMoreData()); - } - else { - connection.WriteObject(new OperationRequestStopData()); - } - } - else if ( response is OperationResponseEnd ) { - break; - } - else { - throw new ConnectorException("Unexpected response: "+response); - } - } - } - - /** - * Partitions arguments into regular arguments and - * stream arguments. - * @param paramTypes The param types of the method - * @param arguments The passed-in arguments. As a - * side-effect will be set to just the regular arguments. - * @return The stream handler arguments. - */ - private static ObjectStreamHandler ExtractStreamHandler(Type [] paramTypes, - IList arguments) { - ObjectStreamHandler rv = null; - IList filteredArguments = new List(); - for ( int i = 0; i < paramTypes.Length; i++ ) { - Type paramType = paramTypes[i]; - object arg = arguments[i]; - if (StreamHandlerUtil.IsAdaptableToObjectStreamHandler(paramType)) { - ObjectStreamHandler handler = StreamHandlerUtil.AdaptToObjectStreamHandler(paramType, arg); - if ( rv != null ) { - throw new InvalidOperationException("Multiple stream handlers not supported"); - } - rv = handler; - } - else { - filteredArguments.Add(arg); - } - } - arguments.Clear(); - CollectionUtil.AddAll(arguments,filteredArguments); - return rv; - } - } - - -} diff --git a/DotNetConnectors.sln/FrameworkInternal/ApiRemoteMessages.cs b/DotNetConnectors.sln/FrameworkInternal/ApiRemoteMessages.cs deleted file mode 100644 index 62c58ab0..00000000 --- a/DotNetConnectors.sln/FrameworkInternal/ApiRemoteMessages.cs +++ /dev/null @@ -1,243 +0,0 @@ -/* - * ==================== - * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER. - * - * Copyright 2007-2008 Sun Microsystems, Inc. All rights reserved. - * - * The contents of this file are subject to the terms of the Common Development - * and Distribution License("CDDL") (the "License"). You may not use this file - * except in compliance with the License. - * - * You can obtain a copy of the License at - * http://IdentityConnectors.dev.java.net/legal/license.txt - * See the License for the specific language governing permissions and limitations - * under the License. - * - * When distributing the Covered Code, include this CDDL Header Notice in each file - * and include the License file at identityconnectors/legal/license.txt. - * If applicable, add the following below this CDDL Header, with the fields - * enclosed by brackets [] replaced by your own identifying information: - * "Portions Copyrighted [year] [name of copyright owner]" - * ==================== - */ -using System; -using System.Collections.Generic; -using Org.IdentityConnectors.Common; -using Org.IdentityConnectors.Framework.Api; -using Org.IdentityConnectors.Framework.Api.Operations; -namespace Org.IdentityConnectors.Framework.Impl.Api.Remote.Messages -{ - /// - /// internal class, public only for unit tests - /// - public class HelloRequest : Message { - - public HelloRequest() { - } - - } - - /// - /// internal class, public only for unit tests - /// - public class HelloResponse : Message { - - /** - * The exception - */ - private Exception _exception; - - /** - * List of connector infos, containing infos for all the connectors - * on the server. - */ - private IList _connectorInfos; - - public HelloResponse(Exception exception, - IList connectorInfos) { - _exception = exception; - _connectorInfos = CollectionUtil.NewReadOnlyList(connectorInfos); - } - - public Exception Exception { - get { - return _exception; - } - } - - public IList ConnectorInfos { - get { - return _connectorInfos; - } - } - - } - - /// - /// internal class, public only for unit tests - /// - - public interface Message { - - } - - /// - /// internal class, public only for unit tests - /// - public class OperationRequest : Message { - /** - * The key of the connector to operate on. - */ - private readonly ConnectorKey _connectorKey; - - /** - * The configuration information to use. - */ - private readonly APIConfigurationImpl _configuration; - - /** - * The operation to perform. - */ - private readonly SafeType _operation; - - /** - * The name of the method since operations can have more - * than one method. - * NOTE: this is case-insensitive - */ - private readonly String _operationMethodName; - - /** - * The arguments to the operation. In general, these correspond - * to the actual arguments of the method. The one exception is - * search - in this case, the callback is not passed. - */ - private readonly IList _arguments; - - public OperationRequest(ConnectorKey key, - APIConfigurationImpl apiConfiguration, - SafeType operation, - string operationMethodName, - IList arguments) { - _connectorKey = key; - _configuration = apiConfiguration; - _operation = operation; - _operationMethodName = operationMethodName; - _arguments = CollectionUtil.NewReadOnlyList(arguments); - } - - public ConnectorKey ConnectorKey { - get { - return _connectorKey; - } - } - - public APIConfigurationImpl Configuration { - get { - return _configuration; - } - } - - public SafeType Operation { - get { - return _operation; - } - } - - public string OperationMethodName { - get { - return _operationMethodName; - } - } - - public IList Arguments { - get { - return _arguments; - } - } - } - - /// - /// internal class, public only for unit tests - /// - public class OperationRequestMoreData : Message { - - public OperationRequestMoreData() { - } - - } - - /// - /// internal class, public only for unit tests - /// - public class OperationRequestStopData : Message { - - public OperationRequestStopData() { - } - - } - - /// - /// internal class, public only for unit tests - /// - public class OperationResponseEnd : Message { - - public OperationResponseEnd() { - } - - } - - /// - /// internal class, public only for unit tests - /// - public class OperationResponsePart : Message { - private Exception _exception; - private Object _result; - - public OperationResponsePart(Exception ex, Object result) { - _exception = ex; - _result = result; - } - - public Exception Exception { - get { - return _exception; - } - } - - public Object Result { - get { - return _result; - } - } - } - - /// - /// internal class, public only for unit tests - /// - public class OperationResponsePause : Message { - - public OperationResponsePause() { - } - - } - - public class EchoMessage : Message { - private object _object; - private string _objectXml; - public EchoMessage(object obj, string xml) { - _object = obj; - _objectXml = xml; - } - public object Object { - get { - return _object; - } - } - public string ObjectXml { - get { - return _objectXml; - } - } - } -} diff --git a/DotNetConnectors.sln/FrameworkInternal/AssemblyInfo.cs b/DotNetConnectors.sln/FrameworkInternal/AssemblyInfo.cs deleted file mode 100644 index 5c1c75d7..00000000 --- a/DotNetConnectors.sln/FrameworkInternal/AssemblyInfo.cs +++ /dev/null @@ -1,53 +0,0 @@ -/* - * ==================== - * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER. - * - * Copyright 2007-2008 Sun Microsystems, Inc. All rights reserved. - * - * The contents of this file are subject to the terms of the Common Development - * and Distribution License("CDDL") (the "License"). You may not use this file - * except in compliance with the License. - * - * You can obtain a copy of the License at - * http://IdentityConnectors.dev.java.net/legal/license.txt - * See the License for the specific language governing permissions and limitations - * under the License. - * - * When distributing the Covered Code, include this CDDL Header Notice in each file - * and include the License file at identityconnectors/legal/license.txt. - * If applicable, add the following below this CDDL Header, with the fields - * enclosed by brackets [] replaced by your own identifying information: - * "Portions Copyrighted [year] [name of copyright owner]" - * ==================== - */ -#region Using directives - -using System; -using System.Reflection; -using System.Runtime.InteropServices; - -#endregion - -// General Information about an assembly is controlled through the following -// set of attributes. Change these attribute values to modify the information -// associated with an assembly. -[assembly: AssemblyTitle("FrameworkInternal")] -[assembly: AssemblyDescription("")] -[assembly: AssemblyConfiguration("")] -[assembly: AssemblyCompany("")] -[assembly: AssemblyProduct("FrameworkInternal")] -[assembly: AssemblyCopyright("")] -[assembly: AssemblyTrademark("")] -[assembly: AssemblyCulture("")] - -// This sets the default COM visibility of types in the assembly to invisible. -// If you need to expose a type to COM, use [ComVisible(true)] on that type. -[assembly: ComVisible(false)] - -// The assembly version has following format : -// -// Major.Minor.Build.Revision -// -// You can specify all the values or you can use the default the Revision and -// Build Numbers by using the '*' as shown below: -[assembly: AssemblyVersion("1.0.0.0")] diff --git a/DotNetConnectors.sln/FrameworkInternal/FrameworkInternal.csproj b/DotNetConnectors.sln/FrameworkInternal/FrameworkInternal.csproj deleted file mode 100644 index 34ca21fd..00000000 --- a/DotNetConnectors.sln/FrameworkInternal/FrameworkInternal.csproj +++ /dev/null @@ -1,103 +0,0 @@ - - - - {5B011775-B121-4EEE-A410-BA2D2F5BFB8B} - Debug - AnyCPU - Library - Org.IdentityConnectors - FrameworkInternal - v3.5 - True - False - 4 - false - - - prompt - 4 - true - bin\Debug\ - Full - False - True - DEBUG;TRACE - - - pdbonly - bin\Release\ - False - prompt - 4 - True - False - TRACE - - - False - Auto - 4194304 - AnyCPU - 4096 - - - - - - - 3.5 - - - - - - - - - - - - - - - - - - - - - Designer - - - - - {F140E8DA-52B4-4159-992A-9DA10EA8EEFB} - Common - - - {8B24461B-456A-4032-89A1-CD418F7B5B62} - Framework - - - diff --git a/DotNetConnectors.sln/FrameworkInternal/Resources.resx b/DotNetConnectors.sln/FrameworkInternal/Resources.resx deleted file mode 100644 index 9d8ea6ed..00000000 --- a/DotNetConnectors.sln/FrameworkInternal/Resources.resx +++ /dev/null @@ -1,492 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - text/microsoft-resx - - - 2.0 - - - System.Resources.ResXResourceReader, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 - - - System.Resources.ResXResourceWriter, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 - - - <?xml version='1.0' encoding='UTF-8'?> - - -<!--=======================================================--> -<!--= =--> -<!--= DTD for Connector Objects =--> -<!--= =--> -<!--=======================================================--> - -<!--=======================================================--> -<!--= =--> -<!--= All XML Objects =--> -<!--= =--> -<!--=======================================================--> - -<!ENTITY % exceptionTypes - "AlreadyExistsException | ConfigurationException | ConnectionBrokenException - | ConnectionFailedException | ConnectorIOException | InvalidPasswordException - | UnknownUidException | InvalidCredentialException | PermissionDeniedException - | ConnectorSecurityException | OperationTimeoutException | ConnectorException - | RuntimeException | Exception | Throwable | PasswordExpiredException - "> - -<!ENTITY % messageTypes - "HelloRequest | HelloResponse | OperationRequest | OperationResponseEnd | - OperationResponsePart | OperationRequestMoreData | OperationRequestStopData | - OperationResponsePause | EchoMessage - "> - -<!ENTITY % filterTypes - "AndFilter | ContainsFilter | EndsWithFilter | EqualsFilter | - GreaterThanFilter | GreaterThanOrEqualFilter | LessThanFilter | - LessThanOrEqualFilter | NotFilter | OrFilter | StartsWithFilter | - ContainsAllValuesFilter - "> - -<!ENTITY % attributeTypes - "Attribute | Uid | Name"> - -<!ENTITY % primitiveTypes - "null | Array | Boolean | boolean | Character | char | Integer | - int | Long | long | Float | float | Double | double | String | - URI | File | BigDecimal | BigInteger | ByteArray | Class | - Map | List | Set | Locale | GuardedString - "> - -<!ENTITY % xmlObject - "%primitiveTypes; | %exceptionTypes; | %messageTypes; | %filterTypes; | %attributeTypes; | -ObjectPoolConfiguration | ConfigurationProperty | ConfigurationProperties | -APIConfiguration | ConnectorMessages | ConnectorKey | ConnectorInfo | -UpdateApiOpType | AttributeInfo | ConnectorObject | ObjectClass | -ObjectClassInfo | Schema | ScriptContext | OperationOptions | -OperationOptionInfo | SyncDeltaType | SyncToken | SyncDelta | QualifiedUid -"> - - - -<!--=======================================================--> -<!--= =--> -<!--= Top Level Element for object streams =--> -<!--= =--> -<!--=======================================================--> - - -<!ELEMENT MultiObject (( - %xmlObject; -)*)> - - -<!--=======================================================--> -<!--= =--> -<!--= Primitives =--> -<!--= =--> -<!--=======================================================--> - - - -<!ELEMENT null EMPTY> -<!ELEMENT Array ((%xmlObject;)*)> -<!ATTLIST Array - componentType CDATA #REQUIRED -> -<!ELEMENT Boolean (#PCDATA)> -<!ELEMENT boolean (#PCDATA)> -<!ELEMENT Character (#PCDATA)> -<!ELEMENT char (#PCDATA)> -<!ELEMENT Integer (#PCDATA)> -<!ELEMENT int (#PCDATA)> -<!ELEMENT Long (#PCDATA)> -<!ELEMENT long (#PCDATA)> -<!ELEMENT Float (#PCDATA)> -<!ELEMENT float (#PCDATA)> -<!ELEMENT Double (#PCDATA)> -<!ELEMENT double (#PCDATA)> -<!ELEMENT String (#PCDATA)> -<!ELEMENT URI (#PCDATA)> -<!ELEMENT File (#PCDATA)> -<!ELEMENT BigDecimal EMPTY> -<!ATTLIST BigDecimal - unscaled CDATA #REQUIRED - scale CDATA #REQUIRED -> -<!ELEMENT BigInteger (#PCDATA)> -<!ELEMENT ByteArray (#PCDATA)> -<!ELEMENT Class (#PCDATA)> -<!ELEMENT Map ((MapEntry)*)> -<!ATTLIST Map - caseInsensitive CDATA #IMPLIED -> -<!ELEMENT MapEntry ((%xmlObject;),(%xmlObject;))> -<!ELEMENT Keys ((%xmlObject;)*)> -<!ELEMENT List ((%xmlObject;)*)> -<!ELEMENT Set ((%xmlObject;)*)> -<!ATTLIST Set - caseInsensitive CDATA #IMPLIED -> -<!ELEMENT Locale EMPTY> -<!ATTLIST Locale - language CDATA #IMPLIED - country CDATA #IMPLIED - variant CDATA #IMPLIED -> -<!ELEMENT GuardedString (#PCDATA)> - -<!--=======================================================--> -<!--= =--> -<!--= APIConfiguration =--> -<!--= =--> -<!--=======================================================--> - -<!ELEMENT ObjectPoolConfiguration EMPTY> -<!ATTLIST ObjectPoolConfiguration - maxObjects CDATA #IMPLIED - maxIdle CDATA #IMPLIED - maxWait CDATA #IMPLIED - minEvictableIdleTimeMillis CDATA #IMPLIED - minIdle CDATA #IMPLIED -> - -<!ELEMENT ConfigurationProperty (value,operations)> -<!ATTLIST ConfigurationProperty - order CDATA #IMPLIED - confidential CDATA #IMPLIED - required CDATA #IMPLIED - name CDATA #REQUIRED - helpMessageKey CDATA #REQUIRED - displayMessageKey CDATA #REQUIRED - type CDATA #REQUIRED -> -<!ELEMENT value (%xmlObject;)> -<!ELEMENT operations (Class)*> -<!ELEMENT ConfigurationProperties ((ConfigurationProperty)*)> - -<!ELEMENT APIConfiguration (connectorPoolConfiguration,ConfigurationProperties,timeoutMap,SupportedOperations)> -<!ATTLIST APIConfiguration - connectorPoolingSupported CDATA #REQUIRED - producerBufferSize CDATA #REQUIRED -> -<!ELEMENT connectorPoolConfiguration ((ObjectPoolConfiguration))> -<!ELEMENT timeoutMap (Map)> -<!ELEMENT SupportedOperations ((Class)*)> -<!ELEMENT ConnectorMessages (catalogs)> -<!ELEMENT catalogs (Map)> -<!ELEMENT ConnectorKey EMPTY> -<!ATTLIST ConnectorKey - bundleName CDATA #REQUIRED - bundleVersion CDATA #REQUIRED - connectorName CDATA #REQUIRED -> -<!ELEMENT ConnectorInfo (ConnectorKey,ConnectorMessages,APIConfiguration)> -<!ATTLIST ConnectorInfo - connectorDisplayNameKey CDATA #REQUIRED -> - -<!--=======================================================--> -<!--= =--> -<!--= Common Objects =--> -<!--= =--> -<!--=======================================================--> -<!ELEMENT Attribute (Values)?> -<!ELEMENT Values ((%xmlObject;)*)> -<!ATTLIST Attribute - name CDATA #REQUIRED -> - -<!ELEMENT Uid (#PCDATA)> -<!ELEMENT Name (#PCDATA)> - - - -<!ELEMENT UpdateApiOpType (#PCDATA)> - - -<!ELEMENT AlreadyExistsException EMPTY> -<!ATTLIST AlreadyExistsException - message CDATA #IMPLIED -> -<!ELEMENT ConfigurationException EMPTY> -<!ATTLIST ConfigurationException - message CDATA #IMPLIED -> -<!ELEMENT ConnectionBrokenException EMPTY> -<!ATTLIST ConnectionBrokenException - message CDATA #IMPLIED -> -<!ELEMENT ConnectionFailedException EMPTY> -<!ATTLIST ConnectionFailedException - message CDATA #IMPLIED -> -<!ELEMENT ConnectorIOException EMPTY> -<!ATTLIST ConnectorIOException - message CDATA #IMPLIED -> -<!ELEMENT InvalidPasswordException EMPTY> -<!ATTLIST InvalidPasswordException - message CDATA #IMPLIED -> -<!ELEMENT PasswordExpiredException (Uid?)> -<!ATTLIST PasswordExpiredException - message CDATA #IMPLIED -> -<!ELEMENT UnknownUidException EMPTY> -<!ATTLIST UnknownUidException - message CDATA #IMPLIED -> -<!ELEMENT InvalidCredentialException EMPTY> -<!ATTLIST InvalidCredentialException - message CDATA #IMPLIED -> -<!ELEMENT PermissionDeniedException EMPTY> -<!ATTLIST PermissionDeniedException - message CDATA #IMPLIED -> -<!ELEMENT ConnectorSecurityException EMPTY> -<!ATTLIST ConnectorSecurityException - message CDATA #IMPLIED -> -<!ELEMENT OperationTimeoutException EMPTY> -<!ATTLIST OperationTimeoutException - message CDATA #IMPLIED -> -<!ELEMENT ConnectorException EMPTY> -<!ATTLIST ConnectorException - message CDATA #IMPLIED -> -<!ELEMENT RuntimeException EMPTY> -<!ATTLIST RuntimeException - message CDATA #IMPLIED -> -<!ELEMENT Exception EMPTY> -<!ATTLIST Exception - message CDATA #IMPLIED -> -<!ELEMENT Throwable EMPTY> -<!ATTLIST Throwable - message CDATA #IMPLIED -> -<!ELEMENT AttributeInfo (AttributeInfoFlag*)> -<!ATTLIST AttributeInfo - name CDATA #REQUIRED - type CDATA #REQUIRED -> -<!ELEMENT AttributeInfoFlag EMPTY> -<!ATTLIST AttributeInfoFlag - value ( REQUIRED | MULTIVALUED | NOT_CREATABLE | NOT_UPDATEABLE | NOT_READABLE | NOT_RETURNED_BY_DEFAULT ) #REQUIRED -> -<!ELEMENT ConnectorObject (ObjectClass,Attributes)> -<!ELEMENT Attributes ((%attributeTypes;)*)> - -<!ELEMENT ObjectClass EMPTY> -<!ATTLIST ObjectClass - type CDATA #REQUIRED -> - -<!ELEMENT ObjectClassInfo (AttributeInfos)> -<!ATTLIST ObjectClassInfo - type CDATA #REQUIRED - container CDATA #IMPLIED -> -<!ELEMENT AttributeInfos ((AttributeInfo)*)> - -<!ELEMENT Schema (ObjectClassInfos,OperationOptionInfos,objectClassesByOperation,optionsByOperation)> -<!ELEMENT ObjectClassInfos ((ObjectClassInfo)*)> -<!ELEMENT OperationOptionInfos ((OperationOptionInfo)*)> -<!ELEMENT objectClassesByOperation (Map)> -<!ELEMENT optionsByOperation (Map)> - -<!ELEMENT ScriptContext (scriptArguments,scriptText)> -<!ATTLIST ScriptContext - scriptLanguage CDATA #REQUIRED -> -<!ELEMENT scriptText (#PCDATA)> -<!ELEMENT scriptArguments (Map)> - -<!ELEMENT OperationOptions (options)> -<!ELEMENT options (Map)> -<!ELEMENT OperationOptionInfo EMPTY> -<!ATTLIST OperationOptionInfo - name CDATA #REQUIRED - type CDATA #REQUIRED -> -<!ELEMENT SyncDeltaType EMPTY> -<!ATTLIST SyncDeltaType - value ( CREATE_OR_UPDATE | DELETE ) #REQUIRED -> - -<!ELEMENT SyncToken (value)> -<!ELEMENT SyncDelta (SyncDeltaType,SyncToken,Uid,ConnectorObject?)> -<!ELEMENT QualifiedUid (ObjectClass,Uid)> - - -<!--=======================================================--> -<!--= =--> -<!--= Filters =--> -<!--= =--> -<!--=======================================================--> - - -<!ELEMENT attribute (%attributeTypes;)> -<!ELEMENT AndFilter ((%filterTypes;),(%filterTypes;))> -<!ELEMENT ContainsFilter (attribute)> -<!ELEMENT EndsWithFilter (attribute)> -<!ELEMENT EqualsFilter (attribute)> -<!ELEMENT GreaterThanFilter (attribute)> -<!ELEMENT GreaterThanOrEqualFilter (attribute)> -<!ELEMENT LessThanFilter (attribute)> -<!ELEMENT LessThanOrEqualFilter (attribute)> -<!ELEMENT NotFilter (%filterTypes;)> -<!ELEMENT OrFilter ((%filterTypes;),(%filterTypes;))> -<!ELEMENT StartsWithFilter (attribute)> -<!ELEMENT ContainsAllValuesFilter (attribute)> - -<!--=======================================================--> -<!--= =--> -<!--= Messages =--> -<!--= =--> -<!--=======================================================--> -<!ELEMENT HelloRequest EMPTY> -<!ELEMENT ConnectorInfos ((ConnectorInfo)*)> -<!ELEMENT exception (%exceptionTypes;)> -<!ELEMENT HelloResponse (exception,ConnectorInfos)> -<!ELEMENT OperationRequest (ConnectorKey,APIConfiguration,Arguments)> -<!ATTLIST OperationRequest - operation CDATA #REQUIRED - operationMethodName CDATA #REQUIRED -> -<!ELEMENT Arguments ((%xmlObject;)*)> -<!ELEMENT OperationResponseEnd EMPTY> -<!ELEMENT OperationResponsePart (exception,result)> -<!ELEMENT result ((%xmlObject;)*)> -<!ELEMENT OperationRequestMoreData EMPTY> -<!ELEMENT OperationRequestStopData EMPTY> -<!ELEMENT OperationResponsePause EMPTY> -<!ELEMENT EchoMessage (value,objectXml?)> -<!ELEMENT objectXml (#PCDATA)> - - - Test Framework Value - - - Account - - - Person - - - Group - - - Organization - - \ No newline at end of file diff --git a/DotNetConnectors.sln/FrameworkInternal/Security.cs b/DotNetConnectors.sln/FrameworkInternal/Security.cs deleted file mode 100644 index 9520c701..00000000 --- a/DotNetConnectors.sln/FrameworkInternal/Security.cs +++ /dev/null @@ -1,124 +0,0 @@ -/* - * ==================== - * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER. - * - * Copyright 2007-2008 Sun Microsystems, Inc. All rights reserved. - * - * The contents of this file are subject to the terms of the Common Development - * and Distribution License("CDDL") (the "License"). You may not use this file - * except in compliance with the License. - * - * You can obtain a copy of the License at - * http://IdentityConnectors.dev.java.net/legal/license.txt - * See the License for the specific language governing permissions and limitations - * under the License. - * - * When distributing the Covered Code, include this CDDL Header Notice in each file - * and include the License file at identityconnectors/legal/license.txt. - * If applicable, add the following below this CDDL Header, with the fields - * enclosed by brackets [] replaced by your own identifying information: - * "Portions Copyrighted [year] [name of copyright owner]" - * ==================== - */ -using System; - -using Org.IdentityConnectors.Common.Security; -using System.Text; -using System.Security.Cryptography; - -namespace Org.IdentityConnectors.Common.Security.Impl -{ - public class EncryptorFactoryImpl : EncryptorFactory { - - private readonly Encryptor _defaultEncryptor; - - public EncryptorFactoryImpl() { - _defaultEncryptor = new EncryptorImpl(); - } - - public override Encryptor GetDefaultEncryptor() { - return _defaultEncryptor; - } - } - - public class EncryptorImpl : Encryptor { - - private readonly static byte [] _defaultKeyBytes = - { - (byte) 0x23,(byte) 0x65,(byte) 0x87,(byte) 0x22, - (byte) 0x59,(byte) 0x78,(byte) 0x54,(byte) 0x43, - (byte) 0x64,(byte) 0x05,(byte) 0x6A,(byte) 0xBD, - (byte) 0x34,(byte) 0xA2,(byte) 0x34,(byte) 0x57, - }; - private readonly static byte [] _defaultIvBytes = - { - (byte) 0x51,(byte) 0x65,(byte) 0x22,(byte) 0x23, - (byte) 0x64,(byte) 0x05,(byte) 0x6A,(byte) 0xBE, - (byte) 0x51,(byte) 0x65,(byte) 0x22,(byte) 0x23, - (byte) 0x64,(byte) 0x05,(byte) 0x6A,(byte) 0xBE, - }; - - - - - public EncryptorImpl() { - } - - public UnmanagedArray Decrypt(byte[] bytes) { - using (SymmetricAlgorithm algo = Aes.Create()) { - algo.Padding = PaddingMode.PKCS7; - algo.Mode = CipherMode.CBC; - algo.Key = _defaultKeyBytes; - algo.IV = _defaultIvBytes; - using (ICryptoTransform transform = algo.CreateDecryptor()) { - return Decrypt2(bytes,transform); - } - } - } - - private unsafe UnmanagedArray Decrypt2(byte [] bytes, ICryptoTransform transform) { - byte [] managedBytes = transform.TransformFinalBlock(bytes,0,bytes.Length); - //pin it ASAP. this is a small race condition that is unavoidable due - //to the way the crypto API's return byte[]. - fixed(byte*dummy = managedBytes) { - try { - UnmanagedByteArray rv = new UnmanagedByteArray(managedBytes.Length); - for ( int i = 0; i < rv.Length; i++ ) { - rv[i] = managedBytes[i]; - } - return rv; - } - finally { - SecurityUtil.Clear(managedBytes); - } - } - } - - public byte[] Encrypt(UnmanagedArray bytes) { - using (SymmetricAlgorithm algo = Aes.Create()) { - algo.Padding = PaddingMode.PKCS7; - algo.Mode = CipherMode.CBC; - algo.Key = _defaultKeyBytes; - algo.IV = _defaultIvBytes; - using (ICryptoTransform transform = algo.CreateEncryptor()) { - return Encrypt2(bytes,transform); - } - } - } - - private unsafe byte[] Encrypt2(UnmanagedArray bytes, ICryptoTransform transform) { - byte [] managedBytes = new byte[bytes.Length]; - fixed(byte*dummy = managedBytes) { - try { - SecurityUtil.UnmanagedBytesToManagedBytes(bytes,managedBytes); - byte [] rv = transform.TransformFinalBlock(managedBytes,0,managedBytes.Length); - return rv; - } - finally { - SecurityUtil.Clear(managedBytes); - } - } - } - - } -} diff --git a/DotNetConnectors.sln/FrameworkInternal/Serializer.cs b/DotNetConnectors.sln/FrameworkInternal/Serializer.cs deleted file mode 100644 index 2fdc9a6a..00000000 --- a/DotNetConnectors.sln/FrameworkInternal/Serializer.cs +++ /dev/null @@ -1,2404 +0,0 @@ -/* - * ==================== - * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER. - * - * Copyright 2007-2008 Sun Microsystems, Inc. All rights reserved. - * - * The contents of this file are subject to the terms of the Common Development - * and Distribution License("CDDL") (the "License"). You may not use this file - * except in compliance with the License. - * - * You can obtain a copy of the License at - * http://IdentityConnectors.dev.java.net/legal/license.txt - * See the License for the specific language governing permissions and limitations - * under the License. - * - * When distributing the Covered Code, include this CDDL Header Notice in each file - * and include the License file at identityconnectors/legal/license.txt. - * If applicable, add the following below this CDDL Header, with the fields - * enclosed by brackets [] replaced by your own identifying information: - * "Portions Copyrighted [year] [name of copyright owner]" - * ==================== - */ -using System; -using System.IO; -using System.Collections.Generic; -using System.Security; -using Org.IdentityConnectors.Common; -using Org.IdentityConnectors.Common.Pooling; -using Org.IdentityConnectors.Common.Security; -using Org.IdentityConnectors.Framework.Api; -using Org.IdentityConnectors.Framework.Api.Operations; -using Org.IdentityConnectors.Framework.Common.Objects; -using Org.IdentityConnectors.Framework.Common.Objects.Filters; -using Org.IdentityConnectors.Framework.Common.Exceptions; -using Org.IdentityConnectors.Framework.Common.Serializer; -using Org.IdentityConnectors.Framework.Impl.Api; -using Org.IdentityConnectors.Framework.Impl.Api.Remote; -using Org.IdentityConnectors.Framework.Impl.Api.Remote.Messages; -using Org.IdentityConnectors.Framework.Impl.Serializer.Binary; -using Org.IdentityConnectors.Framework.Impl.Serializer.Xml; -using System.Linq; -using System.Globalization; -namespace Org.IdentityConnectors.Framework.Impl.Serializer -{ - #region Serialization Framework - internal abstract class AbstractObjectSerializationHandler - : ObjectTypeMapperImpl, ObjectSerializationHandler { - - protected AbstractObjectSerializationHandler(Type handledClass, - String type) - :base(handledClass,type) { - } - /** - * Called to serialize the object. - */ - abstract public void Serialize(Object obj, ObjectEncoder encoder) ; - - /** - * Called to deserialize the object. - */ - abstract public Object Deserialize(ObjectDecoder decoder) ; - } - - - internal class EnumSerializationHandler : - AbstractObjectSerializationHandler { - - public EnumSerializationHandler (Type clazz, String name) - :base(clazz,name) { - } - - - public override object Deserialize(ObjectDecoder decoder) { - String val = decoder.ReadStringField("value",null); - Type enumClass = HandledObjectType; - Object rv = Enum.Parse(enumClass, val); - return rv; - } - - public override void Serialize(object obj, ObjectEncoder encoder) - { - Enum e = (Enum)obj; - encoder.WriteStringField("value",Enum.GetName(e.GetType(),e)); - } - - } - - /** - * Interface to abstract away the difference between deserializing - * xml and binary - */ - internal interface ObjectDecoder { - /** - * Reads an object using the appropriate serializer for that object - * @param fieldName. A hint of the field name. Ignored for binary - * serialization. The subelement name for xml serialization - */ - Object ReadObjectField(String fieldName, - Type expectedType, - Object dflt); - - /** - * Reads a bool. - * @param fieldName. A hint of the field name. Ignored for binary - * serialization. The subelement name for xml serialization - */ - bool ReadBooleanField(String fieldName, bool dflt) ; - - /** - * Reads an int. - * @param fieldName. A hint of the field name. Ignored for binary - * serialization. The subelement name for xml serialization - */ - int ReadIntField(String fieldName, int dflt) ; - - /** - * Reads a long. - * @param fieldName. A hint of the field name. Ignored for binary - * serialization. The subelement name for xml serialization - */ - long ReadLongField(String fieldName, long dflt) ; - - /** - * Reads a float. - * @param fieldName. A hint of the field name. Ignored for binary - * serialization. The subelement name for xml serialization - */ - float ReadFloatField(String fieldName, float dflt) ; - - /** - * Reads a double. - * @param fieldName. A hint of the field name. Ignored for binary - * serialization. The subelement name for xml serialization - * @param v. The value to serialize - */ - double ReadDoubleField(String fieldName, double dflt) ; - - /** - * Reads a double. - * @param fieldName. A hint of the field name. Ignored for binary - * serialization. The subelement name for xml serialization - * @param v. The value to serialize - */ - string ReadStringField(String fieldName, string dflt) ; - - /** - * Reads a double. - * @param fieldName. A hint of the field name. Ignored for binary - * serialization. The subelement name for xml serialization - * @param v. The value to serialize - */ - Type ReadClassField(String fieldName, Type dflt) ; - - /** - * Reads the value in-line. - */ - String ReadStringContents() ; - - /** - * Reads the value in-line. - */ - bool ReadBooleanContents() ; - - /** - * Reads the value in-line. - */ - int ReadIntContents() ; - - /** - * reads the value in-line. - */ - long ReadLongContents() ; - - /** - * Reads the value in-line. - */ - float ReadFloatContents() ; - - /** - * reads the value in-line. - */ - double ReadDoubleContents() ; - - /** - * reads the value in-line. - */ - byte [] ReadByteArrayContents() ; - - /** - * reads the value in-line. - */ - Type ReadClassContents() ; - - /** - * Returns the number of anonymous sub-objects. - * @return - */ - int GetNumSubObjects(); - - /** - * Reads a sub-object - */ - Object ReadObjectContents(int index) ; - - } - - /** - * Interface to abstract away the difference between serializing - * xml and binary - */ - internal interface ObjectEncoder { - /** - * Writes an object using the appropriate serializer for that object - * @param fieldName. A hint of the field name. Ignored for binary - * serialization. Becomes the subelement name for xml serialization - * @param object. The object to serialize - */ - void WriteObjectField(String fieldName, Object obj, bool inline) ; - - /** - * Writes a boolean. - * @param fieldName. A hint of the field name. Ignored for binary - * serialization. Becomes the subelement name for xml serialization - * @param v. The value to serialize - */ - void WriteBooleanField(String fieldName, bool v) ; - - /** - * Writes an int. - * @param fieldName. A hint of the field name. Ignored for binary - * serialization. Becomes the subelement name for xml serialization - * @param v. The value to serialize - */ - void WriteIntField(String fieldName, int v) ; - - /** - * Writes a long. - * @param fieldName. A hint of the field name. Ignored for binary - * serialization. Becomes the subelement name for xml serialization - * @param v. The value to serialize - */ - void WriteLongField(String fieldName, long v) ; - - /** - * Writes a float. - * @param fieldName. A hint of the field name. Ignored for binary - * serialization. Becomes the subelement name for xml serialization - * @param v. The value to serialize - */ - void WriteFloatField(String fieldName, float v) ; - - /** - * Writes a double. - * @param fieldName. A hint of the field name. Ignored for binary - * serialization. Becomes the subelement name for xml serialization - * @param v. The value to serialize - */ - void WriteDoubleField(String fieldName, double v) ; - - /** - * Writes a double. - * @param fieldName. A hint of the field name. Ignored for binary - * serialization. Becomes the subelement name for xml serialization - * @param v. The value to serialize - */ - void WriteStringField(String fieldName, string v) ; - - /** - * Writes a double. - * @param fieldName. A hint of the field name. Ignored for binary - * serialization. Becomes the subelement name for xml serialization - * @param v. The value to serialize - */ - void WriteClassField(String fieldName, Type v) ; - - /** - * Writes the value in-line. - */ - void WriteStringContents(String str) ; - - /** - * Writes the value in-line. - */ - void WriteBooleanContents(bool v) ; - - /** - * Writes the value in-line. - */ - void WriteIntContents(int v) ; - - /** - * Writes the value in-line. - */ - void WriteLongContents(long v) ; - - /** - * Writes the value in-line. - */ - void WriteFloatContents(float v) ; - - /** - * Writes the value in-line. - */ - void WriteDoubleContents(double v) ; - - /** - * Special case for byte [] that uses base64 encoding for XML - */ - void WriteByteArrayContents(byte [] v) ; - - /** - * Writes the value in-line. - */ - void WriteClassContents(Type v) ; - - /** - * Writes a sub-object - */ - void WriteObjectContents(object o) ; - } - - /** - * Interface to be implemented to handle the serialization/ - * deserialization of an object. - */ - internal interface ObjectSerializationHandler : ObjectTypeMapper { - - /** - * Called to serialize the object. - */ - void Serialize(Object obj, ObjectEncoder encoder) ; - - /** - * Called to deserialize the object. - */ - Object Deserialize(ObjectDecoder decoder) ; - } - - internal static class ObjectSerializerRegistry { - - private static readonly IList HANDLERS = - new List(); - - - - - private static readonly IDictionary - HANDLERS_BY_SERIAL_TYPE = new Dictionary(); - - static ObjectSerializerRegistry() { - CollectionUtil.AddAll(HANDLERS,Primitives.HANDLERS); - CollectionUtil.AddAll(HANDLERS,OperationMappings.MAPPINGS); - CollectionUtil.AddAll(HANDLERS,APIConfigurationHandlers.HANDLERS); - CollectionUtil.AddAll(HANDLERS,FilterHandlers.HANDLERS); - CollectionUtil.AddAll(HANDLERS,CommonObjectHandlers.HANDLERS); - CollectionUtil.AddAll(HANDLERS,MessageHandlers.HANDLERS); - //object is special - just map the type, but don't actually - //serialize - HANDLERS.Add(new ObjectTypeMapperImpl(typeof(object),"Object")); - - foreach (ObjectTypeMapper handler in HANDLERS) { - if (HANDLERS_BY_SERIAL_TYPE.ContainsKey(handler.HandledSerialType)) { - throw new Exception("More than one handler of the" + - " same type: "+handler.HandledSerialType); - } - HANDLERS_BY_SERIAL_TYPE[handler.HandledSerialType] = - handler; - } - } - - /** - * Mapping by class. Dynamically built since actual class may be - * a subclass. - */ - private static readonly IDictionary - HANDLERS_BY_OBJECT_TYPE = - new Dictionary(); - - - - public static ObjectTypeMapper GetMapperBySerialType(String type) { - return CollectionUtil.GetValue(HANDLERS_BY_SERIAL_TYPE,type,null); - } - - public static ObjectTypeMapper GetMapperByObjectType(Type clazz) { - lock(HANDLERS_BY_OBJECT_TYPE) { - ObjectTypeMapper rv = - CollectionUtil.GetValue(HANDLERS_BY_OBJECT_TYPE,clazz,null); - if ( rv == null ) { - foreach (ObjectTypeMapper handler in HANDLERS) { - IDelegatingObjectTypeMapper delegator = - handler as IDelegatingObjectTypeMapper; - ObjectTypeMapper effectiveHandler; - if ( delegator != null ) { - effectiveHandler = delegator.FindMapperDelegate(clazz); - } - else { - effectiveHandler = handler; - } - if ( effectiveHandler != null ) { - Type handledClass = - effectiveHandler.HandledObjectType; - if ( effectiveHandler.MatchSubclasses ) { - if ( handledClass.IsAssignableFrom(clazz) ) { - rv = effectiveHandler; - break; - } - } - else if ( handledClass.Equals(clazz) ) { - rv = effectiveHandler; - break; - } - } - } - HANDLERS_BY_OBJECT_TYPE[clazz] = rv; - } - return rv; - } - } - public static ObjectSerializationHandler GetHandlerBySerialType(String type) { - ObjectTypeMapper rv = GetMapperBySerialType(type); - return rv as ObjectSerializationHandler; - } - - public static ObjectSerializationHandler GetHandlerByObjectType(Type clazz) { - ObjectTypeMapper rv = GetMapperByObjectType(clazz); - return rv as ObjectSerializationHandler; - } - } - - /// - /// ObjectTypeMappers can implement this interface as well. If - /// they do, they can handle specific generic types as well. - /// - internal interface IDelegatingObjectTypeMapper { - ObjectTypeMapper FindMapperDelegate(Type type); - } - - /** - * Interface to be implemented to handle the serialization/ - * deserialization of an object. - */ - internal interface ObjectTypeMapper { - - /** - * Returns the type of object being serialized. This is - * an abstract type name that is intended to be language - * neutral. - */ - String HandledSerialType {get;} - - /** - * Returns the java class handled by this handler. - */ - Type HandledObjectType {get;} - - /** - * Should we match subclasses of the given class or only - * the exact class? - */ - bool MatchSubclasses {get;} - - } - - internal class ObjectTypeMapperImpl : ObjectTypeMapper { - - private Type _handledClass; - private String _handledType; - - public ObjectTypeMapperImpl(Type handledClass, String handledType) { - _handledClass = handledClass; - _handledType = handledType; - } - - public Type HandledObjectType { - get { - return _handledClass; - } - } - - public String HandledSerialType { - get { - return _handledType; - } - } - - public virtual bool MatchSubclasses { - get { - return false; - } - } - - } - - internal abstract class AbstractExceptionHandler : AbstractObjectSerializationHandler - where T : Exception - { - - protected AbstractExceptionHandler(String typeName) - : base(typeof(T),typeName) { - } - - - public override Object Deserialize(ObjectDecoder decoder) { - String message = decoder.ReadStringField("message",null); - return CreateException(message); - } - - public override void Serialize(Object obj, ObjectEncoder encoder) - { - Exception val = (Exception)obj; - encoder.WriteStringField("message", val.Message); - } - public override bool MatchSubclasses { - get { return true; } - } - protected abstract T CreateException(String message); - } - - - #endregion - - #region Primitives - - internal static class Primitives { - public static readonly IList HANDLERS = - new List(); - static Primitives() { - HANDLERS.Add(new BooleanHandler(typeof(bool?),"Boolean")); - HANDLERS.Add(new BooleanHandler(typeof(bool),"boolean")); - HANDLERS.Add(new CharacterHandler(typeof(char?),"Character")); - HANDLERS.Add(new CharacterHandler(typeof(char),"char")); - HANDLERS.Add(new IntegerHandler(typeof(int?),"Integer")); - HANDLERS.Add(new IntegerHandler(typeof(int),"int")); - HANDLERS.Add(new LongHandler(typeof(long?),"Long")); - HANDLERS.Add(new LongHandler(typeof(long),"long")); - HANDLERS.Add(new FloatHandler(typeof(float?),"Float")); - HANDLERS.Add(new FloatHandler(typeof(float),"float")); - HANDLERS.Add(new DoubleHandler(typeof(double?),"Double")); - HANDLERS.Add(new DoubleHandler(typeof(double),"double")); - HANDLERS.Add(new StringHandler()); - HANDLERS.Add(new URIHandler()); - HANDLERS.Add(new FileHandler()); - HANDLERS.Add(new BigIntegerHandler()); - HANDLERS.Add(new BigDecimalHandler()); - HANDLERS.Add(new ByteArrayHandler()); - HANDLERS.Add(new ClassHandler()); - HANDLERS.Add(new MapEntryHandler()); - HANDLERS.Add(new MapHandler()); - HANDLERS.Add(new ListHandler()); - HANDLERS.Add(new SetHandler()); - HANDLERS.Add(new LocaleHandler()); - HANDLERS.Add(new GuardedStringHandler()); - } - private class BooleanHandler : AbstractObjectSerializationHandler { - public BooleanHandler(Type objectType, String serialType) - : base(objectType,serialType) { - - } - public override Object Deserialize(ObjectDecoder decoder) { - bool val = decoder.ReadBooleanContents(); - return val; - } - - public override void Serialize(Object obj, ObjectEncoder encoder) { - bool val = (bool)obj; - encoder.WriteBooleanContents(val); - } - } - private class CharacterHandler : AbstractObjectSerializationHandler { - public CharacterHandler(Type objectType, String serialType) - : base(objectType,serialType) { - - } - public override Object Deserialize(ObjectDecoder decoder) { - String val = decoder.ReadStringContents(); - return val.ToCharArray()[0]; - } - - public override void Serialize(Object obj, ObjectEncoder encoder) { - char val = (char)obj; - encoder.WriteStringContents(val.ToString()); - } - } - private class IntegerHandler : AbstractObjectSerializationHandler { - public IntegerHandler(Type objectType, String serialType) - : base(objectType,serialType) { - - } - public override Object Deserialize(ObjectDecoder decoder) { - int val = decoder.ReadIntContents(); - return val; - } - - public override void Serialize(Object obj, ObjectEncoder encoder) { - int val = (int)obj; - encoder.WriteIntContents(val); - } - } - private class LongHandler : AbstractObjectSerializationHandler { - public LongHandler(Type objectType, String serialType) - : base(objectType,serialType) { - - } - public override Object Deserialize(ObjectDecoder decoder) { - long val = decoder.ReadLongContents(); - return val; - } - - public override void Serialize(Object obj, ObjectEncoder encoder) { - long val = (long)obj; - encoder.WriteLongContents(val); - } - } - private class FloatHandler : AbstractObjectSerializationHandler { - public FloatHandler(Type objectType, String serialType) - : base(objectType,serialType) { - - } - public override Object Deserialize(ObjectDecoder decoder) { - float val = decoder.ReadFloatContents(); - return val; - } - - public override void Serialize(Object obj, ObjectEncoder encoder) { - float val = (float)obj; - encoder.WriteFloatContents(val); - } - } - private class DoubleHandler : AbstractObjectSerializationHandler { - public DoubleHandler(Type objectType, String serialType) - : base(objectType,serialType) { - - } - public override Object Deserialize(ObjectDecoder decoder) { - double val = decoder.ReadDoubleContents(); - return val; - } - - public override void Serialize(Object obj, ObjectEncoder encoder) { - double val = (double)obj; - encoder.WriteDoubleContents(val); - } - } - private class StringHandler : AbstractObjectSerializationHandler { - public StringHandler() - : base(typeof(string),"String") { - - } - public override Object Deserialize(ObjectDecoder decoder) { - string val = decoder.ReadStringContents(); - return val; - } - - public override void Serialize(Object obj, ObjectEncoder encoder) { - string val = (string)obj; - encoder.WriteStringContents(val); - } - } - private class URIHandler : AbstractObjectSerializationHandler { - public URIHandler() - : base(typeof(Uri),"URI") { - - } - public override Object Deserialize(ObjectDecoder decoder) { - string val = decoder.ReadStringContents(); - return new Uri(val); - } - - public override void Serialize(Object obj, ObjectEncoder encoder) { - Uri val = (Uri)obj; - encoder.WriteStringContents(val.ToString()); - } - } - private class FileHandler : AbstractObjectSerializationHandler { - public FileHandler() - : base(typeof(FileName),"File") { - - } - public override Object Deserialize(ObjectDecoder decoder) { - string val = decoder.ReadStringContents(); - return new FileName(val); - } - - public override void Serialize(Object obj, ObjectEncoder encoder) { - FileName val = (FileName)obj; - encoder.WriteStringContents(val.Path); - } - } - private class BigDecimalHandler : AbstractObjectSerializationHandler { - public BigDecimalHandler() - : base(typeof(BigDecimal),"BigDecimal") { - - } - public override Object Deserialize(ObjectDecoder decoder) { - BigInteger unscaled = - new BigInteger(decoder.ReadStringField("unscaled",null)); - int scale = decoder.ReadIntField("scale",0); - return new BigDecimal(unscaled,scale); - } - - public override void Serialize(Object obj, ObjectEncoder encoder) { - BigDecimal val = (BigDecimal)obj; - encoder.WriteStringField("unscaled", val.UnscaledValue.Value); - encoder.WriteIntField("scale", val.Scale); - } - } - private class BigIntegerHandler : AbstractObjectSerializationHandler { - public BigIntegerHandler() - : base(typeof(BigInteger),"BigInteger") { - - } - public override Object Deserialize(ObjectDecoder decoder) { - string val = decoder.ReadStringContents(); - return new BigInteger(val); - } - - public override void Serialize(Object obj, ObjectEncoder encoder) { - BigInteger val = (BigInteger)obj; - encoder.WriteStringContents(val.Value); - } - } - private class ByteArrayHandler : AbstractObjectSerializationHandler { - public ByteArrayHandler() - : base(typeof(byte[]),"ByteArray") { - - } - public override Object Deserialize(ObjectDecoder decoder) { - return decoder.ReadByteArrayContents(); - } - - public override void Serialize(Object obj, ObjectEncoder encoder) { - byte [] val = (byte[])obj; - encoder.WriteByteArrayContents(val); - } - } - private class ClassHandler : AbstractObjectSerializationHandler { - public ClassHandler() - : base(typeof(Type),"Class") { - - } - public override Object Deserialize(ObjectDecoder decoder) { - return decoder.ReadClassContents(); - } - - public override void Serialize(Object obj, ObjectEncoder encoder) { - Type val = (Type)obj; - encoder.WriteClassContents(val); - } - - /// - /// In C#, the actual Type of Type is RuntimeType - /// - public override bool MatchSubclasses { - get { return true; } - } - - } - - private class MapEntry { - internal object key; - internal object val; - public MapEntry(object key, object val) { - this.key = key; - this.val = val; - } - } - private class MapEntryHandler : - AbstractObjectSerializationHandler { - - public MapEntryHandler() - : base(typeof(MapEntry),"MapEntry") { - } - public override Object Deserialize(ObjectDecoder decoder) { - Object key = decoder.ReadObjectContents(0); - Object val = decoder.ReadObjectContents(1); - return new MapEntry(key,val); - } - - public override void Serialize(Object obj, ObjectEncoder encoder) - { - MapEntry entry = (MapEntry)obj; - encoder.WriteObjectContents(entry.key); - encoder.WriteObjectContents(entry.val); - } - } - private class MapHandler : - AbstractObjectSerializationHandler, - IDelegatingObjectTypeMapper { - - public MapHandler() - : base(typeof(IDictionary),"Map") { - } - public ObjectTypeMapper FindMapperDelegate(Type type) { - //get the IDictionary interface that this type implements - Type interfaceType = ReflectionUtil.FindInHierarchyOf - (typeof(IDictionary<,>),type); - if ( interfaceType != null ) { - Type [] keyAndValue = interfaceType.GetGenericArguments(); - if (keyAndValue.Length != 2) { - throw new Exception("Cannot serialize type: "+type); - } - Type mapHandlerRawType = typeof(MapHandler<,>); - Type mapHandlerType = - mapHandlerRawType.MakeGenericType(keyAndValue); - return (ObjectTypeMapper)Activator.CreateInstance(mapHandlerType); - } - return null; - } - public override Object Deserialize(ObjectDecoder decoder) { - bool caseInsensitive = - decoder.ReadBooleanField("caseInsensitive",false); - if (caseInsensitive) { - IDictionary rv = - CollectionUtil.NewCaseInsensitiveDictionary(); - int count = decoder.GetNumSubObjects(); - for (int i = 0; i < count; i++) { - MapEntry entry = (MapEntry)decoder.ReadObjectContents(i); - rv[""+entry.key] = entry.val; - } - return rv; - } - else { - IDictionary rv = - new Dictionary(); - int count = decoder.GetNumSubObjects(); - for (int i = 0; i < count; i++) { - MapEntry entry = (MapEntry)decoder.ReadObjectContents(i); - rv[entry.key] = entry.val; - } - return rv; - } - } - - public override void Serialize(Object obj, ObjectEncoder encoder) - { - IDictionary map = (IDictionary)obj; - if (CollectionUtil.IsCaseInsensitiveDictionary(map)) { - encoder.WriteBooleanField("caseInsensitive",true); - } - else if ( map is SortedDictionary ) { - throw new Exception("Serialization of SortedDictionary not supported"); - } - foreach (KeyValuePair entry in map) { - MapEntry myEntry = new MapEntry(entry.Key,entry.Value); - encoder.WriteObjectContents(myEntry); - } - } - - public override bool MatchSubclasses { - get { return true; } - } - } - private class ListHandler : - AbstractObjectSerializationHandler, - IDelegatingObjectTypeMapper { - - public ListHandler() - : base(typeof(IList),"List") { - } - public ObjectTypeMapper FindMapperDelegate(Type type) { - //in C#, arrays implement IList - if (type.IsArray) { - return null; - } - //get the IList interface that this type implements - Type interfaceType = ReflectionUtil.FindInHierarchyOf - (typeof(IList<>),type); - if ( interfaceType != null ) { - Type [] val = interfaceType.GetGenericArguments(); - if (val.Length != 1) { - throw new Exception("Cannot serialize type: "+type); - } - Type listHandlerRawType = typeof(ListHandler<>); - Type listHandlerType = - listHandlerRawType.MakeGenericType(val); - return (ObjectTypeMapper)Activator.CreateInstance(listHandlerType); - } - return null; - } - public override Object Deserialize(ObjectDecoder decoder) { - IList rv = - new List(); - int count = decoder.GetNumSubObjects(); - for ( int i = 0; i < count; i++ ){ - rv.Add(decoder.ReadObjectContents(i)); - } - return rv; - } - - public override void Serialize(Object obj, ObjectEncoder encoder) - { - IList list = (IList)obj; - foreach (T o in list) { - encoder.WriteObjectContents(o); - } - } - - public override bool MatchSubclasses { - get { return true; } - } - } - private class SetHandler : - AbstractObjectSerializationHandler, - IDelegatingObjectTypeMapper { - - public SetHandler() - : base(typeof(ICollection),"Set") { - } - public ObjectTypeMapper FindMapperDelegate(Type type) { - //in C#, arrays implement IList - if (type.IsArray) { - return null; - } - - //get the IList interface that this type implements - Type interfaceType = ReflectionUtil.FindInHierarchyOf - (typeof(ICollection<>),type); - if ( interfaceType != null ) { - Type [] val = interfaceType.GetGenericArguments(); - if (val.Length != 1) { - throw new Exception("Cannot serialize type: "+type); - } - Type setHandlerRawType = typeof(SetHandler<>); - Type setHandlerType = - setHandlerRawType.MakeGenericType(val); - return (ObjectTypeMapper)Activator.CreateInstance(setHandlerType); - } - return null; - } - public override Object Deserialize(ObjectDecoder decoder) { - bool caseInsensitive = - decoder.ReadBooleanField("caseInsensitive",false); - if (caseInsensitive) { - ICollection rv = - CollectionUtil.NewCaseInsensitiveSet(); - int count = decoder.GetNumSubObjects(); - for (int i = 0; i < count; i++) { - rv.Add(""+decoder.ReadObjectContents(i)); - } - return rv; - } - else { - ICollection rv = - new HashSet(); - int count = decoder.GetNumSubObjects(); - for (int i = 0; i < count; i++) { - rv.Add(decoder.ReadObjectContents(i)); - } - return rv; - } - } - - public override void Serialize(Object obj, ObjectEncoder encoder) - { - ICollection list = (ICollection)obj; - if (CollectionUtil.IsCaseInsensitiveSet(list)) { - encoder.WriteBooleanField("caseInsensitive",true); - } - foreach (T o in list) { - encoder.WriteObjectContents(o); - } - } - - public override bool MatchSubclasses { - get { return true; } - } - } - private class LocaleHandler : AbstractObjectSerializationHandler { - public LocaleHandler() - : base(typeof(CultureInfo),"Locale") { - - } - public override Object Deserialize(ObjectDecoder decoder) { - string language = decoder.ReadStringField("language",""); - string country = decoder.ReadStringField("country",""); - string variant = decoder.ReadStringField("variant",""); - Locale locale = new Locale(language,country,variant); - return locale.ToCultureInfo(); - } - - public override void Serialize(Object obj, ObjectEncoder encoder) { - CultureInfo cultureInfo = (CultureInfo)obj; - Locale locale = Locale.FindLocale(cultureInfo); - encoder.WriteStringField("language", locale.Language); - encoder.WriteStringField("country", locale.Country); - encoder.WriteStringField("variant", locale.Variant); - - } - } - - private class GuardedStringHandler : AbstractObjectSerializationHandler { - public GuardedStringHandler() - : base(typeof(GuardedString),"GuardedString") { - - } - public override Object Deserialize(ObjectDecoder decoder) { - byte [] encryptedBytes = null; - UnmanagedArray clearBytes = null; - UnmanagedArray clearChars = null; - try { - encryptedBytes = decoder.ReadByteArrayContents(); - clearBytes = EncryptorFactory.GetInstance().GetDefaultEncryptor().Decrypt(encryptedBytes); - clearChars = SecurityUtil.BytesToChars(clearBytes); - GuardedString rv = new GuardedString(); - for ( int i = 0; i < clearChars.Length; i++ ) { - rv.AppendChar(clearChars[i]); - } - return rv; - } - finally { - if ( clearBytes != null ) { - clearBytes.Dispose(); - } - if ( clearChars != null ) { - clearChars.Dispose(); - } - SecurityUtil.Clear(encryptedBytes); - } - } - - public override void Serialize(Object obj, ObjectEncoder encoder) { - GuardedString str = (GuardedString)obj; - str.Access( - clearChars=> - { - UnmanagedArray clearBytes = null; - byte [] encryptedBytes = null; - try { - clearBytes = SecurityUtil.CharsToBytes(clearChars); - encryptedBytes = EncryptorFactory.GetInstance().GetDefaultEncryptor().Encrypt(clearBytes); - encoder.WriteByteArrayContents(encryptedBytes); - } - finally { - if ( clearBytes != null ) { - clearBytes.Dispose(); - } - SecurityUtil.Clear(encryptedBytes); - } - }); - } - } - } - #endregion - - #region APIConfigurationHandlers - internal static class APIConfigurationHandlers { - public static readonly IList HANDLERS = - new List(); - static APIConfigurationHandlers() { - HANDLERS.Add( new ConnectionPoolingConfigurationHandler() ); - HANDLERS.Add( new ConfigurationPropertyHandler() ); - HANDLERS.Add( new ConfigurationPropertiesHandler() ); - HANDLERS.Add( new APIConfigurationHandler() ); - HANDLERS.Add( new ConnectorMessagesHandler() ); - HANDLERS.Add( new ConnectorKeyHandler() ); - HANDLERS.Add( new ConnectorInfoHandler() ); - } - private class ConnectionPoolingConfigurationHandler : AbstractObjectSerializationHandler { - public ConnectionPoolingConfigurationHandler() - : base(typeof(ObjectPoolConfiguration),"ObjectPoolConfiguration") { - - } - public override Object Deserialize(ObjectDecoder decoder) { - ObjectPoolConfiguration rv = - new ObjectPoolConfiguration(); - rv.MaxObjects=(decoder.ReadIntField("maxObjects",rv.MaxObjects)); - rv.MaxIdle=(decoder.ReadIntField("maxIdle",rv.MaxIdle)); - rv.MaxWait=(decoder.ReadLongField("maxWait",rv.MaxWait)); - rv.MinEvictableIdleTimeMillis=( - decoder.ReadLongField("minEvictableIdleTimeMillis",rv.MinEvictableIdleTimeMillis)); - rv.MinIdle=( - decoder.ReadIntField("minIdle",rv.MinIdle)); - return rv; - } - - public override void Serialize(Object obj, ObjectEncoder encoder) { - ObjectPoolConfiguration val = - (ObjectPoolConfiguration)obj; - encoder.WriteIntField("maxObjects", - val.MaxObjects); - encoder.WriteIntField("maxIdle", - val.MaxIdle); - encoder.WriteLongField("maxWait", - val.MaxWait); - encoder.WriteLongField("minEvictableIdleTimeMillis", - val.MinEvictableIdleTimeMillis); - encoder.WriteIntField("minIdle", - val.MinIdle); - } - } - private class ConfigurationPropertyHandler : AbstractObjectSerializationHandler { - public ConfigurationPropertyHandler() - : base(typeof(ConfigurationPropertyImpl),"ConfigurationProperty") { - - } - public override Object Deserialize(ObjectDecoder decoder) { - ConfigurationPropertyImpl rv = new ConfigurationPropertyImpl(); - rv.Order=(decoder.ReadIntField("order",0)); - rv.IsConfidential=(decoder.ReadBooleanField("confidential",false)); - rv.IsRequired=decoder.ReadBooleanField("required",false); - rv.Name=(decoder.ReadStringField("name",null)); - rv.HelpMessageKey=( - decoder.ReadStringField("helpMessageKey",null)); - rv.DisplayMessageKey=( - decoder.ReadStringField("displayMessageKey",null)); - rv.ValueType=( - decoder.ReadClassField("type",null)); - rv.Value=( - decoder.ReadObjectField("value",null,null)); - ICollection operationsObj = - (ICollection)decoder.ReadObjectField("operations",typeof(ICollection),null); - ICollection> operations = - new HashSet>(); - foreach (object o in operationsObj) { - Type type = (Type)o; - operations.Add(SafeType.ForRawType(type)); - } - rv.Operations = operations; - - return rv; - } - - public override void Serialize(Object obj, ObjectEncoder encoder) { - ConfigurationPropertyImpl val = - (ConfigurationPropertyImpl)obj; - encoder.WriteIntField("order", - val.Order); - encoder.WriteBooleanField("confidential", - val.IsConfidential); - encoder.WriteBooleanField("required", - val.IsRequired); - encoder.WriteStringField("name", - val.Name); - encoder.WriteStringField("helpMessageKey", - val.HelpMessageKey); - encoder.WriteStringField("displayMessageKey", - val.DisplayMessageKey); - encoder.WriteClassField("type", - val.ValueType); - encoder.WriteObjectField("value", - val.Value, - false); - ICollection operationsObj = - new HashSet(); - foreach(SafeType op in val.Operations) { - operationsObj.Add(op.RawType); - } - encoder.WriteObjectField("operations",operationsObj,true); - } - } - private class ConfigurationPropertiesHandler : AbstractObjectSerializationHandler { - public ConfigurationPropertiesHandler() - : base(typeof(ConfigurationPropertiesImpl),"ConfigurationProperties") { - - } - public override Object Deserialize(ObjectDecoder decoder) { - ConfigurationPropertiesImpl rv = - new ConfigurationPropertiesImpl(); - IList props = - new List - (); - int count = decoder.GetNumSubObjects(); - for ( int i = 0; i < count; i++ ) { - ConfigurationPropertyImpl prop = - (ConfigurationPropertyImpl)decoder.ReadObjectContents(i); - props.Add(prop); - } - rv.Properties = props; - return rv; - } - - public override void Serialize(Object obj, ObjectEncoder encoder) { - ConfigurationPropertiesImpl val = - (ConfigurationPropertiesImpl)obj; - IList props = - val.Properties; - foreach (ConfigurationPropertyImpl prop in props) { - encoder.WriteObjectContents(prop); - } - } - } - private class APIConfigurationHandler : AbstractObjectSerializationHandler { - public APIConfigurationHandler() - : base(typeof(APIConfigurationImpl),"APIConfiguration") { - - } - public override Object Deserialize(ObjectDecoder decoder) { - APIConfigurationImpl rv = new APIConfigurationImpl(); - rv.IsConnectorPoolingSupported=( - decoder.ReadBooleanField("connectorPoolingSupported",false)); - rv.ConnectorPoolConfiguration=( - (ObjectPoolConfiguration) - decoder.ReadObjectField("connectorPoolConfiguration",null,null)); - rv.ConfigurationProperties=((ConfigurationPropertiesImpl) - decoder.ReadObjectField("ConfigurationProperties",typeof(ConfigurationPropertiesImpl),null)); - IDictionary timeoutMapObj = - (IDictionary)decoder.ReadObjectField("timeoutMap",null,null); - IDictionary,int> timeoutMap = - new Dictionary,int>(); - foreach (KeyValuePair entry in timeoutMapObj) { - Type type = (Type)entry.Key; - int val = (int)entry.Value; - timeoutMap[SafeType.ForRawType(type)] = val; - } - rv.TimeoutMap=timeoutMap; - ICollection supportedOperationsObj = - (ICollection)decoder.ReadObjectField("SupportedOperations",typeof(ICollection),null); - ICollection> supportedOperations = - new HashSet>(); - foreach (object obj in supportedOperationsObj) { - Type type = (Type)obj; - supportedOperations.Add(SafeType.ForRawType(type)); - } - rv.SupportedOperations=supportedOperations; - rv.ProducerBufferSize=(decoder.ReadIntField("producerBufferSize",0)); - return rv; - } - - public override void Serialize(Object obj, ObjectEncoder encoder) { - APIConfigurationImpl val = - (APIConfigurationImpl)obj; - - ICollection supportedOperations = - new HashSet(); - if ( val.SupportedOperations != null ) { - foreach (SafeType op in val.SupportedOperations ) { - supportedOperations.Add(op.RawType); - } - } - IDictionary timeoutMap = - new Dictionary(); - if ( val.TimeoutMap != null ) { - foreach (KeyValuePair, int> entry in val.TimeoutMap) { - timeoutMap[entry.Key.RawType] = entry.Value; - } - } - - encoder.WriteIntField("producerBufferSize", - val.ProducerBufferSize); - encoder.WriteBooleanField("connectorPoolingSupported", - val.IsConnectorPoolingSupported); - encoder.WriteObjectField("connectorPoolConfiguration", - val.ConnectorPoolConfiguration,false); - encoder.WriteObjectField("ConfigurationProperties", - val.ConfigurationProperties,true); - encoder.WriteObjectField("timeoutMap", - timeoutMap,false); - encoder.WriteObjectField("SupportedOperations", - supportedOperations,true); - } - } - private class ConnectorMessagesHandler : AbstractObjectSerializationHandler { - public ConnectorMessagesHandler() - : base(typeof(ConnectorMessagesImpl),"ConnectorMessages") { - - } - public override Object Deserialize(ObjectDecoder decoder) { - ConnectorMessagesImpl rv = new ConnectorMessagesImpl(); - IDictionary catalogsObj = - (IDictionary)decoder.ReadObjectField("catalogs",null,null); - IDictionary> - catalogs = new Dictionary>(); - foreach (KeyValuePair entry in catalogsObj) { - CultureInfo key = (CultureInfo)entry.Key; - IDictionary valObj = (IDictionary)entry.Value; - IDictionary val = - CollectionUtil.NewDictionary(valObj); - catalogs[key] = val; - } - rv.Catalogs=(catalogs); - return rv; - - } - - public override void Serialize(Object obj, ObjectEncoder encoder) { - ConnectorMessagesImpl val = - (ConnectorMessagesImpl)obj; - encoder.WriteObjectField("catalogs", - val.Catalogs,false); - } - } - - private class ConnectorKeyHandler : AbstractObjectSerializationHandler { - public ConnectorKeyHandler() - : base(typeof(ConnectorKey),"ConnectorKey") { - - } - public override Object Deserialize(ObjectDecoder decoder) { - String bundleName = - decoder.ReadStringField("bundleName",null); - String bundleVersion = - decoder.ReadStringField("bundleVersion",null); - String connectorName = - decoder.ReadStringField("connectorName",null); - return new ConnectorKey(bundleName,bundleVersion,connectorName); - } - - public override void Serialize(Object obj, ObjectEncoder encoder) { - ConnectorKey val = (ConnectorKey)obj; - encoder.WriteStringField("bundleName", - val.BundleName); - encoder.WriteStringField("bundleVersion", - val.BundleVersion); - encoder.WriteStringField("connectorName", - val.ConnectorName); - } - } - - private class ConnectorInfoHandler : AbstractObjectSerializationHandler { - public ConnectorInfoHandler() - : base(typeof(RemoteConnectorInfoImpl),"ConnectorInfo") { - - } - public override Object Deserialize(ObjectDecoder decoder) { - RemoteConnectorInfoImpl rv = new RemoteConnectorInfoImpl(); - rv.ConnectorDisplayNameKey=( - decoder.ReadStringField("connectorDisplayNameKey",null)); - rv.ConnectorKey=((ConnectorKey) - decoder.ReadObjectField("ConnectorKey",typeof(ConnectorKey),null)); - rv.Messages=((ConnectorMessagesImpl) - decoder.ReadObjectField("ConnectorMessages",typeof(ConnectorMessagesImpl),null)); - rv.DefaultAPIConfiguration=((APIConfigurationImpl) - decoder.ReadObjectField("APIConfiguration",typeof(APIConfigurationImpl),null)); - return rv; - } - - public override void Serialize(Object obj, ObjectEncoder encoder) { - RemoteConnectorInfoImpl val = - (RemoteConnectorInfoImpl)obj; - encoder.WriteStringField("connectorDisplayNameKey", - val.ConnectorDisplayNameKey); - encoder.WriteObjectField("ConnectorKey", - val.ConnectorKey,true); - encoder.WriteObjectField("ConnectorMessages", - val.Messages,true); - encoder.WriteObjectField("APIConfiguration", - val.DefaultAPIConfiguration,true); - } - } - - } - #endregion - - #region ObjectSerializerFactoryImpl - public class ObjectSerializerFactoryImpl : ObjectSerializerFactory { - - - public override BinaryObjectDeserializer NewBinaryDeserializer(Stream i) { - return new BinaryObjectDecoder(i); - } - - public override BinaryObjectSerializer NewBinarySerializer(Stream os) { - return new BinaryObjectEncoder(os); - } - public override XmlObjectSerializer NewXmlSerializer(TextWriter w, - bool includeHeader, - bool multiObject) { - return new XmlObjectSerializerImpl(w,includeHeader,multiObject); - } - - public override void DeserializeXmlStream(TextReader reader, - XmlObjectResultsHandler handler, - bool validate) { - XmlObjectParser.parse(reader, handler, validate); - } - - } - #endregion - - #region OperationMappings - internal static class OperationMappings { - - public static readonly IList MAPPINGS = - new List(); - - static OperationMappings() { - MAPPINGS.Add(new ObjectTypeMapperImpl(typeof(AuthenticationApiOp), - "AuthenticationApiOp")); - MAPPINGS.Add(new ObjectTypeMapperImpl(typeof(SearchApiOp), - "SearchApiOp")); - MAPPINGS.Add(new ObjectTypeMapperImpl(typeof(ValidateApiOp), - "ValidateApiOp")); - MAPPINGS.Add(new ObjectTypeMapperImpl(typeof(CreateApiOp), - "CreateApiOp")); - MAPPINGS.Add(new ObjectTypeMapperImpl(typeof(SchemaApiOp), - "SchemaApiOp")); - MAPPINGS.Add(new ObjectTypeMapperImpl(typeof(UpdateApiOp), - "UpdateApiOp")); - MAPPINGS.Add(new ObjectTypeMapperImpl(typeof(DeleteApiOp), - "DeleteApiOp")); - MAPPINGS.Add(new ObjectTypeMapperImpl(typeof(GetApiOp), - "GetApiOp")); - MAPPINGS.Add(new ObjectTypeMapperImpl(typeof(TestApiOp), - "TestApiOp")); - MAPPINGS.Add(new ObjectTypeMapperImpl(typeof(ScriptOnConnectorApiOp), - "ScriptOnConnectorApiOp")); - MAPPINGS.Add(new ObjectTypeMapperImpl(typeof(ScriptOnResourceApiOp), - "ScriptOnResourceApiOp")); - MAPPINGS.Add(new ObjectTypeMapperImpl(typeof(SyncApiOp), - "SyncApiOp")); - } - - } - #endregion - - #region CommonObjectHandlers - internal static class CommonObjectHandlers { - public static readonly IList HANDLERS = - new List(); - static CommonObjectHandlers() { - - HANDLERS.Add( new AlreadyExistsExceptionHandler() ); - HANDLERS.Add( new ConfigurationExceptionHandler() ); - HANDLERS.Add( new ConnectionBrokenExceptionHandler() ); - HANDLERS.Add( new ConnectionFailedExceptionHandler() ); - HANDLERS.Add( new ConnectorIOExceptionHandler() ); - HANDLERS.Add( new PasswordExpiredExceptionHandler() ); - HANDLERS.Add( new InvalidPasswordExceptionHandler() ); - HANDLERS.Add( new UnknownUidExceptionHandler() ); - HANDLERS.Add( new InvalidCredentialExceptionHandler() ); - HANDLERS.Add( new PermissionDeniedExceptionHandler() ); - HANDLERS.Add( new ConnectorSecurityExceptionHandler() ); - HANDLERS.Add( new OperationTimeoutExceptionHandler() ); - HANDLERS.Add( new ConnectorExceptionHandler() ); - HANDLERS.Add( new RuntimeExceptionHandler() ); - HANDLERS.Add( new ExceptionHandler() ); - HANDLERS.Add( new ThrowableHandler() ); - HANDLERS.Add( new AttributeHandler() ); - HANDLERS.Add( new AttributeInfoHandler() ); - HANDLERS.Add( new ConnectorObjectHandler() ); - HANDLERS.Add( new NameHandler() ); - HANDLERS.Add( new ObjectClassHandler() ); - HANDLERS.Add( new ObjectClassInfoHandler() ); - HANDLERS.Add( new SchemaHandler() ); - HANDLERS.Add( new UidHandler() ); - HANDLERS.Add( new ScriptContextHandler() ); - HANDLERS.Add( new OperationOptionsHandler() ); - HANDLERS.Add( new OperationOptionInfoHandler() ); - HANDLERS.Add( new EnumSerializationHandler(typeof(ConnectorAttributeInfo.Flags), - "AttributeInfoFlag")); - HANDLERS.Add( new EnumSerializationHandler(typeof(SyncDeltaType), - "SyncDeltaType") ); - HANDLERS.Add( new SyncTokenHandler() ); - HANDLERS.Add( new SyncDeltaHandler() ); - HANDLERS.Add( new QualifiedUidHandler() ); - } - - private class AlreadyExistsExceptionHandler : AbstractExceptionHandler { - public AlreadyExistsExceptionHandler() - : base("AlreadyExistsException") { - - } - protected override AlreadyExistsException CreateException(String msg) { - return new AlreadyExistsException(msg); - } - } - private class ConfigurationExceptionHandler : AbstractExceptionHandler { - public ConfigurationExceptionHandler() - : base("ConfigurationException") { - - } - protected override ConfigurationException CreateException(String msg) { - return new ConfigurationException(msg); - } - } - private class ConnectionBrokenExceptionHandler : AbstractExceptionHandler { - public ConnectionBrokenExceptionHandler() - : base("ConnectionBrokenException") { - - } - protected override ConnectionBrokenException CreateException(String msg) { - return new ConnectionBrokenException(msg); - } - } - private class ConnectionFailedExceptionHandler : AbstractExceptionHandler { - public ConnectionFailedExceptionHandler() - : base("ConnectionFailedException") { - - } - protected override ConnectionFailedException CreateException(String msg) { - return new ConnectionFailedException(msg); - } - } - private class ConnectorIOExceptionHandler : AbstractExceptionHandler { - public ConnectorIOExceptionHandler() - : base("ConnectorIOException") { - - } - protected override ConnectorIOException CreateException(String msg) { - return new ConnectorIOException(msg); - } - } - private class PasswordExpiredExceptionHandler : AbstractExceptionHandler { - public PasswordExpiredExceptionHandler() - : base("PasswordExpiredException") { - - } - - public override Object Deserialize(ObjectDecoder decoder) { - Uid uid = (Uid)decoder.ReadObjectField("Uid", typeof(Uid), null); - PasswordExpiredException ex = - (PasswordExpiredException)base.Deserialize(decoder); - ex.Uid = uid; - return ex; - } - - public override void Serialize(Object obj, ObjectEncoder encoder) - { - base.Serialize(obj, encoder); - PasswordExpiredException val = (PasswordExpiredException)obj; - encoder.WriteObjectField("Uid", val.Uid, true); - } - protected override PasswordExpiredException CreateException(String msg) { - return new PasswordExpiredException(msg); - } - } - private class InvalidPasswordExceptionHandler : AbstractExceptionHandler { - public InvalidPasswordExceptionHandler() - : base("InvalidPasswordException") { - - } - protected override InvalidPasswordException CreateException(String msg) { - return new InvalidPasswordException(msg); - } - } - private class UnknownUidExceptionHandler : AbstractExceptionHandler { - public UnknownUidExceptionHandler() - : base("UnknownUidException") { - - } - protected override UnknownUidException CreateException(String msg) { - return new UnknownUidException(msg); - } - } - private class InvalidCredentialExceptionHandler : AbstractExceptionHandler { - public InvalidCredentialExceptionHandler() - : base("InvalidCredentialException") { - - } - protected override InvalidCredentialException CreateException(String msg) { - return new InvalidCredentialException(msg); - } - } - private class PermissionDeniedExceptionHandler : AbstractExceptionHandler { - public PermissionDeniedExceptionHandler() - : base("PermissionDeniedException") { - - } - protected override PermissionDeniedException CreateException(String msg) { - return new PermissionDeniedException(msg); - } - } - private class ConnectorSecurityExceptionHandler : AbstractExceptionHandler { - public ConnectorSecurityExceptionHandler() - : base("ConnectorSecurityException") { - - } - protected override ConnectorSecurityException CreateException(String msg) { - return new ConnectorSecurityException(msg); - } - } - private class OperationTimeoutExceptionHandler : AbstractExceptionHandler { - public OperationTimeoutExceptionHandler() - : base("OperationTimeoutException") { - - } - protected override OperationTimeoutException CreateException(String msg) { - return new OperationTimeoutException(msg); - } - } - private class ConnectorExceptionHandler : AbstractExceptionHandler { - public ConnectorExceptionHandler() - : base("ConnectorException") { - - } - protected override ConnectorException CreateException(String msg) { - return new ConnectorException(msg); - } - } - //RuntimeException, Exception, and Throwable - //when going from Java to C#, these always become - //Exception. - //when going from C# to Java, these always become - //RuntimeException - private class RuntimeExceptionHandler : AbstractExceptionHandler { - public RuntimeExceptionHandler() - : base("RuntimeException") { - - } - protected override Exception CreateException(String msg) { - return new Exception(msg); - } - } - private class ExceptionHandler : AbstractExceptionHandler { - public ExceptionHandler() - : base("Exception") { - - } - protected override Exception CreateException(String msg) { - return new Exception(msg); - } - } - private class ThrowableHandler : AbstractExceptionHandler { - public ThrowableHandler() - : base("Throwable") { - - } - protected override Exception CreateException(String msg) { - return new Exception(msg); - } - } - - private abstract class AbstractAttributeHandler - : AbstractObjectSerializationHandler - where T : ConnectorAttribute - { - - protected AbstractAttributeHandler(String typeName) - :base(typeof(T),typeName) { - } - - - public override sealed Object Deserialize(ObjectDecoder decoder) { - String name = decoder.ReadStringField("name",null); - IList val = (IList)decoder.ReadObjectField("Values",typeof(IList),null); - return CreateAttribute(name, val); - } - - public override sealed void Serialize(Object obj, ObjectEncoder encoder) - { - ConnectorAttribute val = (ConnectorAttribute)obj; - encoder.WriteStringField("name", val.Name); - encoder.WriteObjectField("Values", val.Value, true); - } - - protected abstract T CreateAttribute(String name, IList val); - } - - private class AttributeHandler - : AbstractAttributeHandler { - public AttributeHandler() - : base("Attribute") { - - } - protected override ConnectorAttribute CreateAttribute(String name, IList value) { - return ConnectorAttributeBuilder.Build(name, value); - } - } - - private class AttributeInfoHandler : AbstractObjectSerializationHandler { - public AttributeInfoHandler() - : base(typeof(ConnectorAttributeInfo),"AttributeInfo") { - - } - public override Object Deserialize(ObjectDecoder decoder) { - ConnectorAttributeInfoBuilder builder = new ConnectorAttributeInfoBuilder(); - builder.Name=( - decoder.ReadStringField("name",null)); - builder.ValueType=( - decoder.ReadClassField("type",null)); - ConnectorAttributeInfo.Flags flags = ConnectorAttributeInfo.Flags.NONE; - int count = decoder.GetNumSubObjects(); - for ( int i = 0; i < count; i++ ) { - object o = decoder.ReadObjectContents(i); - if ( o is ConnectorAttributeInfo.Flags ) { - ConnectorAttributeInfo.Flags f = - (ConnectorAttributeInfo.Flags)o; - flags |= f; - } - } - builder.InfoFlags = flags; - return builder.Build(); - } - - public override void Serialize(Object obj, ObjectEncoder encoder) { - ConnectorAttributeInfo val = (ConnectorAttributeInfo)obj; - encoder.WriteStringField("name", val.Name); - encoder.WriteClassField("type", val.ValueType); - ConnectorAttributeInfo.Flags flags = val.InfoFlags; - foreach (Enum e in Enum.GetValues(typeof(ConnectorAttributeInfo.Flags))) { - ConnectorAttributeInfo.Flags flag = - (ConnectorAttributeInfo.Flags)e; - if ( (flags & flag) != 0 ) { - encoder.WriteObjectContents(flag); - } - } - } - } - - private class ConnectorObjectHandler : AbstractObjectSerializationHandler { - public ConnectorObjectHandler() - : base(typeof(ConnectorObject),"ConnectorObject") { - - } - public override Object Deserialize(ObjectDecoder decoder) { - ObjectClass oclass = - (ObjectClass)decoder.ReadObjectField("ObjectClass",typeof(ObjectClass),null); - ICollection attsObj = - (ICollection)decoder.ReadObjectField("Attributes",typeof(ICollection),null); - ICollection atts = - CollectionUtil.NewSet(attsObj); - return new ConnectorObject(oclass,atts); - } - - public override void Serialize(Object obj, ObjectEncoder encoder) { - ConnectorObject val = (ConnectorObject)obj; - encoder.WriteObjectField("ObjectClass",val.ObjectClass,true); - encoder.WriteObjectField("Attributes", val.GetAttributes(),true); - } - } - private class NameHandler - : AbstractObjectSerializationHandler { - public NameHandler() - : base(typeof(Name),"Name") { - - } - public override Object Deserialize(ObjectDecoder decoder) { - String str = decoder.ReadStringContents(); - return new Name(str); - } - - public override void Serialize(Object obj, ObjectEncoder encoder) { - Name val = (Name)obj; - encoder.WriteStringContents(val.GetNameValue()); - } - } - private class ObjectClassHandler : AbstractObjectSerializationHandler { - public ObjectClassHandler() - : base(typeof(ObjectClass),"ObjectClass") { - - } - public override object Deserialize(ObjectDecoder decoder) { - string type = decoder.ReadStringField("type",null); - return new ObjectClass(type); - } - - public override void Serialize(object obj, ObjectEncoder encoder) { - ObjectClass val = (ObjectClass)obj; - encoder.WriteStringField("type",val.GetObjectClassValue()); - } - } - - private class ObjectClassInfoHandler : AbstractObjectSerializationHandler { - public ObjectClassInfoHandler() - : base(typeof(ObjectClassInfo),"ObjectClassInfo") { - - } - public override Object Deserialize(ObjectDecoder decoder) { - string type = - decoder.ReadStringField("type",null); - bool container = - decoder.ReadBooleanField("container", false); - - ICollection attrInfoObj = - (ICollection)decoder.ReadObjectField("AttributeInfos",typeof(ICollection),null); - - ICollection attrInfo = - CollectionUtil.NewSet(attrInfoObj); - - return new ObjectClassInfo(type,attrInfo,container); - } - - public override void Serialize(Object obj, ObjectEncoder encoder) { - ObjectClassInfo val = (ObjectClassInfo)obj; - encoder.WriteStringField("type",val.ObjectType); - encoder.WriteBooleanField("container",val.IsContainer); - encoder.WriteObjectField("AttributeInfos", val.ConnectorAttributeInfos,true); - } - } - private class SchemaHandler : AbstractObjectSerializationHandler { - public SchemaHandler() - : base(typeof(Schema),"Schema") { - - } - public override Object Deserialize(ObjectDecoder decoder) { - - ICollection objectClassesObj = - (ICollection)decoder.ReadObjectField("ObjectClassInfos",typeof(ICollection),null); - ICollection objectClasses = - CollectionUtil.NewSet(objectClassesObj); - IDictionary objectClassesByName = new Dictionary(); - foreach (ObjectClassInfo info in objectClasses) { - objectClassesByName[info.ObjectType] = info; - } - ICollection operationOptionsObj = - (ICollection)decoder.ReadObjectField("OperationOptionInfos",typeof(ICollection),null); - ICollection operationOptions = - CollectionUtil.NewSet(operationOptionsObj); - IDictionary optionsByName = new Dictionary(); - foreach (OperationOptionInfo info in operationOptions) { - optionsByName[info.Name] = info; - } - IDictionary objectClassNamesByOperationObj = - (IDictionary)decoder.ReadObjectField("objectClassesByOperation",null,null); - IDictionary,ICollection> - objectClassesByOperation = - new Dictionary, ICollection>(); - foreach (KeyValuePair entry in objectClassNamesByOperationObj) { - SafeType op = SafeType.ForRawType((Type)entry.Key); - ICollection namesObj = - (ICollection)entry.Value; - ICollection infos = - new HashSet(); - foreach (object name in namesObj) { - ObjectClassInfo objectClass = CollectionUtil.GetValue(objectClassesByName,(string)name,null); - if ( objectClass != null ) { - infos.Add(objectClass); - } - } - objectClassesByOperation[op] = infos; - } - IDictionary optionsByOperationObj = - (IDictionary)decoder.ReadObjectField("optionsByOperation",null,null); - IDictionary,ICollection> - optionsByOperation = - new Dictionary, ICollection>(); - foreach (KeyValuePair entry in optionsByOperationObj) { - SafeType op = SafeType.ForRawType((Type)entry.Key); - ICollection namesObj = - (ICollection)entry.Value; - ICollection infos = - new HashSet(); - foreach (object name in namesObj) { - OperationOptionInfo info = CollectionUtil.GetValue(optionsByName,(string)name,null); - if ( info != null ) { - infos.Add(info); - } - } - optionsByOperation[op] = infos; - } - return new Schema(objectClasses, - operationOptions, - objectClassesByOperation, - optionsByOperation); - } - - public override void Serialize(Object obj, ObjectEncoder encoder) { - Schema val = (Schema)obj; - encoder.WriteObjectField("ObjectClassInfos", val.ObjectClassInfo,true); - encoder.WriteObjectField("OperationOptionInfos", val.OperationOptionInfo,true); - IDictionary> - objectClassNamesByOperation = new Dictionary>(); - IDictionary> - optionNamesByOperation = new Dictionary>(); - - - foreach (KeyValuePair, ICollection> - entry in val.SupportedObjectClassesByOperation) { - ICollection value = entry.Value; - ICollection names = new HashSet(); - foreach (ObjectClassInfo info in value) { - names.Add(info.ObjectType); - } - objectClassNamesByOperation[entry.Key.RawType] = names; - } - foreach (KeyValuePair, ICollection> - entry in val.SupportedOptionsByOperation) { - ICollection value = entry.Value; - ICollection names = new HashSet(); - foreach (OperationOptionInfo info in value) { - names.Add(info.Name); - } - optionNamesByOperation[entry.Key.RawType] = names; - } - encoder.WriteObjectField("objectClassesByOperation",objectClassNamesByOperation,false); - encoder.WriteObjectField("optionsByOperation",optionNamesByOperation,false); - } - } - private class UidHandler - : AbstractObjectSerializationHandler { - public UidHandler() - : base(typeof(Uid),"Uid") { - - } - public override Object Deserialize(ObjectDecoder decoder) { - String str = decoder.ReadStringContents(); - return new Uid(str); - } - - public override void Serialize(Object obj, ObjectEncoder encoder) { - Uid val = (Uid)obj; - encoder.WriteStringContents(val.GetUidValue()); - } - } - private class ScriptContextHandler : AbstractObjectSerializationHandler { - public ScriptContextHandler() - : base(typeof(ScriptContext),"ScriptContext") { - - } - public override Object Deserialize(ObjectDecoder decoder) { - String scriptLanguage = - decoder.ReadStringField("scriptLanguage",null); - IDictionary arguments = - (IDictionary)decoder.ReadObjectField("scriptArguments",null,null); - String scriptText = - (String)decoder.ReadObjectField("scriptText",typeof(string),null); - IDictionary arguments2 = - CollectionUtil.NewDictionary(arguments); - return new ScriptContext(scriptLanguage,scriptText,arguments2); - } - - public override void Serialize(Object obj, ObjectEncoder encoder) { - ScriptContext val = (ScriptContext)obj; - encoder.WriteStringField("scriptLanguage", val.ScriptLanguage); - encoder.WriteObjectField("scriptArguments", val.ScriptArguments,false); - encoder.WriteObjectField("scriptText", val.ScriptText,true); - } - } - private class OperationOptionsHandler : AbstractObjectSerializationHandler { - public OperationOptionsHandler() - : base(typeof(OperationOptions),"OperationOptions") { - - } - public override Object Deserialize(ObjectDecoder decoder) { - IDictionary options = - (IDictionary)decoder.ReadObjectField("options",null,null); - IDictionary options2 = - CollectionUtil.NewDictionary(options); - return new OperationOptions(options2); - } - - public override void Serialize(Object obj, ObjectEncoder encoder) { - OperationOptions val = (OperationOptions)obj; - encoder.WriteObjectField("options", val.Options,false); - } - } - private class OperationOptionInfoHandler : AbstractObjectSerializationHandler { - public OperationOptionInfoHandler() - : base(typeof(OperationOptionInfo),"OperationOptionInfo") { - - } - public override Object Deserialize(ObjectDecoder decoder) { - String name = decoder.ReadStringField("name",null); - Type type = decoder.ReadClassField("type",null); - return new OperationOptionInfo(name,type); - } - - public override void Serialize(Object obj, ObjectEncoder encoder) { - OperationOptionInfo val = (OperationOptionInfo)obj; - encoder.WriteStringField("name", val.Name); - encoder.WriteClassField("type", val.OptionType); - } - } - private class SyncTokenHandler : AbstractObjectSerializationHandler { - public SyncTokenHandler() - : base(typeof(SyncToken),"SyncToken") { - - } - public override Object Deserialize(ObjectDecoder decoder) { - Object value = decoder.ReadObjectField("value",null,null); - return new SyncToken(value); - } - - public override void Serialize(Object obj, ObjectEncoder encoder) { - SyncToken val = (SyncToken)obj; - encoder.WriteObjectField("value", val.Value,false); - } - } - private class SyncDeltaHandler : AbstractObjectSerializationHandler { - public SyncDeltaHandler() - : base(typeof(SyncDelta),"SyncDelta") { - - } - public override Object Deserialize(ObjectDecoder decoder) { - SyncDeltaBuilder builder = new SyncDeltaBuilder(); - builder.DeltaType=((SyncDeltaType)decoder.ReadObjectField("SyncDeltaType",typeof(SyncDeltaType),null)); - builder.Token=((SyncToken)decoder.ReadObjectField("SyncToken",typeof(SyncToken),null)); - builder.Uid=((Uid)decoder.ReadObjectField("Uid",typeof(Uid),null)); - builder.Object=((ConnectorObject)decoder.ReadObjectField("ConnectorObject",typeof(ConnectorObject),null)); - return builder.Build(); - } - - public override void Serialize(Object obj, ObjectEncoder encoder) { - SyncDelta val = (SyncDelta)obj; - encoder.WriteObjectField("SyncDeltaType", val.DeltaType,true); - encoder.WriteObjectField("SyncToken", val.Token,true); - encoder.WriteObjectField("Uid", val.Uid,true); - encoder.WriteObjectField("ConnectorObject", val.Object, true); - } - } - private class QualifiedUidHandler : AbstractObjectSerializationHandler { - public QualifiedUidHandler() - : base(typeof(QualifiedUid),"QualifiedUid") { - - } - public override Object Deserialize(ObjectDecoder decoder) { - ObjectClass objectClass = (ObjectClass)decoder.ReadObjectField("ObjectClass", typeof(ObjectClass),null); - Uid uid = (Uid)decoder.ReadObjectField("Uid",typeof(Uid),null); - return new QualifiedUid(objectClass,uid); - } - - public override void Serialize(Object obj, ObjectEncoder encoder) { - QualifiedUid val = (QualifiedUid)obj; - encoder.WriteObjectField("ObjectClass", val.ObjectClass, true); - encoder.WriteObjectField("Uid", val.Uid, true); - } - } - - } - #endregion - - #region FilterHandlers - internal static class FilterHandlers { - public static readonly IList HANDLERS = - new List(); - static FilterHandlers() { - HANDLERS.Add(new AndFilterHandler()); - HANDLERS.Add(new ContainsFilterHandler()); - HANDLERS.Add(new EndsWithFilterHandler()); - HANDLERS.Add(new EqualsFilterHandler()); - HANDLERS.Add(new GreaterThanFilterHandler()); - HANDLERS.Add(new GreaterThanOrEqualFilterHandler()); - HANDLERS.Add(new LessThanFilterHandler()); - HANDLERS.Add(new LessThanOrEqualFilterHandler()); - HANDLERS.Add(new NotFilterHandler()); - HANDLERS.Add(new OrFilterHandler()); - HANDLERS.Add(new StartsWithFilterHandler()); - HANDLERS.Add(new ContainsAllValuesFilterHandler()); - } - - private abstract class CompositeFilterHandler - : AbstractObjectSerializationHandler - where T : CompositeFilter { - - protected CompositeFilterHandler(String typeName) - : base(typeof(T),typeName) { - } - - - public override sealed Object Deserialize(ObjectDecoder decoder) { - Filter left = (Filter)decoder.ReadObjectContents(0); - Filter right = (Filter)decoder.ReadObjectContents(1); - return CreateFilter(left, right); - } - - public override sealed void Serialize(Object obj, ObjectEncoder encoder) { - CompositeFilter val = (CompositeFilter)obj; - encoder.WriteObjectContents(val.Left); - encoder.WriteObjectContents(val.Right); - } - - protected abstract T CreateFilter(Filter left, Filter right); - } - - private abstract class AttributeFilterHandler - : AbstractObjectSerializationHandler - where T : AttributeFilter { - - protected AttributeFilterHandler(String typeName) - : base(typeof(T),typeName) { - } - - - public override sealed Object Deserialize(ObjectDecoder decoder) { - ConnectorAttribute attribute = (ConnectorAttribute)decoder.ReadObjectField("attribute",null,null); - return CreateFilter(attribute); - } - - public override sealed void Serialize(Object obj, ObjectEncoder encoder) - { - AttributeFilter val = (AttributeFilter)obj; - encoder.WriteObjectField("attribute", val.GetAttribute(), false); - } - - protected abstract T CreateFilter(ConnectorAttribute attribute); - } - - - - - private class AndFilterHandler : CompositeFilterHandler { - public AndFilterHandler() - : base("AndFilter") { - } - protected override AndFilter CreateFilter(Filter left, Filter right) { - return new AndFilter(left,right); - } - } - - private class ContainsFilterHandler : AttributeFilterHandler { - public ContainsFilterHandler() - : base("ContainsFilter") { - } - protected override ContainsFilter CreateFilter(ConnectorAttribute attribute) { - return new ContainsFilter(attribute); - } - } - - private class EndsWithFilterHandler : AttributeFilterHandler { - public EndsWithFilterHandler() - : base("EndsWithFilter") { - } - protected override EndsWithFilter CreateFilter(ConnectorAttribute attribute) { - return new EndsWithFilter(attribute); - } - } - - private class EqualsFilterHandler : AttributeFilterHandler { - public EqualsFilterHandler() - : base("EqualsFilter") { - } - protected override EqualsFilter CreateFilter(ConnectorAttribute attribute) { - return new EqualsFilter(attribute); - } - } - - private class GreaterThanFilterHandler : AttributeFilterHandler { - public GreaterThanFilterHandler() - : base("GreaterThanFilter") { - } - protected override GreaterThanFilter CreateFilter(ConnectorAttribute attribute) { - return new GreaterThanFilter(attribute); - } - } - - private class GreaterThanOrEqualFilterHandler : AttributeFilterHandler { - public GreaterThanOrEqualFilterHandler() - : base("GreaterThanOrEqualFilter") { - } - protected override GreaterThanOrEqualFilter CreateFilter(ConnectorAttribute attribute) { - return new GreaterThanOrEqualFilter(attribute); - } - } - private class LessThanFilterHandler : AttributeFilterHandler { - public LessThanFilterHandler() - : base("LessThanFilter") { - } - protected override LessThanFilter CreateFilter(ConnectorAttribute attribute) { - return new LessThanFilter(attribute); - } - } - private class LessThanOrEqualFilterHandler : AttributeFilterHandler { - public LessThanOrEqualFilterHandler() - : base("LessThanOrEqualFilter") { - } - protected override LessThanOrEqualFilter CreateFilter(ConnectorAttribute attribute) { - return new LessThanOrEqualFilter(attribute); - } - } - private class NotFilterHandler - : AbstractObjectSerializationHandler { - - public NotFilterHandler() - : base(typeof(NotFilter),"NotFilter") { - } - - - public override sealed Object Deserialize(ObjectDecoder decoder) { - Filter filter = - (Filter)decoder.ReadObjectContents(0); - return new NotFilter(filter); - } - - public override sealed void Serialize(Object obj, ObjectEncoder encoder) - { - NotFilter val = (NotFilter)obj; - encoder.WriteObjectContents(val.Filter); - } - - } - private class OrFilterHandler : CompositeFilterHandler { - public OrFilterHandler() - : base("OrFilter") { - } - protected override OrFilter CreateFilter(Filter left, Filter right) { - return new OrFilter(left,right); - } - } - private class StartsWithFilterHandler : AttributeFilterHandler { - public StartsWithFilterHandler() - : base("StartsWithFilter") { - } - protected override StartsWithFilter CreateFilter(ConnectorAttribute attribute) { - return new StartsWithFilter(attribute); - } - } - - private class ContainsAllValuesFilterHandler : AttributeFilterHandler { - public ContainsAllValuesFilterHandler() - : base("ContainsAllValuesFilter") { - } - protected override ContainsAllValuesFilter CreateFilter(ConnectorAttribute attribute) { - return new ContainsAllValuesFilter(attribute); - } - } - - } - #endregion - - #region MessageHandlers - internal static class MessageHandlers { - public static readonly IList HANDLERS = - new List(); - static MessageHandlers() { - HANDLERS.Add(new HelloRequestHandler()); - HANDLERS.Add(new HelloResponseHandler()); - HANDLERS.Add(new OperationRequestHandler()); - HANDLERS.Add(new OperationResponseEndHandler()); - HANDLERS.Add(new OperationResponsePartHandler()); - HANDLERS.Add(new OperationRequestMoreDataHandler()); - HANDLERS.Add(new OperationRequestStopDataHandler()); - HANDLERS.Add(new OperationResponsePauseHandler()); - HANDLERS.Add(new EchoMessageHandler()); - } - private class HelloRequestHandler - : AbstractObjectSerializationHandler { - - public HelloRequestHandler() - : base(typeof(HelloRequest),"HelloRequest") { - } - - - public override sealed Object Deserialize(ObjectDecoder decoder) { - return new HelloRequest(); - } - - public override sealed void Serialize(Object obj, ObjectEncoder encoder) - { - } - - } - private class HelloResponseHandler - : AbstractObjectSerializationHandler { - - public HelloResponseHandler() - : base(typeof(HelloResponse),"HelloResponse") { - } - - - public override sealed Object Deserialize(ObjectDecoder decoder) { - Exception exception = - (Exception)decoder.ReadObjectField("exception",null,null); - IList connectorInfosObj = - (IList)decoder.ReadObjectField("ConnectorInfos",typeof(IList),null); - IList connectorInfos = - CollectionUtil.NewList(connectorInfosObj); - return new HelloResponse(exception,connectorInfos); - } - - public override sealed void Serialize(Object obj, ObjectEncoder encoder) - { - HelloResponse val = (HelloResponse)obj; - encoder.WriteObjectField("exception", val.Exception, false); - encoder.WriteObjectField("ConnectorInfos", val.ConnectorInfos, true); - } - - } - private class OperationRequestHandler - : AbstractObjectSerializationHandler { - - public OperationRequestHandler() - : base(typeof(OperationRequest),"OperationRequest") { - } - - - public override sealed Object Deserialize(ObjectDecoder decoder) { - ConnectorKey connectorKey = - (ConnectorKey)decoder.ReadObjectField("ConnectorKey",typeof(ConnectorKey),null); - APIConfigurationImpl configuration = - (APIConfigurationImpl)decoder.ReadObjectField("APIConfiguration",typeof(APIConfigurationImpl),null); - Type operation = - decoder.ReadClassField("operation",null); - string operationMethodName = - decoder.ReadStringField("operationMethodName",null); - IList arguments = (IList) - decoder.ReadObjectField("Arguments",typeof(IList),null); - return new OperationRequest(connectorKey, - configuration, - SafeType.ForRawType(operation), - operationMethodName, - arguments); - } - - public override sealed void Serialize(Object obj, ObjectEncoder encoder) - { - OperationRequest val = - (OperationRequest)obj; - encoder.WriteClassField("operation", - val.Operation.RawType); - encoder.WriteStringField("operationMethodName", - val.OperationMethodName); - encoder.WriteObjectField("ConnectorKey", - val.ConnectorKey,true); - encoder.WriteObjectField("APIConfiguration", - val.Configuration,true); - encoder.WriteObjectField("Arguments", - val.Arguments,true); - } - - } - private class OperationResponseEndHandler - : AbstractObjectSerializationHandler { - - public OperationResponseEndHandler() - : base(typeof(OperationResponseEnd),"OperationResponseEnd") { - } - - - public override sealed Object Deserialize(ObjectDecoder decoder) { - return new OperationResponseEnd(); - } - - public override sealed void Serialize(Object obj, ObjectEncoder encoder) - { - } - } - private class OperationResponsePartHandler - : AbstractObjectSerializationHandler { - - public OperationResponsePartHandler() - : base(typeof(OperationResponsePart),"OperationResponsePart") { - } - - - public override sealed Object Deserialize(ObjectDecoder decoder) { - Exception exception = - (Exception)decoder.ReadObjectField("exception",null,null); - Object result = - decoder.ReadObjectField("result",null,null); - - return new OperationResponsePart(exception,result); - } - - public override sealed void Serialize(Object obj, ObjectEncoder encoder) - { - OperationResponsePart val = (OperationResponsePart)obj; - encoder.WriteObjectField("exception", val.Exception, false); - encoder.WriteObjectField("result", val.Result, false); - } - } - private class OperationRequestMoreDataHandler - : AbstractObjectSerializationHandler { - - public OperationRequestMoreDataHandler() - : base(typeof(OperationRequestMoreData),"OperationRequestMoreData") { - } - - - public override sealed Object Deserialize(ObjectDecoder decoder) { - return new OperationRequestMoreData(); - } - - public override sealed void Serialize(Object obj, ObjectEncoder encoder) - { - } - } - private class OperationRequestStopDataHandler - : AbstractObjectSerializationHandler { - - public OperationRequestStopDataHandler() - : base(typeof(OperationRequestStopData),"OperationRequestStopData") { - } - - - public override sealed Object Deserialize(ObjectDecoder decoder) { - return new OperationRequestStopData(); - } - - public override sealed void Serialize(Object obj, ObjectEncoder encoder) - { - } - } - private class OperationResponsePauseHandler - : AbstractObjectSerializationHandler { - - public OperationResponsePauseHandler() - : base(typeof(OperationResponsePause),"OperationResponsePause") { - } - - - public override sealed Object Deserialize(ObjectDecoder decoder) { - return new OperationResponsePause(); - } - - public override sealed void Serialize(Object obj, ObjectEncoder encoder) - { - } - } - private class EchoMessageHandler - : AbstractObjectSerializationHandler { - - public EchoMessageHandler() - : base(typeof(EchoMessage),"EchoMessage") { - } - - - public override sealed Object Deserialize(ObjectDecoder decoder) { - return new EchoMessage(decoder.ReadObjectField("value",null,null), - (String)decoder.ReadObjectField("objectXml",typeof(string),null)); - } - - public override sealed void Serialize(Object obj, ObjectEncoder encoder) - { - EchoMessage val = (EchoMessage)obj; - encoder.WriteObjectField("value",val.Object,false); - encoder.WriteObjectField("objectXml",val.ObjectXml,true); - } - } - - } - #endregion - -} diff --git a/DotNetConnectors.sln/FrameworkInternal/SerializerBinary.cs b/DotNetConnectors.sln/FrameworkInternal/SerializerBinary.cs deleted file mode 100644 index c98692ac..00000000 --- a/DotNetConnectors.sln/FrameworkInternal/SerializerBinary.cs +++ /dev/null @@ -1,775 +0,0 @@ -/* - * ==================== - * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER. - * - * Copyright 2007-2008 Sun Microsystems, Inc. All rights reserved. - * - * The contents of this file are subject to the terms of the Common Development - * and Distribution License("CDDL") (the "License"). You may not use this file - * except in compliance with the License. - * - * You can obtain a copy of the License at - * http://IdentityConnectors.dev.java.net/legal/license.txt - * See the License for the specific language governing permissions and limitations - * under the License. - * - * When distributing the Covered Code, include this CDDL Header Notice in each file - * and include the License file at identityconnectors/legal/license.txt. - * If applicable, add the following below this CDDL Header, with the fields - * enclosed by brackets [] replaced by your own identifying information: - * "Portions Copyrighted [year] [name of copyright owner]" - * ==================== - */ -using System; -using System.Collections.Generic; -using Org.IdentityConnectors.Common; -using Org.IdentityConnectors.Framework.Common.Exceptions; -using Org.IdentityConnectors.Framework.Common.Serializer; -using Org.IdentityConnectors.Framework.Impl.Serializer; -using System.IO; -using System.Text; -namespace Org.IdentityConnectors.Framework.Impl.Serializer.Binary -{ - - internal class InternalEncoder { - - - /** - * Mapping from type name to the ID we serialize so we only have to - */ - private IDictionary _constantPool = - new Dictionary(); - - private IList _constantBuffer = new List(); - - private IList _outputBufferStack = new List(); - internal Stream _rootOutput; - private bool _firstObject = true; - - public InternalEncoder(Stream output) { - _rootOutput = output; - } - - public void WriteObject(ObjectEncoder encoder, Object obj) { - - if (_firstObject) { - WriteInt(BinaryObjectEncoder.OBJECT_MAGIC); - WriteInt(BinaryObjectEncoder.ENCODING_VERSION); - _firstObject = false; - } - - MemoryStream objectBuffer = new MemoryStream(); - _outputBufferStack.Add(objectBuffer); - - if ( obj == null ) { - WriteByte(BinaryObjectEncoder.OBJECT_TYPE_NULL); - } - else { - Type clazz = obj.GetType(); - WriteClass(clazz); - ObjectSerializationHandler handler = - ObjectSerializerRegistry.GetHandlerByObjectType(clazz); - if ( handler == null ) { - //we may have special handlers for certain types of arrays - //if handler is null, treat like any other array - if ( obj is Array ) { - Array array = (Array)obj; - int length = array.Length; - for ( int i = 0; i < length; i++ ) { - Object val = array.GetValue(i); - StartAnonymousField(); - WriteObject(encoder,val); - EndField(); - } - } - else { - throw new ConnectorException("No serializer for class: "+clazz); - } - } - else { - handler.Serialize(obj, encoder); - } - } - - //write end-object into the current obj buffer - WriteByte(BinaryObjectEncoder.FIELD_TYPE_END_OBJECT); - - //pop the stack - _outputBufferStack.RemoveAt(_outputBufferStack.Count-1); - - //it's a top-level object, flush the constant pool - if (_outputBufferStack.Count == 0) { - WriteInt(_constantBuffer.Count); - foreach (String constant in _constantBuffer) { - WriteString(constant,false); - WriteInt(_constantPool[constant]); - } - _constantBuffer.Clear(); - } - - //now write the actual object - objectBuffer.Close(); - byte [] bytes = objectBuffer.ToArray(); - WriteBytes(bytes); - } - - public void WriteClass(Type clazz) - { - ObjectSerializationHandler handler = - ObjectSerializerRegistry.GetHandlerByObjectType(clazz); - ObjectTypeMapper mapper = - ObjectSerializerRegistry.GetMapperByObjectType(clazz); - if ( handler == null && clazz.IsArray ) { - //we may have special handlers for certain types of arrays - //if handler is null, treat like any other array - WriteByte(BinaryObjectEncoder.OBJECT_TYPE_ARRAY); - WriteClass(clazz.GetElementType()); - } - else if ( mapper == null ) { - throw new ConnectorException("No serializer for class: "+clazz); - } - else { - String typeName = mapper.HandledSerialType; - WriteByte(BinaryObjectEncoder.OBJECT_TYPE_CLASS); - WriteString(typeName,true); - } - } - - public void StartAnonymousField() { - WriteByte(BinaryObjectEncoder.FIELD_TYPE_ANONYMOUS_FIELD); - MemoryStream buf = new MemoryStream(); - _outputBufferStack.Add(buf); - } - - public void StartField(String name) { - WriteByte(BinaryObjectEncoder.FIELD_TYPE_NAMED_FIELD); - WriteString(name,true); - MemoryStream buf = new MemoryStream(); - _outputBufferStack.Add(buf); - } - - public void EndField() { - MemoryStream buf = _outputBufferStack[_outputBufferStack.Count-1]; - _outputBufferStack.RemoveAt(_outputBufferStack.Count-1); - buf.Close(); - byte [] bytes = buf.ToArray(); - WriteByteArray(bytes); - } - - public void WriteInt(int v) - { - Stream output = GetCurrentOutput(); - output.WriteByte((byte)(0xff & (v >> 24))); - output.WriteByte((byte)(0xff & (v >> 16))); - output.WriteByte((byte)(0xff & (v >> 8))); - output.WriteByte((byte)(0xff & v)); - } - - public void WriteLong(long v) - { - Stream output = GetCurrentOutput(); - output.WriteByte((byte)(0xff & (v >> 56))); - output.WriteByte((byte)(0xff & (v >> 48))); - output.WriteByte((byte)(0xff & (v >> 40))); - output.WriteByte((byte)(0xff & (v >> 32))); - output.WriteByte((byte)(0xff & (v >> 24))); - output.WriteByte((byte)(0xff & (v >> 16))); - output.WriteByte((byte)(0xff & (v >> 8))); - output.WriteByte((byte)(0xff & v)); - } - - public void WriteDouble(double l) - { - long val = BitConverter.DoubleToInt64Bits(l); - WriteLong(val); - } - - public void WriteByteArray(byte[] v) - { - WriteInt(v.Length); - WriteBytes(v); - } - - public void WriteByte(byte b) - { - GetCurrentOutput().WriteByte(b); - } - - public void WriteBoolean(bool b) - { - WriteByte(b ? (byte)1 : (byte)0); - } - - public void WriteString(String str, bool intern) - { - if ( intern ) { - int code = InternIdentifier(str); - WriteInt(code); - return; - } - byte [] bytes = Encoding.UTF8.GetBytes(str); - WriteByteArray(bytes); - } - - private int InternIdentifier(String name) { - int code = CollectionUtil.GetValue(_constantPool,name,-1); - if ( code == -1 ) { - code = _constantPool.Count; - _constantPool[name] = code; - _constantBuffer.Add(name); - } - return code; - } - - private void WriteBytes(byte [] v) - { - //only write if length > 0 - C# seems to have a problem with - //zero-length byte arrays - if ( v.Length > 0 ) { - GetCurrentOutput().Write(v,0,v.Length); - } - } - - private Stream GetCurrentOutput() { - if (_outputBufferStack.Count == 0) { - return _rootOutput; - } - else { - MemoryStream buf = _outputBufferStack[_outputBufferStack.Count-1]; - return buf; - } - } - - } - - - - internal class BinaryObjectEncoder : ObjectEncoder,BinaryObjectSerializer { - - - - public const int ENCODING_VERSION = 1; - - public const int OBJECT_MAGIC = 0xFAFB; - - public const byte OBJECT_TYPE_NULL = 60; - public const byte OBJECT_TYPE_CLASS = 61; - public const byte OBJECT_TYPE_ARRAY = 62; - - public const byte FIELD_TYPE_ANONYMOUS_FIELD = 70; - public const byte FIELD_TYPE_NAMED_FIELD = 71; - public const byte FIELD_TYPE_END_OBJECT = 72; - - - private InternalEncoder _internalEncoder; - - - - - public BinaryObjectEncoder(Stream output) { - _internalEncoder = new InternalEncoder(new BufferedStream(output,4096)); - } - - public void Flush() { - _internalEncoder._rootOutput.Flush(); - } - - public void Close() { - _internalEncoder._rootOutput.Close(); - } - - public void WriteObject(Object o) { - _internalEncoder.WriteObject(this,o); - } - - public void WriteBooleanContents(bool v) { - _internalEncoder.StartAnonymousField(); - _internalEncoder.WriteBoolean(v); - _internalEncoder.EndField(); - } - - public void WriteBooleanField(String fieldName, bool v) { - _internalEncoder.StartField(fieldName); - _internalEncoder.WriteBoolean(v); - _internalEncoder.EndField(); - } - - public void WriteByteArrayContents(byte[] v) { - _internalEncoder.StartAnonymousField(); - _internalEncoder.WriteByteArray(v); - _internalEncoder.EndField(); - } - - public void WriteClassContents(Type v) { - _internalEncoder.StartAnonymousField(); - _internalEncoder.WriteClass(v); - _internalEncoder.EndField(); - } - - public void WriteClassField(string fieldName, Type v) { - if ( v != null ) { - _internalEncoder.StartField(fieldName); - _internalEncoder.WriteClass(v); - _internalEncoder.EndField(); - } - } - - public void WriteDoubleContents(double v) { - _internalEncoder.StartAnonymousField(); - _internalEncoder.WriteDouble(v); - _internalEncoder.EndField(); - } - - public void WriteDoubleField(String fieldName, double v) { - _internalEncoder.StartField(fieldName); - _internalEncoder.WriteDouble(v); - _internalEncoder.EndField(); - } - - public void WriteFloatContents(float v) { - _internalEncoder.StartAnonymousField(); - //write as double since C# only knows how to deal with that - _internalEncoder.WriteDouble((double)v); - _internalEncoder.EndField(); - } - - public void WriteFloatField(String fieldName, float v) { - _internalEncoder.StartField(fieldName); - //write as double since C# only knows how to deal with that - _internalEncoder.WriteDouble((double)v); - _internalEncoder.EndField(); - } - - public void WriteIntContents(int v) { - _internalEncoder.StartAnonymousField(); - _internalEncoder.WriteInt(v); - _internalEncoder.EndField(); - } - - public void WriteIntField(String fieldName, int v) { - _internalEncoder.StartField(fieldName); - _internalEncoder.WriteInt(v); - _internalEncoder.EndField(); - } - - public void WriteLongContents(long v) { - _internalEncoder.StartAnonymousField(); - _internalEncoder.WriteLong(v); - _internalEncoder.EndField(); - } - - public void WriteLongField(String fieldName, long v) { - _internalEncoder.StartField(fieldName); - _internalEncoder.WriteLong(v); - _internalEncoder.EndField(); - } - - public void WriteObjectContents(Object obj) { - _internalEncoder.StartAnonymousField(); - _internalEncoder.WriteObject(this,obj); - _internalEncoder.EndField(); - } - - public void WriteObjectField(String fieldName, Object obj, bool inline) { - _internalEncoder.StartField(fieldName); - _internalEncoder.WriteObject(this,obj); - _internalEncoder.EndField(); - } - - public void WriteStringContents(String str) { - _internalEncoder.StartAnonymousField(); - _internalEncoder.WriteString(str,false); - _internalEncoder.EndField(); - } - - public void WriteStringField(String fieldName, String val) { - if ( val != null ) { - _internalEncoder.StartField(fieldName); - _internalEncoder.WriteString(val,false); - _internalEncoder.EndField(); - } - } - } - - internal class ReadState { - public IDictionary objectFields = new Dictionary(); - public IList anonymousFields = new List(); - public Stream currentInput; - public ReadState() { - } - public bool StartField(String name) { - currentInput = null; - byte [] content = CollectionUtil.GetValue(objectFields,name,null); - if ( content == null ) { - return false; - } - else { - currentInput = new MemoryStream(content); - return true; - } - } - public void StartAnonymousField(int index) { - if ( index >= anonymousFields.Count ) { - throw new ConnectorException("Anonymous content not found"); - } - currentInput = new MemoryStream(anonymousFields[index]); - } - } - - internal class InternalDecoder { - private readonly byte [] _int_buf = new byte[4]; - private readonly byte [] _long_buf = new byte[8]; - private readonly byte [] _byte_buf = new byte[1]; - - private bool _firstObject = true; - - private readonly IDictionary _constantPool = - new Dictionary(); - - private readonly IList _readStateStack = new List(); - internal readonly Stream _rootInput; - - public InternalDecoder(Stream input) { - _rootInput = input; - } - - public Object ReadObject(ObjectDecoder decoder) { - - if (_firstObject) { - int magic = ReadInt(); - if ( magic != BinaryObjectEncoder.OBJECT_MAGIC) { - throw new ConnectorException("Bad magic number: "+magic); - } - int version = ReadInt(); - if ( version != BinaryObjectEncoder.ENCODING_VERSION ) { - throw new ConnectorException("Unexpected version: "+version); - } - _firstObject = false; - } - - //if it's a top-level object, it's proceeded by a constant pool - if (_readStateStack.Count == 0) - { - int size = ReadInt(); - for ( int i = 0; i < size; i++ ) { - String constant = ReadString(false); - int code = ReadInt(); - _constantPool[code] = constant; - } - } - - Type clazz = ReadClass(); - - ReadState state = new ReadState(); - while(true) { - byte type = ReadByte(); - if ( type == BinaryObjectEncoder.FIELD_TYPE_END_OBJECT ) { - break; - } - else if ( type == BinaryObjectEncoder.FIELD_TYPE_ANONYMOUS_FIELD ) { - byte [] bytes = ReadByteArray(); - state.anonymousFields.Add(bytes); - } - else if ( type == BinaryObjectEncoder.FIELD_TYPE_NAMED_FIELD ) { - String fieldName = ReadString(true); - byte [] bytes = ReadByteArray(); - state.objectFields[fieldName] = bytes; - } - else { - throw new ConnectorException("Unknown type: "+type); - } - } - _readStateStack.Add(state); - - Object rv; - if ( clazz == null ) { - rv = null; - } - else { - ObjectSerializationHandler handler = - ObjectSerializerRegistry.GetHandlerByObjectType(clazz); - if ( handler == null ) { - //we may have special handlers for certain types of arrays - //if handler is null, treat like any other array - if ( clazz.IsArray ) { - int length = GetNumAnonymousFields(); - Array array = Array.CreateInstance(clazz.GetElementType(), - length); - for ( int i = 0; i < length; i++) { - StartAnonymousField(i); - Object element = ReadObject(decoder); - array.SetValue( element, i ); - } - rv = array; - } - else { - throw new ConnectorException("No deserializer for type: "+clazz); - } - } - else { - rv = handler.Deserialize(decoder); - } - } - _readStateStack.RemoveAt(_readStateStack.Count-1); - return rv; - } - - public Type ReadClass() { - int type = ReadByte(); - if ( type == BinaryObjectEncoder.OBJECT_TYPE_NULL ) { - return null; - } - else if ( type == BinaryObjectEncoder.OBJECT_TYPE_ARRAY ) { - Type componentClass = ReadClass(); - return componentClass.MakeArrayType(); - } - else if ( type == BinaryObjectEncoder.OBJECT_TYPE_CLASS ) { - String typeName = ReadString(true); - ObjectTypeMapper mapper = - ObjectSerializerRegistry.GetMapperBySerialType(typeName); - if ( mapper == null ) { - throw new ConnectorException("No deserializer for type: "+typeName); - } - return mapper.HandledObjectType; - } - else { - throw new ConnectorException("Bad type value: "+type); - } - } - - public int GetNumAnonymousFields() { - ReadState readState = _readStateStack[_readStateStack.Count-1]; - return readState.anonymousFields.Count; - } - - public void StartAnonymousField(int index) { - ReadState readState = _readStateStack[_readStateStack.Count-1]; - readState.StartAnonymousField(index); - } - - public bool StartField(String name) { - ReadState readState = _readStateStack[_readStateStack.Count-1]; - return readState.StartField(name); - } - - public int ReadInt() { - ReadByteArrayFully(_int_buf); - return (((_int_buf[0] & 0xff) << 24) | ((_int_buf[1] & 0xff) << 16) | - ((_int_buf[2] & 0xff) << 8) | (_int_buf[3] & 0xff)); - } - - public long ReadLong() { - ReadByteArrayFully(_long_buf); - return (((long)(_long_buf[0] & 0xff) << 56) | - ((long)(_long_buf[1] & 0xff) << 48) | - ((long)(_long_buf[2] & 0xff) << 40) | - ((long)(_long_buf[3] & 0xff) << 32) | - ((long)(_long_buf[4] & 0xff) << 24) | - ((long)(_long_buf[5] & 0xff) << 16) | - ((long)(_long_buf[6] & 0xff) << 8) | - ((long)(_long_buf[7] & 0xff))); - } - - public double ReadDouble() { - long v = ReadLong(); - return BitConverter.Int64BitsToDouble(v); - } - - public byte [] ReadByteArray() { - int len = ReadInt(); - byte [] bytes = new byte[len]; - ReadByteArrayFully(bytes); - return bytes; - } - - public byte ReadByte() - { - ReadByteArrayFully(_byte_buf); - return _byte_buf[0]; - } - - public bool ReadBoolean() { - byte b = ReadByte(); - return b != 0; - } - - public String ReadString(bool interned) { - if ( interned ) { - int code = ReadInt(); - String name = CollectionUtil.GetValue(_constantPool,code,null); - if ( name == null ) { - throw new ConnectorException("Undeclared code: "+code); - } - return name; - } - byte [] bytes = ReadByteArray(); - return Encoding.UTF8.GetString(bytes); - } - - private Stream GetCurrentInput() { - if (_readStateStack.Count > 0) { - ReadState state = _readStateStack[_readStateStack.Count-1]; - return state.currentInput; - } - else { - return _rootInput; - } - } - - private void ReadByteArrayFully(byte [] bytes) - { - int pos = 0; - while ( pos < bytes.Length ) { - int count = GetCurrentInput().Read(bytes,pos,bytes.Length-pos); - if ( count <= 0 ) { - throw new EndOfStreamException(); - } - pos+=count; - } - } - } - - internal class BinaryObjectDecoder : ObjectDecoder,BinaryObjectDeserializer { - - private InternalDecoder _internalDecoder; - - public BinaryObjectDecoder(Stream inp) { - _internalDecoder = new InternalDecoder(new BufferedStream(inp,4096)); - } - - public void Close() { - _internalDecoder._rootInput.Close(); - } - - public Object ReadObject() { - return _internalDecoder.ReadObject(this); - } - - public bool ReadBooleanContents() { - _internalDecoder.StartAnonymousField(0); - return _internalDecoder.ReadBoolean(); - } - - public bool ReadBooleanField(String fieldName, bool dflt) { - if (_internalDecoder.StartField(fieldName)) { - return _internalDecoder.ReadBoolean(); - } - else { - return dflt; - } - } - - public byte[] ReadByteArrayContents() { - _internalDecoder.StartAnonymousField(0); - return _internalDecoder.ReadByteArray(); - } - - public Type ReadClassContents() { - _internalDecoder.StartAnonymousField(0); - return _internalDecoder.ReadClass(); - } - - public Type ReadClassField(string fieldName, Type dflt) { - if (_internalDecoder.StartField(fieldName)) { - return _internalDecoder.ReadClass(); - } - else { - return dflt; - } - } - - public double ReadDoubleContents() { - _internalDecoder.StartAnonymousField(0); - return _internalDecoder.ReadDouble(); - } - - public double ReadDoubleField(String fieldName, double dflt) { - if (_internalDecoder.StartField(fieldName)) { - return ReadDoubleContents(); - } - else { - return dflt; - } - } - - public float ReadFloatContents() { - _internalDecoder.StartAnonymousField(0); - //read as double since C# only knows how to deal with that - return (float)_internalDecoder.ReadDouble(); - } - - public float ReadFloatField(String fieldName, float dflt) { - if (_internalDecoder.StartField(fieldName)) { - //read as double since C# only knows how to deal with that - return (float)_internalDecoder.ReadDouble(); - } - else { - return dflt; - } - } - - public int ReadIntContents() { - _internalDecoder.StartAnonymousField(0); - return _internalDecoder.ReadInt(); - } - - public int ReadIntField(String fieldName, int dflt) { - if (_internalDecoder.StartField(fieldName)) { - return _internalDecoder.ReadInt(); - } - else { - return dflt; - } - } - - public long ReadLongContents() { - _internalDecoder.StartAnonymousField(0); - return _internalDecoder.ReadLong(); - } - - public long ReadLongField(String fieldName, long dflt) { - if (_internalDecoder.StartField(fieldName)) { - return _internalDecoder.ReadLong(); - } - else { - return dflt; - } - } - - public int GetNumSubObjects() - { - return _internalDecoder.GetNumAnonymousFields(); - } - - public Object ReadObjectContents(int index) { - _internalDecoder.StartAnonymousField(index); - return _internalDecoder.ReadObject(this); - } - - public Object ReadObjectField(String fieldName, Type expected, Object dflt) { - if (_internalDecoder.StartField(fieldName)) { - return _internalDecoder.ReadObject(this); - } - else { - return dflt; - } - } - - public String ReadStringContents() { - _internalDecoder.StartAnonymousField(0); - return _internalDecoder.ReadString(false); - } - - public String ReadStringField(String fieldName, String dflt) { - if (_internalDecoder.StartField(fieldName)) { - return _internalDecoder.ReadString(false); - } - else { - return dflt; - } - } - - } - - -} diff --git a/DotNetConnectors.sln/FrameworkInternal/SerializerXml.cs b/DotNetConnectors.sln/FrameworkInternal/SerializerXml.cs deleted file mode 100644 index 44556848..00000000 --- a/DotNetConnectors.sln/FrameworkInternal/SerializerXml.cs +++ /dev/null @@ -1,794 +0,0 @@ -/* - * ==================== - * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER. - * - * Copyright 2007-2008 Sun Microsystems, Inc. All rights reserved. - * - * The contents of this file are subject to the terms of the Common Development - * and Distribution License("CDDL") (the "License"). You may not use this file - * except in compliance with the License. - * - * You can obtain a copy of the License at - * http://IdentityConnectors.dev.java.net/legal/license.txt - * See the License for the specific language governing permissions and limitations - * under the License. - * - * When distributing the Covered Code, include this CDDL Header Notice in each file - * and include the License file at identityconnectors/legal/license.txt. - * If applicable, add the following below this CDDL Header, with the fields - * enclosed by brackets [] replaced by your own identifying information: - * "Portions Copyrighted [year] [name of copyright owner]" - * ==================== - */ -using System; -using System.Collections.Generic; -using System.IO; -using System.Resources; -using System.Text; -using System.Net; -using System.Xml; -using Org.IdentityConnectors.Common; -using Org.IdentityConnectors.Framework.Common.Exceptions; -using Org.IdentityConnectors.Framework.Common.Serializer; -namespace Org.IdentityConnectors.Framework.Impl.Serializer.Xml -{ - public class XmlObjectEncoder : ObjectEncoder { - - private StringBuilder _rootBuilder; - private XmlWriter _writer; - - public XmlObjectEncoder(StringBuilder builder) { - Assertions.NullCheck(builder, "builder"); - _rootBuilder = builder; - } - - public String WriteObject(Object o) { - XmlWriterSettings settings = new XmlWriterSettings(); - settings.Indent = true; - settings.OmitXmlDeclaration = true; - _writer = XmlWriter.Create(_rootBuilder, settings); - String rv = WriteObjectInternal(o,false); - _writer.Close(); - return rv; - } - - public void WriteBooleanContents(bool v) { - WriteStringContentsInternal(EncodeBoolean(v)); - } - - public void WriteBooleanField(String fieldName, bool v) { - WriteAttributeInternal(fieldName,EncodeBoolean(v)); - } - - public void WriteByteArrayContents(byte[] v) { - WriteStringContentsInternal(EncodeByteArray(v)); - } - - public void WriteClassContents(Type v) { - WriteStringContentsInternal(EncodeClass(v)); - } - - public void WriteClassField(String name, Type v) { - if ( v != null ) { - WriteAttributeInternal(name, EncodeClass(v)); - } - } - - public void WriteDoubleContents(double v) { - WriteStringContentsInternal(EncodeDouble(v)); - } - - public void WriteDoubleField(String fieldName, double v) { - WriteAttributeInternal(fieldName,EncodeDouble(v)); - } - - public void WriteFloatContents(float v) { - WriteStringContentsInternal(EncodeFloat(v)); - } - - public void WriteFloatField(String fieldName, float v) { - WriteAttributeInternal(fieldName,EncodeFloat(v)); - } - - public void WriteIntContents(int v) { - WriteStringContentsInternal(EncodeInt(v)); - } - - public void WriteIntField(String fieldName, int v) { - WriteAttributeInternal(fieldName,EncodeInt(v)); - } - - public void WriteLongContents(long v) { - WriteStringContentsInternal(EncodeLong(v)); - } - - public void WriteLongField(String fieldName, long v) { - WriteAttributeInternal(fieldName,EncodeLong(v)); - } - - public void WriteObjectContents(Object o) { - WriteObjectInternal(o,false); - } - - public void WriteObjectField(String fieldName, Object obj, bool inline) { - if (inline && obj == null) - { - return; //don't write anything - } - BeginElement(fieldName); - WriteObjectInternal(obj,inline); - EndElement(); - } - - public void WriteStringContents(String str) { - WriteStringContentsInternal(str); - } - - public void WriteStringField(String fieldName, String str) { - if ( str != null ) { - WriteAttributeInternal(fieldName, str); - } - } - - internal static String EncodeBoolean(bool b) { - return b.ToString(); - } - - private static String EncodeByteArray(byte [] bytes) { - return Convert.ToBase64String(bytes); - } - - private static String EncodeClass(Type clazz) { - ObjectSerializationHandler handler = - ObjectSerializerRegistry.GetHandlerByObjectType(clazz); - ObjectTypeMapper mapper = - ObjectSerializerRegistry.GetMapperByObjectType(clazz); - if ( handler == null && clazz.IsArray ) { - //we may have special handlers for certain types of arrays - //if handler is null, treat like any other array - return EncodeClass(clazz.GetElementType())+"[]"; - } - else if ( mapper == null ) { - throw new ConnectorException("No serializer for class: "+clazz); - } - else { - String typeName = mapper.HandledSerialType; - return typeName; - } - } - - internal static String EncodeDouble(double d) { - return d.ToString("R"); - } - - internal static String EncodeFloat(float d) { - return d.ToString("R"); - } - - internal static String EncodeInt(int d) { - return d.ToString(); - } - - internal static String EncodeLong(long d) { - return d.ToString(); - } - - /** - * Writes the object - * @param object - * @param inline - * @return The type name (regardless of whether it was inlined) - */ - String WriteObjectInternal(Object obj, bool inline) { - if ( obj == null ) { - if ( inline ) { - throw new ArgumentException("null cannot be inlined"); - } - BeginElement("null"); - EndElement(); - return "null"; - } - else { - Type clazz = obj.GetType(); - ObjectSerializationHandler handler = - ObjectSerializerRegistry.GetHandlerByObjectType(clazz); - if ( handler == null ) { - //we may have special handlers for certain types of arrays - //if handler is null, treat like any other array - if ( clazz.IsArray ) { - if (!inline) { - String componentTypeName = EncodeClass(clazz.GetElementType()); - BeginElement("Array"); - WriteAttributeInternal("componentType", componentTypeName); - } - Array array = (Array)obj; - int length = array.Length; - for ( int i = 0; i < length; i++ ) { - Object val = array.GetValue(i); - WriteObjectInternal(val,false); - } - if (!inline) { - EndElement(); - } - return "Array"; - } - else { - throw new ConnectorException("No serializer for class: "+clazz); - } - } - else { - String typeName = EncodeClass(clazz); - if (!inline) { - BeginElement(typeName); - } - handler.Serialize(obj, this); - if (!inline) { - EndElement(); - } - return typeName; - } - } - - } - - ////////////////////////////////////////////////////////////////// - // - // xml encoding - // - ///////////////////////////////////////////////////////////////// - - - - private void BeginElement(String name) { - _writer.WriteStartElement(name); - } - - private void EndElement() { - _writer.WriteEndElement(); - } - private void WriteAttributeInternal(String fieldName, String str) { - _writer.WriteAttributeString(fieldName,str); - } - private void WriteStringContentsInternal(String str) { - _writer.WriteString(str); - } - } - - public class XmlObjectDecoder : ObjectDecoder { - - private readonly XmlElement _node; - private readonly Type _expectedClass; - - public XmlObjectDecoder(XmlElement node, Type expectedClass) { - _node = node; - _expectedClass = expectedClass; - } - - public Object ReadObject() { - return ReadObjectInternal(); - } - - public bool ReadBooleanContents() { - return DecodeBoolean(ReadStringContentsInternal()); - } - - public bool ReadBooleanField(String fieldName, bool dflt) { - return DecodeBoolean(ReadStringAttributeInternal(fieldName,XmlObjectEncoder.EncodeBoolean(dflt))); - } - - public byte[] ReadByteArrayContents() { - return DecodeByteArray(ReadStringContentsInternal()); - } - - public Type ReadClassContents() { - return DecodeClass(ReadStringContentsInternal()); - } - - public Type ReadClassField(String name, Type dflt) { - String val = ReadStringAttributeInternal(name, null); - if ( val == null ) { - return dflt; - } - else { - return DecodeClass(val); - } - } - - public double ReadDoubleContents() { - return DecodeDouble(ReadStringContentsInternal()); - } - - public double ReadDoubleField(String fieldName, double dflt) { - return DecodeDouble(ReadStringAttributeInternal(fieldName,XmlObjectEncoder.EncodeDouble(dflt))); - } - - public float ReadFloatContents() { - return DecodeFloat(ReadStringContentsInternal()); - } - - public float ReadFloatField(String fieldName, float dflt) { - return DecodeFloat(ReadStringAttributeInternal(fieldName,XmlObjectEncoder.EncodeFloat(dflt))); - } - - public int ReadIntContents() { - return DecodeInt(ReadStringContentsInternal()); - } - - public int ReadIntField(String fieldName, int dflt) { - return DecodeInt(ReadStringAttributeInternal(fieldName,XmlObjectEncoder.EncodeInt(dflt))); - } - - public long ReadLongContents() { - return DecodeLong(ReadStringContentsInternal()); - } - - public long ReadLongField(String fieldName, long dflt) { - return DecodeLong(ReadStringAttributeInternal(fieldName,XmlObjectEncoder.EncodeLong(dflt))); - } - - public int GetNumSubObjects() { - int count = 0; - for (XmlElement subElement = XmlUtil.GetFirstChildElement(_node); - subElement != null; - subElement = XmlUtil.GetNextElement(subElement)) { - count++; - } - return count; - } - - public Object ReadObjectContents(int index) { - XmlElement subElement = XmlUtil.GetFirstChildElement(_node); - for ( int i = 0; i < index; i++) { - subElement = XmlUtil.GetNextElement(subElement); - } - - if ( subElement == null ) { - throw new ConnectorException("Missing subelement number: "+index); - } - - return new XmlObjectDecoder(subElement,null).ReadObject(); - } - - public Object ReadObjectField(String fieldName, Type expected, Object dflt) { - XmlElement child = XmlUtil.FindImmediateChildElement(_node, fieldName); - if ( child == null ) { - return dflt; - } - if ( expected != null ) { - return new XmlObjectDecoder(child,expected).ReadObject(); - } - XmlElement subElement = XmlUtil.GetFirstChildElement(child); - if ( subElement == null ) { - return dflt; - } - //if they specify null, don't apply defaults - return new XmlObjectDecoder(subElement,null).ReadObject(); - } - - public String ReadStringContents() { - String rv = ReadStringContentsInternal(); - return rv == null ? "" : rv; - } - - public String ReadStringField(String fieldName, String dflt) { - return ReadStringAttributeInternal(fieldName, dflt); - } - - private String ReadStringContentsInternal() { - String xml = XmlUtil.GetContent(_node); - return xml; - } - - private String ReadStringAttributeInternal(String name, String dflt) { - XmlAttribute attr = _node.GetAttributeNode(name); - if ( attr == null ) { - return dflt; - } - return attr.Value; - } - - private bool DecodeBoolean(String v) { - return Boolean.Parse(v); - } - - private byte [] DecodeByteArray(String base64) { - return Convert.FromBase64String(base64); - } - - private Type DecodeClass(String type) { - if ( type.EndsWith("[]") ) { - String componentName = type.Substring(0,type.Length-"[]".Length); - Type componentClass = - DecodeClass(componentName); - Type arrayClass = - componentClass.MakeArrayType(); - return arrayClass; - } - else { - ObjectTypeMapper mapper = - ObjectSerializerRegistry.GetMapperBySerialType(type); - if ( mapper == null ) { - throw new ConnectorException("No deserializer for type: "+type); - } - Type clazz = mapper.HandledObjectType; - return clazz; - } - } - - private double DecodeDouble(String val) { - return Double.Parse(val); - } - - private float DecodeFloat(String val) { - return Single.Parse(val); - } - - private int DecodeInt(String val) { - return Int32.Parse(val); - } - - private long DecodeLong(String val) { - return Int64.Parse(val); - } - - private Object ReadObjectInternal() { - if (_expectedClass != null) { - ObjectSerializationHandler handler = - ObjectSerializerRegistry.GetHandlerByObjectType(_expectedClass); - if ( handler == null ) { - if (_expectedClass.IsArray) { - IList temp = new List(); - for (XmlElement child = XmlUtil.GetFirstChildElement(_node); child != null; - child = XmlUtil.GetNextElement(child)) { - XmlObjectDecoder sub = new XmlObjectDecoder(child,null); - Object obj = sub.ReadObject(); - temp.Add(obj); - } - int length = temp.Count; - Array array = Array.CreateInstance(_expectedClass.GetElementType(),length); - for ( int i = 0; i < length; i++) { - Object element = temp[i]; - array.SetValue(element,i); - } - return array; - } - else { - throw new ConnectorException("No deserializer for type: "+_expectedClass); - } - } - else { - return handler.Deserialize(this); - } - } - else if ( _node.LocalName.Equals("null") ) { - return null; - } - else if (_node.LocalName.Equals("Array")) { - String componentType = XmlUtil.GetAttribute(_node, "componentType"); - if ( componentType == null ) { - componentType = "Object"; - } - Type componentClass = DecodeClass(componentType); - IList temp = new List(); - for (XmlElement child = XmlUtil.GetFirstChildElement(_node); child != null; - child = XmlUtil.GetNextElement(child)) { - XmlObjectDecoder sub = new XmlObjectDecoder(child,null); - Object obj = sub.ReadObject(); - temp.Add(obj); - } - int length = temp.Count; - Array array = Array.CreateInstance(componentClass, - length); - for ( int i = 0; i < length; i++) { - Object element = temp[i]; - array.SetValue(element,i); - } - return array; - } - else { - Type clazz = - DecodeClass(_node.LocalName); - ObjectSerializationHandler handler = - ObjectSerializerRegistry.GetHandlerByObjectType(clazz); - if ( handler == null ) { - throw new ConnectorException("No deserializer for type: "+clazz); - } - else { - return handler.Deserialize(this); - } - } - } - - } - - public class XmlObjectParser { - - public static void parse(TextReader inputSource, - XmlObjectResultsHandler handler, - bool validate) - { - XmlReaderSettings mySettings = - new XmlReaderSettings(); - if ( validate ) { - mySettings.ValidationType = ValidationType.DTD; - } - mySettings.ProhibitDtd = false; - mySettings.XmlResolver = new MyEntityResolver(validate); - XmlReader reader = XmlReader.Create(inputSource,mySettings); - MyParser parser = new MyParser(handler); - parser.Parse(reader); - } - - - private class MyEntityResolver : XmlResolver - { - private readonly bool _validate; - - public MyEntityResolver(bool validate) - { - _validate = validate; - } - - public override Object GetEntity(Uri absoluteUri, string role, Type ofObject) - { - String text = null; - if (absoluteUri.AbsolutePath.EndsWith(XmlObjectSerializerImpl.CONNECTORS_DTD)) { - if (!_validate) { - text = ""; - } - else { - text = GetDTD(); - } - } - if ( text != null ) { - byte [] bytes = Encoding.UTF8.GetBytes(text); - return new MemoryStream(bytes); - } - return null; - } - - public override ICredentials Credentials { - set { - - } - } - private static String GetDTD() - { - ResourceManager manager = - new ResourceManager("Org.IdentityConnectors.Resources", - typeof(XmlObjectParser).Assembly); - String contents = (String)manager.GetObject(XmlObjectSerializerImpl.CONNECTORS_DTD); - return contents; - } - } - - private class MyParser - { - /** - * The document for the current top-level element. with each top-level element, - * we discard the previous to avoid accumulating memory - */ - private XmlDocument _currentTopLevelElementDocument; - - - /** - * Stack of elements we are creating - */ - private IList _elementStack = new List(10); - - /** - * Results handler that we write our objects to - */ - private readonly XmlObjectResultsHandler _handler; - - /** - * Is the handler still handing - */ - private bool _stillHandling = true; - - - public MyParser(XmlObjectResultsHandler handler) - { - _handler = handler; - } - - public void Parse(XmlReader reader) - { - while ( _stillHandling && reader.Read() ) { - XmlNodeType nodeType = reader.NodeType; - switch (nodeType) { - case XmlNodeType.Element: - StartElement(reader.LocalName); - bool empty = reader.IsEmptyElement; - if (reader.MoveToFirstAttribute()) { - AddAttribute(reader.LocalName,reader.Value); - while (reader.MoveToNextAttribute()) { - AddAttribute(reader.LocalName,reader.Value); - } - } - if ( empty ) { - EndElement(); - } - break; - case XmlNodeType.Text: - case XmlNodeType.CDATA: - case XmlNodeType.Whitespace: - case XmlNodeType.SignificantWhitespace: - AddText(reader.Value); - break; - case XmlNodeType.EndElement: - EndElement(); - break; - } - } - } - - private XmlElement GetCurrentElement() - { - if (_elementStack.Count > 0 ) - { - return _elementStack[_elementStack.Count-1]; - } - else - { - return null; - } - } - - private void AddText(String text) - { - XmlElement currentElement = GetCurrentElement(); - if ( currentElement != null ) - { - currentElement.AppendChild(_currentTopLevelElementDocument.CreateTextNode(text)); - } - } - - private void EndElement() - { - if (_elementStack.Count > 0) //we don't push the top-level MULTI_OBJECT_ELEMENT on the stack - { - XmlElement element = _elementStack[_elementStack.Count-1]; - _elementStack.RemoveAt(_elementStack.Count-1); - if (_elementStack.Count == 0) { - _currentTopLevelElementDocument = null; - if (_stillHandling) { - XmlObjectDecoder decoder = new XmlObjectDecoder(element,null); - Object obj = decoder.ReadObject(); - _stillHandling = _handler(obj); - } - } - } - } - - - private void StartElement(String localName) - { - XmlElement element = null; - if (_elementStack.Count == 0) - { - if (!XmlObjectSerializerImpl.MULTI_OBJECT_ELEMENT.Equals(localName)) - { - _currentTopLevelElementDocument = new XmlDocument(); - element = _currentTopLevelElementDocument.CreateElement(localName); - } - } - else - { - element = - _currentTopLevelElementDocument.CreateElement(localName); - GetCurrentElement().AppendChild(element); - } - if (element != null) - { - _elementStack.Add(element); - } - } - - private void AddAttribute(String name, String val) - { - XmlElement element = GetCurrentElement(); - if ( element != null ) - { - element.SetAttribute(name, val); - } - } - } - } - - public class XmlObjectSerializerImpl : XmlObjectSerializer { - - public const String MULTI_OBJECT_ELEMENT = "MultiObject"; - public const String CONNECTORS_DTD = "connectors.dtd"; - - private readonly TextWriter _output; - - private readonly bool _multiObject; - - private readonly bool _includeHeader; - - private bool _firstObjectWritten; - - private bool _documentEnded; - - public XmlObjectSerializerImpl(TextWriter output, bool includeHeader, bool multiObject) { - _output = output; - _includeHeader = includeHeader; - _multiObject = multiObject; - } - - - /** - * Writes the next object to the stream. - * @param object The object to write. - * @see ObjectSerializerFactory for a list of supported types. - * @throws ConnectorException if there is more than one object - * and this is not configured for multi-object document. - */ - public void WriteObject(Object obj) { - if (_documentEnded) { - throw new InvalidOperationException("Attempt to writeObject after the document is already closed"); - } - StringBuilder buf = new StringBuilder(); - XmlObjectEncoder encoder = new XmlObjectEncoder(buf); - String elementName = encoder.WriteObject(obj); - if (!_firstObjectWritten) { - StartDocument(elementName); - } - else { - if (!_multiObject) { - throw new InvalidOperationException("Attempt to write multiple objects on a single-object document"); - } - } - Write(buf.ToString()); - _firstObjectWritten = true; - } - - public void Flush() { - _output.Flush(); - } - - public void Close( bool closeStream ) { - if (!_documentEnded) { - if (!_firstObjectWritten) { - if (!_multiObject) { - throw new InvalidOperationException("Attempt to write zero objects on a single-object document"); - } - StartDocument(null); - } - WriteEndDocument(); - _documentEnded = true; - } - if (closeStream) { - _output.Close(); - } - } - - private void StartDocument(String firstElement) { - if (_includeHeader) { - String docType = _multiObject ? MULTI_OBJECT_ELEMENT : firstElement; - String line1 = "\n"; - String line2 = "\n"; - Write(line1); - Write(line2); - } - if (_multiObject) { - String line3 = "<"+MULTI_OBJECT_ELEMENT+">\n"; - Write(line3); - } - } - - private void WriteEndDocument() { - if (_multiObject) { - String line1 = "\n"; - Write(line1); - } - } - - private void Write(String str) { - _output.Write(str); - } - - } - -} diff --git a/DotNetConnectors.sln/FrameworkInternal/Server.cs b/DotNetConnectors.sln/FrameworkInternal/Server.cs deleted file mode 100644 index 70f3a7a3..00000000 --- a/DotNetConnectors.sln/FrameworkInternal/Server.cs +++ /dev/null @@ -1,900 +0,0 @@ -/* - * ==================== - * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER. - * - * Copyright 2007-2008 Sun Microsystems, Inc. All rights reserved. - * - * The contents of this file are subject to the terms of the Common Development - * and Distribution License("CDDL") (the "License"). You may not use this file - * except in compliance with the License. - * - * You can obtain a copy of the License at - * http://IdentityConnectors.dev.java.net/legal/license.txt - * See the License for the specific language governing permissions and limitations - * under the License. - * - * When distributing the Covered Code, include this CDDL Header Notice in each file - * and include the License file at identityconnectors/legal/license.txt. - * If applicable, add the following below this CDDL Header, with the fields - * enclosed by brackets [] replaced by your own identifying information: - * "Portions Copyrighted [year] [name of copyright owner]" - * ==================== - */ -using System; -using System.Collections.Generic; -using System.Diagnostics; -using System.Globalization; -using System.Net; -using System.Net.Security; -using System.Security; -using System.Security.Cryptography.X509Certificates; -using System.Net.Sockets; -using System.IO; -using System.Linq; -using System.Threading; -using System.Reflection; -using System.Security.Authentication; -using System.Text; -using Org.IdentityConnectors.Common; -using Org.IdentityConnectors.Common.Security; -using Org.IdentityConnectors.Framework.Api; -using Org.IdentityConnectors.Framework.Api.Operations; -using Org.IdentityConnectors.Framework.Common.Objects; -using Org.IdentityConnectors.Framework.Common.Exceptions; -using Org.IdentityConnectors.Framework.Common.Objects.Filters; -using Org.IdentityConnectors.Framework.Common.Serializer; -using Org.IdentityConnectors.Framework.Server; -using Org.IdentityConnectors.Framework.Impl.Api; -using Org.IdentityConnectors.Framework.Impl.Api.Remote.Messages; -using Org.IdentityConnectors.Framework.Impl.Api.Local; -using Org.IdentityConnectors.Framework.Impl.Api.Local.Operations; -using Org.IdentityConnectors.Framework.Impl.Api.Remote; -namespace Org.IdentityConnectors.Framework.Server -{ - /** - * Connector server interface. - */ - public abstract class ConnectorServer { - - // At some point we might make this pluggable, but for now, hard-code - private const String IMPL_NAME - = "Org.IdentityConnectors.Framework.Impl.Server.ConnectorServerImpl"; - - - /** - * The port to listen on; - */ - private int _port = 0; - - /** - * Base 64 sha1 hash of the connector server key - */ - private String _keyHash; - - /** - * The number of connections to queue - */ - private int _maxConnections = 300; - - /** - * The minimum number of worker threads - */ - private int _minWorkers = 10; - - /** - * The maximum number of worker threads - */ - private int _maxWorkers = 100; - - /** - * The network interface address to use. - */ - private IPAddress _ifAddress = null; - - /** - * Listen on SSL - */ - private bool _useSSL = false; - - /** - * The server certificate to use - */ - private X509Certificate _serverCertificate = null; - - /** - * Get the singleton instance of the {@link ConnectorServer}. - */ - public static ConnectorServer NewInstance() { - SafeType type = - SafeType.ForRawType(Type.GetType(IMPL_NAME,true)); - return type.CreateInstance(); - } - - private void AssertNotStarted() { - if ( IsStarted() ) { - throw new InvalidOperationException("Operation cannot be performed " + - "while server is running"); - } - } - - /** - * Returns the port to listen on. - * @return The port to listen on. - */ - public int Port { - get { - return _port; - } - set { - AssertNotStarted(); - _port = value; - } - } - - /** - * Returns the max connections to queue - * @return The max connections to queue - */ - public int MaxConnections { - get { - return _maxConnections; - } - set { - AssertNotStarted(); - _maxConnections = value; - } - } - - - /** - * Returns the max worker threads to allow. - * @return The max worker threads to allow. - */ - public int MaxWorkers { - get { - return _maxWorkers; - } - set { - AssertNotStarted(); - _maxWorkers = value; - } - } - - - - /** - * Returns the min worker threads to allow. - * @return The min worker threads to allow. - */ - public int MinWorkers { - get { - return _minWorkers; - } - set { - AssertNotStarted(); - _minWorkers = value; - } - } - - - - /** - * Returns the network interface address to bind to. May be null. - * @return The network interface address to bind to or null. - */ - public IPAddress IfAddress { - get { - return _ifAddress; - } - set { - AssertNotStarted(); - _ifAddress = value; - } - } - - /** - * Returns true iff we are to use SSL. - * @return true iff we are to use SSL. - */ - public bool UseSSL { - get { - return _useSSL; - } - set { - AssertNotStarted(); - _useSSL = value; - } - } - - - /** - * Returns the certificate to use for the SSL connection. - */ - public X509Certificate ServerCertificate { - get { - return _serverCertificate; - } - set { - AssertNotStarted(); - _serverCertificate = value; - } - } - - public String KeyHash { - get { - return _keyHash; - } - set { - AssertNotStarted(); - _keyHash = value; - } - } - - /** - * Produces a thread dump of all pending requests - */ - abstract public void DumpRequests(); - - /** - * Starts the server. All server settings must be configured prior - * to calling. The following methods are required to be called: - *
    - *
  • {@link #Port(int)}
  • - *
  • {@link #KeyHash(String)}
  • - *
- */ - abstract public void Start(); - - /** - * Stops the server gracefully. Returns when all in-progress connections - * have been serviced. - */ - abstract public void Stop(); - - /** - * Return true iff the server is started. Note that started is a - * logical state (start method has been called). It does not necessarily - * reflect the health of the server - * @return true iff the server is started. - */ - abstract public bool IsStarted(); - } -} - -namespace Org.IdentityConnectors.Framework.Impl.Server -{ - public class ConnectionProcessor { - - - private class RemoteResultsHandler : ObjectStreamHandler - { - private const int PAUSE_INTERVAL = 200; - - private readonly RemoteFrameworkConnection _connection; - private long _count = 0; - public RemoteResultsHandler(RemoteFrameworkConnection conn) - { - _connection = conn; - } - - public bool Handle(Object obj) { - try { - OperationResponsePart part = - new OperationResponsePart(null,obj); - _connection.WriteObject(part); - _count++; - if ( _count % PAUSE_INTERVAL == 0 ) { - _connection.WriteObject(new OperationResponsePause()); - Object message = - _connection.ReadObject(); - return message is OperationRequestMoreData; - } - else { - return true; - } - } - catch (IOException e) { - throw new BrokenConnectionException(e); - } - catch (Exception) { - throw; - } - } - } - - private readonly ConnectorServerImpl _server; - private readonly RemoteFrameworkConnection _connection; - - public ConnectionProcessor(ConnectorServerImpl server, - RemoteFrameworkConnection connection) { - _server = server; - _connection = connection; - } - - public void Run() { - try { - _server.BeginRequest(); - try { - while ( true ) { - bool keepGoing = ProcessRequest(); - if (!keepGoing) { - break; - } - } - } - finally { - try { - _connection.Dispose(); - } - catch (Exception e) { - TraceUtil.TraceException(null,e); - } - } - } - catch (Exception e) { - TraceUtil.TraceException(null,e); - } - finally { - _server.EndRequest(); - } - } - - private bool ProcessRequest() - { - - CultureInfo locale; - try { - locale = (CultureInfo)_connection.ReadObject(); - } - catch (EndOfStreamException) { - return false; - } - - //We can't set this because C# does not like language-neutral - //cultures for CurrentCulture - this tends to blow up - //TODO: think more about this... - //Thread.CurrentThread.CurrentCulture = locale; - Thread.CurrentThread.CurrentUICulture = locale; - - GuardedString key = (GuardedString)_connection.ReadObject(); - - bool authorized; - try { - authorized = key.VerifyBase64SHA1Hash(_server.KeyHash); - } - finally { - key.Dispose(); - } - Org.IdentityConnectors.Framework.Common.Exceptions.InvalidCredentialException authException = null; - if (!authorized) { - authException = new Org.IdentityConnectors.Framework.Common.Exceptions.InvalidCredentialException("Remote framework key is invalid"); - } - Object requestObject = _connection.ReadObject(); - if ( requestObject is HelloRequest ) { - if ( authException != null ) { - HelloResponse response = - new HelloResponse(authException,null); - _connection.WriteObject(response); - } - else { - HelloResponse response = - ProcessHelloRequest((HelloRequest)requestObject); - _connection.WriteObject(response); - } - } - else if ( requestObject is OperationRequest ) { - if ( authException != null ) { - OperationResponsePart part = - new OperationResponsePart(authException,null); - _connection.WriteObject(part); - } - else { - OperationRequest opRequest = - (OperationRequest)requestObject; - OperationResponsePart part = - ProcessOperationRequest(opRequest); - _connection.WriteObject(part); - } - } - else if (requestObject is EchoMessage) { - if ( authException != null ) { - //echo message probably doesn't need auth, but - //it couldn't hurt - actually it does for test connection - EchoMessage part = - new EchoMessage(authException,null); - _connection.WriteObject(part); - } - else { - EchoMessage message = (EchoMessage)requestObject; - Object obj = message.Object; - String xml = message.ObjectXml; - if ( xml != null ) { - Console.WriteLine("xml: \n"+xml); - Object xmlClone = - SerializerUtil.DeserializeXmlObject(xml,true); - xml = - SerializerUtil.SerializeXmlObject(xmlClone,true); - } - EchoMessage message2 = new EchoMessage(obj,xml); - _connection.WriteObject(message2); - } - } - else { - throw new Exception("Unexpected request: "+requestObject); - } - return true; - } - - private ConnectorInfoManager GetConnectorInfoManager() { - return ConnectorInfoManagerFactory.GetInstance().GetLocalManager(); - } - - private HelloResponse ProcessHelloRequest(HelloRequest request) { - IList connectorInfo; - Exception exception = null; - try { - ConnectorInfoManager manager = - GetConnectorInfoManager(); - IList localInfos = - manager.ConnectorInfos; - connectorInfo = new List(); - foreach (ConnectorInfo localInfo in localInfos) { - LocalConnectorInfoImpl localInfoImpl = - (LocalConnectorInfoImpl)localInfo; - RemoteConnectorInfoImpl remoteInfo = - localInfoImpl.ToRemote(); - connectorInfo.Add(remoteInfo); - } - } - catch (Exception e) { - TraceUtil.TraceException(null,e); - exception = e; - connectorInfo = null; - } - return new HelloResponse(exception,connectorInfo); - } - - private MethodInfo GetOperationMethod(OperationRequest request) { - MethodInfo [] methods = - request.Operation.RawType.GetMethods(); - MethodInfo found = null; - foreach (MethodInfo m in methods) { - if ( m.Name.ToUpper().Equals(request.OperationMethodName.ToUpper()) ) { - if ( found != null ) { - throw new ConnectorException("APIOperations are expected " - +"to have exactly one method of a given name: "+request.Operation); - } - found = m; - } - } - - if ( found == null ) { - throw new ConnectorException("APIOperations are expected " - +"to have exactly one method of a given name: "+request.Operation+" "+methods.Length); - } - return found; - } - - private OperationResponsePart - ProcessOperationRequest(OperationRequest request) { - Object result; - Exception exception = null; - try { - MethodInfo method = GetOperationMethod(request); - APIOperation operation = GetAPIOperation(request); - IList arguments = request.Arguments; - IList argumentsAndStreamHandlers = - PopulateStreamHandlers(ReflectionUtil.GetParameterTypes(method), - arguments); - try { - Object [] args = argumentsAndStreamHandlers.ToArray(); - FixupArguments(method,args); - result = method.Invoke(operation, args); - } - catch (TargetInvocationException e) { - throw e.InnerException; - } - bool anyStreams = - argumentsAndStreamHandlers.Count > arguments.Count; - if ( anyStreams ) { - try { - _connection.WriteObject(new OperationResponseEnd()); - } - catch (IOException e) { - throw new BrokenConnectionException(e); - } - } - } - catch (BrokenConnectionException w) { - //at this point the stream is broken - just give up - throw w.GetIOException(); - } - catch (Exception e) { - TraceUtil.TraceException(null,e); - exception = e; - result = null; - } - return new OperationResponsePart(exception,result); - } - - private IList PopulateStreamHandlers(Type [] paramTypes, IList arguments) { - IList rv = new List(); - bool firstStream = true; - IEnumerator argIt = arguments.GetEnumerator(); - foreach (Type paramType in paramTypes) { - if ( StreamHandlerUtil.IsAdaptableToObjectStreamHandler(paramType) ) { - if (!firstStream) { - throw new InvalidOperationException("At most one stream handler is supported"); - } - ObjectStreamHandler osh = - new RemoteResultsHandler(_connection); - rv.Add(StreamHandlerUtil.AdaptFromObjectStreamHandler(paramType, osh)); - firstStream = false; - } - else { - argIt.MoveNext(); - rv.Add(argIt.Current); - } - } - return rv; - } - - /// - /// When arguments are serialized, we loose the - /// generic-type of collections. We must fix - /// the arguments - /// - /// - /// - private void FixupArguments(MethodInfo method, - object [] args) - { - Type [] paramTypes = - ReflectionUtil.GetParameterTypes(method); - if (paramTypes.Length != args.Length) { - throw new ArgumentException("Number of arguments does not match for method: "+method); - } - for ( int i = 0; i < args.Length; i++ ) { - args[i] = FixupArgument(paramTypes[i], - args[i]); - } - } - - private object FixupArgument(Type expectedType, - object argument) - { - //at some point, we might want this to be more general-purpose - //for now we just handle those cases that we need to - if (typeof(ICollection).Equals(expectedType)) { - ICollection val = - (ICollection)argument; - return CollectionUtil.NewSet(val); - } - else { - return argument; - } - } - - private APIOperation GetAPIOperation(OperationRequest request) - { - ConnectorInfoManager manager = - GetConnectorInfoManager(); - ConnectorInfo info = manager.FindConnectorInfo( - request.ConnectorKey); - if ( info == null ) { - throw new Exception("No such connector: " - +request.ConnectorKey); - } - APIConfigurationImpl config = - request.Configuration; - - //re-wire the configuration with its connector info - config.ConnectorInfo=(AbstractConnectorInfo)info; - - ConnectorFacade facade = - ConnectorFacadeFactory.GetInstance().NewInstance(config); - - return facade.GetOperation(request.Operation); - } - - private class BrokenConnectionException : Exception { - - - public BrokenConnectionException(IOException ex) - : base("",ex) { - } - - public IOException GetIOException() { - return (IOException)InnerException; - } - } - - } - - class ConnectionListener { - - /** - * This is the size of our internal queue. For now I have this - * relatively small because I want the OS to manage the connect - * queue coming in. That way it can properly turn away excessive - * requests - */ - private const int INTERNAL_QUEUE_SIZE = 2; - - - /** - * The server object that we are using - */ - private readonly ConnectorServerImpl _server; - - /** - * The server socket. This must be bound at the time - * of creation. - */ - private readonly TcpListener _socket; - - /** - * Pool of executors - */ - //TODO: add a thread pool - //private readonly ExecutorService _threadPool; - - /** - * Set to indicated we need to start shutting down - */ - private bool _stopped = false; - - private Thread _thisThread; - - private readonly Object MUTEX = new Object(); - - /** - * Creates the listener thread - * @param server The server object - * @param socket The socket (should already be bound) - */ - public ConnectionListener(ConnectorServerImpl server, - TcpListener socket) { - _server = server; - _socket = socket; - //TODO: thread pool -/* _threadPool = - new ThreadPoolExecutor - (server.getMinWorkers(), - server.getMaxWorkers(), - 30, //idle time timeout - TimeUnit.SECONDS, - new ArrayBlockingQueue( - INTERNAL_QUEUE_SIZE, - true)); //fair*/ - } - - - - public void Run(Object o) { - Trace.TraceInformation("Server started on port: "+_server.Port); - _thisThread = (Thread)o; - while (!IsStopped()) { - try { - TcpClient connection = null; - Stream stream = null; - try { - connection = _socket.AcceptTcpClient(); - stream = connection.GetStream(); - if ( _server.UseSSL ) - { - SslStream sslStream = new SslStream(stream,false); - stream = sslStream; - sslStream.AuthenticateAsServer(_server.ServerCertificate, - false, - SslProtocols.Tls, - false); - } - - ConnectionProcessor processor = - new ConnectionProcessor(_server, - new RemoteFrameworkConnection(connection,stream)); - Thread thread = new Thread(processor.Run); - thread.IsBackground = false; - thread.Start(); - } - catch (Exception) { - if ( stream != null ) - { - try { stream.Close(); } catch (Exception) {} - } - if ( connection != null ) - { - try { connection.Close(); } catch (Exception) {} - } - throw; - } - } - catch (Exception e) { - //log the error unless it's because we've stopped - if (!IsStopped() || !(e is SocketException)) { - TraceUtil.TraceException("Error processing request",e); - } - //wait a second before trying again - if (!IsStopped()) { - Thread.Sleep(1000); - } - } - } - } - - private void MarkStopped() { - lock(MUTEX) { - _stopped = true; - } - } - - private bool IsStopped() { - lock(MUTEX) { - return _stopped; - } - } - - public void Shutdown() { - if (Object.ReferenceEquals(Thread.CurrentThread,_thisThread)) { - throw new ArgumentException("Shutdown may not be called from this thread"); - } - if (!IsStopped()) { - //set the stopped flag so we no its a normal - //shutdown and don't log the SocketException - MarkStopped(); - //close the socket - this causes accept to throw an exception - _socket.Stop(); - //wait for the main listener thread to die so we don't - //get any new requests - _thisThread.Join(); - //TODO: shutdown thread pool - //wait for all in-progress requests to finish - //_threadPool.shutdown(); - } - } - } - - internal class RequestStats { - public RequestStats() { - } - public Thread RequestThread { get; set; } - public long StartTimeMillis { get; set; } - public long RequestID { get; set; } - } - - public class ConnectorServerImpl : ConnectorServer { - - private readonly IDictionary - _pendingRequests = CollectionUtil.NewIdentityDictionary(); - private ConnectionListener _listener; - private Object COUNT_LOCK = new Object(); - private long _requestCount = 0; - - public override bool IsStarted() { - return _listener != null; - } - - public void BeginRequest() { - long requestID; - lock(COUNT_LOCK) { - requestID = _requestCount++; - } - Thread requestThread = Thread.CurrentThread; - RequestStats stats = new RequestStats(); - stats.StartTimeMillis = - DateTimeUtil.GetCurrentUtcTimeMillis(); - stats.RequestThread = Thread.CurrentThread; - stats.RequestID = requestID; - lock (_pendingRequests) { - _pendingRequests[stats.RequestThread] - = stats; - } - } - - public void EndRequest() { - lock (_pendingRequests) { - _pendingRequests.Remove(Thread.CurrentThread); - } - } - public override void DumpRequests() { - long currentTime = DateTimeUtil.GetCurrentUtcTimeMillis(); - IDictionary - pending; - lock(_pendingRequests) { - pending = new Dictionary(_pendingRequests); - } - StringBuilder builder = new StringBuilder(); - builder.Append("****Pending Requests Summary*****"); - foreach (RequestStats stats in pending.Values) { - DumpStats(stats,builder,currentTime); - } - //here we purposefully use write line since - //we always want to see it. in general, don't - //use this method - Trace.WriteLine(builder.ToString()); - } - - private void DumpStats(RequestStats stats, - StringBuilder builder, - long currentTime) - { - builder.AppendLine("**Request #"+stats.RequestID+" pending for "+(currentTime-stats.StartTimeMillis)+" millis."); - StackTrace stackTrace = GetStackTrace(stats.RequestThread); - if ( stackTrace == null ) { - builder.AppendLine(" "); - } - else { - builder.AppendLine(stackTrace.ToString()); - } - } - - private static StackTrace GetStackTrace(Thread thread) { - bool suspended = false; - try { - thread.Suspend(); - suspended = true; - return new StackTrace(thread,true); - } - catch (ThreadStateException) { - return null; //we missed this one - } - finally { - if ( suspended ) { - thread.Resume(); - } - } - } - - public override void Start() { - if ( IsStarted() ) { - throw new InvalidOperationException("Server is already running."); - } - if ( Port == 0 ) { - throw new InvalidOperationException("Port must be set prior to starting server."); - } - if (KeyHash == null) { - throw new InvalidOperationException("Key hash must be set prior to starting server."); - } - if ( UseSSL && ServerCertificate == null ) { - throw new InvalidOperationException("ServerCertificate must be set if using SSL."); - } - //make sure we are configured properly - ConnectorInfoManagerFactory.GetInstance().GetLocalManager(); - _requestCount = 0; - _pendingRequests.Clear(); - TcpListener socket = - CreateServerSocket(); - ConnectionListener listener = new ConnectionListener(this,socket); - Thread thread = new Thread(listener.Run); - thread.Name="ConnectionListener"; - thread.Start(thread); - thread.IsBackground = false; - _listener = listener; - } - - private TcpListener CreateServerSocket() { - IPAddress addr = IfAddress; - - if ( addr == null ) { - addr = IOUtil.GetIPAddress("0.0.0.0"); - } - TcpListener rv = new TcpListener(addr,Port); - //TODO: specify accept count - rv.Start(); - return rv; - } - - - public override void Stop() { - if (_listener != null) { - _listener.Shutdown(); - _listener = null; - } - ConnectorFacadeFactory.GetInstance().Dispose(); - } - - } - - -} diff --git a/DotNetConnectors.sln/FrameworkInternal/Test.cs b/DotNetConnectors.sln/FrameworkInternal/Test.cs deleted file mode 100644 index 553155d3..00000000 --- a/DotNetConnectors.sln/FrameworkInternal/Test.cs +++ /dev/null @@ -1,121 +0,0 @@ -/* - * ==================== - * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER. - * - * Copyright 2007-2008 Sun Microsystems, Inc. All rights reserved. - * - * The contents of this file are subject to the terms of the Common Development - * and Distribution License("CDDL") (the "License"). You may not use this file - * except in compliance with the License. - * - * You can obtain a copy of the License at - * http://IdentityConnectors.dev.java.net/legal/license.txt - * See the License for the specific language governing permissions and limitations - * under the License. - * - * When distributing the Covered Code, include this CDDL Header Notice in each file - * and include the License file at identityconnectors/legal/license.txt. - * If applicable, add the following below this CDDL Header, with the fields - * enclosed by brackets [] replaced by your own identifying information: - * "Portions Copyrighted [year] [name of copyright owner]" - * ==================== - */ -using System; -using System.Text; - -using Org.IdentityConnectors.Common; -using Org.IdentityConnectors.Framework.Api; -using Org.IdentityConnectors.Framework.Impl.Api; -using Org.IdentityConnectors.Framework.Impl.Api.Local; -using Org.IdentityConnectors.Framework.Impl.Api.Local.Operations; -using Org.IdentityConnectors.Framework.Common; -using Org.IdentityConnectors.Framework.Common.Objects; -using Org.IdentityConnectors.Framework.Common.Objects.Filters; -using Org.IdentityConnectors.Framework.Spi; -using Org.IdentityConnectors.Framework.Spi.Operations; -using Org.IdentityConnectors.Framework.Test; - -namespace Org.IdentityConnectors.Framework.Impl.Test -{ - public class TestHelpersImpl : TestHelpers { - - /** - * Method for convenient testing of local connectors. - */ - protected override APIConfiguration CreateTestConfigurationImpl(SafeType clazz, - Configuration config) { - LocalConnectorInfoImpl info = new LocalConnectorInfoImpl(); - info.ConnectorConfigurationClass=SafeType.Get(config); - info.ConnectorClass=(clazz); - info.ConnectorDisplayNameKey=("DUMMY_DISPLAY_NAME"); - info.ConnectorKey=( - new ConnectorKey(clazz.RawType.Name+".bundle", - "1.0", - clazz.RawType.Name)); - info.Messages=(CreateDummyMessages()); - APIConfigurationImpl rv = new APIConfigurationImpl(); - rv.IsConnectorPoolingSupported=( - IsConnectorPoolingSupported(clazz)); - ConfigurationPropertiesImpl properties = - CSharpClassProperties.CreateConfigurationProperties(config); - rv.ConfigurationProperties=(properties); - rv.ConnectorInfo=(info); - rv.SupportedOperations=( - FrameworkUtil.GetDefaultSupportedOperations(clazz)); - info.DefaultAPIConfiguration=( - rv); - return rv; - } - - - private static bool IsConnectorPoolingSupported(SafeType clazz) { - return ReflectionUtil.IsParentTypeOf(typeof(PoolableConnector),clazz.RawType); - } - - /** - * Performs a raw, unfiltered search at the SPI level, - * eliminating duplicates from the result set. - * @param search The search SPI - * @param oclass The object class - passed through to - * connector so it may be null if the connecor - * allowing it to be null. (This is convenient for - * unit tests, but will not be the case in general) - * @param filter The filter to search on - * @param handler The result handler - * @param options The options - may be null - will - * be cast to an empty OperationOptions - */ - protected override void SearchImpl(SearchOp search, - ObjectClass oclass, - Filter filter, - ResultsHandler handler, - OperationOptions options) { - if ( options == null ) { - options = new OperationOptionsBuilder().Build(); - } - RawSearcherImpl.RawSearch( - search, oclass, filter, handler, options); - } - - protected override ConnectorMessages CreateDummyMessagesImpl() { - return new DummyConnectorMessages(); - } - - private class DummyConnectorMessages : ConnectorMessages { - public String Format(String key, String dflt, params Object [] args) { - StringBuilder builder = new StringBuilder(); - builder.Append(key); - builder.Append(": "); - String sep = ""; - foreach (Object arg in args ) { - builder.Append(sep); - builder.Append(arg); - sep=", "; - } - return builder.ToString(); - } - } - - } - -} diff --git a/DotNetConnectors.sln/FrameworkTests/AssemblyInfo.cs b/DotNetConnectors.sln/FrameworkTests/AssemblyInfo.cs deleted file mode 100644 index 61f6c635..00000000 --- a/DotNetConnectors.sln/FrameworkTests/AssemblyInfo.cs +++ /dev/null @@ -1,53 +0,0 @@ -/* - * ==================== - * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER. - * - * Copyright 2007-2008 Sun Microsystems, Inc. All rights reserved. - * - * The contents of this file are subject to the terms of the Common Development - * and Distribution License("CDDL") (the "License"). You may not use this file - * except in compliance with the License. - * - * You can obtain a copy of the License at - * http://IdentityConnectors.dev.java.net/legal/license.txt - * See the License for the specific language governing permissions and limitations - * under the License. - * - * When distributing the Covered Code, include this CDDL Header Notice in each file - * and include the License file at identityconnectors/legal/license.txt. - * If applicable, add the following below this CDDL Header, with the fields - * enclosed by brackets [] replaced by your own identifying information: - * "Portions Copyrighted [year] [name of copyright owner]" - * ==================== - */ -#region Using directives - -using System; -using System.Reflection; -using System.Runtime.InteropServices; - -#endregion - -// General Information about an assembly is controlled through the following -// set of attributes. Change these attribute values to modify the information -// associated with an assembly. -[assembly: AssemblyTitle("FrameworkTests")] -[assembly: AssemblyDescription("")] -[assembly: AssemblyConfiguration("")] -[assembly: AssemblyCompany("")] -[assembly: AssemblyProduct("FrameworkTests")] -[assembly: AssemblyCopyright("")] -[assembly: AssemblyTrademark("")] -[assembly: AssemblyCulture("")] - -// This sets the default COM visibility of types in the assembly to invisible. -// If you need to expose a type to COM, use [ComVisible(true)] on that type. -[assembly: ComVisible(false)] - -// The assembly version has following format : -// -// Major.Minor.Build.Revision -// -// You can specify all the values or you can use the default the Revision and -// Build Numbers by using the '*' as shown below: -[assembly: AssemblyVersion("1.0.0.0")] diff --git a/DotNetConnectors.sln/FrameworkTests/CollectionUtilTests.cs b/DotNetConnectors.sln/FrameworkTests/CollectionUtilTests.cs deleted file mode 100644 index 289a91ca..00000000 --- a/DotNetConnectors.sln/FrameworkTests/CollectionUtilTests.cs +++ /dev/null @@ -1,160 +0,0 @@ -/* - * ==================== - * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER. - * - * Copyright 2007-2008 Sun Microsystems, Inc. All rights reserved. - * - * The contents of this file are subject to the terms of the Common Development - * and Distribution License("CDDL") (the "License"). You may not use this file - * except in compliance with the License. - * - * You can obtain a copy of the License at - * http://IdentityConnectors.dev.java.net/legal/license.txt - * See the License for the specific language governing permissions and limitations - * under the License. - * - * When distributing the Covered Code, include this CDDL Header Notice in each file - * and include the License file at identityconnectors/legal/license.txt. - * If applicable, add the following below this CDDL Header, with the fields - * enclosed by brackets [] replaced by your own identifying information: - * "Portions Copyrighted [year] [name of copyright owner]" - * ==================== - */ -using System; -using System.Collections.Generic; -using NUnit.Framework; -using NUnit.Framework.SyntaxHelpers; -using Org.IdentityConnectors.Common; -namespace FrameworkTests -{ - [TestFixture] - public class CollectionUtilTests - { - [Test] - public void TestEquals() { - Assert.IsTrue(CollectionUtil.Equals(null, null)); - Assert.IsFalse(CollectionUtil.Equals(null, "str")); - Assert.IsTrue(CollectionUtil.Equals("str", "str")); - - byte [] arr1 = new byte[] { 1,2,3 }; - byte [] arr2 = new byte[] { 1,2,3 }; - byte [] arr3 = new byte[] { 1,2,4 }; - byte [] arr4 = new byte[] { 1,2 }; - int [] arr5 = new int[] {1,2,3}; - - Assert.IsTrue(CollectionUtil.Equals(arr1, arr2)); - Assert.IsFalse(CollectionUtil.Equals(arr2, arr3)); - Assert.IsFalse(CollectionUtil.Equals(arr2, arr4)); - Assert.IsFalse(CollectionUtil.Equals(arr2, arr5)); - - IList list1 = new List(); - IList list2 = new List(); - list1.Add(arr1); - list2.Add(arr2); - - Assert.IsTrue(CollectionUtil.Equals(list1, list2)); - - list2.Add(arr2); - Assert.IsFalse(CollectionUtil.Equals(list1, list2)); - - list1.Add(arr1); - Assert.IsTrue(CollectionUtil.Equals(list1, list2)); - - list1.Add(arr1); - list2.Add(arr3); - Assert.IsFalse(CollectionUtil.Equals(list1, list2)); - - IDictionary map1 = new Dictionary(); - IDictionary map2 = new Dictionary(); - map1["key1"] = arr1; - map2["key1"] = arr2; - Assert.IsTrue(CollectionUtil.Equals(map1, map2)); - map2["key2"] = arr2; - Assert.IsFalse(CollectionUtil.Equals(map1, map2)); - map1["key2"] = arr1; - Assert.IsTrue(CollectionUtil.Equals(map1, map2)); - map1["key2"] = arr3; - Assert.IsFalse(CollectionUtil.Equals(map1, map2)); - - ICollection set1 = new HashSet(); - ICollection set2 = new HashSet(); - set1.Add("val"); - set2.Add("val"); - Assert.IsTrue(CollectionUtil.Equals(set1, set2)); - set2.Add("val2"); - Assert.IsFalse(CollectionUtil.Equals(set1, set2)); - set1.Add("val2"); - Assert.IsTrue(CollectionUtil.Equals(set1, set2)); - } - [Test] - public void TestHashCode() { - Assert.AreEqual(0,CollectionUtil.GetHashCode(null)); - Assert.AreEqual("str".GetHashCode(),CollectionUtil.GetHashCode("str")); - - byte [] arr1 = new byte[] { 1,2,3 }; - byte [] arr2 = new byte[] { 1,2,3 }; - byte [] arr3 = new byte[] { 1,2,4 }; - byte [] arr4 = new byte[] { 1,2 }; - int [] arr5 = new int[] {1,2,3}; - - Assert.AreEqual(CollectionUtil.GetHashCode(arr1), - CollectionUtil.GetHashCode(arr2)); - Assert.IsFalse(CollectionUtil.GetHashCode(arr2) == - CollectionUtil.GetHashCode(arr3)); - Assert.IsFalse(CollectionUtil.GetHashCode(arr2) == - CollectionUtil.GetHashCode(arr4)); - Assert.IsTrue(CollectionUtil.GetHashCode(arr2) == - CollectionUtil.GetHashCode(arr5)); - - List list1 = new List(); - List list2 = new List(); - list1.Add(arr1); - list2.Add(arr2); - - Assert.IsTrue(CollectionUtil.GetHashCode(list1) == - CollectionUtil.GetHashCode(list2)); - - list2.Add(arr2); - Assert.IsFalse(CollectionUtil.GetHashCode(list1) == - CollectionUtil.GetHashCode(list2)); - - list1.Add(arr1); - Assert.IsTrue(CollectionUtil.GetHashCode(list1) == - CollectionUtil.GetHashCode(list2)); - - list1.Add(arr1); - list2.Add(arr3); - Assert.IsFalse(CollectionUtil.GetHashCode(list1) == - CollectionUtil.GetHashCode(list2)); - - Dictionary map1 = new Dictionary(); - Dictionary map2 = new Dictionary(); - map1["key1"] = arr1; - map2["key1"] = arr2; - Assert.IsTrue(CollectionUtil.GetHashCode(map1) == - CollectionUtil.GetHashCode(map2)); - map2["key2"] = arr2; - Assert.IsFalse(CollectionUtil.GetHashCode(map1) == - CollectionUtil.GetHashCode(map2)); - map1["key2"] = arr1; - Assert.IsTrue(CollectionUtil.GetHashCode(map1) == - CollectionUtil.GetHashCode(map2)); - map1["key2"] = arr3; - Assert.IsFalse(CollectionUtil.GetHashCode(map1) == - CollectionUtil.GetHashCode(map2)); - - HashSet set1 = new HashSet(); - HashSet set2 = new HashSet(); - set1.Add("val"); - set2.Add("val"); - Assert.IsTrue(CollectionUtil.GetHashCode(set1) == - CollectionUtil.GetHashCode(set2)); - set2.Add("val2"); - Assert.IsFalse(CollectionUtil.GetHashCode(set1) == - CollectionUtil.GetHashCode(set2)); - set1.Add("val2"); - Assert.IsTrue(CollectionUtil.GetHashCode(set1) == - CollectionUtil.GetHashCode(set2)); - } - } -} diff --git a/DotNetConnectors.sln/FrameworkTests/ConnectorInfoManagerTests.cs b/DotNetConnectors.sln/FrameworkTests/ConnectorInfoManagerTests.cs deleted file mode 100644 index 16b8f43f..00000000 --- a/DotNetConnectors.sln/FrameworkTests/ConnectorInfoManagerTests.cs +++ /dev/null @@ -1,592 +0,0 @@ -/* - * ==================== - * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER. - * - * Copyright 2007-2008 Sun Microsystems, Inc. All rights reserved. - * - * The contents of this file are subject to the terms of the Common Development - * and Distribution License("CDDL") (the "License"). You may not use this file - * except in compliance with the License. - * - * You can obtain a copy of the License at - * http://IdentityConnectors.dev.java.net/legal/license.txt - * See the License for the specific language governing permissions and limitations - * under the License. - * - * When distributing the Covered Code, include this CDDL Header Notice in each file - * and include the License file at identityconnectors/legal/license.txt. - * If applicable, add the following below this CDDL Header, with the fields - * enclosed by brackets [] replaced by your own identifying information: - * "Portions Copyrighted [year] [name of copyright owner]" - * ==================== - */ -using System; -using NUnit.Framework; -using NUnit.Framework.SyntaxHelpers; -using System.Collections.Generic; -using System.Diagnostics; -using Org.IdentityConnectors.Common; -using Org.IdentityConnectors.Common.Security; -using Org.IdentityConnectors.Framework.Api; -using Org.IdentityConnectors.Framework.Api.Operations; -using Org.IdentityConnectors.Framework.Common.Exceptions; -using Org.IdentityConnectors.Framework.Common.Objects; -using Org.IdentityConnectors.Framework.Server; -using Org.IdentityConnectors.Framework.Impl.Api; -using Org.IdentityConnectors.Framework.Impl.Api.Local; -using System.Security; -using System.Threading; -using System.Globalization; -using System.Net; -using System.Net.Security; -using System.Security.Cryptography.X509Certificates; -namespace FrameworkTests -{ - [TestFixture] - public class ConnectorInfoManagerTests - { - private static ConnectorInfo FindConnectorInfo - (ConnectorInfoManager manager, - string version, - string connectorName) { - foreach (ConnectorInfo info in manager.ConnectorInfos) { - ConnectorKey key = info.ConnectorKey; - if ( version.Equals(key.BundleVersion) && - connectorName.Equals(key.ConnectorName) ) { - //intentionally ineffecient to test - //more code - return manager.FindConnectorInfo(key); - } - } - return null; - } - - [Test] - public void TestClassLoading() { - ConnectorInfoManager manager = - GetConnectorInfoManager(); - ConnectorInfo info1 = - FindConnectorInfo(manager, - "1.0.0.0", - "org.identityconnectors.testconnector.TstConnector"); - Assert.IsNotNull(info1); - ConnectorInfo info2 = - FindConnectorInfo(manager, - "2.0.0.0", - "org.identityconnectors.testconnector.TstConnector"); - - Assert.IsNotNull(info2); - - ConnectorFacade facade1 = - ConnectorFacadeFactory.GetInstance().NewInstance(info1.CreateDefaultAPIConfiguration()); - - ConnectorFacade facade2 = - ConnectorFacadeFactory.GetInstance().NewInstance(info2.CreateDefaultAPIConfiguration()); - - ICollection attrs = new HashSet(); - Assert.AreEqual("1.0", facade1.Create(ObjectClass.ACCOUNT,attrs,null).GetUidValue()); - Assert.AreEqual("2.0", facade2.Create(ObjectClass.ACCOUNT,attrs,null).GetUidValue()); - - ShutdownConnnectorInfoManager(); - } - - [Test] - public void TestAPIConfiguration() - { - ConnectorInfoManager manager = - GetConnectorInfoManager(); - ConnectorInfo info = - FindConnectorInfo(manager, - "1.0.0.0", - "org.identityconnectors.testconnector.TstConnector"); - Assert.IsNotNull(info); - APIConfiguration api = info.CreateDefaultAPIConfiguration(); - - ConfigurationProperties props = api.ConfigurationProperties; - ConfigurationProperty property = props.GetProperty("tstField"); - - Assert.IsNotNull(property); - ICollection> operations = - property.Operations; - Assert.AreEqual(1, operations.Count); - Assert.IsTrue(operations.Contains(SafeType.Get())); - - Thread.CurrentThread.CurrentUICulture = new CultureInfo("en"); - Assert.AreEqual("Help for test field.",property.GetHelpMessage(null)); - Assert.AreEqual("Display for test field.",property.GetDisplayName(null)); - Assert.AreEqual("Test Framework Value", - info.Messages.Format("TEST_FRAMEWORK_KEY", "empty")); - - CultureInfo eslocale = new CultureInfo("es"); - Thread.CurrentThread.CurrentUICulture = eslocale; - Assert.AreEqual("tstField.help_es",property.GetHelpMessage(null)); - Assert.AreEqual("tstField.display_es",property.GetDisplayName(null)); - - CultureInfo esESlocale = new CultureInfo("es-ES"); - Thread.CurrentThread.CurrentUICulture = esESlocale; - Assert.AreEqual("tstField.help_es-ES",property.GetHelpMessage(null)); - Assert.AreEqual("tstField.display_es-ES",property.GetDisplayName(null)); - - CultureInfo esARlocale = new CultureInfo("es-AR"); - Thread.CurrentThread.CurrentUICulture = esARlocale; - Assert.AreEqual("tstField.help_es",property.GetHelpMessage(null)); - Assert.AreEqual("tstField.display_es",property.GetDisplayName(null)); - - ConnectorFacadeFactory facf = ConnectorFacadeFactory.GetInstance(); - ConnectorFacade facade = facf.NewInstance(api); - // call the various create/update/delete commands.. - facade.Schema(); - ShutdownConnnectorInfoManager(); - } - - [Test] - public void TestValidate() { - ConnectorInfoManager manager = - GetConnectorInfoManager(); - ConnectorInfo info = - FindConnectorInfo(manager, - "1.0.0.0", - "org.identityconnectors.testconnector.TstConnector"); - - APIConfiguration api = info.CreateDefaultAPIConfiguration(); - - ConfigurationProperties props = api.ConfigurationProperties; - ConfigurationProperty property = props.GetProperty("failValidation"); - property.Value=false; - ConnectorFacadeFactory facf = ConnectorFacadeFactory.GetInstance(); - ConnectorFacade facade = facf.NewInstance(api); - facade.Validate(); - property.Value=true; - facade = facf.NewInstance(api); - try { - Thread.CurrentThread.CurrentUICulture = new CultureInfo("en"); - facade.Validate(); - Assert.Fail("exception expected"); - } - catch (ConnectorException e) { - Assert.AreEqual("validation failed en", e.Message); - } - try { - Thread.CurrentThread.CurrentUICulture = new CultureInfo("es"); - facade.Validate(); - Assert.Fail("exception expected"); - } - catch (ConnectorException e) { - Assert.AreEqual("validation failed es", e.Message); - } - ShutdownConnnectorInfoManager(); - } - - - - /** - * Main purpose of this is to test searching with - * many results and that we can properly handle - * stopping in the middle of this. There's a bunch of - * code in the remote stuff that is there to handle this - * in particular that we want to excercise. - */ - [Test] - public void TestSearchWithManyResults() { - ConnectorInfoManager manager = - GetConnectorInfoManager(); - ConnectorInfo info = - FindConnectorInfo(manager, - "1.0.0.0", - "org.identityconnectors.testconnector.TstConnector"); - - APIConfiguration api = info.CreateDefaultAPIConfiguration(); - - ConfigurationProperties props = api.ConfigurationProperties; - ConfigurationProperty property = props.GetProperty("numResults"); - - //1000 is several times the remote size between pauses - property.Value=1000; - - ConnectorFacadeFactory facf = ConnectorFacadeFactory.GetInstance(); - ConnectorFacade facade = facf.NewInstance(api); - - IList results = new List(); - - facade.Search(ObjectClass.ACCOUNT,null, - obj => { - results.Add(obj); - return true; - },null); - - Assert.AreEqual(1000,results.Count); - for ( int i = 0; i < results.Count; i++ ) { - ConnectorObject obj = results[i]; - Assert.AreEqual(i.ToString(), - obj.Uid.GetUidValue()); - } - - results.Clear(); - - facade.Search(ObjectClass.ACCOUNT,null, - obj => { - if ( results.Count < 500 ) { - results.Add(obj); - return true; - } - else { - return false; - } - },null); - - Assert.AreEqual(500,results.Count); - for ( int i = 0; i < results.Count; i++ ) { - ConnectorObject obj = results[i]; - Assert.AreEqual(i.ToString(), - obj.Uid.GetUidValue()); - } - - ShutdownConnnectorInfoManager(); - } - /** - * Main purpose of this is to test sync with - * many results and that we can properly handle - * stopping in the middle of this. There's a bunch of - * code in the remote stuff that is there to handle this - * in particular that we want to excercise. - */ - [Test] - public void TestSyncWithManyResults() { - ConnectorInfoManager manager = - GetConnectorInfoManager(); - ConnectorInfo info = - FindConnectorInfo(manager, - "1.0.0.0", - "org.identityconnectors.testconnector.TstConnector"); - - APIConfiguration api = info.CreateDefaultAPIConfiguration(); - - ConfigurationProperties props = api.ConfigurationProperties; - ConfigurationProperty property = props.GetProperty("numResults"); - - //1000 is several times the remote size between pauses - property.Value=(1000); - - ConnectorFacadeFactory facf = ConnectorFacadeFactory.GetInstance(); - ConnectorFacade facade = facf.NewInstance(api); - - SyncToken latest = facade.GetLatestSyncToken(ObjectClass.ACCOUNT); - Assert.AreEqual("mylatest",latest.Value); - IList results = new List(); - - facade.Sync(ObjectClass.ACCOUNT, null, obj => { - results.Add(obj); - return true; - },null); - - Assert.AreEqual(1000,results.Count); - for ( int i = 0; i < results.Count; i++ ) { - SyncDelta obj = results[i]; - Assert.AreEqual(i.ToString(), - obj.Uid.GetUidValue()); - } - - results.Clear(); - - facade.Sync(ObjectClass.ACCOUNT, - null, - obj => { - if ( results.Count < 500 ) { - results.Add(obj); - return true; - } - else { - return false; - } - },null); - - Assert.AreEqual(500,results.Count); - for ( int i = 0; i < results.Count; i++ ) { - SyncDelta obj = results[i]; - Assert.AreEqual(i.ToString(), - obj.Uid.GetUidValue()); - } - - ShutdownConnnectorInfoManager(); - } - - [Test] - public void TestConnectionPooling() { - ConnectorPoolManager.Dispose(); - ConnectorInfoManager manager = - GetConnectorInfoManager(); - ConnectorInfo info1 = - FindConnectorInfo(manager, - "1.0.0.0", - "org.identityconnectors.testconnector.TstConnector"); - Assert.IsNotNull(info1); - //reset connection count - { - //trigger TstConnection.init to be called - APIConfiguration config2 = - info1.CreateDefaultAPIConfiguration(); - config2.ConfigurationProperties.GetProperty("resetConnectionCount").Value=(true); - ConnectorFacade facade2 = - ConnectorFacadeFactory.GetInstance().NewInstance(config2); - facade2.Schema(); //force instantiation - } - - APIConfiguration config = - info1.CreateDefaultAPIConfiguration(); - - config.ConnectorPoolConfiguration.MinIdle=(0); - config.ConnectorPoolConfiguration.MaxIdle=(0); - - ConnectorFacade facade1 = - ConnectorFacadeFactory.GetInstance().NewInstance(config); - - OperationOptionsBuilder builder = new OperationOptionsBuilder(); - builder.SetOption("testPooling", "true"); - OperationOptions options = builder.Build(); - ICollection attrs = CollectionUtil.NewReadOnlySet(); - Assert.AreEqual("1", facade1.Create(ObjectClass.ACCOUNT,attrs,options).GetUidValue()); - Assert.AreEqual("2", facade1.Create(ObjectClass.ACCOUNT,attrs,options).GetUidValue()); - Assert.AreEqual("3", facade1.Create(ObjectClass.ACCOUNT,attrs,options).GetUidValue()); - Assert.AreEqual("4", facade1.Create(ObjectClass.ACCOUNT,attrs,options).GetUidValue()); - config = - info1.CreateDefaultAPIConfiguration(); - config.ConnectorPoolConfiguration.MinIdle=(1); - config.ConnectorPoolConfiguration.MaxIdle=(2); - facade1 = - ConnectorFacadeFactory.GetInstance().NewInstance(config); - Assert.AreEqual("5", facade1.Create(ObjectClass.ACCOUNT,attrs,options).GetUidValue()); - Assert.AreEqual("5", facade1.Create(ObjectClass.ACCOUNT,attrs,options).GetUidValue()); - Assert.AreEqual("5", facade1.Create(ObjectClass.ACCOUNT,attrs,options).GetUidValue()); - Assert.AreEqual("5", facade1.Create(ObjectClass.ACCOUNT,attrs,options).GetUidValue()); - ShutdownConnnectorInfoManager(); - } - - [Test] - public void TestScripting() - { - ConnectorInfoManager manager = - GetConnectorInfoManager(); - ConnectorInfo info = - FindConnectorInfo(manager, - "1.0.0.0", - "org.identityconnectors.testconnector.TstConnector"); - APIConfiguration api = info.CreateDefaultAPIConfiguration(); - - - ConnectorFacadeFactory facf = ConnectorFacadeFactory.GetInstance(); - ConnectorFacade facade = facf.NewInstance(api); - - ScriptContextBuilder builder = new ScriptContextBuilder(); - builder.AddScriptArgument("arg1", "value1"); - builder.AddScriptArgument("arg2", "value2"); - builder.ScriptLanguage=("BOO"); - - //test that they can run the script and access the - //connector object - { - String SCRIPT = - "connector.concat(arg1,arg2)"; - builder.ScriptText=(SCRIPT); - String result = (String)facade.RunScriptOnConnector(builder.Build(), - null); - - Assert.AreEqual("value1value2", result); - } - - //test that they can access a class in the class loader - { - String SCRIPT = - "import org.identityconnectors.testconnector\n"+ - "TstConnector.getVersion()"; - builder.ScriptText=(SCRIPT); - String result = (String)facade.RunScriptOnConnector(builder.Build(), - null); - Assert.AreEqual("1.0", result); - } - - //test that they cannot access a class in internal - { - Type clazz = typeof(ConfigurationPropertyImpl); - - String SCRIPT = - "import "+clazz.Namespace+"\n"+ - clazz.Name+"()"; - builder.ScriptText=(SCRIPT); - try { - facade.RunScriptOnConnector(builder.Build(), - null); - Assert.Fail("exception expected"); - } - catch (Exception e) { - String msg = e.Message; - String expectedMessage = - "Namespace '"+clazz.Namespace+"' not found"; - Assert.IsTrue( - msg.Contains(expectedMessage), - "Unexpected message: "+msg); - } - } - - // test that they can access a class in common - { - Type clazz = typeof(ConnectorAttributeBuilder); - String SCRIPT = - "import "+clazz.Namespace+"\n"+ - clazz.Name + ".Build(\"myattr\")"; - builder.ScriptText=(SCRIPT); - ConnectorAttribute attr = (ConnectorAttribute) facade.RunScriptOnConnector(builder.Build(), null); - Assert.AreEqual("myattr", attr.Name); - } - ShutdownConnnectorInfoManager(); - } - - protected virtual ConnectorInfoManager GetConnectorInfoManager() { - ConnectorInfoManagerFactory fact = ConnectorInfoManagerFactory.GetInstance(); - ConnectorInfoManager manager = fact.GetLocalManager(); - return manager; - } - - protected virtual void ShutdownConnnectorInfoManager() { - - } - } - - [TestFixture] - public class RemoteConnectorInfoManagerClearTests : ConnectorInfoManagerTests { - - private ConnectorServer _server; - - protected override ConnectorInfoManager GetConnectorInfoManager() { - TestUtil.InitializeLogging(); - - ShutdownConnnectorInfoManager(); - - GuardedString str = new GuardedString(); - str.AppendChar('c'); - str.AppendChar('h'); - str.AppendChar('a'); - str.AppendChar('n'); - str.AppendChar('g'); - str.AppendChar('e'); - str.AppendChar('i'); - str.AppendChar('t'); - -#if DEBUG - const int PORT = 8758; -#else - const int PORT = 8759; -#endif - _server = ConnectorServer.NewInstance(); - _server.Port=PORT; - _server.IfAddress=(IOUtil.GetIPAddress("127.0.0.1")); - _server.KeyHash=str.GetBase64SHA1Hash(); - _server.Start(); - //while ( true ) { - // Thread.Sleep(1000); - //} - ConnectorInfoManagerFactory fact = ConnectorInfoManagerFactory.GetInstance(); - - RemoteFrameworkConnectionInfo connInfo = new - RemoteFrameworkConnectionInfo("127.0.0.1",PORT,str); - - ConnectorInfoManager manager = fact.GetRemoteManager(connInfo); - - return manager; - } - - protected override void ShutdownConnnectorInfoManager() { - if (_server != null) { - _server.Stop(); - _server = null; - } - } - } - - internal class MyCertificateValidationCallback - { - public bool Validate(Object sender, - X509Certificate certificate, - X509Chain chain, - SslPolicyErrors sslPolicyErrors) { - Trace.TraceInformation("validating: "+certificate.Subject); - return true; - } - } - - [TestFixture] - public class RemoteConnectorInfoManagerSSLTests : ConnectorInfoManagerTests { - - //To generate test certificate do the following: - // - //makecert -r -pe -n "CN=localhost" -ss TestCertificateStore -sr currentuser -sky exchange - // - //In MMC, go to the certificate, export - private ConnectorServer _server; - private const String CERT_PATH = "../../../server.pfx"; - protected override ConnectorInfoManager GetConnectorInfoManager() { - TestUtil.InitializeLogging(); - - ShutdownConnnectorInfoManager(); - GuardedString str = new GuardedString(); - str.AppendChar('c'); - str.AppendChar('h'); - str.AppendChar('a'); - str.AppendChar('n'); - str.AppendChar('g'); - str.AppendChar('e'); - str.AppendChar('i'); - str.AppendChar('t'); - -#if DEBUG - const int PORT = 8762; -#else - const int PORT = 8761; -#endif - - /*X509Store store = new X509Store("TestCertificateStore", - StoreLocation.CurrentUser); - store.Open(OpenFlags.ReadOnly|OpenFlags.OpenExistingOnly); - X509Certificate certificate = store.Certificates[0]; - store.Close();*/ - - X509Certificate2 certificate = new - X509Certificate2(CERT_PATH, - "changeit"); - //Trace.TraceInformation("certificate: "+certificate); - _server = ConnectorServer.NewInstance(); - _server.Port=PORT; - _server.KeyHash=str.GetBase64SHA1Hash(); - _server.IfAddress=(IOUtil.GetIPAddress("localhost")); - _server.UseSSL = true; - _server.ServerCertificate = certificate; - _server.Start(); - //while ( true ) { - // Thread.Sleep(1000); - //} - ConnectorInfoManagerFactory fact = ConnectorInfoManagerFactory.GetInstance(); - MyCertificateValidationCallback - callback = new MyCertificateValidationCallback(); - RemoteFrameworkConnectionInfo connInfo = new - RemoteFrameworkConnectionInfo("localhost", - PORT, - str, - true, - callback.Validate, - 60000); - - ConnectorInfoManager manager = fact.GetRemoteManager(connInfo); - - return manager; - } - - protected override void ShutdownConnnectorInfoManager() { - if (_server != null) { - _server.Stop(); - _server = null; - } - } - } - - - -} diff --git a/DotNetConnectors.sln/FrameworkTests/FilterTranslatorTests.cs b/DotNetConnectors.sln/FrameworkTests/FilterTranslatorTests.cs deleted file mode 100644 index f545c6b2..00000000 --- a/DotNetConnectors.sln/FrameworkTests/FilterTranslatorTests.cs +++ /dev/null @@ -1,609 +0,0 @@ -/* - * ==================== - * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER. - * - * Copyright 2007-2008 Sun Microsystems, Inc. All rights reserved. - * - * The contents of this file are subject to the terms of the Common Development - * and Distribution License("CDDL") (the "License"). You may not use this file - * except in compliance with the License. - * - * You can obtain a copy of the License at - * http://IdentityConnectors.dev.java.net/legal/license.txt - * See the License for the specific language governing permissions and limitations - * under the License. - * - * When distributing the Covered Code, include this CDDL Header Notice in each file - * and include the License file at identityconnectors/legal/license.txt. - * If applicable, add the following below this CDDL Header, with the fields - * enclosed by brackets [] replaced by your own identifying information: - * "Portions Copyrighted [year] [name of copyright owner]" - * ==================== - */ -using System; -using System.Collections.Generic; -using NUnit.Framework; -using NUnit.Framework.SyntaxHelpers; -using Org.IdentityConnectors.Common; -using Org.IdentityConnectors.Framework.Common.Objects; -using Org.IdentityConnectors.Framework.Common.Objects.Filters; -namespace FrameworkTests -{ - - internal class AllFiltersTranslator : AbstractFilterTranslator - { - protected override String CreateAndExpression(String leftExpression, String rightExpression) { - return "( & " + leftExpression+" "+rightExpression+" )"; - } - - protected override String CreateOrExpression(String leftExpression, String rightExpression) { - return "( | " + leftExpression+" "+rightExpression+" )"; - } - - protected override String CreateContainsExpression(ContainsFilter filter, bool not) { - String rv = "( CONTAINS "+filter.GetName()+" "+filter.GetValue()+" )"; - return Not(rv,not); - } - - protected override String CreateEndsWithExpression(EndsWithFilter filter, bool not) { - String rv = "( ENDS-WITH "+filter.GetName()+" "+filter.GetValue()+" )"; - return Not(rv,not); - } - - protected override String CreateEqualsExpression(EqualsFilter filter, bool not) { - String rv = "( = "+filter.GetAttribute().Name+" ["+filter.GetAttribute().Value[0]+"] )"; - return Not(rv,not); - } - - protected override String CreateGreaterThanExpression(GreaterThanFilter filter, bool not) { - String rv = "( > "+filter.GetName()+" "+filter.GetValue()+" )"; - return Not(rv,not); - } - - protected override String CreateGreaterThanOrEqualExpression(GreaterThanOrEqualFilter filter, bool not) { - String rv = "( >= "+filter.GetName()+" "+filter.GetValue()+" )"; - return Not(rv,not); - } - - protected override String CreateLessThanExpression(LessThanFilter filter, bool not) { - String rv = "( < "+filter.GetName()+" "+filter.GetValue()+" )"; - return Not(rv,not); - } - - protected override String CreateLessThanOrEqualExpression(LessThanOrEqualFilter filter, bool not) { - String rv = "( <= "+filter.GetName()+" "+filter.GetValue()+" )"; - return Not(rv,not); - } - - protected override String CreateStartsWithExpression(StartsWithFilter filter, bool not) { - String rv = "( STARTS-WITH "+filter.GetName()+" "+filter.GetValue()+" )"; - return Not(rv,not); - } - protected override String CreateContainsAllValuesExpression(ContainsAllValuesFilter filter, bool not) { - String rv = "( CONTAINS-ALL-VALUES "+filter.GetAttribute()+" )"; - return Not(rv,not); - } - private String Not(String orig, bool not) { - if ( not ) { - return "( ! " + orig + " )"; - } - else { - return orig; - } - } - - } - - /** - * Everything but Or - */ - internal class NoOrTranslator : AllFiltersTranslator - { - protected override String CreateOrExpression(String leftExpression, String rightExpression) { - return null; - } - } - /** - * Everything but EndsWith - */ - internal class NoEndsWithTranslator : AllFiltersTranslator - { - protected override String CreateEndsWithExpression(EndsWithFilter filter, bool not) { - return null; - } - } - /** - * Everything but EndsWith,Or - */ - internal class NoEndsWithNoOrTranslator : AllFiltersTranslator - { - protected override String CreateOrExpression(String leftExpression, String rightExpression) { - return null; - } - protected override String CreateEndsWithExpression(EndsWithFilter filter, bool not) { - return null; - } - } - - /** - * Everything but And - */ - internal class NoAndTranslator : AllFiltersTranslator - { - protected override String CreateAndExpression(String leftExpression, String rightExpression) { - return null; - } - } - /** - * Everything but And - */ - internal class NoAndNoEndsWithTranslator : AllFiltersTranslator - { - protected override String CreateAndExpression(String leftExpression, String rightExpression) { - return null; - } - protected override String CreateEndsWithExpression(EndsWithFilter filter, bool not) { - return null; - } - - } - /** - * Everything but And - */ - internal class NoAndNoOrNoEndsWithTranslator : AllFiltersTranslator - { - protected override String CreateAndExpression(String leftExpression, String rightExpression) { - return null; - } - protected override String CreateEndsWithExpression(EndsWithFilter filter, bool not) { - return null; - } - protected override String CreateOrExpression(String leftExpression, String rightExpression) { - return null; - } - - } - [TestFixture] - public class FilterTranslatorTests - { - - /** - * Test all operations when everything is fully implemented. - * Test not normalization as well. - */ - [Test] - public void TestBasics() { - ConnectorAttribute attribute = - ConnectorAttributeBuilder.Build("att-name", "att-value"); - ConnectorAttribute attribute2 = - ConnectorAttributeBuilder.Build("att-name2", "att-value2"); - AllFiltersTranslator translator = new - AllFiltersTranslator(); - - { - Filter filter = - FilterBuilder.Contains(attribute); - String expected = "( CONTAINS att-name att-value )"; - String actual = - TranslateSingle(translator, filter); - Assert.AreEqual(expected, actual); - - filter = FilterBuilder.Not(filter); - expected = "( ! "+expected+" )"; - actual = - TranslateSingle(translator, filter); - Assert.AreEqual(expected, actual); - } - - { - Filter filter = - FilterBuilder.EndsWith(attribute); - String expected = "( ENDS-WITH att-name att-value )"; - String actual = - TranslateSingle(translator, filter); - Assert.AreEqual(expected, actual); - - filter = FilterBuilder.Not(filter); - expected = "( ! "+expected+" )"; - actual = - TranslateSingle(translator, filter); - Assert.AreEqual(expected, actual); - } - - { - Filter filter = - FilterBuilder.EqualTo(attribute); - String expected = "( = att-name [att-value] )"; - String actual = - TranslateSingle(translator, filter); - Assert.AreEqual(expected, actual); - - filter = FilterBuilder.Not(filter); - expected = "( ! "+expected+" )"; - actual = - TranslateSingle(translator, filter); - Assert.AreEqual(expected, actual); - } - - { - Filter filter = - FilterBuilder.GreaterThan(attribute); - String expected = "( > att-name att-value )"; - String actual = - TranslateSingle(translator, filter); - Assert.AreEqual(expected, actual); - - filter = FilterBuilder.Not(filter); - expected = "( ! "+expected+" )"; - actual = - TranslateSingle(translator, filter); - Assert.AreEqual(expected, actual); - } - - { - Filter filter = - FilterBuilder.GreaterThanOrEqualTo(attribute); - String expected = "( >= att-name att-value )"; - String actual = - TranslateSingle(translator, filter); - Assert.AreEqual(expected, actual); - - filter = FilterBuilder.Not(filter); - expected = "( ! "+expected+" )"; - actual = - TranslateSingle(translator, filter); - Assert.AreEqual(expected, actual); - } - - { - Filter filter = - FilterBuilder.LessThan(attribute); - String expected = "( < att-name att-value )"; - String actual = - TranslateSingle(translator, filter); - Assert.AreEqual(expected, actual); - - filter = FilterBuilder.Not(filter); - expected = "( ! "+expected+" )"; - actual = - TranslateSingle(translator, filter); - Assert.AreEqual(expected, actual); - } - - { - Filter filter = - FilterBuilder.LessThanOrEqualTo(attribute); - String expected = "( <= att-name att-value )"; - String actual = - TranslateSingle(translator, filter); - Assert.AreEqual(expected, actual); - - filter = FilterBuilder.Not(filter); - expected = "( ! "+expected+" )"; - actual = - TranslateSingle(translator, filter); - Assert.AreEqual(expected, actual); - } - - { - Filter filter = - FilterBuilder.StartsWith(attribute); - String expected = "( STARTS-WITH att-name att-value )"; - String actual = - TranslateSingle(translator, filter); - Assert.AreEqual(expected, actual); - - filter = FilterBuilder.Not(filter); - expected = "( ! "+expected+" )"; - actual = - TranslateSingle(translator, filter); - Assert.AreEqual(expected, actual); - } - - { - Filter filter = - FilterBuilder.ContainsAllValues(attribute); - String expected = "( CONTAINS-ALL-VALUES "+attribute+" )"; - String actual = - TranslateSingle(translator, filter); - Assert.AreEqual(expected, actual); - - filter = FilterBuilder.Not(filter); - expected = "( ! "+expected+" )"; - actual = - TranslateSingle(translator, filter); - Assert.AreEqual(expected, actual); - } - - //and - { - Filter left = - FilterBuilder.Contains(attribute); - Filter right = - FilterBuilder.Contains(attribute2); - String expectedLeft = "( CONTAINS att-name att-value )"; - String expectedRight = "( CONTAINS att-name2 att-value2 )"; - Filter filter = - FilterBuilder.And(left, right); - String expected = - "( & "+expectedLeft+" "+expectedRight+" )"; - String actual = - TranslateSingle(translator, filter); - Assert.AreEqual(expected, actual); - - filter = FilterBuilder.Not(filter); - expectedLeft = "( ! "+expectedLeft+" )"; - expectedRight = "( ! "+expectedRight+" )"; - expected = - "( | "+expectedLeft+" "+expectedRight+" )"; - actual = - TranslateSingle(translator, filter); - Assert.AreEqual(expected, actual); - - } - - //or - { - Filter left = - FilterBuilder.Contains(attribute); - Filter right = - FilterBuilder.Contains(attribute2); - String expectedLeft = "( CONTAINS att-name att-value )"; - String expectedRight = "( CONTAINS att-name2 att-value2 )"; - Filter filter = - FilterBuilder.Or(left, right); - String expected = - "( | "+expectedLeft+" "+expectedRight+" )"; - String actual = - TranslateSingle(translator, filter); - Assert.AreEqual(expected, actual); - - filter = FilterBuilder.Not(filter); - expectedLeft = "( ! "+expectedLeft+" )"; - expectedRight = "( ! "+expectedRight+" )"; - expected = - "( & "+expectedLeft+" "+expectedRight+" )"; - actual = - TranslateSingle(translator, filter); - Assert.AreEqual(expected, actual); - } - - //double-negative - { - Filter filter = - FilterBuilder.Contains(attribute); - filter = FilterBuilder.Not(filter); - filter = FilterBuilder.Not(filter); - String expected = "( CONTAINS att-name att-value )"; - String actual = - TranslateSingle(translator, filter); - Assert.AreEqual(expected, actual); - } - - } - - /** - * (a OR b) AND ( c OR d) needs to become - * (a AND c) OR ( a AND d) OR (b AND c) OR (b AND d) is - * OR is not implemented. Otherwise it should stay - * as-is. - */ - [Test] - public void TestDistribution() - { - Filter a = - FilterBuilder.Contains(ConnectorAttributeBuilder.Build("a", "a")); - Filter b = - FilterBuilder.Contains(ConnectorAttributeBuilder.Build("b", "b")); - Filter c = - FilterBuilder.Contains(ConnectorAttributeBuilder.Build("c", "c")); - Filter d = - FilterBuilder.Contains(ConnectorAttributeBuilder.Build("d", "d")); - - Filter filter = - FilterBuilder.And( - FilterBuilder.Or(a, b), - FilterBuilder.Or(c, d)); - String expected = "( & ( | ( CONTAINS a a ) ( CONTAINS b b ) ) ( | ( CONTAINS c c ) ( CONTAINS d d ) ) )"; - String actual = - TranslateSingle(new AllFiltersTranslator(),filter); - - Assert.AreEqual(expected, actual); - - IList results = - new NoOrTranslator().Translate(filter); - Assert.AreEqual(4, results.Count); - - Assert.AreEqual("( & ( CONTAINS a a ) ( CONTAINS c c ) )", - results[0]); - Assert.AreEqual("( & ( CONTAINS a a ) ( CONTAINS d d ) )", - results[1]); - Assert.AreEqual("( & ( CONTAINS b b ) ( CONTAINS c c ) )", - results[2]); - Assert.AreEqual("( & ( CONTAINS b b ) ( CONTAINS d d ) )", - results[3]); - } - - //test simplification - //-no leaf - [Test] - public void TestSimplifyNoLeaf() { - Filter a = - FilterBuilder.Contains(ConnectorAttributeBuilder.Build("a", "a")); - Filter b = - FilterBuilder.Contains(ConnectorAttributeBuilder.Build("b", "b")); - Filter c = - FilterBuilder.EndsWith(ConnectorAttributeBuilder.Build("c", "c")); - Filter d = - FilterBuilder.Contains(ConnectorAttributeBuilder.Build("d", "d")); - - Filter filter = - FilterBuilder.And( - FilterBuilder.Or(a, b), - FilterBuilder.Or(c, d)); - String expected = "( | ( CONTAINS a a ) ( CONTAINS b b ) )"; - String actual = - TranslateSingle(new NoEndsWithTranslator(),filter); - Assert.AreEqual(expected, actual); - - } - //-no leaf + no or - [Test] - public void TestSimplifyNoLeafNoOr() { - Filter a = - FilterBuilder.Contains(ConnectorAttributeBuilder.Build("a", "a")); - Filter b = - FilterBuilder.Contains(ConnectorAttributeBuilder.Build("b", "b")); - Filter c = - FilterBuilder.EndsWith(ConnectorAttributeBuilder.Build("c", "c")); - Filter d = - FilterBuilder.Contains(ConnectorAttributeBuilder.Build("d", "d")); - - Filter filter = - FilterBuilder.And( - FilterBuilder.Or(a, b), - FilterBuilder.Or(c, d)); - IList results = - new NoEndsWithNoOrTranslator().Translate(filter); - Assert.AreEqual(2, results.Count); - Assert.AreEqual("( CONTAINS a a )", - results[0]); - Assert.AreEqual("( CONTAINS b b )", - results[1]); - - } - - //-no and - [Test] - public void TestSimplifyNoAnd() { - Filter a = - FilterBuilder.Contains(ConnectorAttributeBuilder.Build("a", "a")); - Filter b = - FilterBuilder.Contains(ConnectorAttributeBuilder.Build("b", "b")); - Filter c = - FilterBuilder.EndsWith(ConnectorAttributeBuilder.Build("c", "c")); - Filter d = - FilterBuilder.Contains(ConnectorAttributeBuilder.Build("d", "d")); - - Filter filter = - FilterBuilder.And( - FilterBuilder.Or(a, b), - FilterBuilder.Or(c, d)); - String expected = "( | ( CONTAINS a a ) ( CONTAINS b b ) )"; - String actual = - TranslateSingle(new NoAndTranslator(),filter); - Assert.AreEqual(expected, actual); - } - - //-no and+no leaf - [Test] - public void TestSimplifyNoAndNoLeaf() { - Filter a = - FilterBuilder.Contains(ConnectorAttributeBuilder.Build("a", "a")); - Filter b = - FilterBuilder.Contains(ConnectorAttributeBuilder.Build("b", "b")); - Filter c = - FilterBuilder.EndsWith(ConnectorAttributeBuilder.Build("c", "c")); - Filter d = - FilterBuilder.Contains(ConnectorAttributeBuilder.Build("d", "d")); - - Filter filter = - FilterBuilder.And( - FilterBuilder.Or(a, b), - FilterBuilder.Or(c, d)); - String expected = "( | ( CONTAINS a a ) ( CONTAINS b b ) )"; - String actual = - TranslateSingle(new NoAndNoEndsWithTranslator(),filter); - Assert.AreEqual(expected, actual); - - a = - FilterBuilder.Contains(ConnectorAttributeBuilder.Build("a", "a")); - b = - FilterBuilder.EndsWith(ConnectorAttributeBuilder.Build("b", "b")); - c = - FilterBuilder.Contains(ConnectorAttributeBuilder.Build("c", "c")); - d = - FilterBuilder.Contains(ConnectorAttributeBuilder.Build("d", "d")); - - filter = - FilterBuilder.And( - FilterBuilder.Or(a, b), - FilterBuilder.Or(c, d)); - expected = "( | ( CONTAINS c c ) ( CONTAINS d d ) )"; - actual = - TranslateSingle(new NoAndNoEndsWithTranslator(),filter); - Assert.AreEqual(expected, actual); - - a = - FilterBuilder.Contains(ConnectorAttributeBuilder.Build("a", "a")); - b = - FilterBuilder.EndsWith(ConnectorAttributeBuilder.Build("b", "b")); - c = - FilterBuilder.Contains(ConnectorAttributeBuilder.Build("c", "c")); - d = - FilterBuilder.EndsWith(ConnectorAttributeBuilder.Build("d", "d")); - - filter = - FilterBuilder.And( - FilterBuilder.Or(a, b), - FilterBuilder.Or(c, d)); - IList results = - new NoAndNoEndsWithTranslator().Translate(filter); - Assert.AreEqual(0, results.Count); - } - - //-no and, no or, no leaf - [Test] - public void TestSimplifyNoAndNoOrNoLeaf() { - Filter a = - FilterBuilder.Contains(ConnectorAttributeBuilder.Build("a", "a")); - Filter b = - FilterBuilder.Contains(ConnectorAttributeBuilder.Build("b", "b")); - Filter c = - FilterBuilder.EndsWith(ConnectorAttributeBuilder.Build("c", "c")); - Filter d = - FilterBuilder.Contains(ConnectorAttributeBuilder.Build("d", "d")); - - Filter filter = - FilterBuilder.And( - FilterBuilder.Or(a, b), - FilterBuilder.Or(c, d)); - IList results = - new NoAndNoOrNoEndsWithTranslator().Translate(filter); - Assert.AreEqual(2, results.Count); - Assert.AreEqual("( CONTAINS a a )", - results[0]); - Assert.AreEqual("( CONTAINS b b )", - results[1]); - - a = - FilterBuilder.Contains(ConnectorAttributeBuilder.Build("a", "a")); - b = - FilterBuilder.EndsWith(ConnectorAttributeBuilder.Build("b", "b")); - c = - FilterBuilder.Contains(ConnectorAttributeBuilder.Build("c", "c")); - d = - FilterBuilder.Contains(ConnectorAttributeBuilder.Build("d", "d")); - filter = - FilterBuilder.And( - FilterBuilder.Or(a, b), - FilterBuilder.Or(c, d)); - results = - new NoAndNoOrNoEndsWithTranslator().Translate(filter); - Assert.AreEqual(2, results.Count); - Assert.AreEqual("( CONTAINS c c )", - results[0]); - Assert.AreEqual("( CONTAINS d d )", - results[1]); - - } - - private static String TranslateSingle(AbstractFilterTranslator translator, - Filter filter) { - IList translated = - translator.Translate(filter); - Assert.AreEqual(1, translated.Count); - return translated[0]; - } - } -} diff --git a/DotNetConnectors.sln/FrameworkTests/FrameworkTests.csproj b/DotNetConnectors.sln/FrameworkTests/FrameworkTests.csproj deleted file mode 100644 index 8137402f..00000000 --- a/DotNetConnectors.sln/FrameworkTests/FrameworkTests.csproj +++ /dev/null @@ -1,153 +0,0 @@ - - - - {32804F5A-812C-4FA6-835C-BDAE5B24D355} - Debug - AnyCPU - Library - FrameworkTests - FrameworkTests - v3.5 - - - bin\Debug\ - True - Full - False - True - DEBUG;TRACE - - - bin\Release\ - False - None - True - False - TRACE - - - - - - - 3.5 - - - - 3.5 - - - - 3.5 - - - - - - - - - - - - - - - - - - - - Always - - - Always - - - - - {F140E8DA-52B4-4159-992A-9DA10EA8EEFB} - Common - - - {8B24461B-456A-4032-89A1-CD418F7B5B62} - Framework - - - {5B011775-B121-4EEE-A410-BA2D2F5BFB8B} - FrameworkInternal - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/DotNetConnectors.sln/FrameworkTests/GuardedStringTests.cs b/DotNetConnectors.sln/FrameworkTests/GuardedStringTests.cs deleted file mode 100644 index 789cfc60..00000000 --- a/DotNetConnectors.sln/FrameworkTests/GuardedStringTests.cs +++ /dev/null @@ -1,100 +0,0 @@ -/* - * ==================== - * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER. - * - * Copyright 2007-2008 Sun Microsystems, Inc. All rights reserved. - * - * The contents of this file are subject to the terms of the Common Development - * and Distribution License("CDDL") (the "License"). You may not use this file - * except in compliance with the License. - * - * You can obtain a copy of the License at - * http://IdentityConnectors.dev.java.net/legal/license.txt - * See the License for the specific language governing permissions and limitations - * under the License. - * - * When distributing the Covered Code, include this CDDL Header Notice in each file - * and include the License file at identityconnectors/legal/license.txt. - * If applicable, add the following below this CDDL Header, with the fields - * enclosed by brackets [] replaced by your own identifying information: - * "Portions Copyrighted [year] [name of copyright owner]" - * ==================== - */ -using System; -using System.Security; -using System.Runtime.InteropServices; -using System.Text; -using Org.IdentityConnectors.Common.Security; -using Org.IdentityConnectors.Framework.Common.Serializer; -using NUnit.Framework; -using NUnit.Framework.SyntaxHelpers; - -namespace FrameworkTests -{ - [TestFixture] - public class GuardedStringTests - { - [Test] - public void TestBasics() - { - GuardedString ss = new GuardedString(); - ss.AppendChar('f'); - ss.AppendChar('o'); - ss.AppendChar('o'); - ss.AppendChar('b'); - ss.AppendChar('a'); - ss.AppendChar('r'); - String decrypted = DecryptToString(ss); - Assert.AreEqual("foobar",decrypted); - String hash = ss.GetBase64SHA1Hash(); - Assert.IsTrue(ss.VerifyBase64SHA1Hash(hash)); - ss.AppendChar('2'); - Assert.IsFalse(ss.VerifyBase64SHA1Hash(hash)); - } - [Test] - public void TestUnicode() { - - for ( int i = 0; i < 0xFFFF; i++) { - int expected = i; - char c = (char)i; - GuardedString gs = new GuardedString(); - gs = (GuardedString)SerializerUtil.CloneObject(gs); - gs.AppendChar(c); - gs.Access(clearChars=> { - int v = (int)clearChars[0]; - Assert.AreEqual(expected, v); - }); - - } - } - - [Test] - public void TestEquals() { - GuardedString str1 = new GuardedString(); - GuardedString str2 = new GuardedString(); - Assert.AreEqual(str1, str2); - str2.AppendChar('2'); - Assert.AreNotEqual(str1,str2); - str1.AppendChar('2'); - Assert.AreEqual(str1, str2); - } - - - /** - * Highly insecure method! Do not do this in production - * code. This is only for test purposes - */ - private String DecryptToString(GuardedString str) { - StringBuilder buf = new StringBuilder(); - str.Access( - array=> - { - for ( int i = 0 ; i < array.Length; i++) - { - buf.Append(array[i]); - } - }); - return buf.ToString(); - } - } -} diff --git a/DotNetConnectors.sln/FrameworkTests/LocaleTests.cs b/DotNetConnectors.sln/FrameworkTests/LocaleTests.cs deleted file mode 100644 index 0176a1af..00000000 --- a/DotNetConnectors.sln/FrameworkTests/LocaleTests.cs +++ /dev/null @@ -1,210 +0,0 @@ -/* - * ==================== - * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER. - * - * Copyright 2007-2008 Sun Microsystems, Inc. All rights reserved. - * - * The contents of this file are subject to the terms of the Common Development - * and Distribution License("CDDL") (the "License"). You may not use this file - * except in compliance with the License. - * - * You can obtain a copy of the License at - * http://IdentityConnectors.dev.java.net/legal/license.txt - * See the License for the specific language governing permissions and limitations - * under the License. - * - * When distributing the Covered Code, include this CDDL Header Notice in each file - * and include the License file at identityconnectors/legal/license.txt. - * If applicable, add the following below this CDDL Header, with the fields - * enclosed by brackets [] replaced by your own identifying information: - * "Portions Copyrighted [year] [name of copyright owner]" - * ==================== - */ -using System; -using NUnit.Framework; -using NUnit.Framework.SyntaxHelpers; -using System.Globalization; -using System.Collections.Generic; -using Org.IdentityConnectors.Common; -namespace FrameworkTests -{ - [TestFixture] - public class LocaleTests - { - - - [Test] - public void TestJava2CSharp() - { - HashSet - cultures = new HashSet(CultureInfo.GetCultures(CultureTypes.AllCultures)); - - TestJavaLocale(cultures, new Locale("ar", "", ""), "Arabic"); - TestJavaLocale(cultures, new Locale("be", "", ""), "Belarusian"); - TestJavaLocale(cultures, new Locale("bg", "", ""), "Bulgarian"); - TestJavaLocale(cultures, new Locale("ca", "", ""), "Catalan"); - TestJavaLocale(cultures, new Locale("cs", "", ""), "Czech"); - TestJavaLocale(cultures, new Locale("da", "", ""), "Danish"); - TestJavaLocale(cultures, new Locale("de", "", ""), "German"); - TestJavaLocale(cultures, new Locale("el", "", ""), "Greek"); - TestJavaLocale(cultures, new Locale("en", "", ""), "English"); - TestJavaLocale(cultures, new Locale("es", "", ""), "Spanish"); - TestJavaLocale(cultures, new Locale("et", "", ""), "Estonian"); - TestJavaLocale(cultures, new Locale("fi", "", ""), "Finnish"); - TestJavaLocale(cultures, new Locale("fr", "", ""), "French"); - TestJavaLocale(cultures, new Locale("hr", "", ""), "Croatian"); - TestJavaLocale(cultures, new Locale("hu", "", ""), "Hungarian"); - TestJavaLocale(cultures, new Locale("is", "", ""), "Icelandic"); - TestJavaLocale(cultures, new Locale("it", "", ""), "Italian"); - TestJavaLocale(cultures, new Locale("iw", "", ""), "Hebrew"); - TestJavaLocale(cultures, new Locale("ja", "", ""), "Japanese"); - TestJavaLocale(cultures, new Locale("ko", "", ""), "Korean"); - TestJavaLocale(cultures, new Locale("lt", "", ""), "Lithuanian"); - TestJavaLocale(cultures, new Locale("lv", "", ""), "Latvian"); - TestJavaLocale(cultures, new Locale("mk", "", ""), "Macedonian"); - TestJavaLocale(cultures, new Locale("nl", "", ""), "Dutch"); - TestJavaLocale(cultures, new Locale("no", "", ""), "Norwegian"); - TestJavaLocale(cultures, new Locale("pl", "", ""), "Polish"); - TestJavaLocale(cultures, new Locale("pt", "", ""), "Portuguese"); - TestJavaLocale(cultures, new Locale("ro", "", ""), "Romanian"); - TestJavaLocale(cultures, new Locale("ru", "", ""), "Russian"); - TestJavaLocale(cultures, new Locale("sk", "", ""), "Slovak"); - TestJavaLocale(cultures, new Locale("sl", "", ""), "Slovenian"); - TestJavaLocale(cultures, new Locale("sq", "", ""), "Albanian"); - TestJavaLocale(cultures, new Locale("sr", "", ""), "Serbian"); - TestJavaLocale(cultures, new Locale("sv", "", ""), "Swedish"); - TestJavaLocale(cultures, new Locale("th", "", ""), "Thai"); - TestJavaLocale(cultures, new Locale("tr", "", ""), "Turkish"); - TestJavaLocale(cultures, new Locale("uk", "", ""), "Ukrainian"); - TestJavaLocale(cultures, new Locale("vi", "", ""), "Vietnamese"); - TestJavaLocale(cultures, new Locale("zh", "", ""), "Chinese"); - TestJavaLocale(cultures, new Locale("ar", "AE", ""), "Arabic (United Arab Emirates)"); - TestJavaLocale(cultures, new Locale("ar", "BH", ""), "Arabic (Bahrain)"); - TestJavaLocale(cultures, new Locale("ar", "DZ", ""), "Arabic (Algeria)"); - TestJavaLocale(cultures, new Locale("ar", "EG", ""), "Arabic (Egypt)"); - TestJavaLocale(cultures, new Locale("ar", "IQ", ""), "Arabic (Iraq)"); - TestJavaLocale(cultures, new Locale("ar", "JO", ""), "Arabic (Jordan)"); - TestJavaLocale(cultures, new Locale("ar", "KW", ""), "Arabic (Kuwait)"); - TestJavaLocale(cultures, new Locale("ar", "LB", ""), "Arabic (Lebanon)"); - TestJavaLocale(cultures, new Locale("ar", "LY", ""), "Arabic (Libya)"); - TestJavaLocale(cultures, new Locale("ar", "MA", ""), "Arabic (Morocco)"); - TestJavaLocale(cultures, new Locale("ar", "OM", ""), "Arabic (Oman)"); - TestJavaLocale(cultures, new Locale("ar", "QA", ""), "Arabic (Qatar)"); - TestJavaLocale(cultures, new Locale("ar", "SA", ""), "Arabic (Saudi Arabia)"); - TestJavaLocale(cultures, - new Locale("ar", "SD", ""), - "Arabic (Sudan)", - new Locale("ar")); - TestJavaLocale(cultures, new Locale("ar", "SY", ""), "Arabic (Syria)"); - TestJavaLocale(cultures, new Locale("ar", "TN", ""), "Arabic (Tunisia)"); - TestJavaLocale(cultures, new Locale("ar", "YE", ""), "Arabic (Yemen)"); - TestJavaLocale(cultures, new Locale("be", "BY", ""), "Belarusian (Belarus)"); - TestJavaLocale(cultures, new Locale("bg", "BG", ""), "Bulgarian (Bulgaria)"); - TestJavaLocale(cultures, new Locale("ca", "ES", ""), "Catalan (Spain)"); - TestJavaLocale(cultures, new Locale("cs", "CZ", ""), "Czech (Czech Republic)"); - TestJavaLocale(cultures, new Locale("da", "DK", ""), "Danish (Denmark)"); - TestJavaLocale(cultures, new Locale("de", "AT", ""), "German (Austria)"); - TestJavaLocale(cultures, new Locale("de", "CH", ""), "German (Switzerland)"); - TestJavaLocale(cultures, new Locale("de", "DE", ""), "German (Germany)"); - TestJavaLocale(cultures, new Locale("de", "LU", ""), "German (Luxembourg)"); - TestJavaLocale(cultures, new Locale("el", "GR", ""), "Greek (Greece)"); - TestJavaLocale(cultures, new Locale("en", "AU", ""), "English (Australia)"); - TestJavaLocale(cultures, new Locale("en", "CA", ""), "English (Canada)"); - TestJavaLocale(cultures, new Locale("en", "GB", ""), "English (United Kingdom)"); - TestJavaLocale(cultures, new Locale("en", "IE", ""), "English (Ireland)"); - TestJavaLocale(cultures, new Locale("en", "IN", ""), - "English (India)", - new Locale("en")); - TestJavaLocale(cultures, new Locale("en", "NZ", ""), "English (New Zealand)"); - TestJavaLocale(cultures, new Locale("en", "US", ""), "English (United States)"); - TestJavaLocale(cultures, new Locale("en", "ZA", ""), "English (South Africa)"); - TestJavaLocale(cultures, new Locale("es", "AR", ""), "Spanish (Argentina)"); - TestJavaLocale(cultures, new Locale("es", "BO", ""), "Spanish (Bolivia)"); - TestJavaLocale(cultures, new Locale("es", "CL", ""), "Spanish (Chile)"); - TestJavaLocale(cultures, new Locale("es", "CO", ""), "Spanish (Colombia)"); - TestJavaLocale(cultures, new Locale("es", "CR", ""), "Spanish (Costa Rica)"); - TestJavaLocale(cultures, new Locale("es", "DO", ""), "Spanish (Dominican Republic)"); - TestJavaLocale(cultures, new Locale("es", "EC", ""), "Spanish (Ecuador)"); - TestJavaLocale(cultures, new Locale("es", "ES", ""), "Spanish (Spain)"); - TestJavaLocale(cultures, new Locale("es", "GT", ""), "Spanish (Guatemala)"); - TestJavaLocale(cultures, new Locale("es", "HN", ""), "Spanish (Honduras)"); - TestJavaLocale(cultures, new Locale("es", "MX", ""), "Spanish (Mexico)"); - TestJavaLocale(cultures, new Locale("es", "NI", ""), "Spanish (Nicaragua)"); - TestJavaLocale(cultures, new Locale("es", "PA", ""), "Spanish (Panama)"); - TestJavaLocale(cultures, new Locale("es", "PE", ""), "Spanish (Peru)"); - TestJavaLocale(cultures, new Locale("es", "PR", ""), "Spanish (Puerto Rico)"); - TestJavaLocale(cultures, new Locale("es", "PY", ""), "Spanish (Paraguay)"); - TestJavaLocale(cultures, new Locale("es", "SV", ""), "Spanish (El Salvador)"); - TestJavaLocale(cultures, new Locale("es", "UY", ""), "Spanish (Uruguay)"); - TestJavaLocale(cultures, new Locale("es", "VE", ""), "Spanish (Venezuela)"); - TestJavaLocale(cultures, new Locale("et", "EE", ""), "Estonian (Estonia)"); - TestJavaLocale(cultures, new Locale("fi", "FI", ""), "Finnish (Finland)"); - TestJavaLocale(cultures, new Locale("fr", "BE", ""), "French (Belgium)"); - TestJavaLocale(cultures, new Locale("fr", "CA", ""), "French (Canada)"); - TestJavaLocale(cultures, new Locale("fr", "CH", ""), "French (Switzerland)"); - TestJavaLocale(cultures, new Locale("fr", "FR", ""), "French (France)"); - TestJavaLocale(cultures, new Locale("fr", "LU", ""), "French (Luxembourg)"); - TestJavaLocale(cultures, new Locale("hi", "IN", ""), "Hindi (India)"); - TestJavaLocale(cultures, new Locale("hr", "HR", ""), "Croatian (Croatia)"); - TestJavaLocale(cultures, new Locale("hu", "HU", ""), "Hungarian (Hungary)"); - TestJavaLocale(cultures, new Locale("is", "IS", ""), "Icelandic (Iceland)"); - TestJavaLocale(cultures, new Locale("it", "CH", ""), "Italian (Switzerland)"); - TestJavaLocale(cultures, new Locale("it", "IT", ""), "Italian (Italy)"); - TestJavaLocale(cultures, new Locale("iw", "IL", ""), "Hebrew (Israel)"); - TestJavaLocale(cultures, new Locale("ja", "JP", ""), "Japanese (Japan)"); - TestJavaLocale(cultures, new Locale("ko", "KR", ""), "Korean (South Korea)"); - TestJavaLocale(cultures, new Locale("lt", "LT", ""), "Lithuanian (Lithuania)"); - TestJavaLocale(cultures, new Locale("lv", "LV", ""), "Latvian (Latvia)"); - TestJavaLocale(cultures, new Locale("mk", "MK", ""), "Macedonian (Macedonia)"); - TestJavaLocale(cultures, new Locale("nl", "BE", ""), "Dutch (Belgium)"); - TestJavaLocale(cultures, new Locale("nl", "NL", ""), "Dutch (Netherlands)"); - TestJavaLocale(cultures, new Locale("no", "NO", ""), "Norwegian (Norway)"); - TestJavaLocale(cultures, new Locale("no", "NO", "NY"), "Norwegian (Norway,Nynorsk)"); - TestJavaLocale(cultures, new Locale("pl", "PL", ""), "Polish (Poland)"); - TestJavaLocale(cultures, new Locale("pt", "BR", ""), "Portuguese (Brazil)"); - TestJavaLocale(cultures, new Locale("pt", "PT", ""), "Portuguese (Portugal)"); - TestJavaLocale(cultures, new Locale("ro", "RO", ""), "Romanian (Romania)"); - TestJavaLocale(cultures, new Locale("ru", "RU", ""), "Russian (Russia)"); - TestJavaLocale(cultures, new Locale("sk", "SK", ""), "Slovak (Slovakia)"); - TestJavaLocale(cultures, new Locale("sl", "SI", ""), "Slovenian (Slovenia)"); - TestJavaLocale(cultures, new Locale("sq", "AL", ""), "Albanian (Albania)"); - TestJavaLocale(cultures, new Locale("sr", "BA", ""), - "Serbian (Bosnia and Herzegovina)", - new Locale("sr")); - TestJavaLocale(cultures, new Locale("sr", "CS", ""), - "Serbian (Serbia and Montenegro)", - new Locale("sr")); - TestJavaLocale(cultures, new Locale("sv", "SE", ""), "Swedish (Sweden)"); - TestJavaLocale(cultures, new Locale("th", "TH", ""), "Thai (Thailand)"); - TestJavaLocale(cultures, new Locale("th", "TH", "TH"), - "Thai (Thailand,TH)", - new Locale("th","TH")); - TestJavaLocale(cultures, new Locale("tr", "TR", ""), "Turkish (Turkey)"); - TestJavaLocale(cultures, new Locale("uk", "UA", ""), "Ukrainian (Ukraine)"); - TestJavaLocale(cultures, new Locale("vi", "VN", ""), "Vietnamese (Vietnam)"); - TestJavaLocale(cultures, new Locale("zh", "CN", ""), "Chinese (China)"); - TestJavaLocale(cultures, new Locale("zh", "HK", ""), "Chinese (Hong Kong)"); - TestJavaLocale(cultures, new Locale("zh", "TW", ""), "Chinese (Taiwan)"); - - //foreach (CultureInfo info in cultures) { - // Console.WriteLine("remaining: "+info+" "+info.DisplayName+" "+info.TwoLetterISOLanguageName); - //} - } - private void TestJavaLocale(HashSet cultures, - Locale original, - String display) { - TestJavaLocale(cultures,original,display,null); - } - private void TestJavaLocale(HashSet cultures, - Locale original, - String display, - Locale expected) { - if ( expected == null ) { - expected = original; - } - CultureInfo cinfo = original.ToCultureInfo(); - Locale actual = Locale.FindLocale(cinfo); - Assert.AreEqual(expected,actual,display+" ("+original+") "+" didn't map"); - } - } -} diff --git a/DotNetConnectors.sln/FrameworkTests/ObjectNormalizerFacadeTests.cs b/DotNetConnectors.sln/FrameworkTests/ObjectNormalizerFacadeTests.cs deleted file mode 100644 index 475f770e..00000000 --- a/DotNetConnectors.sln/FrameworkTests/ObjectNormalizerFacadeTests.cs +++ /dev/null @@ -1,246 +0,0 @@ -/* - * ==================== - * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER. - * - * Copyright 2007-2008 Sun Microsystems, Inc. All rights reserved. - * - * The contents of this file are subject to the terms of the Common Development - * and Distribution License("CDDL") (the "License"). You may not use this file - * except in compliance with the License. - * - * You can obtain a copy of the License at - * http://IdentityConnectors.dev.java.net/legal/license.txt - * See the License for the specific language governing permissions and limitations - * under the License. - * - * When distributing the Covered Code, include this CDDL Header Notice in each file - * and include the License file at identityconnectors/legal/license.txt. - * If applicable, add the following below this CDDL Header, with the fields - * enclosed by brackets [] replaced by your own identifying information: - * "Portions Copyrighted [year] [name of copyright owner]" - * ==================== - */ -using System; -using NUnit.Framework; -using NUnit.Framework.SyntaxHelpers; -using Org.IdentityConnectors.Framework.Common.Objects; -using Org.IdentityConnectors.Framework.Common.Objects.Filters; -using Org.IdentityConnectors.Framework.Common.Serializer; -using Org.IdentityConnectors.Framework.Spi; -using Org.IdentityConnectors.Framework.Impl.Api.Local.Operations; -namespace FrameworkTests -{ - [TestFixture] - public class ObjectNormalizerFacadeTests - { - public class MyAttributeNormalizer : AttributeNormalizer { - public ConnectorAttribute NormalizeAttribute(ObjectClass oclass, ConnectorAttribute attribute) { - if ( attribute.Is("foo")) { - String val = ConnectorAttributeUtil.GetStringValue(attribute); - return ConnectorAttributeBuilder.Build("foo",val.Trim()); - } - else { - return attribute; - } - } - } - - private ConnectorAttribute CreateTestAttribute() - { - return ConnectorAttributeBuilder.Build("foo"," bar "); - } - - private ConnectorAttribute CreateNormalizedTestAttribute() - { - return ConnectorAttributeBuilder.Build("foo","bar"); - } - - private ObjectNormalizerFacade CreateTestNormalizer() - { - ObjectNormalizerFacade facade = new - ObjectNormalizerFacade(ObjectClass.ACCOUNT, - new MyAttributeNormalizer()); - return facade; - } - - private void AssertNormalizedFilter(Filter expectedNormalizedFilter, - Filter filter) { - ObjectNormalizerFacade facade = - CreateTestNormalizer(); - filter = facade.NormalizeFilter(filter); - String expectedXml = SerializerUtil.SerializeXmlObject(expectedNormalizedFilter, false); - String actualXml = SerializerUtil.SerializeXmlObject(filter, false); - Assert.AreEqual(expectedXml, actualXml); - } - - - - [Test] - public void TestEndsWith() - { - Filter expected = - FilterBuilder.EndsWith(CreateNormalizedTestAttribute()); - Filter filter = - FilterBuilder.EndsWith(CreateTestAttribute()); - AssertNormalizedFilter(expected,filter); - } - [Test] - public void TestStartsWith() - { - Filter expected = - FilterBuilder.StartsWith(CreateNormalizedTestAttribute()); - Filter filter = - FilterBuilder.StartsWith(CreateTestAttribute()); - AssertNormalizedFilter(expected,filter); - } - [Test] - public void TestContains() - { - Filter expected = - FilterBuilder.Contains(CreateNormalizedTestAttribute()); - Filter filter = - FilterBuilder.Contains(CreateTestAttribute()); - AssertNormalizedFilter(expected,filter); - } - [Test] - public void TestEqualTo() - { - Filter expected = - FilterBuilder.EqualTo(CreateNormalizedTestAttribute()); - Filter filter = - FilterBuilder.EqualTo(CreateTestAttribute()); - AssertNormalizedFilter(expected,filter); - } - [Test] - public void TestGreaterThanOrEqualTo() - { - Filter expected = - FilterBuilder.GreaterThanOrEqualTo(CreateNormalizedTestAttribute()); - Filter filter = - FilterBuilder.GreaterThanOrEqualTo(CreateTestAttribute()); - AssertNormalizedFilter(expected,filter); - } - [Test] - public void TestLessThanOrEqualTo() - { - Filter expected = - FilterBuilder.LessThanOrEqualTo(CreateNormalizedTestAttribute()); - Filter filter = - FilterBuilder.LessThanOrEqualTo(CreateTestAttribute()); - AssertNormalizedFilter(expected,filter); - } - [Test] - public void TestLessThan() - { - Filter expected = - FilterBuilder.LessThan(CreateNormalizedTestAttribute()); - Filter filter = - FilterBuilder.LessThan(CreateTestAttribute()); - AssertNormalizedFilter(expected,filter); - } - [Test] - public void TestGreaterThan() - { - Filter expected = - FilterBuilder.GreaterThan(CreateNormalizedTestAttribute()); - Filter filter = - FilterBuilder.GreaterThan(CreateTestAttribute()); - AssertNormalizedFilter(expected,filter); - } - [Test] - public void TestAnd() - { - Filter expected = - FilterBuilder.And(FilterBuilder.Contains(CreateNormalizedTestAttribute()), - FilterBuilder.Contains(CreateNormalizedTestAttribute())); - Filter filter = - FilterBuilder.And(FilterBuilder.Contains(CreateTestAttribute()), - FilterBuilder.Contains(CreateTestAttribute())); - AssertNormalizedFilter(expected,filter); - } - [Test] - public void TestOr() - { - Filter expected = - FilterBuilder.Or(FilterBuilder.Contains(CreateNormalizedTestAttribute()), - FilterBuilder.Contains(CreateNormalizedTestAttribute())); - Filter filter = - FilterBuilder.Or(FilterBuilder.Contains(CreateTestAttribute()), - FilterBuilder.Contains(CreateTestAttribute())); - AssertNormalizedFilter(expected,filter); - } - [Test] - public void TestNot() - { - Filter expected = - FilterBuilder.Not(FilterBuilder.Contains(CreateNormalizedTestAttribute())); - Filter filter = - FilterBuilder.Not(FilterBuilder.Contains(CreateTestAttribute())); - AssertNormalizedFilter(expected,filter); - } - [Test] - public void TestContainsAllValues() - { - Filter expected = - FilterBuilder.ContainsAllValues(CreateNormalizedTestAttribute()); - Filter filter = - FilterBuilder.ContainsAllValues(CreateTestAttribute()); - AssertNormalizedFilter(expected,filter); - } - [Test] - public void TestConnectorObject() - { - ConnectorObjectBuilder builder = - new ConnectorObjectBuilder(); - builder.SetName("myname"); - builder.SetUid("myuid"); - builder.AddAttribute(CreateTestAttribute()); - ConnectorObject v1 = builder.Build(); - ConnectorObject v2 = CreateTestNormalizer().NormalizeObject(v1); - builder = - new ConnectorObjectBuilder(); - builder.SetName("myname"); - builder.SetUid("myuid"); - builder.AddAttribute(CreateNormalizedTestAttribute()); - ConnectorObject expected = builder.Build(); - Assert.AreEqual(expected, v2); - Assert.IsFalse(expected.Equals(v1)); - } - - [Test] - public void TestSyncDelta() - { - ConnectorObjectBuilder objbuilder = - new ConnectorObjectBuilder(); - objbuilder.SetName("myname"); - objbuilder.SetUid("myuid"); - objbuilder.AddAttribute(CreateTestAttribute()); - ConnectorObject obj = objbuilder.Build(); - - SyncDeltaBuilder builder = - new SyncDeltaBuilder(); - builder.DeltaType=(SyncDeltaType.DELETE); - builder.Token=(new SyncToken("mytoken")); - builder.Uid=(new Uid("myuid")); - builder.Object=(obj); - SyncDelta v1 = builder.Build(); - SyncDelta v2 = CreateTestNormalizer().NormalizeSyncDelta(v1); - builder = - new SyncDeltaBuilder(); - builder.DeltaType=(SyncDeltaType.DELETE); - builder.Token=(new SyncToken("mytoken")); - builder.Uid=(new Uid("myuid")); - objbuilder = - new ConnectorObjectBuilder(); - objbuilder.SetName("myname"); - objbuilder.SetUid("myuid"); - objbuilder.AddAttribute(CreateNormalizedTestAttribute()); - builder.Object = objbuilder.Build(); - SyncDelta expected = builder.Build(); - Assert.AreEqual(expected, v2); - Assert.IsFalse(expected.Equals(v1)); - - } - - } -} diff --git a/DotNetConnectors.sln/FrameworkTests/ObjectPoolTests.cs b/DotNetConnectors.sln/FrameworkTests/ObjectPoolTests.cs deleted file mode 100644 index 3c09f8ec..00000000 --- a/DotNetConnectors.sln/FrameworkTests/ObjectPoolTests.cs +++ /dev/null @@ -1,252 +0,0 @@ -/* - * ==================== - * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER. - * - * Copyright 2007-2008 Sun Microsystems, Inc. All rights reserved. - * - * The contents of this file are subject to the terms of the Common Development - * and Distribution License("CDDL") (the "License"). You may not use this file - * except in compliance with the License. - * - * You can obtain a copy of the License at - * http://IdentityConnectors.dev.java.net/legal/license.txt - * See the License for the specific language governing permissions and limitations - * under the License. - * - * When distributing the Covered Code, include this CDDL Header Notice in each file - * and include the License file at identityconnectors/legal/license.txt. - * If applicable, add the following below this CDDL Header, with the fields - * enclosed by brackets [] replaced by your own identifying information: - * "Portions Copyrighted [year] [name of copyright owner]" - * ==================== - */ -using System; -using System.Threading; -using NUnit.Framework; -using NUnit.Framework.SyntaxHelpers; - -using Org.IdentityConnectors.Common.Pooling; -using Org.IdentityConnectors.Framework.Common.Exceptions; -using Org.IdentityConnectors.Framework.Common.Objects; -using Org.IdentityConnectors.Framework.Spi; -using Org.IdentityConnectors.Framework.Impl.Api.Local; - -namespace FrameworkTests -{ - [TestFixture] - public class ObjectPoolTests - { - private class MyTestConnection { - private bool _isGood = true; - - public void Test() { - if (!_isGood) { - throw new ConnectorException("Connection is bad"); - } - } - - public void Dispose() { - _isGood = false; - } - - public bool IsGood { - get { - return _isGood; - } - } - } - - private class MyTestConnectionFactory : ObjectPoolHandler { - - private bool _createBadConnection = false; - private int _totalCreatedConnections = 0; - - public MyTestConnection NewObject() { - _totalCreatedConnections++; - MyTestConnection rv = new MyTestConnection(); - if (_createBadConnection) { - rv.Dispose(); - } - return rv; - } - public void TestObject(MyTestConnection obj) { - obj.Test(); - } - public void DisposeObject(MyTestConnection obj) { - obj.Dispose(); - } - - public int TotalCreatedConnections { - get { - return _totalCreatedConnections; - } - } - - public bool CreateBadConnection { - set { - _createBadConnection = value; - } - } - } - - private class MyTestThread { - private readonly ObjectPool _pool; - private readonly int _numIterations; - private Exception _exception; - private Thread _thisThread; - public MyTestThread(ObjectPool pool, - int numIterations) { - _pool = pool; - _numIterations = numIterations; - } - public void Run(object o) { - _thisThread = (Thread)o; - try { - for ( int i = 0; i < _numIterations; i++ ) { - MyTestConnection con = - _pool.BorrowObject(); - Thread.Sleep(300); - _pool.ReturnObject(con); - } - } - catch (Exception e) { - _exception = e; - } - } - public void Shutdown() { - _thisThread.Join(); - if (_exception != null) { - throw _exception; - } - } - - } - - [Test] - public void TestWithManyThreads() - { - int NUM_ITERATIONS = 10; - int NUM_THREADS = 10; - int MAX_CONNECTIONS = NUM_THREADS-3; //make sure we get some waiting - ObjectPoolConfiguration config = new ObjectPoolConfiguration(); - config.MaxObjects=(MAX_CONNECTIONS); - config.MaxIdle=(MAX_CONNECTIONS); - config.MinIdle=(MAX_CONNECTIONS); - config.MinEvictableIdleTimeMillis=(60*1000); - config.MaxWait=(60*1000); - MyTestConnectionFactory fact = new MyTestConnectionFactory(); - - ObjectPool pool = new ObjectPool(fact,config); - - MyTestThread [] threads = new MyTestThread[NUM_THREADS]; - for (int i = 0; i < threads.Length; i++) { - threads[i] = new MyTestThread(pool,NUM_ITERATIONS); - Thread thread = new Thread(threads[i].Run); - thread.Start(thread); - } - - foreach (MyTestThread thread in threads) { - thread.Shutdown(); - } - - //these should be the same since we never - //should have disposed anything - Assert.AreEqual(MAX_CONNECTIONS, fact.TotalCreatedConnections); - ObjectPool.Statistics stats = pool.GetStatistics(); - Assert.AreEqual(0, stats.NumActive); - Assert.AreEqual(MAX_CONNECTIONS, stats.NumIdle); - - pool.Shutdown(); - stats = pool.GetStatistics(); - Assert.AreEqual(0, stats.NumActive); - Assert.AreEqual(0, stats.NumIdle); - - } - - [Test] - public void TestBadConnection() - { - int MAX_CONNECTIONS = 3; - ObjectPoolConfiguration config = new ObjectPoolConfiguration(); - config.MaxObjects=(MAX_CONNECTIONS); - config.MaxIdle=(MAX_CONNECTIONS); - config.MinIdle=(MAX_CONNECTIONS); - config.MinEvictableIdleTimeMillis=(60*1000); - config.MaxWait=(60*1000); - MyTestConnectionFactory fact = new MyTestConnectionFactory(); - - ObjectPool pool = new ObjectPool(fact,config); - - //borrow first connection and return - MyTestConnection conn = pool.BorrowObject(); - Assert.AreEqual(1, fact.TotalCreatedConnections); - pool.ReturnObject(conn); - Assert.AreEqual(1, fact.TotalCreatedConnections); - - //re-borrow same connection and return - conn = pool.BorrowObject(); - Assert.AreEqual(1, fact.TotalCreatedConnections); - pool.ReturnObject(conn); - Assert.AreEqual(1, fact.TotalCreatedConnections); - - //dispose and make sure we get a new connection - conn.Dispose(); - conn = pool.BorrowObject(); - Assert.AreEqual(2, fact.TotalCreatedConnections); - pool.ReturnObject(conn); - Assert.AreEqual(2, fact.TotalCreatedConnections); - } - - [Test] - public void TestIdleCleanup() - { - ObjectPoolConfiguration config = new ObjectPoolConfiguration(); - config.MaxObjects=(3); - config.MaxIdle=(2); - config.MinIdle=(1); - config.MinEvictableIdleTimeMillis=(3000); - config.MaxWait=(60*1000); - MyTestConnectionFactory fact = new MyTestConnectionFactory(); - - ObjectPool pool = new ObjectPool(fact,config); - - MyTestConnection conn1 = (MyTestConnection)pool.BorrowObject(); - MyTestConnection conn2 = (MyTestConnection)pool.BorrowObject(); - MyTestConnection conn3 = (MyTestConnection)pool.BorrowObject(); - - Assert.AreEqual(3, fact.TotalCreatedConnections); - pool.ReturnObject(conn1); - Assert.AreEqual(1, pool.GetStatistics().NumIdle); - pool.ReturnObject(conn2); - Assert.AreEqual(2, pool.GetStatistics().NumIdle); - pool.ReturnObject(conn3); - Assert.AreEqual(2, pool.GetStatistics().NumIdle); - Assert.AreEqual(false, conn1.IsGood); - Assert.AreEqual(true, conn2.IsGood); - Assert.AreEqual(true, conn3.IsGood); - Thread.Sleep(((int)(config.MinEvictableIdleTimeMillis+1000))); - MyTestConnection conn4 = (MyTestConnection)pool.BorrowObject(); - Assert.AreSame(conn3, conn4); - Assert.AreEqual(false, conn1.IsGood); - Assert.AreEqual(false, conn2.IsGood); - Assert.AreEqual(true, conn3.IsGood); - Assert.AreEqual(true, conn4.IsGood); - } - - [Test] - public void TestCreateBadConnection() - { - MyTestConnectionFactory fact = new MyTestConnectionFactory(); - fact.CreateBadConnection=(true); - - ObjectPool pool = new ObjectPool(fact,new ObjectPoolConfiguration()); - try { - pool.BorrowObject(); - Assert.Fail("expected exception"); - } - catch (ConnectorException e) { - Assert.AreEqual("Connection is bad", e.Message); - } - } - } -} diff --git a/DotNetConnectors.sln/FrameworkTests/ObjectSerializationTests.cs b/DotNetConnectors.sln/FrameworkTests/ObjectSerializationTests.cs deleted file mode 100644 index 637d68f2..00000000 --- a/DotNetConnectors.sln/FrameworkTests/ObjectSerializationTests.cs +++ /dev/null @@ -1,1128 +0,0 @@ -/* - * ==================== - * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER. - * - * Copyright 2007-2008 Sun Microsystems, Inc. All rights reserved. - * - * The contents of this file are subject to the terms of the Common Development - * and Distribution License("CDDL") (the "License"). You may not use this file - * except in compliance with the License. - * - * You can obtain a copy of the License at - * http://IdentityConnectors.dev.java.net/legal/license.txt - * See the License for the specific language governing permissions and limitations - * under the License. - * - * When distributing the Covered Code, include this CDDL Header Notice in each file - * and include the License file at identityconnectors/legal/license.txt. - * If applicable, add the following below this CDDL Header, with the fields - * enclosed by brackets [] replaced by your own identifying information: - * "Portions Copyrighted [year] [name of copyright owner]" - * ==================== - */ -using System; -using System.IO; -using System.Text; -using NUnit.Framework; -using NUnit.Framework.SyntaxHelpers; -using System.Collections.Generic; -using System.Globalization; -using System.Security; -using System.Linq; -using System.Xml; -using Org.IdentityConnectors.Framework.Impl.Serializer.Binary; -using Org.IdentityConnectors.Common; -using Org.IdentityConnectors.Common.Pooling; -using Org.IdentityConnectors.Common.Security; -using Org.IdentityConnectors.Framework.Api; -using Org.IdentityConnectors.Framework.Api.Operations; -using Org.IdentityConnectors.Framework.Common; -using Org.IdentityConnectors.Framework.Common.Exceptions; -using Org.IdentityConnectors.Framework.Common.Objects; -using Org.IdentityConnectors.Framework.Common.Objects.Filters; -using Org.IdentityConnectors.Framework.Common.Serializer; -using Org.IdentityConnectors.Framework.Impl.Api; -using Org.IdentityConnectors.Framework.Impl.Api.Remote; -using Org.IdentityConnectors.Framework.Impl.Api.Remote.Messages; -using Org.IdentityConnectors.Framework.Impl.Serializer.Xml; -namespace FrameworkTests -{ - [TestFixture] - public class ObjectSerializationTests - { - [Test] - public void TestBoolean() - { - bool v1 = true; - bool v2 = (bool)CloneObject(v1); - Assert.AreEqual(v1, v2); - - v1 = false; - v2 = (bool)CloneObject(v1); - Assert.AreEqual(v1, v2); - } - [Test] - public void TestCharacter() { - char v1 = 'h'; - char v2 = (char)CloneObject(v1); - Assert.AreEqual(v1, v2); - } - [Test] - public void TestInteger() { - int v1 = 12345; - int v2 = (int)CloneObject(v1); - Assert.AreEqual(v1,v2); - - v1 = Int32.MinValue; - v2 = (int)CloneObject(v1); - Assert.AreEqual(v1,v2); - - v1 = Int32.MaxValue; - v2 = (int)CloneObject(v1); - Assert.AreEqual(v1,v2); - - v1 = -1; - v2 = (int)CloneObject(v1); - Assert.AreEqual(v1,v2); - } - - [Test] - public void TestLong() { - long v1 = 12345; - long v2 = (long)CloneObject(v1); - Assert.AreEqual(v1,v2); - - v1 = Int64.MinValue; - v2 = (long)CloneObject(v1); - Assert.AreEqual(v1,v2); - - v1 = Int64.MaxValue; - v2 = (long)CloneObject(v1); - Assert.AreEqual(v1,v2); - - v1 = -1; - v2 = (long)CloneObject(v1); - Assert.AreEqual(v1,v2); - } - - [Test] - public void TestFloat() { - float v1 = 1.1F; - float v2 = (float)CloneObject(v1); - Assert.IsTrue(!Object.ReferenceEquals(v1,v2)); - Assert.AreEqual(v1,v2); - - v1 = Single.Epsilon; - v2 = (float)CloneObject(v1); - Assert.AreEqual(v1,v2); - - v1 = Single.NaN; - v2 = (float)CloneObject(v1); - Assert.AreEqual(v1,v2); - - v1 = Single.NegativeInfinity; - v2 = (float)CloneObject(v1); - Assert.AreEqual(v1,v2); - - v1 = Single.PositiveInfinity; - v2 = (float)CloneObject(v1); - Assert.AreEqual(v1,v2); - - v1 = Single.MinValue; - v2 = (float)CloneObject(v1); - Assert.AreEqual(v1,v2); - - v1 = Single.MaxValue; - v2 = (float)CloneObject(v1); - Assert.AreEqual(v1,v2); - } - - [Test] - public void TestDouble() { - double v1 = 1.1; - double v2 = (double)CloneObject(v1); - Assert.IsTrue(!Object.ReferenceEquals(v1,v2)); - Assert.AreEqual(v1,v2); - - v1 = Double.Epsilon; - v2 = (double)CloneObject(v1); - Assert.AreEqual(v1,v2); - - v1 = Double.NaN; - v2 = (double)CloneObject(v1); - Assert.AreEqual(v1,v2); - - v1 = Double.NegativeInfinity; - v2 = (double)CloneObject(v1); - Assert.AreEqual(v1,v2); - - v1 = Double.PositiveInfinity; - v2 = (double)CloneObject(v1); - Assert.AreEqual(v1,v2); - - v1 = Double.MaxValue; - v2 = (double)CloneObject(v1); - Assert.AreEqual(v1,v2); - - v1 = Double.MinValue; - v2 = (double)CloneObject(v1); - Assert.AreEqual(v1,v2); - } - - [Test] - public void TestString() { - string v1 = "abcd"; - string v2 = (string)CloneObject(v1); - Assert.IsTrue(!Object.ReferenceEquals(v1,v2)); - Assert.AreEqual(v1,v2); - } - - [Test] - public void TestURI() { - Uri v1 = new Uri("mailto:java-net@java.sun.com"); - Uri v2 = (Uri)CloneObject(v1); - Assert.IsTrue(!Object.ReferenceEquals(v1,v2)); - Assert.AreEqual(v1, v2); - } - - [Test] - public void TestFile() { - FileName v1 = new FileName("c:/foo.txt"); - FileName v2 = (FileName)CloneObject(v1); - Assert.IsTrue(!Object.ReferenceEquals(v1,v2)); - Assert.AreEqual(v1, v2); - } - - [Test] - public void TestBigInteger() { - BigInteger v1 = new BigInteger("983423442347324324324"); - BigInteger v2 = (BigInteger)CloneObject(v1); - Assert.AreEqual(v1, v2); - } - - [Test] - public void TestBigDecimal() { - BigDecimal v1 = new BigDecimal(new BigInteger("9847324324324"),45); - BigDecimal v2 = (BigDecimal)CloneObject(v1); - Assert.AreEqual(v1, v2); - } - - [Test] - public void TestByteArray() { - byte [] v1 = {1,2,3}; - byte [] v2 = (byte[])CloneObject(v1); - Assert.AreEqual(3,v2.Length); - Assert.AreEqual(1,v2[0]); - Assert.AreEqual(2,v2[1]); - Assert.AreEqual(3,v2[2]); - Assert.AreEqual(v1,v2); - } - - [Test] - public void TestClasses() { - Assert.AreEqual(typeof(bool), - CloneObject(typeof(bool))); - Assert.AreEqual(typeof(bool?), - CloneObject(typeof(bool?))); - Assert.AreEqual(typeof(bool[][]), - CloneObject(typeof(bool[][]))); - Assert.AreEqual(typeof(bool?[][]), - CloneObject(typeof(bool?[][]))); - Assert.AreEqual(typeof(char), - CloneObject(typeof(char))); - Assert.AreEqual(typeof(char?), - CloneObject(typeof(char?))); - //if this fails, we have added a new type and we need to add - //a serializer for it - Assert.IsTrue( - CollectionUtil.SetsEqual( - CollectionUtil.NewSet(FrameworkUtil.GetAllSupportedConfigTypes()), - (ICollection)CloneObject(FrameworkUtil.GetAllSupportedConfigTypes()))); - //if this fails, we have added a new type and we need to add - //a serializer for it - Assert.IsTrue( - CollectionUtil.SetsEqual( - CollectionUtil.NewSet(FrameworkUtil.GetAllSupportedAttributeTypes()), - (ICollection)CloneObject(FrameworkUtil.GetAllSupportedAttributeTypes()))); - ICollection apiOperations = - new HashSet(); - foreach (SafeType op in FrameworkUtil.AllAPIOperations()) { - apiOperations.Add(op.RawType); - } - //if this fails, need to add to the OperationMappings class - Assert.IsTrue( - CollectionUtil.SetsEqual( - CollectionUtil.NewSet(apiOperations), - (ICollection)CloneObject(apiOperations))); - - } - - [Test] - public void TestArrays() { - int [] v1 = {1,2,3}; - int [] v2 = (int[])CloneObject(v1); - Assert.AreEqual(3, v2.Length); - Assert.AreEqual(1,v2[0]); - Assert.AreEqual(2,v2[1]); - Assert.AreEqual(3,v2[2]); - } - - - [Test] - public void TestObjectArrays() { - object [] v1 = {"1","2","3"}; - object [] v2 = (object[])CloneObject(v1); - Assert.AreEqual(3, v2.Length); - Assert.AreEqual("1",v2[0]); - Assert.AreEqual("2",v2[1]); - Assert.AreEqual("3",v2[2]); - } - - [Test] - public void TestDictionary() { - IDictionary map = new Dictionary(); - map["key1"] = "val1"; - map["key2"] = "val2"; - IDictionary map2 = (IDictionary)CloneObject(map); - Assert.AreEqual("val1",map["key1"]); - Assert.AreEqual("val2",map["key2"]); - - IDictionary map3 = new Dictionary(); - map3["key1"] = "val1"; - map3["key2"] = "val2"; - IDictionary map4 = (IDictionary)CloneObject(map3); - Assert.AreEqual("val1",map4["key1"]); - Assert.AreEqual("val2",map4["key2"]); - } - [Test] - public void TestCaseInsensitiveMap() { - HashSet set = new HashSet(); - set.Add(ConnectorAttributeBuilder.Build("foo1")); - set.Add(ConnectorAttributeBuilder.Build("foo2")); - IDictionary map = ConnectorAttributeUtil.ToMap(set); - Assert.IsTrue(map.ContainsKey("Foo1")); - Assert.IsTrue(map.ContainsKey("Foo2")); - IDictionary map2 = (IDictionary)CloneObject(map); - Assert.IsTrue(map2.ContainsKey("Foo1")); - Assert.IsTrue(map2.ContainsKey("Foo2")); - } - - [Test] - public void TestList() { - IList v1 = new List(); - v1.Add("val1"); - v1.Add("val2"); - IList v2 = (IList)CloneObject(v1); - Assert.AreEqual(2,v2.Count); - Assert.AreEqual("val1",v2[0]); - Assert.AreEqual("val2",v2[1]); - - IList v3 = new List(); - v3.Add("val1"); - v3.Add("val2"); - - IList v4 = (IList)CloneObject(v3); - Assert.AreEqual(2,v4.Count); - Assert.AreEqual("val1",v4[0]); - Assert.AreEqual("val2",v4[1]); - } - - [Test] - public void TestSet() { - //underneath the covers, this creates an - //ICollection - we need to test that ICollection - //becomes a Set - ICollection v1 = - CollectionUtil.NewReadOnlySet("val1","val2"); - HashSet v2 = (HashSet)CloneObject(v1); - Assert.AreEqual(2,v2.Count); - Assert.IsTrue(v2.Contains("val1")); - Assert.IsTrue(v2.Contains("val2")); - - HashSet v3 = new HashSet(); - v3.Add("val1"); - v3.Add("val2"); - - HashSet v4 = (HashSet)CloneObject(v3); - Assert.AreEqual(2,v4.Count); - Assert.IsTrue(v4.Contains("val1")); - Assert.IsTrue(v4.Contains("val2")); - } - - [Test] - public void TestCaseInsensitiveSet() { - ICollection v1 = CollectionUtil.NewCaseInsensitiveSet(); - v1.Add("foo"); - v1.Add("foo2"); - ICollection v2 = (ICollection)CloneObject(v1); - Assert.IsTrue(v2.Contains("Foo")); - Assert.IsTrue(v2.Contains("Foo2")); - } - - [Test] - public void TestCultureInfo() { - //use this one since it uses all 3 fields of locale - CultureInfo v1 = new CultureInfo("nn-NO"); - CultureInfo v2 = (CultureInfo)CloneObject(v1); - Assert.AreEqual(v1,v2); - } - - [Test] - public void TestObjectPoolConfiguration() { - ObjectPoolConfiguration v1 = new ObjectPoolConfiguration(); - v1.MaxObjects = 1; - v1.MaxIdle = 2; - v1.MaxWait = 3; - v1.MinEvictableIdleTimeMillis = 4; - v1.MinIdle = 5; - ObjectPoolConfiguration v2 = - (ObjectPoolConfiguration)CloneObject(v1); - Assert.IsTrue(!Object.ReferenceEquals(v1,v2)); - - Assert.AreEqual(v1,v2); - - Assert.AreEqual(1, v2.MaxObjects); - Assert.AreEqual(2, v2.MaxIdle); - Assert.AreEqual(3, v2.MaxWait); - Assert.AreEqual(4, v2.MinEvictableIdleTimeMillis); - Assert.AreEqual(5, v2.MinIdle); - } - [Test] - public void TestConfigurationProperty() { - ConfigurationPropertyImpl v1 = new ConfigurationPropertyImpl(); - v1.Order = (1); - v1.IsConfidential=(true); - v1.IsRequired=true; - v1.Name=("foo"); - v1.HelpMessageKey=("help key"); - v1.DisplayMessageKey=("display key"); - v1.Value=("bar"); - v1.ValueType=(typeof(string)); - v1.Operations = FrameworkUtil.AllAPIOperations(); - - ConfigurationPropertyImpl v2 = (ConfigurationPropertyImpl) - CloneObject(v1); - Assert.AreEqual(1, v2.Order); - Assert.IsTrue(v2.IsConfidential); - Assert.IsTrue(v2.IsRequired); - Assert.AreEqual("foo", v2.Name); - Assert.AreEqual("help key", v2.HelpMessageKey); - Assert.AreEqual("display key", v2.DisplayMessageKey); - Assert.AreEqual("bar", v2.Value); - Assert.AreEqual(typeof(string), v2.ValueType); - Assert.IsTrue(CollectionUtil.Equals( - FrameworkUtil.AllAPIOperations(),v2.Operations)); - } - - [Test] - public void TestConfigurationProperties() { - ConfigurationPropertyImpl prop1 = new ConfigurationPropertyImpl(); - prop1.Order = (1); - prop1.IsConfidential=(true); - prop1.Name=("foo"); - prop1.HelpMessageKey=("help key"); - prop1.DisplayMessageKey=("display key"); - prop1.Value=("bar"); - prop1.ValueType=(typeof(string)); - prop1.Operations=null; - - ConfigurationPropertiesImpl v1 = new ConfigurationPropertiesImpl(); - v1.Properties=(CollectionUtil.NewReadOnlyList(prop1)); - v1.SetPropertyValue("foo", "bar"); - - ConfigurationPropertiesImpl v2 = (ConfigurationPropertiesImpl) - CloneObject(v1); - Assert.AreEqual("bar", v2.GetProperty("foo").Value); - } - - [Test] - public void TestAPIConfiguration() { - ConfigurationPropertyImpl prop1 = new ConfigurationPropertyImpl(); - prop1.Order = (1); - prop1.IsConfidential=(true); - prop1.Name=("foo"); - prop1.HelpMessageKey=("help key"); - prop1.DisplayMessageKey=("display key"); - prop1.Value=("bar"); - prop1.ValueType=(typeof(string)); - prop1.Operations=null; - - ConfigurationPropertiesImpl props1 = new ConfigurationPropertiesImpl(); - props1.Properties=(CollectionUtil.NewReadOnlyList(prop1)); - - APIConfigurationImpl v1 = new APIConfigurationImpl(); - v1.ConnectorPoolConfiguration=(new ObjectPoolConfiguration()); - v1.ConfigurationProperties=(props1); - v1.IsConnectorPoolingSupported=(true); - v1.ProducerBufferSize=(200); - v1.SupportedOperations=(FrameworkUtil.AllAPIOperations()); - IDictionary,int> map = - CollectionUtil.NewDictionary,int>(SafeType.Get(),6); - v1.TimeoutMap=(map); - - APIConfigurationImpl v2 = (APIConfigurationImpl) - CloneObject(v1); - Assert.IsTrue(!Object.ReferenceEquals(v1, v2)); - Assert.IsNotNull(v2.ConnectorPoolConfiguration); - Assert.IsNotNull(v2.ConfigurationProperties); - Assert.AreEqual(v1.ConnectorPoolConfiguration,v2.ConnectorPoolConfiguration); - Assert.AreEqual(v1.ConfigurationProperties,v2.ConfigurationProperties); - Assert.IsTrue(v2.IsConnectorPoolingSupported); - Assert.AreEqual(200, v2.ProducerBufferSize); - Assert.IsTrue(CollectionUtil.SetsEqual( - FrameworkUtil.AllAPIOperations(), - v2.SupportedOperations)); - Assert.AreEqual(map, v2.TimeoutMap); - } - - [Test] - public void TestConnectorMessages() { - ConnectorMessagesImpl v1 = new ConnectorMessagesImpl(); - IDictionary defaultMap = new Dictionary(); - defaultMap["key1"]="val1"; - IDictionary> messages = - new Dictionary>(); - messages[new CultureInfo("en")]=defaultMap; - messages[new CultureInfo("")]=defaultMap; - v1.Catalogs=(messages); - - ConnectorMessagesImpl v2 = (ConnectorMessagesImpl) - CloneObject(v1); - Assert.IsTrue( - CollectionUtil.SetsEqual(messages[new CultureInfo("")], - v2.Catalogs[new CultureInfo("")])); - Assert.IsTrue( - CollectionUtil.SetsEqual(messages[new CultureInfo("en")], - v2.Catalogs[new CultureInfo("en")])); - } - - [Test] - public void TestRemoteConnectorInfo() { - RemoteConnectorInfoImpl v1 = new RemoteConnectorInfoImpl(); - v1.Messages=(new ConnectorMessagesImpl()); - v1.ConnectorKey=(new ConnectorKey("my bundle", - "my version", - "my connector")); - ConfigurationPropertiesImpl configProperties = new ConfigurationPropertiesImpl(); - configProperties.Properties=(new List()); - APIConfigurationImpl apiImpl = new APIConfigurationImpl(); - apiImpl.ConfigurationProperties=(configProperties); - v1.DefaultAPIConfiguration=(apiImpl); - v1.ConnectorDisplayNameKey=("mykey"); - - RemoteConnectorInfoImpl v2 = (RemoteConnectorInfoImpl) - CloneObject(v1); - - Assert.IsNotNull(v2.Messages); - Assert.AreEqual("my bundle", v2.ConnectorKey.BundleName); - Assert.AreEqual("my version", v2.ConnectorKey.BundleVersion); - Assert.AreEqual("my connector", v2.ConnectorKey.ConnectorName); - Assert.AreEqual("mykey",v2.ConnectorDisplayNameKey); - Assert.IsNotNull(v2.DefaultAPIConfiguration); - } - - [Test] - public void TestAttribute() { - ConnectorAttribute v1 = ConnectorAttributeBuilder.Build("foo", "val1", "val2"); - ConnectorAttribute v2 = (ConnectorAttribute)CloneObject(v1); - Assert.AreEqual(v1,v2); - } - - [Test] - public void TestAttributeInfo() { - - ConnectorAttributeInfoBuilder builder = new ConnectorAttributeInfoBuilder(); - builder.Name=("foo"); - builder.ValueType=(typeof(String)); - builder.Required=(true); - builder.Readable=(true); - builder.Creatable=(true); - builder.Updateable=(true); - builder.MultiValued=(true); - builder.ReturnedByDefault = false; - ConnectorAttributeInfo v1 = builder.Build(); - ConnectorAttributeInfo v2 = (ConnectorAttributeInfo)CloneObject(v1); - Assert.AreEqual(v1,v2); - Assert.AreEqual("foo", v2.Name); - Assert.AreEqual(typeof(String), v2.ValueType); - Assert.IsTrue(v2.IsMultiValued); - Assert.IsTrue(v2.IsReadable); - Assert.IsTrue(v2.IsRequired); - Assert.IsTrue(v2.IsUpdateable); - Assert.IsTrue(v2.IsCreatable); - Assert.IsFalse(v2.IsReturnedByDefault); - - builder.InfoFlags = AllFlags(); - v1 = builder.Build(); - v2 = (ConnectorAttributeInfo)CloneObject(v1); - Assert.AreEqual(v1,v2); - } - - private ConnectorAttributeInfo.Flags AllFlags() - { - ConnectorAttributeInfo.Flags flags = - ConnectorAttributeInfo.Flags.NONE; - foreach (Enum e in Enum.GetValues(typeof(ConnectorAttributeInfo.Flags))) { - ConnectorAttributeInfo.Flags f = - (ConnectorAttributeInfo.Flags)e; - flags |= f; - } - return flags; - } - - [Test] - public void TestConnectorObject() { - ConnectorObjectBuilder bld = new ConnectorObjectBuilder(); - bld.SetUid("foo"); - bld.SetName("fooToo"); - - ConnectorObject v1 = bld.Build(); - ConnectorObject v2 = (ConnectorObject)CloneObject(v1); - - Assert.AreEqual(v1, v2); - } - - [Test] - public void TestName() { - Name v1 = new Name("Test"); - Name v2 = (Name)CloneObject(v1); - Assert.AreEqual(v1, v2); - } - - [Test] - public void TestObjectClass() { - ObjectClass v1 = new ObjectClass("test"); - ObjectClass v2 = (ObjectClass)CloneObject(v1); - Assert.AreEqual(v1, v2); - } - - [Test] - public void TestObjectClassInfo() { - ConnectorAttributeInfoBuilder builder = new ConnectorAttributeInfoBuilder(); - builder.Name=("foo"); - builder.ValueType=(typeof(String)); - builder.Required=(true); - builder.Readable=(true); - builder.Updateable=(true); - builder.MultiValued=(true); - ObjectClassInfoBuilder obld = new ObjectClassInfoBuilder(); - obld.ObjectType = ObjectClass.ORGANIZATION_NAME; - obld.IsContainer = true; - obld.AddAttributeInfo(builder.Build()); - ObjectClassInfo v1 = obld.Build(); - ObjectClassInfo v2 = (ObjectClassInfo)CloneObject(v1); - Assert.AreEqual(v1, v2); - Assert.IsTrue(v2.IsContainer); - } - - [Test] - public void TestUid() { - Uid v1 = new Uid("test"); - Uid v2 = (Uid)CloneObject(v1); - Assert.AreEqual(v1, v2); - } - - [Test] - public void testOperationOptionInfo() { - OperationOptionInfo v1 = - new OperationOptionInfo("name",typeof(int?)); - OperationOptionInfo v2 = - (OperationOptionInfo)CloneObject(v1); - Assert.AreEqual(v1, v2); - } - - [Test] - public void TestSchema() { - OperationOptionInfo opInfo = - new OperationOptionInfo("name",typeof(int?)); - ObjectClassInfoBuilder bld = new ObjectClassInfoBuilder(); - bld.ObjectType = ObjectClass.ORGANIZATION_NAME; - ObjectClassInfo info = bld.Build(); - ICollection temp = CollectionUtil.NewSet(info); - IDictionary,ICollection> map = - new Dictionary,ICollection>(); - map[SafeType.Get()] = temp; - ICollection temp2 = CollectionUtil.NewSet(opInfo); - IDictionary,ICollection> map2 = - new Dictionary,ICollection>(); - map2[SafeType.Get()] = temp2; - Schema v1 = new Schema(CollectionUtil.NewSet(info), - CollectionUtil.NewSet(opInfo), - map, - map2); - Schema v2 = (Schema)CloneObject(v1); - Assert.AreEqual(v1, v2); - Assert.AreEqual(info, v2.ObjectClassInfo.First()); - Assert.AreEqual(1, v2.SupportedObjectClassesByOperation.Count); - Assert.AreEqual(1, v2.SupportedOptionsByOperation.Count); - Assert.AreEqual(1, v2.OperationOptionInfo.Count); - } - - [Test] - public void TestContainsFilter() { - ContainsFilter v1 = new ContainsFilter(ConnectorAttributeBuilder.Build("foo", "bar")); - ContainsFilter v2 = (ContainsFilter)CloneObject(v1); - Assert.AreEqual(v1.GetAttribute(), v2.GetAttribute()); - } - - [Test] - public void TestAndFilter() { - ContainsFilter left1 = new ContainsFilter(ConnectorAttributeBuilder.Build("foo", "bar")); - ContainsFilter right1 = new ContainsFilter(ConnectorAttributeBuilder.Build("foo2", "bar2")); - AndFilter v1 = new AndFilter(left1,right1); - AndFilter v2 = (AndFilter)CloneObject(v1); - ContainsFilter left2 = (ContainsFilter)v2.Left; - ContainsFilter right2 = (ContainsFilter)v2.Right; - Assert.AreEqual(left1.GetAttribute(),left2.GetAttribute()); - Assert.AreEqual(right1.GetAttribute(),right2.GetAttribute()); - } - - [Test] - public void TestEndsWithFilter() { - EndsWithFilter v1 = new EndsWithFilter(ConnectorAttributeBuilder.Build("foo", "bar")); - EndsWithFilter v2 = (EndsWithFilter)CloneObject(v1); - Assert.AreEqual(v1.GetAttribute(), v2.GetAttribute()); - } - - [Test] - public void TestEqualsFilter() { - EqualsFilter v1 = new EqualsFilter(ConnectorAttributeBuilder.Build("foo", "bar")); - EqualsFilter v2 = (EqualsFilter)CloneObject(v1); - Assert.AreEqual(v1.GetAttribute(), v2.GetAttribute()); - } - - [Test] - public void TestGreaterThanFilter() { - GreaterThanFilter v1 = new GreaterThanFilter(ConnectorAttributeBuilder.Build("foo", "bar")); - GreaterThanFilter v2 = (GreaterThanFilter)CloneObject(v1); - Assert.AreEqual(v1.GetAttribute(), v2.GetAttribute()); - } - - [Test] - public void TestGreaterThanOrEqualFilter() { - GreaterThanOrEqualFilter v1 = new GreaterThanOrEqualFilter(ConnectorAttributeBuilder.Build("foo", "bar")); - GreaterThanOrEqualFilter v2 = (GreaterThanOrEqualFilter)CloneObject(v1); - Assert.AreEqual(v1.GetAttribute(), v2.GetAttribute()); - } - - [Test] - public void TestLessThanFilter() { - LessThanFilter v1 = new LessThanFilter(ConnectorAttributeBuilder.Build("foo", "bar")); - LessThanFilter v2 = (LessThanFilter)CloneObject(v1); - Assert.AreEqual(v1.GetAttribute(), v2.GetAttribute()); - } - - [Test] - public void TestLessThanOrEqualFilter() { - LessThanOrEqualFilter v1 = new LessThanOrEqualFilter(ConnectorAttributeBuilder.Build("foo", "bar")); - LessThanOrEqualFilter v2 = (LessThanOrEqualFilter)CloneObject(v1); - Assert.AreEqual(v1.GetAttribute(), v2.GetAttribute()); - } - - [Test] - public void TestNotFilter() { - ContainsFilter left1 = new ContainsFilter(ConnectorAttributeBuilder.Build("foo", "bar")); - NotFilter v1 = new NotFilter(left1); - NotFilter v2 = (NotFilter)CloneObject(v1); - ContainsFilter left2 = (ContainsFilter)v2.Filter; - Assert.AreEqual(left1.GetAttribute(),left2.GetAttribute()); - } - - [Test] - public void TestOrFilter() { - ContainsFilter left1 = new ContainsFilter(ConnectorAttributeBuilder.Build("foo", "bar")); - ContainsFilter right1 = new ContainsFilter(ConnectorAttributeBuilder.Build("foo2", "bar2")); - OrFilter v1 = new OrFilter(left1,right1); - OrFilter v2 = (OrFilter)CloneObject(v1); - ContainsFilter left2 = (ContainsFilter)v2.Left; - ContainsFilter right2 = (ContainsFilter)v2.Right; - Assert.AreEqual(left1.GetAttribute(),left2.GetAttribute()); - Assert.AreEqual(right1.GetAttribute(),right2.GetAttribute()); - } - - [Test] - public void TestStartsWithFilter() { - StartsWithFilter v1 = new StartsWithFilter(ConnectorAttributeBuilder.Build("foo", "bar")); - StartsWithFilter v2 = (StartsWithFilter)CloneObject(v1); - Assert.AreEqual(v1.GetAttribute(), v2.GetAttribute()); - } - - [Test] - public void TestContainsAllValuesFilter() { - ContainsAllValuesFilter v1 = new ContainsAllValuesFilter(ConnectorAttributeBuilder.Build("foo", "bar", "foo")); - ContainsAllValuesFilter v2 = (ContainsAllValuesFilter)CloneObject(v1); - Assert.AreEqual(v1.GetAttribute(), v2.GetAttribute()); - } - - [Test] - public void TestExceptions() { - { - AlreadyExistsException v1 = new AlreadyExistsException("ex"); - AlreadyExistsException v2 = (AlreadyExistsException)CloneObject(v1); - Assert.AreEqual("ex",v2.Message); - } - - { - ConfigurationException v1 = new ConfigurationException("ex"); - ConfigurationException v2 = (ConfigurationException)CloneObject(v1); - Assert.AreEqual("ex",v2.Message); - } - - { - ConnectionBrokenException v1 = new ConnectionBrokenException("ex"); - ConnectionBrokenException v2 = (ConnectionBrokenException)CloneObject(v1); - Assert.AreEqual("ex",v2.Message); - } - - { - ConnectionFailedException v1 = new ConnectionFailedException("ex"); - ConnectionFailedException v2 = (ConnectionFailedException)CloneObject(v1); - Assert.AreEqual("ex",v2.Message); - } - - { - ConnectorException v1 = new ConnectorException("ex"); - ConnectorException v2 = (ConnectorException)CloneObject(v1); - Assert.AreEqual("ex",v2.Message); - } - { - ConnectorIOException v1 = new ConnectorIOException("ex"); - ConnectorIOException v2 = (ConnectorIOException)CloneObject(v1); - Assert.AreEqual("ex",v2.Message); - } - { - ConnectorSecurityException v1 = new ConnectorSecurityException("ex"); - ConnectorSecurityException v2 = (ConnectorSecurityException)CloneObject(v1); - Assert.AreEqual("ex",v2.Message); - } - - { - InvalidCredentialException v1 = new InvalidCredentialException("ex"); - InvalidCredentialException v2 = (InvalidCredentialException)CloneObject(v1); - Assert.AreEqual("ex",v2.Message); - } - - { - InvalidPasswordException v1 = new InvalidPasswordException("ex"); - InvalidPasswordException v2 = (InvalidPasswordException)CloneObject(v1); - Assert.AreEqual("ex",v2.Message); - } - - { - PasswordExpiredException v1 = new PasswordExpiredException("ex"); - v1.Uid=(new Uid("myuid")); - PasswordExpiredException v2 = (PasswordExpiredException)CloneObject(v1); - Assert.AreEqual("ex",v2.Message); - Assert.AreEqual("myuid", v2.Uid.GetUidValue()); - } - - { - OperationTimeoutException v1 = new OperationTimeoutException("ex"); - OperationTimeoutException v2 = (OperationTimeoutException)CloneObject(v1); - Assert.AreEqual("ex",v2.Message); - } - - { - PermissionDeniedException v1 = new PermissionDeniedException("ex"); - PermissionDeniedException v2 = (PermissionDeniedException)CloneObject(v1); - Assert.AreEqual("ex",v2.Message); - } - - { - UnknownUidException v1 = new UnknownUidException("ex"); - UnknownUidException v2 = (UnknownUidException)CloneObject(v1); - Assert.AreEqual("ex",v2.Message); - } - - { - ArgumentException v1 = new ArgumentException("my msg"); - Exception v2 = (Exception)CloneObject(v1); - Assert.AreEqual("my msg", v2.Message); - } - - { - Exception v1 = new Exception("my msg2"); - Exception v2 = (Exception)CloneObject(v1); - Assert.AreEqual("my msg2", v2.Message); - } - - } - - [Test] - public void TestHelloRequest() { - HelloRequest v1 = new HelloRequest(); - HelloRequest v2 = (HelloRequest)CloneObject(v1); - Assert.IsNotNull(v2); - } - - [Test] - public void TestHelloResponse() { - RemoteConnectorInfoImpl info = new RemoteConnectorInfoImpl(); - info.Messages=(new ConnectorMessagesImpl()); - info.ConnectorKey=(new ConnectorKey("my bundle", - "my version", - "my connector")); - ConfigurationPropertiesImpl configProperties = new ConfigurationPropertiesImpl(); - configProperties.Properties=(new List()); - APIConfigurationImpl apiImpl = new APIConfigurationImpl(); - apiImpl.ConfigurationProperties=(configProperties); - info.DefaultAPIConfiguration=(apiImpl); - info.ConnectorDisplayNameKey=("mykey"); - - Exception ex = new Exception("foo"); - HelloResponse v1 = new HelloResponse(ex,CollectionUtil.NewReadOnlyList(info)); - HelloResponse v2 = (HelloResponse)CloneObject(v1); - Assert.IsNotNull(v2.Exception); - Assert.IsNotNull(v2.ConnectorInfos.First()); - } - - [Test] - public void TestOperationRequest() { - ConfigurationPropertiesImpl configProperties = new ConfigurationPropertiesImpl(); - configProperties.Properties=(new List()); - APIConfigurationImpl apiImpl = new APIConfigurationImpl(); - apiImpl.ConfigurationProperties=(configProperties); - - IList args = new List(); - args.Add("my arg"); - OperationRequest v1 = new - OperationRequest(new ConnectorKey("my bundle", - "my version", - "my connector"), - apiImpl, - SafeType.Get(), - "mymethodName", - args); - OperationRequest v2 = (OperationRequest)CloneObject(v1); - Assert.AreEqual("my bundle", v2.ConnectorKey.BundleName); - Assert.AreEqual("my version", v2.ConnectorKey.BundleVersion); - Assert.AreEqual("my connector", v2.ConnectorKey.ConnectorName); - Assert.IsNotNull(v2.Configuration); - Assert.AreEqual(SafeType.Get(), v2.Operation); - Assert.AreEqual("mymethodName",v2.OperationMethodName); - Assert.IsTrue( - CollectionUtil.Equals( - args, v2.Arguments)); - } - - [Test] - public void TestOperationResponseEnd() { - OperationResponseEnd v1 = new OperationResponseEnd(); - OperationResponseEnd v2 = (OperationResponseEnd)CloneObject(v1); - Assert.IsNotNull(v2); - } - [Test] - public void TestOperationResponsePart() { - Exception ex = new Exception("foo"); - OperationResponsePart v1 = new OperationResponsePart(ex,"bar"); - OperationResponsePart v2 = (OperationResponsePart)CloneObject(v1); - Assert.IsNotNull(v2.Exception); - Assert.AreEqual("bar", v2.Result); - } - - [Test] - public void TestOperationResponsePause() { - OperationResponsePause v1 = new OperationResponsePause(); - OperationResponsePause v2 = (OperationResponsePause)CloneObject(v1); - Assert.IsNotNull(v2); - } - - [Test] - public void TestOperationRequestMoreData() { - OperationRequestMoreData v1 = new OperationRequestMoreData(); - OperationRequestMoreData v2 = (OperationRequestMoreData)CloneObject(v1); - Assert.IsNotNull(v2); - } - - [Test] - public void TestOperationRequestStopData() { - OperationRequestStopData v1 = new OperationRequestStopData(); - OperationRequestStopData v2 = (OperationRequestStopData)CloneObject(v1); - Assert.IsNotNull(v2); - } - - [Test] - public void TestEchoMessage() { - EchoMessage v1 = new EchoMessage("test","xml"); - EchoMessage v2 = (EchoMessage)CloneObject(v1); - Assert.AreEqual("test", v2.Object); - Assert.AreEqual("xml", v2.ObjectXml); - } - - [Test] - public void TestOperationOptions() { - OperationOptionsBuilder builder = new OperationOptionsBuilder(); - builder.SetOption("foo", "bar"); - builder.SetOption("foo2", "bar2"); - OperationOptions v1 = builder.Build(); - OperationOptions v2 = (OperationOptions)CloneObject(v1); - Assert.AreEqual(2,v2.Options.Count); - Assert.AreEqual("bar",v2.Options["foo"]); - Assert.AreEqual("bar2",v2.Options["foo2"]); - } - - [Test] - public void TestScriptContext() { - ScriptContextBuilder builder = new ScriptContextBuilder(); - builder.ScriptLanguage=("language"); - builder.ScriptText=("text"); - builder.AddScriptArgument("foo", "bar"); - builder.AddScriptArgument("foo2", "bar2"); - ScriptContext v1 = builder.Build(); - ScriptContext v2 = (ScriptContext)CloneObject(v1); - Assert.AreEqual(2,v2.ScriptArguments.Count); - Assert.AreEqual("bar",v2.ScriptArguments["foo"]); - Assert.AreEqual("bar2",v2.ScriptArguments["foo2"]); - Assert.AreEqual("language",v2.ScriptLanguage); - Assert.AreEqual("text",v2.ScriptText); - } - [Test] - public void TestSyncDeltaType() { - SyncDeltaType v1 = SyncDeltaType.DELETE; - SyncDeltaType v2 = (SyncDeltaType)CloneObject(v1); - Assert.AreEqual(v1, v2); - } - - [Test] - public void TestSyncToken() { - SyncToken v1 = new SyncToken("mytoken"); - SyncToken v2 = (SyncToken)CloneObject(v1); - Assert.AreEqual(v1.Value,v2.Value); - Assert.AreEqual(v1,v2); - } - - [Test] - public void TestSyncDelta() { - ConnectorObjectBuilder bld = new ConnectorObjectBuilder(); - bld.SetUid("foo"); - bld.SetName("name"); - SyncDeltaBuilder builder = new SyncDeltaBuilder(); - builder.Uid=(new Uid("myuid")); - builder.DeltaType=(SyncDeltaType.CREATE_OR_UPDATE); - builder.Token=(new SyncToken("mytoken")); - builder.Object=(bld.Build()); - SyncDelta v1 = builder.Build(); - SyncDelta v2 = (SyncDelta)CloneObject(v1); - Assert.AreEqual(new Uid("foo"),v2.Uid); - Assert.AreEqual(new SyncToken("mytoken"),v2.Token); - Assert.AreEqual(SyncDeltaType.CREATE_OR_UPDATE,v2.DeltaType); - Assert.AreEqual(v1,v2); - } - - [Test] - public void TestNull() { - Object v1 = null; - Object v2 = CloneObject(v1); - Assert.IsNull(v2); - } - - [Test] - public void TestGuardedString() { - GuardedString v1 = new GuardedString(); - v1.AppendChar('f'); - v1.AppendChar('o'); - v1.AppendChar('o'); - v1.AppendChar('b'); - v1.AppendChar('a'); - v1.AppendChar('r'); - GuardedString v2 = (GuardedString)CloneObject(v1); - Assert.AreEqual("foobar",DecryptToString(v2)); - } - - [Test] - public void TestQualifiedUid() { - QualifiedUid v1 = new QualifiedUid(new ObjectClass("myclass"), - new Uid("myuid")); - QualifiedUid v2 = (QualifiedUid)CloneObject(v1); - Assert.AreEqual(v1, v2); - Assert.AreEqual("myclass", v2.ObjectClass.GetObjectClassValue()); - Assert.AreEqual("myuid", v2.Uid.GetUidValue()); - } - - /** - * Highly insecure method! Do not do this in production - * code. This is only for test purposes - */ - private String DecryptToString(GuardedString str) { - StringBuilder buf = new StringBuilder(); - str.Access( - array=> - { - for ( int i = 0 ; i < array.Length; i++) - { - buf.Append(array[i]); - } - }); - return buf.ToString(); - } - - protected virtual Object CloneObject(Object o) { - return SerializerUtil.CloneObject(o); - } - } - [TestFixture] - public class XmlSerializationTests : ObjectSerializationTests - { - protected override Object CloneObject(Object o) - { - String xml = SerializerUtil.SerializeXmlObject(o, true); - Console.WriteLine(xml); - o = SerializerUtil.DeserializeXmlObject(xml, true); - - //pass through a list to make sure dtd correctly defines all xml objects - List list = new List(); - list.Add(o); - xml = SerializerUtil.SerializeXmlObject(list, true); - Console.WriteLine(xml); - IList rv = (IList)SerializerUtil.DeserializeXmlObject(xml, true); - return rv[0]; - } - - [Test] - public void TestMultiObject() { - ObjectSerializerFactory factory = ObjectSerializerFactory.GetInstance(); - StringWriter sw = new StringWriter(); - XmlObjectSerializer ser = factory.NewXmlSerializer(sw,true,true); - ser.WriteObject("foo"); - ser.WriteObject("bar"); - ser.Close(true); - String xml = sw.ToString(); - Console.WriteLine(xml); - IList results = new List(); - factory.DeserializeXmlStream(new StringReader(xml), - o=>{ - results.Add(o); - return true; - }, - true); - Assert.AreEqual(2,results.Count); - Assert.AreEqual("foo",results[0]); - Assert.AreEqual("bar",results[1]); - } - - [Test] - public void TestWriter() - { - XmlWriterSettings settings = new XmlWriterSettings(); - settings.Indent = true; - settings.OmitXmlDeclaration = true; - XmlWriter writer = XmlWriter.Create(Console.Out, settings); - - // Write the book element. - writer.WriteStartElement("book"); - writer.WriteEndElement(); - writer.Close(); - // Write the title element. - //writer.WriteStartElement("title"); - //writer.WriteString("Pride And Prejudice<<"); - //writer.WriteEndElement(); - - // Write the close tag for the root element. - //writer.WriteEndElement(); - - // Write the XML and close the writer. - //writer.Close(); - - } - } -} diff --git a/DotNetConnectors.sln/FrameworkTests/ProxyTests.cs b/DotNetConnectors.sln/FrameworkTests/ProxyTests.cs deleted file mode 100644 index 863ba446..00000000 --- a/DotNetConnectors.sln/FrameworkTests/ProxyTests.cs +++ /dev/null @@ -1,100 +0,0 @@ -/* - * ==================== - * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER. - * - * Copyright 2007-2008 Sun Microsystems, Inc. All rights reserved. - * - * The contents of this file are subject to the terms of the Common Development - * and Distribution License("CDDL") (the "License"). You may not use this file - * except in compliance with the License. - * - * You can obtain a copy of the License at - * http://IdentityConnectors.dev.java.net/legal/license.txt - * See the License for the specific language governing permissions and limitations - * under the License. - * - * When distributing the Covered Code, include this CDDL Header Notice in each file - * and include the License file at identityconnectors/legal/license.txt. - * If applicable, add the following below this CDDL Header, with the fields - * enclosed by brackets [] replaced by your own identifying information: - * "Portions Copyrighted [year] [name of copyright owner]" - * ==================== - */ -using System; -using NUnit.Framework; -using NUnit.Framework.SyntaxHelpers; -using System.Reflection; -using System.Reflection.Emit; -using Org.IdentityConnectors.Common.Proxy; -using System.Collections.Generic; -using Org.IdentityConnectors.Common; -namespace FrameworkTests -{ - public interface MyTestInterface - { - string TestProperty {get;set;} - - String TestTwoArgs(string val1, string val2); - void TestVoid(IList list); - int TestPrimitive(int arg); - DateTime TestStruct(DateTime arg); - } - - [TestFixture] - public class ProxyTests - { - public class MyHandler : InvocationHandler - { - private string _testProperty; - - public Object Invoke(Object proxy, MethodInfo method, Object [] args) - { - if (method.Name.Equals("TestTwoArgs")) - { - return ""+method.Name+" "+args[0]+" "+args[1]; - } - else if ( method.Name.Equals("TestVoid") ) - { - IList arg = (IList)args[0]; - arg.Add("my void result"); - return null; - } - else if ( method.Name.Equals("get_TestProperty") ) - { - return _testProperty; - } - else if ( method.Name.Equals("set_TestProperty") ) - { - _testProperty = (string)args[0]; - return null; - } - else - { - return args[0]; - } - } - } - - [Test] - public void TestProxy() - { - InvocationHandler handler = new MyHandler(); - - MyTestInterface inter = - (MyTestInterface)Proxy.NewProxyInstance(typeof(MyTestInterface), - handler); - Assert.AreEqual("TestTwoArgs foo bar", - inter.TestTwoArgs("foo","bar")); - IList temp = new List(); - inter.TestVoid(temp); - Assert.AreEqual("my void result",temp[0]); - Assert.AreEqual(10,inter.TestPrimitive(10)); - Assert.AreEqual(1000L,inter.TestStruct(new DateTime(1000)).Ticks); - inter.TestProperty = "my property"; - Assert.AreEqual("my property",inter.TestProperty); - } - - } - - -} diff --git a/DotNetConnectors.sln/FrameworkTests/SafeTypeTest.cs b/DotNetConnectors.sln/FrameworkTests/SafeTypeTest.cs deleted file mode 100644 index 49567e30..00000000 --- a/DotNetConnectors.sln/FrameworkTests/SafeTypeTest.cs +++ /dev/null @@ -1,46 +0,0 @@ -/* - * ==================== - * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER. - * - * Copyright 2007-2008 Sun Microsystems, Inc. All rights reserved. - * - * The contents of this file are subject to the terms of the Common Development - * and Distribution License("CDDL") (the "License"). You may not use this file - * except in compliance with the License. - * - * You can obtain a copy of the License at - * http://IdentityConnectors.dev.java.net/legal/license.txt - * See the License for the specific language governing permissions and limitations - * under the License. - * - * When distributing the Covered Code, include this CDDL Header Notice in each file - * and include the License file at identityconnectors/legal/license.txt. - * If applicable, add the following below this CDDL Header, with the fields - * enclosed by brackets [] replaced by your own identifying information: - * "Portions Copyrighted [year] [name of copyright owner]" - * ==================== - */ -using System; -using NUnit.Framework; -using NUnit.Framework.SyntaxHelpers; -using Org.IdentityConnectors.Common; -using Org.IdentityConnectors.Framework.Api.Operations; -namespace FrameworkTests -{ - [TestFixture] - public class SafeTypeTest - { - [Test] - public void TestSafeType() - { - //compile-time type safe - SafeType op = - SafeType.Get(); - Assert.AreEqual(typeof(ScriptOnResourceApiOp),op.RawType); - //runtime type safe. needed for marshalling code, etc - op = - SafeType.ForRawType(typeof(SchemaApiOp)); - Assert.AreEqual(typeof(SchemaApiOp),op.RawType); - } - } -} diff --git a/DotNetConnectors.sln/FrameworkTests/ScriptTests.cs b/DotNetConnectors.sln/FrameworkTests/ScriptTests.cs deleted file mode 100644 index a22d85b9..00000000 --- a/DotNetConnectors.sln/FrameworkTests/ScriptTests.cs +++ /dev/null @@ -1,62 +0,0 @@ -/* - * ==================== - * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER. - * - * Copyright 2007-2008 Sun Microsystems, Inc. All rights reserved. - * - * The contents of this file are subject to the terms of the Common Development - * and Distribution License("CDDL") (the "License"). You may not use this file - * except in compliance with the License. - * - * You can obtain a copy of the License at - * http://IdentityConnectors.dev.java.net/legal/license.txt - * See the License for the specific language governing permissions and limitations - * under the License. - * - * When distributing the Covered Code, include this CDDL Header Notice in each file - * and include the License file at identityconnectors/legal/license.txt. - * If applicable, add the following below this CDDL Header, with the fields - * enclosed by brackets [] replaced by your own identifying information: - * "Portions Copyrighted [year] [name of copyright owner]" - * ==================== - */ -using System; -using System.Collections.Generic; -using System.Reflection; -using Org.IdentityConnectors.Common; -using Org.IdentityConnectors.Common.Script; -using NUnit.Framework; -using NUnit.Framework.SyntaxHelpers; - -namespace FrameworkTests -{ - /// - /// Description of ScriptTests. - /// - [TestFixture] - public class ScriptTests - { - [Test] - public void testBooScripting() { - ScriptExecutorFactory factory = ScriptExecutorFactory.NewInstance("BOO"); - ScriptExecutor exe = factory.NewScriptExecutor(new Assembly[0],"x", false); - IDictionary vals = new Dictionary(); - vals["x"] = 1; - Assert.AreEqual(1, exe.Execute(vals)); - vals["x"] = 2; - Assert.AreEqual(2, exe.Execute(vals)); - } - [Test] - public void testShellScripting() { - ScriptExecutorFactory factory = ScriptExecutorFactory.NewInstance("Shell"); - ScriptExecutor exe = factory.NewScriptExecutor(new Assembly[0],"echo bob", false); - IDictionary vals = new Dictionary(); - Assert.AreEqual(0, exe.Execute(vals)); - } - [Test] - [ExpectedException(typeof(ArgumentException))] - public void testUnsupported() { - ScriptExecutorFactory.NewInstance("fadsflkj"); - } - } -} diff --git a/DotNetConnectors.sln/FrameworkTests/TestHelperTests.cs b/DotNetConnectors.sln/FrameworkTests/TestHelperTests.cs deleted file mode 100644 index 5d79c29d..00000000 --- a/DotNetConnectors.sln/FrameworkTests/TestHelperTests.cs +++ /dev/null @@ -1,72 +0,0 @@ -/* - * ==================== - * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER. - * - * Copyright 2007-2008 Sun Microsystems, Inc. All rights reserved. - * - * The contents of this file are subject to the terms of the Common Development - * and Distribution License("CDDL") (the "License"). You may not use this file - * except in compliance with the License. - * - * You can obtain a copy of the License at - * http://IdentityConnectors.dev.java.net/legal/license.txt - * See the License for the specific language governing permissions and limitations - * under the License. - * - * When distributing the Covered Code, include this CDDL Header Notice in each file - * and include the License file at identityconnectors/legal/license.txt. - * If applicable, add the following below this CDDL Header, with the fields - * enclosed by brackets [] replaced by your own identifying information: - * "Portions Copyrighted [year] [name of copyright owner]" - * ==================== - */ -using System; -using System.IO; -using System.Xml; -using System.Collections.Generic; - -using NUnit.Framework; -using NUnit.Framework.SyntaxHelpers; - -using Org.IdentityConnectors.Framework.Test; - -namespace FrameworkTests -{ - /// - /// Description of TestHelperTests. - /// - [TestFixture] - public class TestHelperTests - { - [Test] - public void testLoadProperties() - { - string tmpFn = Path.GetTempFileName(); - try { - // create some xml text - TextWriter stringWriter = new StringWriter(); - XmlTextWriter w = new XmlTextWriter(stringWriter); - w.WriteStartElement("configuration"); - w.WriteStartElement("property"); - w.WriteAttributeString("name", "bob"); - w.WriteAttributeString("value", "bobsValue"); - w.WriteEndElement(); - w.WriteStartElement("property"); - w.WriteAttributeString("name", "bob2"); - w.WriteAttributeString("value", "bob2sValue"); - w.WriteEndElement(); - w.Close(); - File.WriteAllText(tmpFn, stringWriter.ToString()); - // load the properties files - IDictionary dict =TestHelpers.LoadPropertiesFile(tmpFn); - Assert.AreEqual(dict["bob"], "bobsValue"); - } finally { - File.Delete(tmpFn); - } - } - [Test] - public void testGetProperties() { - Assert.IsTrue(TestHelpers.GetProperty("Help", null).Equals("Me")); - } - } -} diff --git a/DotNetConnectors.sln/FrameworkTests/TestUtil.cs b/DotNetConnectors.sln/FrameworkTests/TestUtil.cs deleted file mode 100644 index 309f8b91..00000000 --- a/DotNetConnectors.sln/FrameworkTests/TestUtil.cs +++ /dev/null @@ -1,53 +0,0 @@ -/* - * ==================== - * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER. - * - * Copyright 2007-2008 Sun Microsystems, Inc. All rights reserved. - * - * The contents of this file are subject to the terms of the Common Development - * and Distribution License("CDDL") (the "License"). You may not use this file - * except in compliance with the License. - * - * You can obtain a copy of the License at - * http://IdentityConnectors.dev.java.net/legal/license.txt - * See the License for the specific language governing permissions and limitations - * under the License. - * - * When distributing the Covered Code, include this CDDL Header Notice in each file - * and include the License file at identityconnectors/legal/license.txt. - * If applicable, add the following below this CDDL Header, with the fields - * enclosed by brackets [] replaced by your own identifying information: - * "Portions Copyrighted [year] [name of copyright owner]" - * ==================== - */ -using System; -using System.Diagnostics; - -namespace FrameworkTests -{ - /// - /// Description of TestUtil. - /// - public static class TestUtil - { - private static readonly object LOCK = new Object(); - private static bool _initialized; - - - public static void InitializeLogging() - { - lock(LOCK) - { - if (!_initialized) - { - ConsoleTraceListener listener = - new ConsoleTraceListener(); - listener.TraceOutputOptions = - TraceOptions.ThreadId|TraceOptions.Timestamp; - Trace.Listeners.Add(listener); - _initialized = true; - } - } - } - } -} diff --git a/DotNetConnectors.sln/FrameworkTests/UpdateImplTests.cs b/DotNetConnectors.sln/FrameworkTests/UpdateImplTests.cs deleted file mode 100644 index f71f7c52..00000000 --- a/DotNetConnectors.sln/FrameworkTests/UpdateImplTests.cs +++ /dev/null @@ -1,209 +0,0 @@ -/* - * ==================== - * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER. - * - * Copyright 2007-2008 Sun Microsystems, Inc. All rights reserved. - * - * The contents of this file are subject to the terms of the Common Development - * and Distribution License("CDDL") (the "License"). You may not use this file - * except in compliance with the License. - * - * You can obtain a copy of the License at - * http://IdentityConnectors.dev.java.net/legal/license.txt - * See the License for the specific language governing permissions and limitations - * under the License. - * - * When distributing the Covered Code, include this CDDL Header Notice in each file - * and include the License file at identityconnectors/legal/license.txt. - * If applicable, add the following below this CDDL Header, with the fields - * enclosed by brackets [] replaced by your own identifying information: - * "Portions Copyrighted [year] [name of copyright owner]" - * ==================== - */ -using System; -using System.Security; -using NUnit.Framework; -using NUnit.Framework.SyntaxHelpers; -using System.Collections.Generic; -using Org.IdentityConnectors.Common; -using Org.IdentityConnectors.Framework.Api; -using Org.IdentityConnectors.Framework.Api.Operations; -using Org.IdentityConnectors.Framework.Impl.Api.Local.Operations; -using Org.IdentityConnectors.Framework.Common.Objects; -namespace FrameworkTests -{ - /// - /// Description of UpdateImplTests. - /// - [TestFixture] - public class UpdateImplTests - { - [Test] - [ExpectedException(typeof(ArgumentNullException ))] - public void ValidateUidArg() { - UpdateImpl.ValidateInput(ObjectClass.ACCOUNT, null, new HashSet(),true); - } - [Test] - [ExpectedException(typeof(ArgumentNullException ))] - public void ValidateObjectClassArg() { - UpdateImpl.ValidateInput(null, new Uid("foo"), new HashSet(),true); - } - - [Test] - [ExpectedException(typeof(ArgumentNullException ))] - public void ValidateAttrsArg() { - UpdateImpl.ValidateInput(ObjectClass.ACCOUNT, new Uid("foo"),null,true); - } - - [Test] - [ExpectedException(typeof(ArgumentException ))] - public void ValidateUidAttribute() { - HashSet attrs=new HashSet(); - attrs.Add(new Uid("foo")); - UpdateImpl.ValidateInput(ObjectClass.ACCOUNT, new Uid("foo"),attrs,true); - } - - [Test] - [ExpectedException(typeof(ArgumentException ))] - public void ValidateAddWithNullAttribute() { - ICollection attrs = new HashSet(); - attrs.Add(ConnectorAttributeBuilder.Build("something")); - UpdateImpl.ValidateInput(ObjectClass.ACCOUNT, new Uid("foo"), attrs, true); - } - - [Test] - [ExpectedException(typeof(ArgumentException))] - public void ValidateAttemptToAddName() { - ICollection attrs = new HashSet(); - attrs.Add(new Name("fadf")); - UpdateImpl.ValidateInput(ObjectClass.ACCOUNT, new Uid("foo"), attrs, true); - } - - [Test] - public void ValidateAttemptToAddDeleteOperationalAttribute() { - // list of all the operational attributes.. - ICollection list = new List(); - list.Add(ConnectorAttributeBuilder.BuildEnabled(false)); - list.Add(ConnectorAttributeBuilder.BuildLockOut(true)); - list.Add(ConnectorAttributeBuilder.BuildCurrentPassword(newSecureString("fadsf"))); - list.Add(ConnectorAttributeBuilder.BuildPasswordExpirationDate(DateTime.Now)); - list.Add(ConnectorAttributeBuilder.BuildPassword(newSecureString("fadsf"))); - foreach (ConnectorAttribute attr in list) { - ICollection attrs = new HashSet(); - attrs.Add(attr); - try { - UpdateImpl.ValidateInput(ObjectClass.ACCOUNT, new Uid("1"), attrs, true); - Assert.Fail("Failed: " + attr.Name); - } catch (ArgumentException) { - // this is a good thing.. - } - } - } - - private static SecureString newSecureString(string password) { - SecureString rv = new SecureString(); - foreach (char c in password.ToCharArray()) { - rv.AppendChar(c); - } - return rv; - } - - /// - /// Validate two collections are equal. (Not fast but effective) - /// - public static bool AreEqual(ICollection arg1, - ICollection arg2) { - if (arg1.Count != arg2.Count) { - return false; - } - foreach (ConnectorAttribute attr in arg1) { - if (!arg2.Contains(attr)) { - return false; - } - } - return true; - } - [Test] - public void MergeAddAttribute() { - UpdateImpl up = new UpdateImpl(null, null); - ICollection actual; - ICollection baseAttrs = CollectionUtil.NewSet(); - ICollection expected = CollectionUtil.NewSet(); - ICollection changeset = CollectionUtil.NewSet(); - // attempt to add a value to an attribute.. - ConnectorAttribute cattr = ConnectorAttributeBuilder.Build("abc", 2); - changeset.Add(cattr); - expected.Add(ConnectorAttributeBuilder.Build("abc", 2)); - actual = up.Merge(changeset, baseAttrs,true); - Assert.IsTrue(AreEqual(expected, actual)); - } - - [Test] - public void MergeAddToExistingAttribute() { - UpdateImpl up = new UpdateImpl(null, null); - ICollection actual; - ICollection baseAttrs = CollectionUtil.NewSet(); - ICollection expected = CollectionUtil.NewSet(); - ICollection changeset = CollectionUtil.NewSet(); - // attempt to add a value to an attribute.. - ConnectorAttribute battr = ConnectorAttributeBuilder.Build("abc", 1); - ConnectorAttribute cattr = ConnectorAttributeBuilder.Build("abc", 2); - baseAttrs.Add(battr); - changeset.Add(cattr); - expected.Add(ConnectorAttributeBuilder.Build("abc", 1, 2)); - actual = up.Merge(changeset, baseAttrs, true); - Assert.IsTrue(AreEqual(expected, actual)); - } - - [Test] - public void MergeDeleteNonExistentAttribute() { - UpdateImpl up = new UpdateImpl(null, null); - ICollection actual; - ICollection baseAttrs = CollectionUtil.NewSet(); - ICollection expected = CollectionUtil.NewSet(); - ICollection changeset = CollectionUtil.NewSet(); - // attempt to add a value to an attribute.. - ConnectorAttribute cattr = ConnectorAttributeBuilder.Build("abc", 2); - changeset.Add(cattr); - actual = up.Merge(changeset, baseAttrs, false); - Assert.IsTrue(AreEqual(expected, actual)); - } - - [Test] - public void MergeDeleteToExistingAttribute() { - UpdateImpl up = new UpdateImpl(null, null); - ICollection actual; - ICollection baseAttrs = CollectionUtil.NewSet(); - ICollection expected = CollectionUtil.NewSet(); - ICollection changeset = CollectionUtil.NewSet(); - // attempt to add a value to an attribute.. - ConnectorAttribute battr = ConnectorAttributeBuilder.Build("abc", 1, 2); - ConnectorAttribute cattr = ConnectorAttributeBuilder.Build("abc", 2); - baseAttrs.Add(battr); - changeset.Add(cattr); - expected.Add(ConnectorAttributeBuilder.Build("abc", 1)); - actual = up.Merge(changeset, baseAttrs, false); - Assert.IsTrue(AreEqual(expected, actual)); - } - - [Test] - public void MergeDeleteToExistingAttributeCompletely() { - UpdateImpl up = new UpdateImpl(null, null); - ICollection actual; - ICollection baseAttrs = CollectionUtil.NewSet(); - ICollection expected = CollectionUtil.NewSet(); - ICollection changeset = CollectionUtil.NewSet(); - // attempt to add a value to an attribute.. - ConnectorAttribute battr = ConnectorAttributeBuilder.Build("abc", 1, 2); - ConnectorAttribute cattr = ConnectorAttributeBuilder.Build("abc", 1, 2); - baseAttrs.Add(battr); - changeset.Add(cattr); - expected.Add(ConnectorAttributeBuilder.Build("abc")); - actual = up.Merge(changeset, baseAttrs, false); - Assert.IsTrue(AreEqual(expected, actual)); - } - - - - } -} diff --git a/DotNetConnectors.sln/FrameworkTests/app.config b/DotNetConnectors.sln/FrameworkTests/app.config deleted file mode 100644 index e228e5fa..00000000 --- a/DotNetConnectors.sln/FrameworkTests/app.config +++ /dev/null @@ -1,24 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - diff --git a/DotNetConnectors.sln/FrameworkTests/project.xml b/DotNetConnectors.sln/FrameworkTests/project.xml deleted file mode 100644 index 6f40613f..00000000 --- a/DotNetConnectors.sln/FrameworkTests/project.xml +++ /dev/null @@ -1,5 +0,0 @@ - - - - - \ No newline at end of file diff --git a/DotNetConnectors.sln/Service/AssemblyInfo.cs b/DotNetConnectors.sln/Service/AssemblyInfo.cs deleted file mode 100644 index b5a7e910..00000000 --- a/DotNetConnectors.sln/Service/AssemblyInfo.cs +++ /dev/null @@ -1,53 +0,0 @@ -/* - * ==================== - * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER. - * - * Copyright 2007-2008 Sun Microsystems, Inc. All rights reserved. - * - * The contents of this file are subject to the terms of the Common Development - * and Distribution License("CDDL") (the "License"). You may not use this file - * except in compliance with the License. - * - * You can obtain a copy of the License at - * http://IdentityConnectors.dev.java.net/legal/license.txt - * See the License for the specific language governing permissions and limitations - * under the License. - * - * When distributing the Covered Code, include this CDDL Header Notice in each file - * and include the License file at identityconnectors/legal/license.txt. - * If applicable, add the following below this CDDL Header, with the fields - * enclosed by brackets [] replaced by your own identifying information: - * "Portions Copyrighted [year] [name of copyright owner]" - * ==================== - */ -#region Using directives - -using System.Reflection; -using System.Runtime.CompilerServices; -using System.Runtime.InteropServices; - -#endregion - -// General Information about an assembly is controlled through the following -// set of attributes. Change these attribute values to modify the information -// associated with an assembly. -[assembly: AssemblyTitle("Service")] -[assembly: AssemblyDescription("")] -[assembly: AssemblyConfiguration("")] -[assembly: AssemblyCompany("")] -[assembly: AssemblyProduct("Service")] -[assembly: AssemblyCopyright("")] -[assembly: AssemblyTrademark("")] -[assembly: AssemblyCulture("")] - -// This sets the default COM visibility of types in the assembly to invisible. -// If you need to expose a type to COM, use [ComVisible(true)] on that type. -[assembly: ComVisible(false)] - -// The assembly version has following format : -// -// Major.Minor.Build.Revision -// -// You can specify all the values or you can use the default the Revision and -// Build Numbers by using the '*' as shown below: -[assembly: AssemblyVersion("1.0.0.0")] diff --git a/DotNetConnectors.sln/Service/Program.cs b/DotNetConnectors.sln/Service/Program.cs deleted file mode 100644 index aff6237a..00000000 --- a/DotNetConnectors.sln/Service/Program.cs +++ /dev/null @@ -1,230 +0,0 @@ -/* - * ==================== - * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER. - * - * Copyright 2007-2008 Sun Microsystems, Inc. All rights reserved. - * - * The contents of this file are subject to the terms of the Common Development - * and Distribution License("CDDL") (the "License"). You may not use this file - * except in compliance with the License. - * - * You can obtain a copy of the License at - * http://IdentityConnectors.dev.java.net/legal/license.txt - * See the License for the specific language governing permissions and limitations - * under the License. - * - * When distributing the Covered Code, include this CDDL Header Notice in each file - * and include the License file at identityconnectors/legal/license.txt. - * If applicable, add the following below this CDDL Header, with the fields - * enclosed by brackets [] replaced by your own identifying information: - * "Portions Copyrighted [year] [name of copyright owner]" - * ==================== - */ -using System; -using System.ComponentModel; -using System.Configuration; -using System.Configuration.Install; -using System.Collections.Generic; -using System.Security; -using System.Reflection; -using System.ServiceProcess; -using System.Text; -using Org.IdentityConnectors.Common.Security; - -namespace Org.IdentityConnectors.Framework.Service -{ - - static class Program - { - private const string OPT_SERVICE_NAME="/serviceName"; - - private static void Usage() - { - Console.WriteLine("Usage: ConnectorServer.exe [option], where command is one of the following: "); - Console.WriteLine(" /install [/serviceName ] - Installs the service."); - Console.WriteLine(" /uninstall [/serviceName ] - Uninstalls the service."); - Console.WriteLine(" /run - Runs the service from the console."); - Console.WriteLine(" /setKey [] - Sets the connector server key."); - } - - private static IDictionary ParseOptions(string [] args) - { - IDictionary rv = new Dictionary(); - String serviceName = null; - for ( int i = 1; i < args.Length; i++) { - String opt = args[i].ToLower(); - if (OPT_SERVICE_NAME.ToLower().Equals(opt)) { - i++; - if (i < args.Length) { - serviceName = args[i]; - } - else { - Usage(); - return null; - } - } - else { - Usage(); - return null; - } - } - rv["/serviceName"] = serviceName; - return rv; - } - - /// - /// This method starts the service. - /// - static void Main(string [] args) - { - if ( args.Length == 0 ) - { - //no args - start the service - ServiceBase.Run(new ServiceBase[] { new Service() }); - } - else - { - String cmd = args[0].ToLower(); - if ( cmd.Equals("/setkey") ) { - if ( args.Length > 2 ) { - Usage(); - return; - } - DoSetKey(args.Length>1?args[1]:null); - return; - } - IDictionary options = - ParseOptions(args); - if ( options == null ) { - //there's a parse error in the options, return - return; - } - if ( "/install".Equals(cmd) ) { - DoInstall(options); - } - else if ("/uninstall".Equals(cmd)) { - DoUninstall(options); - } - else if ("/run".Equals(cmd)) { - DoRun(options); - } - else { - Usage(); - return; - } - } - } - - private static void DoInstall(IDictionary options) - { - String serviceName = options[OPT_SERVICE_NAME]; - if ( serviceName != null ) { - ProjectInstaller.ServiceName = serviceName; - } - TransactedInstaller ti = new TransactedInstaller(); - string [] cmdline = - { - Assembly.GetExecutingAssembly ().Location - }; - AssemblyInstaller ai = new AssemblyInstaller( - cmdline[0], - new string[0]); - ti.Installers.Add (ai); - InstallContext ctx = new InstallContext ("install.log", - cmdline ); - ti.Context = ctx ; - ti.Install ( new System.Collections.Hashtable()); - } - - private static void DoUninstall(IDictionary options) - { - String serviceName = options[OPT_SERVICE_NAME]; - if ( serviceName != null ) { - ProjectInstaller.ServiceName = serviceName; - } - TransactedInstaller ti = new TransactedInstaller(); - string [] cmdline = - { - Assembly.GetExecutingAssembly ().Location - }; - AssemblyInstaller ai = new AssemblyInstaller( - cmdline[0], - new string[0]); - ti.Installers.Add (ai); - InstallContext ctx = new InstallContext ("uninstall.log", - cmdline ); - ti.Context = ctx ; - ti.Uninstall ( null); - } - - private static void DoRun(IDictionary options) - { - Service svc = new Service(); - - svc.StartService(new String[0]); - - Console.WriteLine("Press q to shutdown."); - Console.WriteLine("Press t for a thread dump."); - - while ( true ) { - ConsoleKeyInfo info = Console.ReadKey(); - if ( info.KeyChar == 'q' ) { - break; - } - else if ( info.KeyChar == 't' ) { - svc.DumpRequests(); - } - } - - svc.StopService(); - } - - private static GuardedString ReadPassword() - { - GuardedString rv = new GuardedString(); - while (true) { - ConsoleKeyInfo info = Console.ReadKey(true); - if ( info.Key == ConsoleKey.Enter ) { - Console.WriteLine(); - rv.MakeReadOnly(); - return rv; - } - else { - Console.Write("*"); - rv.AppendChar(info.KeyChar); - } - } - } - - private static void DoSetKey(string key) - { - GuardedString str; - if ( key == null ) { - Console.Write("Enter Key: "); - GuardedString v1 = ReadPassword(); - Console.Write("Confirm Key: "); - GuardedString v2 = ReadPassword(); - if (!v1.Equals(v2)) { - Console.WriteLine("Error: Key mismatch."); - return; - } - str = v2; - } - else { - str = new GuardedString(); - foreach (char c in key.ToCharArray()) { - str.AppendChar(c); - } - } - Configuration config = - ConfigurationManager.OpenExeConfiguration(ConfigurationUserLevel.None); - config.AppSettings.Settings.Remove(Service.PROP_KEY); - config.AppSettings.Settings.Add(Service.PROP_KEY,str.GetBase64SHA1Hash()); - config.Save(ConfigurationSaveMode.Modified); - Console.WriteLine("Key Updated."); - } - } - - - -} diff --git a/DotNetConnectors.sln/Service/ProjectInstaller.cs b/DotNetConnectors.sln/Service/ProjectInstaller.cs deleted file mode 100644 index c119abc6..00000000 --- a/DotNetConnectors.sln/Service/ProjectInstaller.cs +++ /dev/null @@ -1,56 +0,0 @@ -/* - * ==================== - * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER. - * - * Copyright 2007-2008 Sun Microsystems, Inc. All rights reserved. - * - * The contents of this file are subject to the terms of the Common Development - * and Distribution License("CDDL") (the "License"). You may not use this file - * except in compliance with the License. - * - * You can obtain a copy of the License at - * http://IdentityConnectors.dev.java.net/legal/license.txt - * See the License for the specific language governing permissions and limitations - * under the License. - * - * When distributing the Covered Code, include this CDDL Header Notice in each file - * and include the License file at identityconnectors/legal/license.txt. - * If applicable, add the following below this CDDL Header, with the fields - * enclosed by brackets [] replaced by your own identifying information: - * "Portions Copyrighted [year] [name of copyright owner]" - * ==================== - */ -using System; -using System.Collections.Generic; -using System.ComponentModel; -using System.Configuration.Install; -using System.ServiceProcess; - -namespace Org.IdentityConnectors.Framework.Service -{ - [RunInstaller(true)] - public class ProjectInstaller : Installer - { - public static string ServiceName { get; set; } - - static ProjectInstaller() - { - ServiceName = "Connector Server"; - } - - private ServiceProcessInstaller serviceProcessInstaller; - private ServiceInstaller serviceInstaller; - - public ProjectInstaller() - { - serviceProcessInstaller = new ServiceProcessInstaller(); - serviceInstaller = new ServiceInstaller(); - - // Here you can set properties on serviceProcessInstaller or register event handlers - serviceProcessInstaller.Account = ServiceAccount.LocalSystem; - - serviceInstaller.ServiceName = ServiceName; - this.Installers.AddRange(new Installer[] { serviceProcessInstaller, serviceInstaller }); - } - } -} diff --git a/DotNetConnectors.sln/Service/Service.cs b/DotNetConnectors.sln/Service/Service.cs deleted file mode 100644 index 2855a94e..00000000 --- a/DotNetConnectors.sln/Service/Service.cs +++ /dev/null @@ -1,182 +0,0 @@ -/* - * ==================== - * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER. - * - * Copyright 2007-2008 Sun Microsystems, Inc. All rights reserved. - * - * The contents of this file are subject to the terms of the Common Development - * and Distribution License("CDDL") (the "License"). You may not use this file - * except in compliance with the License. - * - * You can obtain a copy of the License at - * http://IdentityConnectors.dev.java.net/legal/license.txt - * See the License for the specific language governing permissions and limitations - * under the License. - * - * When distributing the Covered Code, include this CDDL Header Notice in each file - * and include the License file at identityconnectors/legal/license.txt. - * If applicable, add the following below this CDDL Header, with the fields - * enclosed by brackets [] replaced by your own identifying information: - * "Portions Copyrighted [year] [name of copyright owner]" - * ==================== - */ -using System; -using System.Collections.Generic; -using System.Collections.Specialized; -using System.ComponentModel; -using System.Configuration; -using System.Data; -using System.Diagnostics; -using System.IO; -using System.Reflection; -using System.Security.Cryptography.X509Certificates; -using System.ServiceProcess; -using System.Text; -using Org.IdentityConnectors.Common; -using Org.IdentityConnectors.Framework.Server; - -namespace Org.IdentityConnectors.Framework.Service -{ - public class Service : ServiceBase - { - private const string PROP_PORT = "connectorserver.port"; - private const string PROP_SSL = "connectorserver.usessl"; - private const string PROP_CERTSTORE = "connectorserver.certificatestorename"; - private const string PROP_IFADDRESS = "connectorserver.ifaddress"; - public const string PROP_KEY = "connectorserver.key"; - - private ConnectorServer _server; - - public Service() - { - } - - public void DumpRequests() - { - _server.DumpRequests(); - } - - - /// - /// Clean up any resources being used. - /// - protected override void Dispose(bool disposing) - { - base.Dispose(disposing); - } - - private void initializeCurrentDirectory() - { - Assembly assembly = - Assembly.GetExecutingAssembly(); - FileInfo thisAssemblyFile = - new FileInfo(assembly.Location); - DirectoryInfo directory = - thisAssemblyFile.Directory; - Environment.CurrentDirectory = - directory.FullName; - - } - - private NameValueCollection GetApplicationSettings() - { - return ConfigurationManager.AppSettings; - } - - private X509Certificate GetCertificate() - { - NameValueCollection settings = - GetApplicationSettings(); - String storeName = settings.Get(PROP_CERTSTORE); - if (storeName == null) { - throw new Org.IdentityConnectors.Framework.Common.Exceptions.ConfigurationException("Missing required configuration setting: "+PROP_CERTSTORE); - } - - X509Store store = new X509Store(storeName, - StoreLocation.LocalMachine); - - store.Open(OpenFlags.ReadOnly|OpenFlags.OpenExistingOnly); - X509CertificateCollection certificates = store.Certificates; - if ( certificates.Count != 1 ) { - throw new Org.IdentityConnectors.Framework.Common.Exceptions.ConfigurationException("There is supported to be exactly one certificate in the store: "+storeName); - } - X509Certificate certificate = store.Certificates[0]; - store.Close(); - return certificate; - } - - public void StartService(string [] args) - { - OnStart(args); - } - - /// - /// Start this service. - /// - protected override void OnStart(string[] args) - { - try { - initializeCurrentDirectory(); - Trace.TraceInformation("Starting connector server: "+Environment.CurrentDirectory); - NameValueCollection settings = - GetApplicationSettings(); - String portStr = - settings.Get(PROP_PORT); - if ( portStr == null ) { - throw new Org.IdentityConnectors.Framework.Common.Exceptions.ConfigurationException("Missing required configuration property: "+PROP_PORT); - } - String keyHash = settings.Get(PROP_KEY); - if ( keyHash == null ) { - throw new Org.IdentityConnectors.Framework.Common.Exceptions.ConfigurationException("Missing required configuration property: "+PROP_KEY); - } - - int port = Int32.Parse(portStr); - bool useSSL = Boolean.Parse(settings.Get(PROP_SSL)??"false"); - _server = ConnectorServer.NewInstance(); - _server.Port = port; - _server.UseSSL = useSSL; - _server.KeyHash = keyHash; - if (useSSL) { - _server.ServerCertificate = - GetCertificate(); - } - String ifaddress = settings.Get(PROP_IFADDRESS); - if ( ifaddress != null ) { - _server.IfAddress = - IOUtil.GetIPAddress(ifaddress); - } - _server.Start(); - Trace.TraceInformation("Started connector server"); - } - catch (Exception e) { - TraceUtil.TraceException("Exception occured starting connector server", - e); - throw; - } - } - - public void StopService() - { - OnStop(); - } - - - /// - /// Stop this service. - /// - protected override void OnStop() - { - try { - Trace.TraceInformation("Stopping connector server"); - if (_server != null) { - _server.Stop(); - } - Trace.TraceInformation("Stopped connector server"); - } - catch (Exception e) { - TraceUtil.TraceException("Exception occured stopping connector server", - e); - } - } - } -} diff --git a/DotNetConnectors.sln/Service/Service.csproj b/DotNetConnectors.sln/Service/Service.csproj deleted file mode 100644 index ea668380..00000000 --- a/DotNetConnectors.sln/Service/Service.csproj +++ /dev/null @@ -1,119 +0,0 @@ - - - - {A9D6374A-D51F-4FA3-8C02-5B1D23FAA82E} - Debug - AnyCPU - Exe - Sun.OpenConnectors.Framework.Service - ConnectorServer - v3.5 - False - false - - - prompt - ./bin/Debug/ - true - Full - False - True - DEBUG;TRACE - Exe - ConnectorServer - False - 4 - - - ./bin/Release/ - False - pdbonly - True - False - TRACE - prompt - Exe - ConnectorServer - False - 4 - - - False - Auto - 4194304 - AnyCPU - 4096 - - - - - - - - - 3.5 - - - - - - - - - - Component - - - Component - - - - - - - - - - - - - - - - - - - {F140E8DA-52B4-4159-992A-9DA10EA8EEFB} - Common - - - {5B011775-B121-4EEE-A410-BA2D2F5BFB8B} - FrameworkInternal - - - {8B24461B-456A-4032-89A1-CD418F7B5B62} - Framework - - - diff --git a/DotNetConnectors.sln/Service/app.config b/DotNetConnectors.sln/Service/app.config deleted file mode 100644 index 342a7974..00000000 --- a/DotNetConnectors.sln/Service/app.config +++ /dev/null @@ -1,37 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/DotNetConnectors.sln/ServiceInstall/ExtBuild.proj b/DotNetConnectors.sln/ServiceInstall/ExtBuild.proj deleted file mode 100644 index df976a43..00000000 --- a/DotNetConnectors.sln/ServiceInstall/ExtBuild.proj +++ /dev/null @@ -1,63 +0,0 @@ - - - - Debug - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/DotNetConnectors.sln/ServiceInstall/File.bottom b/DotNetConnectors.sln/ServiceInstall/File.bottom deleted file mode 100644 index 8de6ef93..00000000 --- a/DotNetConnectors.sln/ServiceInstall/File.bottom +++ /dev/null @@ -1,6 +0,0 @@ - - - - - - \ No newline at end of file diff --git a/DotNetConnectors.sln/ServiceInstall/File.top b/DotNetConnectors.sln/ServiceInstall/File.top deleted file mode 100644 index 84086191..00000000 --- a/DotNetConnectors.sln/ServiceInstall/File.top +++ /dev/null @@ -1,21 +0,0 @@ - - - - - - - - - - - - diff --git a/DotNetConnectors.sln/ServiceInstall/ServiceInstall.wixproj b/DotNetConnectors.sln/ServiceInstall/ServiceInstall.wixproj deleted file mode 100644 index 4af8a460..00000000 --- a/DotNetConnectors.sln/ServiceInstall/ServiceInstall.wixproj +++ /dev/null @@ -1,69 +0,0 @@ - - - - {1CBA8F74-050C-432B-8437-08BD13BDC684} - Debug - AnyCPU - Package - ServiceInstall - ServiceInstall - $(WIX_HOME) - $(WixToolPath)\wix.targets - $(WixToolPath)\WixTasks.dll - ICE45 - WixUILicenseRtf=license.rtf - - - prompt - 4 - AnyCPU - bin\Debug\ - True - Full - False - True - DEBUG;TRACE - - - pdbonly - bin\Release\ - TRACE - prompt - 4 - AnyCPU - False - True - False - - - - - c:\Program Files\Windows Installer XML v3\bin\WixUIExtension.dll - - - - - - - \ No newline at end of file diff --git a/DotNetConnectors.sln/ServiceInstall/Setup.wxs b/DotNetConnectors.sln/ServiceInstall/Setup.wxs deleted file mode 100644 index 54724a64..00000000 --- a/DotNetConnectors.sln/ServiceInstall/Setup.wxs +++ /dev/null @@ -1,50 +0,0 @@ - - - - - - - - - - - - - - - - - - diff --git a/DotNetConnectors.sln/ServiceInstall/license.rtf b/DotNetConnectors.sln/ServiceInstall/license.rtf deleted file mode 100644 index 5ecc7cf2..00000000 --- a/DotNetConnectors.sln/ServiceInstall/license.rtf +++ /dev/null @@ -1,30 +0,0 @@ -{\rtf1\ansi\deff0\adeflang1025 -{\fonttbl{\f0\froman\fprq2\fcharset0 Thorndale{\*\falt Times New Roman};}{\f1\froman\fprq2\fcharset0 Thorndale{\*\falt Times New Roman};}{\f2\fswiss\fprq2\fcharset0 Albany{\*\falt Arial};}{\f3\fnil\fprq2\fcharset0 Andale Sans UI{\*\falt Arial Unicode MS};}{\f4\fnil\fprq2\fcharset0 Tahoma;}{\f5\fnil\fprq0\fcharset0 Tahoma;}} -{\colortbl;\red0\green0\blue0;\red128\green128\blue128;} -{\stylesheet{\s1\cf0{\*\hyphen2\hyphlead2\hyphtrail2\hyphmax0}\rtlch\af4\afs24\lang255\ltrch\dbch\af3\langfe255\hich\f0\fs24\lang1033\loch\f0\fs24\lang1033\snext1 Normal;} -{\s2\sb240\sa120\keepn\cf0{\*\hyphen2\hyphlead2\hyphtrail2\hyphmax0}\rtlch\afs28\lang255\ltrch\dbch\langfe255\hich\f2\fs28\lang1033\loch\f2\fs28\lang1033\sbasedon1\snext3 Heading;} -{\s3\sa120\cf0{\*\hyphen2\hyphlead2\hyphtrail2\hyphmax0}\rtlch\af4\afs24\lang255\ltrch\dbch\af3\langfe255\hich\f0\fs24\lang1033\loch\f0\fs24\lang1033\sbasedon1\snext3 Body Text;} -{\s4\sa120\cf0{\*\hyphen2\hyphlead2\hyphtrail2\hyphmax0}\rtlch\af5\afs24\lang255\ltrch\dbch\af3\langfe255\hich\f0\fs24\lang1033\loch\f0\fs24\lang1033\sbasedon3\snext4 List;} -{\s5\sb120\sa120\cf0{\*\hyphen2\hyphlead2\hyphtrail2\hyphmax0}\rtlch\af5\afs24\lang255\ai\ltrch\dbch\af3\langfe255\hich\f0\fs24\lang1033\i\loch\f0\fs24\lang1033\i\sbasedon1\snext5 caption;} -{\s6\cf0{\*\hyphen2\hyphlead2\hyphtrail2\hyphmax0}\rtlch\af5\afs24\lang255\ltrch\dbch\af3\langfe255\hich\f0\fs24\lang1033\loch\f0\fs24\lang1033\sbasedon1\snext6 Index;} -} -{\info{\author William Droste}{\creatim\yr2008\mo8\dy7\hr10\min50}{\revtim\yr0\mo0\dy0\hr0\min0}{\printim\yr0\mo0\dy0\hr0\min0}{\comment StarWriter}{\vern6800}}\deftab709 -{\*\pgdsctbl -{\pgdsc0\pgdscuse195\pgwsxn12240\pghsxn15840\marglsxn1134\margrsxn1134\margtsxn1134\margbsxn1134\pgdscnxt0 Standard;}} -\paperh15840\paperw12240\margl1134\margr1134\margt1134\margb1134\sectd\sbknone\pgwsxn12240\pghsxn15840\marglsxn1134\margrsxn1134\margtsxn1134\margbsxn1134\ftnbj\ftnstart1\ftnrstcont\ftnnar\aenddoc\aftnrstcont\aftnstart1\aftnnrlc -\pard\plain \ltrpar\s1\cf0{\*\hyphen2\hyphlead2\hyphtrail2\hyphmax0}\rtlch\af4\afs24\lang255\ltrch\dbch\af3\langfe255\hich\f0\fs24\lang1033\loch\f0\fs24\lang1033 {\rtlch \ltrch\loch\f0\fs24\lang1033\i0\b0 Copyright \'a9 2008 Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, California 95054, U.S.A. All rights reserved.} -\par \pard\plain \ltrpar\s1\cf0{\*\hyphen2\hyphlead2\hyphtrail2\hyphmax0}\rtlch\af4\afs24\lang255\ltrch\dbch\af3\langfe255\hich\f0\fs24\lang1033\loch\f0\fs24\lang1033 -\par \pard\plain \ltrpar\s1\cf0{\*\hyphen2\hyphlead2\hyphtrail2\hyphmax0}\rtlch\af4\afs24\lang255\ltrch\dbch\af3\langfe255\hich\f0\fs24\lang1033\loch\f0\fs24\lang1033 {\rtlch \ltrch\loch }{\rtlch \ltrch\loch\f0\fs24\lang1033\i0\b0 U.S. Government Rights - Commercial software. Government users are subject to the Sun Microsystems, Inc. standard license agreement and applicable provisions of the FAR and its supplements.} -\par \pard\plain \ltrpar\s1\cf0{\*\hyphen2\hyphlead2\hyphtrail2\hyphmax0}\rtlch\af4\afs24\lang255\ltrch\dbch\af3\langfe255\hich\f0\fs24\lang1033\loch\f0\fs24\lang1033 -\par \pard\plain \ltrpar\s1\cf0{\*\hyphen2\hyphlead2\hyphtrail2\hyphmax0}\rtlch\af4\afs24\lang255\ltrch\dbch\af3\langfe255\hich\f0\fs24\lang1033\loch\f0\fs24\lang1033 {\rtlch \ltrch\loch }{\rtlch \ltrch\loch\f0\fs24\lang1033\i0\b0 Use is subject to license terms.} -\par \pard\plain \ltrpar\s1\cf0{\*\hyphen2\hyphlead2\hyphtrail2\hyphmax0}\rtlch\af4\afs24\lang255\ltrch\dbch\af3\langfe255\hich\f0\fs24\lang1033\loch\f0\fs24\lang1033 -\par \pard\plain \ltrpar\s1\cf0{\*\hyphen2\hyphlead2\hyphtrail2\hyphmax0}\rtlch\af4\afs24\lang255\ltrch\dbch\af3\langfe255\hich\f0\fs24\lang1033\loch\f0\fs24\lang1033 {\rtlch \ltrch\loch }{\rtlch \ltrch\loch\f0\fs24\lang1033\i0\b0 This distribution may include materials developed by third parties.} -\par \pard\plain \ltrpar\s1\cf0{\*\hyphen2\hyphlead2\hyphtrail2\hyphmax0}\rtlch\af4\afs24\lang255\ltrch\dbch\af3\langfe255\hich\f0\fs24\lang1033\loch\f0\fs24\lang1033 -\par \pard\plain \ltrpar\s1\cf0{\*\hyphen2\hyphlead2\hyphtrail2\hyphmax0}\rtlch\af4\afs24\lang255\ltrch\dbch\af3\langfe255\hich\f0\fs24\lang1033\loch\f0\fs24\lang1033 {\rtlch \ltrch\loch }{\rtlch \ltrch\loch\f0\fs24\lang1033\i0\b0 Sun, Sun Microsystems, the Sun logo, Java and Project Identity Connectors are trademarks or registered trademarks of Sun Microsystems, Inc. in the U.S. and other countries.} -\par \pard\plain \ltrpar\s1\cf0{\*\hyphen2\hyphlead2\hyphtrail2\hyphmax0}\rtlch\af4\afs24\lang255\ltrch\dbch\af3\langfe255\hich\f0\fs24\lang1033\loch\f0\fs24\lang1033 -\par \pard\plain \ltrpar\s1\cf0{\*\hyphen2\hyphlead2\hyphtrail2\hyphmax0}\rtlch\af4\afs24\lang255\ltrch\dbch\af3\langfe255\hich\f0\fs24\lang1033\loch\f0\fs24\lang1033 {\rtlch \ltrch\loch }{\rtlch \ltrch\loch\f0\fs24\lang1033\i0\b0 UNIX is a registered trademark in the U.S. and other countries, exclusively licensed through X/Open Company, Ltd.} -\par \pard\plain \ltrpar\s1\cf0{\*\hyphen2\hyphlead2\hyphtrail2\hyphmax0}\rtlch\af4\afs24\lang255\ltrch\dbch\af3\langfe255\hich\f0\fs24\lang1033\loch\f0\fs24\lang1033 -\par \pard\plain \ltrpar\s1\cf0{\*\hyphen2\hyphlead2\hyphtrail2\hyphmax0}\rtlch\af4\afs24\lang255\ltrch\dbch\af3\langfe255\hich\f0\fs24\lang1033\loch\f0\fs24\lang1033 {\rtlch \ltrch\loch }{\rtlch \ltrch\loch\f0\fs24\lang1033\i0\b0 This product is covered and controlled by U.S. Export Control laws and may be subject to the export or import laws in other countries. Nuclear, missile, chemical biological weapons or nuclear maritime end uses or end users, whether direct or indirec -t, are strictly prohibited. Export or reexport to countries subject to U.S. embargo or to entities identified on U.S. export exclusion lists, including, but not limited to, the denied persons and specially designated nationals lists is strictly prohibited. - } -\par } \ No newline at end of file diff --git a/DotNetConnectors.sln/ShellScriptExecutorFactory/AssemblyInfo.cs b/DotNetConnectors.sln/ShellScriptExecutorFactory/AssemblyInfo.cs deleted file mode 100644 index 4e907090..00000000 --- a/DotNetConnectors.sln/ShellScriptExecutorFactory/AssemblyInfo.cs +++ /dev/null @@ -1,53 +0,0 @@ -/* - * ==================== - * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER. - * - * Copyright 2007-2008 Sun Microsystems, Inc. All rights reserved. - * - * The contents of this file are subject to the terms of the Common Development - * and Distribution License("CDDL") (the "License"). You may not use this file - * except in compliance with the License. - * - * You can obtain a copy of the License at - * http://IdentityConnectors.dev.java.net/legal/license.txt - * See the License for the specific language governing permissions and limitations - * under the License. - * - * When distributing the Covered Code, include this CDDL Header Notice in each file - * and include the License file at identityconnectors/legal/license.txt. - * If applicable, add the following below this CDDL Header, with the fields - * enclosed by brackets [] replaced by your own identifying information: - * "Portions Copyrighted [year] [name of copyright owner]" - * ==================== - */ -#region Using directives - -using System; -using System.Reflection; -using System.Runtime.InteropServices; - -#endregion - -// General Information about an assembly is controlled through the following -// set of attributes. Change these attribute values to modify the information -// associated with an assembly. -[assembly: AssemblyTitle("ShellScriptExecutorFactory")] -[assembly: AssemblyDescription("")] -[assembly: AssemblyConfiguration("")] -[assembly: AssemblyCompany("")] -[assembly: AssemblyProduct("ShellScriptExecutorFactory")] -[assembly: AssemblyCopyright("")] -[assembly: AssemblyTrademark("")] -[assembly: AssemblyCulture("")] - -// This sets the default COM visibility of types in the assembly to invisible. -// If you need to expose a type to COM, use [ComVisible(true)] on that type. -[assembly: ComVisible(false)] - -// The assembly version has following format : -// -// Major.Minor.Build.Revision -// -// You can specify all the values or you can use the default the Revision and -// Build Numbers by using the '*' as shown below: -[assembly: AssemblyVersion("1.0.*")] diff --git a/DotNetConnectors.sln/ShellScriptExecutorFactory/ShellScriptExecutorFactory.cs b/DotNetConnectors.sln/ShellScriptExecutorFactory/ShellScriptExecutorFactory.cs deleted file mode 100644 index 60be5ff6..00000000 --- a/DotNetConnectors.sln/ShellScriptExecutorFactory/ShellScriptExecutorFactory.cs +++ /dev/null @@ -1,128 +0,0 @@ -/* - * ==================== - * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER. - * - * Copyright 2007-2008 Sun Microsystems, Inc. All rights reserved. - * - * The contents of this file are subject to the terms of the Common Development - * and Distribution License("CDDL") (the "License"). You may not use this file - * except in compliance with the License. - * - * You can obtain a copy of the License at - * http://IdentityConnectors.dev.java.net/legal/license.txt - * See the License for the specific language governing permissions and limitations - * under the License. - * - * When distributing the Covered Code, include this CDDL Header Notice in each file - * and include the License file at identityconnectors/legal/license.txt. - * If applicable, add the following below this CDDL Header, with the fields - * enclosed by brackets [] replaced by your own identifying information: - * "Portions Copyrighted [year] [name of copyright owner]" - * ==================== - */ -using System; -using System.IO; -using System.Security; -using System.Diagnostics; -using System.Reflection; -using System.Collections.Generic; -using System.Text.RegularExpressions; -using Org.IdentityConnectors.Common.Security; - -namespace Org.IdentityConnectors.Common.Script.Shell -{ - - /// - /// Process shell scripts. Valid arguments are below, the rest will be - /// used as environment variables. The return is the exit code of the - /// process. - ///
    - ///
  • USERNAME - name of the user to run this script as..
  • - ///
  • PASSWORD - (GuardedString or SecureString) password for the user to run this script as..
  • - ///
  • WORKINGDIR - working directory run this script in..
  • - ///
  • TIMEOUT - timeout waiting for script to finish in ms (default: 30 secs)
  • - ///
- ///
- [ScriptExecutorFactoryClass("Shell")] - public class ShellScriptExecutorFactory : ScriptExecutorFactory - { - - /// - /// Creates a script executor give the Shell script. - /// - override - public ScriptExecutor NewScriptExecutor(Assembly [] refs, string script, bool compile) { - return new ShellScriptExecutor(script); - } - - /// - /// Processes the script. - /// - class ShellScriptExecutor : ScriptExecutor { - private readonly string _script; - - public ShellScriptExecutor(string script) { - _script = script; - } - public object Execute(IDictionary arguments) { - // create the process info.. - Process process = new Process(); - // set the defaults.. - process.StartInfo.CreateNoWindow = true; - process.StartInfo.UseShellExecute = true; - // set the default timeout.. - int timeout = 1000 * 30; // 30 secss - // if there are any environment varibles set to false.. - process.StartInfo.UseShellExecute = arguments.Count == 0; - // take out username and password if they're in the options. - foreach (KeyValuePair kv in arguments) { - if (kv.Key.ToUpper().Equals("USERNAME")) { - string domainUser = kv.Value.ToString(); - string[] split = domainUser.Split(new char[] {'\\'}); - if (split.Length == 1) { - process.StartInfo.UserName = split[0]; - } else { - process.StartInfo.Domain = split[0]; - process.StartInfo.UserName = split[1]; - } - } else if (kv.Key.ToUpper().Equals("PASSWORD")) { - if (kv.Value is SecureString) { - process.StartInfo.Password = (SecureString)kv.Value; - } - else if (kv.Value is GuardedString) { - process.StartInfo.Password = ((GuardedString)kv.Value).ToSecureString(); - } else { - throw new ArgumentException("Invalid type for password."); - } - } else if (kv.Key.ToUpper().Equals("WORKINGDIR")) { - process.StartInfo.WorkingDirectory = kv.Value.ToString(); - } else if (kv.Key.ToUpper().Equals("TIMEOUT")) { - timeout = Int32.Parse(kv.Value.ToString()); - } else { - process.StartInfo.EnvironmentVariables[kv.Key] = kv.Value.ToString(); - } - } - // write out the script.. - string fn = Path.GetTempFileName() + ".cmd"; - using (StreamWriter sw = new StreamWriter(fn)) { - sw.Write(_script); - } - // set temp file.. - process.StartInfo.FileName = fn; - // execute script.. - process.Start(); - // wait for the process to exit.. - if (!process.WaitForExit(timeout)) { - process.Close(); - throw new TimeoutException("Script failed to exit in time!"); - } - int exitCode = process.ExitCode; - // close up the process and return the exit code.. - process.Close(); - // clean up temp file.. - File.Delete(fn); - return exitCode; - } - } - } -} diff --git a/DotNetConnectors.sln/ShellScriptExecutorFactory/ShellScriptExecutorFactory.csproj b/DotNetConnectors.sln/ShellScriptExecutorFactory/ShellScriptExecutorFactory.csproj deleted file mode 100644 index 0779540f..00000000 --- a/DotNetConnectors.sln/ShellScriptExecutorFactory/ShellScriptExecutorFactory.csproj +++ /dev/null @@ -1,81 +0,0 @@ - - - - {4700690A-2D29-40A0-86AC-E5A9F71A479A} - Debug - AnyCPU - Library - Sun.OpenConnectors.Common.Script.Shell - Shell.ScriptExecutorFactory - v3.5 - - - prompt - 4 - AnyCPU - bin\Debug\ - True - Full - False - True - DEBUG;TRACE - - - pdbonly - bin\Release\ - prompt - False - 4 - AnyCPU - None - True - False - TRACE - - - - - - 3.5 - - - - 3.5 - - - - - - - - - {F140E8DA-52B4-4159-992A-9DA10EA8EEFB} - Common - - - - - - - diff --git a/DotNetConnectors.sln/TestBundleV1/AssemblyInfo.cs b/DotNetConnectors.sln/TestBundleV1/AssemblyInfo.cs deleted file mode 100644 index f1b27d89..00000000 --- a/DotNetConnectors.sln/TestBundleV1/AssemblyInfo.cs +++ /dev/null @@ -1,57 +0,0 @@ -/* - * ==================== - * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER. - * - * Copyright 2007-2008 Sun Microsystems, Inc. All rights reserved. - * - * The contents of this file are subject to the terms of the Common Development - * and Distribution License("CDDL") (the "License"). You may not use this file - * except in compliance with the License. - * - * You can obtain a copy of the License at - * http://IdentityConnectors.dev.java.net/legal/license.txt - * See the License for the specific language governing permissions and limitations - * under the License. - * - * When distributing the Covered Code, include this CDDL Header Notice in each file - * and include the License file at identityconnectors/legal/license.txt. - * If applicable, add the following below this CDDL Header, with the fields - * enclosed by brackets [] replaced by your own identifying information: - * "Portions Copyrighted [year] [name of copyright owner]" - * ==================== - */ -#region Using directives - -using System; -using System.Reflection; -using System.Runtime.InteropServices; -using Org.IdentityConnectors.Framework.Spi; -using System.Resources; - -#endregion - -// General Information about an assembly is controlled through the following -// set of attributes. Change these attribute values to modify the information -// associated with an assembly. -[assembly: AssemblyTitle("TestBundleV1")] -[assembly: AssemblyDescription("")] -[assembly: AssemblyConfiguration("")] -[assembly: AssemblyCompany("")] -[assembly: AssemblyProduct("TestBundleV1")] -[assembly: AssemblyCopyright("")] -[assembly: AssemblyTrademark("")] -[assembly: AssemblyCulture("")] - -// This sets the default COM visibility of types in the assembly to invisible. -// If you need to expose a type to COM, use [ComVisible(true)] on that type. -[assembly: ComVisible(false)] - -// The assembly version has following format : -// -// Major.Minor.Build.Revision -// -// You can specify all the values or you can use the default the Revision and -// Build Numbers by using the '*' as shown below: -[assembly: AssemblyVersion("1.0.0.0")] -[assembly: NeutralResourcesLanguage("en-US")] - diff --git a/DotNetConnectors.sln/TestBundleV1/Messages.es-ES.resx b/DotNetConnectors.sln/TestBundleV1/Messages.es-ES.resx deleted file mode 100644 index ec894ca7..00000000 --- a/DotNetConnectors.sln/TestBundleV1/Messages.es-ES.resx +++ /dev/null @@ -1,126 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - text/microsoft-resx - - - 2.0 - - - System.Resources.ResXResourceReader, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 - - - System.Resources.ResXResourceWriter, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 - - - tstField.display_es-ES - - - tstField.help_es-ES - - \ No newline at end of file diff --git a/DotNetConnectors.sln/TestBundleV1/Messages.es.resx b/DotNetConnectors.sln/TestBundleV1/Messages.es.resx deleted file mode 100644 index 523473e4..00000000 --- a/DotNetConnectors.sln/TestBundleV1/Messages.es.resx +++ /dev/null @@ -1,126 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - text/microsoft-resx - - - 2.0 - - - System.Resources.ResXResourceReader, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 - - - System.Resources.ResXResourceWriter, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 - - - tstField.help_es - - - tstField.display_es - - \ No newline at end of file diff --git a/DotNetConnectors.sln/TestBundleV1/Messages.resx b/DotNetConnectors.sln/TestBundleV1/Messages.resx deleted file mode 100644 index a988e1e7..00000000 --- a/DotNetConnectors.sln/TestBundleV1/Messages.resx +++ /dev/null @@ -1,126 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - text/microsoft-resx - - - 2.0 - - - System.Resources.ResXResourceReader, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 - - - System.Resources.ResXResourceWriter, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 - - - Help for test field. - - - Display for test field. - - \ No newline at end of file diff --git a/DotNetConnectors.sln/TestBundleV1/TestBundleV1.csproj b/DotNetConnectors.sln/TestBundleV1/TestBundleV1.csproj deleted file mode 100644 index 0c67a441..00000000 --- a/DotNetConnectors.sln/TestBundleV1/TestBundleV1.csproj +++ /dev/null @@ -1,91 +0,0 @@ - - - - {0BC2A013-56FE-46DD-90FC-2D2D57748FB6} - Debug - AnyCPU - Library - TestBundleV1 - TestBundleV1.Connector - v3.5 - OnBuildSuccess - - - prompt - 4 - AnyCPU - bin\Debug\ - True - Full - False - True - DEBUG;TRACE - - - pdbonly - bin\Release\ - TRACE - prompt - 4 - AnyCPU - False - True - False - - - - - - - - - - - - - - Designer - - - Designer - - - Designer - - - - - {F140E8DA-52B4-4159-992A-9DA10EA8EEFB} - Common - - - {8B24461B-456A-4032-89A1-CD418F7B5B62} - Framework - - - - - - - diff --git a/DotNetConnectors.sln/TestBundleV1/TestConnector.cs b/DotNetConnectors.sln/TestBundleV1/TestConnector.cs deleted file mode 100644 index abc8f446..00000000 --- a/DotNetConnectors.sln/TestBundleV1/TestConnector.cs +++ /dev/null @@ -1,216 +0,0 @@ -/* - * ==================== - * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER. - * - * Copyright 2007-2008 Sun Microsystems, Inc. All rights reserved. - * - * The contents of this file are subject to the terms of the Common Development - * and Distribution License("CDDL") (the "License"). You may not use this file - * except in compliance with the License. - * - * You can obtain a copy of the License at - * http://IdentityConnectors.dev.java.net/legal/license.txt - * See the License for the specific language governing permissions and limitations - * under the License. - * - * When distributing the Covered Code, include this CDDL Header Notice in each file - * and include the License file at identityconnectors/legal/license.txt. - * If applicable, add the following below this CDDL Header, with the fields - * enclosed by brackets [] replaced by your own identifying information: - * "Portions Copyrighted [year] [name of copyright owner]" - * ==================== - */ -using System; -using System.Collections.Generic; -using System.Globalization; -using System.Threading; -using Org.IdentityConnectors.Common; -using Org.IdentityConnectors.Common.Pooling; -using Org.IdentityConnectors.Framework.Common.Objects; -using Org.IdentityConnectors.Framework.Common.Objects.Filters; -using Org.IdentityConnectors.Framework.Common.Exceptions; -using Org.IdentityConnectors.Framework.Spi; -using Org.IdentityConnectors.Framework.Spi.Operations; -namespace org.identityconnectors.testconnector -{ - public class TstConnectorConfig : AbstractConfiguration - { - /// - /// keep lower case for consistent unit tests - /// - [ConfigurationProperty(OperationTypes=new Type[]{typeof(SyncOp)})] - public string tstField{get;set;} - - /// - /// keep lower case for consistent unit tests - /// - public int numResults{get;set;} - - /// - /// keep lower case for consistent unit tests - /// - public bool failValidation{get;set;} - - /// - /// keep lower case for consistent unit tests - /// - public bool resetConnectionCount{get;set;} - - public override void Validate() { - if (failValidation) { - throw new ConnectorException("validation failed "+CultureInfo.CurrentUICulture.TwoLetterISOLanguageName); - } - } - - } - - public class MyTstConnection { - private readonly int _connectionNumber; - private bool _isGood = true; - - public MyTstConnection(int connectionNumber) { - _connectionNumber = connectionNumber; - } - - public void Test() { - if (!_isGood) { - throw new ConnectorException("Connection is bad"); - } - } - - public void Dispose() { - _isGood = false; - } - - public bool IsGood() { - return _isGood; - } - - public int GetConnectionNumber() { - return _connectionNumber; - } - } - - [ConnectorClass("TestConnector", - typeof(TstConnectorConfig), - MessageCatalogPaths= new String[]{"TestBundleV1.Messages"} - )] - public class TstConnector : CreateOp, PoolableConnector, SchemaOp, SearchOp, SyncOp - { - private static int _connectionCount = 0; - private MyTstConnection _myConnection; - private TstConnectorConfig _config; - - public Uid Create(ObjectClass oclass, ICollection attrs, OperationOptions options) { - int? delay = (int?)CollectionUtil.GetValue(options.Options,"delay",null); - if ( delay != null ) { - Thread.Sleep((int)delay); - } - - if ( options.Options.ContainsKey("testPooling")) { - return new Uid(_myConnection.GetConnectionNumber().ToString()); - } - else { - String version = "1.0"; - return new Uid(version); - } - } - public void Init(Configuration cfg) { - _config = (TstConnectorConfig)cfg; - if (_config.resetConnectionCount) { - _connectionCount = 0; - } - _myConnection = new MyTstConnection(_connectionCount++); - } - - public static String getVersion() - { - return "1.0"; - } - - public void Dispose() { - if (_myConnection != null) { - _myConnection.Dispose(); - _myConnection = null; - } - } - /** - * Used by the script tests - */ - public String concat(String s1, String s2) { - return s1+s2; - } - - public void CheckAlive() { - _myConnection.Test(); - } - - private class MyTranslator : AbstractFilterTranslator { - - } - public FilterTranslator CreateFilterTranslator(ObjectClass oclass, OperationOptions options) { - return new MyTranslator(); - } - public void ExecuteQuery(ObjectClass oclass, String query, ResultsHandler handler, OperationOptions options) { - - for ( int i = 0; i < _config.numResults; i++ ) { - int? delay = (int?)CollectionUtil.GetValue(options.Options,"delay",null); - if ( delay != null ) { - Thread.Sleep((int)delay); - } - ConnectorObjectBuilder builder = - new ConnectorObjectBuilder(); - builder.SetUid(""+i); - builder.SetName(i.ToString()); - builder.ObjectClass = oclass; - for ( int j = 0; j < 50; j++ ) { - builder.AddAttribute("myattribute"+j,"myvaluevaluevalue"+j); - } - ConnectorObject rv = builder.Build(); - if (!handler(rv)) { - break; - } - } - } - public void Sync(ObjectClass objClass, SyncToken token, - SyncResultsHandler handler, - OperationOptions options) { - for (int i = 0; i < _config.numResults; i++ ) { - ConnectorObjectBuilder obuilder = - new ConnectorObjectBuilder(); - obuilder.SetUid(i.ToString()); - obuilder.SetName(i.ToString()); - obuilder.ObjectClass=(objClass); - - SyncDeltaBuilder builder = - new SyncDeltaBuilder(); - builder.Object=(obuilder.Build()); - builder.DeltaType=(SyncDeltaType.CREATE_OR_UPDATE); - builder.Token=(new SyncToken("mytoken")); - SyncDelta rv = builder.Build(); - if (!handler(rv)) { - break; - } - } - } - - public SyncToken GetLatestSyncToken(ObjectClass objectClass) - { - return new SyncToken("mylatest"); - } - - public Schema Schema() { - SchemaBuilder builder = new SchemaBuilder(SafeType.Get()); - for ( int i = 0 ; i < 2; i++ ) { - ObjectClassInfoBuilder classBuilder = new ObjectClassInfoBuilder(); - classBuilder.ObjectType=("class"+i); - for ( int j = 0; j < 200; j++) { - classBuilder.AddAttributeInfo(ConnectorAttributeInfoBuilder.Build("attributename"+j, typeof(String))); - } - builder.DefineObjectClass(classBuilder.Build()); - } - return builder.Build(); - } - - } -} diff --git a/DotNetConnectors.sln/TestBundleV2/AssemblyInfo.cs b/DotNetConnectors.sln/TestBundleV2/AssemblyInfo.cs deleted file mode 100644 index 9c8de8e4..00000000 --- a/DotNetConnectors.sln/TestBundleV2/AssemblyInfo.cs +++ /dev/null @@ -1,55 +0,0 @@ -/* - * ==================== - * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER. - * - * Copyright 2007-2008 Sun Microsystems, Inc. All rights reserved. - * - * The contents of this file are subject to the terms of the Common Development - * and Distribution License("CDDL") (the "License"). You may not use this file - * except in compliance with the License. - * - * You can obtain a copy of the License at - * http://IdentityConnectors.dev.java.net/legal/license.txt - * See the License for the specific language governing permissions and limitations - * under the License. - * - * When distributing the Covered Code, include this CDDL Header Notice in each file - * and include the License file at identityconnectors/legal/license.txt. - * If applicable, add the following below this CDDL Header, with the fields - * enclosed by brackets [] replaced by your own identifying information: - * "Portions Copyrighted [year] [name of copyright owner]" - * ==================== - */ -#region Using directives - -using System; -using System.Reflection; -using System.Runtime.InteropServices; -using Org.IdentityConnectors.Framework.Spi; - -#endregion - -// General Information about an assembly is controlled through the following -// set of attributes. Change these attribute values to modify the information -// associated with an assembly. -[assembly: AssemblyTitle("TestBundleV2")] -[assembly: AssemblyDescription("")] -[assembly: AssemblyConfiguration("")] -[assembly: AssemblyCompany("")] -[assembly: AssemblyProduct("TestBundleV2")] -[assembly: AssemblyCopyright("")] -[assembly: AssemblyTrademark("")] -[assembly: AssemblyCulture("")] - -// This sets the default COM visibility of types in the assembly to invisible. -// If you need to expose a type to COM, use [ComVisible(true)] on that type. -[assembly: ComVisible(false)] - -// The assembly version has following format : -// -// Major.Minor.Build.Revision -// -// You can specify all the values or you can use the default the Revision and -// Build Numbers by using the '*' as shown below: -[assembly: AssemblyVersion("2.0.0.0")] - diff --git a/DotNetConnectors.sln/TestBundleV2/TestBundleV2.csproj b/DotNetConnectors.sln/TestBundleV2/TestBundleV2.csproj deleted file mode 100644 index 2b6e3cfe..00000000 --- a/DotNetConnectors.sln/TestBundleV2/TestBundleV2.csproj +++ /dev/null @@ -1,82 +0,0 @@ - - - - {3E737796-3A83-4924-9FF1-DC542F21F59E} - Debug - AnyCPU - Library - TestBundleV2 - TestBundleV2.Connector - v3.5 - OnBuildSuccess - - - prompt - 4 - AnyCPU - bin\Debug\ - True - Full - False - True - DEBUG;TRACE - - - pdbonly - bin\Release\ - TRACE - prompt - 4 - AnyCPU - False - True - False - - - - - - - - - - - - - - - - {F140E8DA-52B4-4159-992A-9DA10EA8EEFB} - Common - - - {8B24461B-456A-4032-89A1-CD418F7B5B62} - Framework - - - - - - - diff --git a/DotNetConnectors.sln/TestBundleV2/TestConnector.cs b/DotNetConnectors.sln/TestBundleV2/TestConnector.cs deleted file mode 100644 index cce29c4a..00000000 --- a/DotNetConnectors.sln/TestBundleV2/TestConnector.cs +++ /dev/null @@ -1,61 +0,0 @@ -/* - * ==================== - * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER. - * - * Copyright 2007-2008 Sun Microsystems, Inc. All rights reserved. - * - * The contents of this file are subject to the terms of the Common Development - * and Distribution License("CDDL") (the "License"). You may not use this file - * except in compliance with the License. - * - * You can obtain a copy of the License at - * http://IdentityConnectors.dev.java.net/legal/license.txt - * See the License for the specific language governing permissions and limitations - * under the License. - * - * When distributing the Covered Code, include this CDDL Header Notice in each file - * and include the License file at identityconnectors/legal/license.txt. - * If applicable, add the following below this CDDL Header, with the fields - * enclosed by brackets [] replaced by your own identifying information: - * "Portions Copyrighted [year] [name of copyright owner]" - * ==================== - */ -using System; -using System.Collections.Generic; -using System.Globalization; -using Org.IdentityConnectors.Common.Pooling; -using Org.IdentityConnectors.Framework.Common.Objects; -using Org.IdentityConnectors.Framework.Spi; -using Org.IdentityConnectors.Framework.Spi.Operations; -namespace org.identityconnectors.testconnector -{ - public class TstConnectorConfig : AbstractConfiguration - { - public override void Validate() { - } - } - - [ConnectorClass("TestConnector", - typeof(TstConnectorConfig), - MessageCatalogPaths=new String[]{"TestBundleV2.Messages"} - )] - public class TstConnector : CreateOp, Connector, SchemaOp - { - public Uid Create(ObjectClass oclass, ICollection attrs, OperationOptions options) { - String version = "2.0"; - return new Uid(version); - } - public void Init(Configuration cfg) { - } - - public void Dispose() { - - } - - public Schema Schema() { - return null; - } - - - } -} diff --git a/DotNetConnectors.sln/build.xml b/DotNetConnectors.sln/build.xml deleted file mode 100644 index 3a8cc532..00000000 --- a/DotNetConnectors.sln/build.xml +++ /dev/null @@ -1,82 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - \ No newline at end of file diff --git a/DotNetConnectors.sln/server.pfx b/DotNetConnectors.sln/server.pfx deleted file mode 100644 index 63f632a287bcbab06114bdde513c6b95c616be6d..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 1704 zcmY+Dc|6p47{`Aze*K1Q>@-RyjO5G?zsZ=HZN$hmMvf#`VMnfF4I$Tr#^#7D8sr|i z!V*e!IEzZDSSqvFvTg2;DNWL>{pr>2zV?sr^L?K8b9}#lJRg_`RiaT+Fc10xlG%~W zOcrlIVNe1dv=8J#b_lkCc~UL^OG#CLJgG7S7a=DflKtBC&3Y7Cz(ZewdFU2c8Cw4j zMuy9Q(yVc-fI8yT5E_kn1oF^Xbc#*1_s~A#@Ol4_{1b&gn@69;7rhl%cs$Nj91|Kl z&TeB+#~Ww+*jjI791dETOOBQ%BpL`0izlv+uW}8P0`+5(%UaPj6tfZ!?ScG&=PTJt z4ilYGH@7wP@sv}U^eg%ANA)I!qjVtOKy;3(`;vGab2$=t;J<@V?%Q9`ROFi_(#8t>&7 zoL3VNq~t7X0p}s>Y*Tg6`r|KBrdh+Fa@}bY{)0lp)s=Ow5(8HwGYok~CQoUfkF1DzRsa3I~Ea1Ek110bF;u_mz`&iFgr;+$nIZBc^HY!jcj5?7v@uP$E zjz^!n3+}a>-mSurz(Gsq$vUrm7K3s~?Q|BV*i4R_y4$<<$rNL>xfsAph$YLFg3Y5n z$@_E%bmSDaEo~g@Y3@@_YWAR>CYHJNvQgN##9`{Rk%f|Fr@Yk%*>F1`tfyY z!0~4o8lJ>{nVvC^{`6dxN*bg6YD|Z~Tf-@+YT3fPG2u?f;koDT{7v>@Cx*LC$t9U` zHkZ0ru*7OWqGd&7RqdZJxxrEZ|a{ zmsjQ$D~B3S+FG^lnay2Th%8qis4&?n+gtTdY9Ig5mD>Bw2+d|=g|7@=Y4LOZe!eE@ z__Yx z2>}9-$Q^;Vyp)})%qLo>_lTW-sup11OqYu+0e@2O3XPH)_YcN*zs%jTl> z&SmIaT!|Y#dfR%Yf=)5>n#b6_FRAC+Wd~^xo@IoncI?%0$=UsW^ATG{6mRk}&G)sy zygKvck-{ldNA=6t%T`hBqLQvMpIv)z4i`18f*)_y=iX-ztb{S%Q54|raojntLp6!f z2H@SzwIf$s-M94;tSTdadCoE5+!PwZLOvz%%#Ju|@}TI$L^R=YeG{Sy{P zx3_QMrT=z4HP}&iB&5=b0?XMgblcM&Hk}RBoQ{8bQJB<@7 zNRPa>_?}nf-Vc4X_uaB{et5==kTHuCL`+8Kk$}ig@8(@`62?G&@%0OpF@4rdQNEF_ zmXV`*-{kxFok=R+`c3^lm&CM`xMmomKI|6I$lN7cZQ8oNcu8O&`IF_43J! zrvv1`;DGt|ycVC7Snc|IH4Y%vHB{QHZ6?TWIjf_>9URCMZK)H@&HQyWY#yr>ACFeg zm~UJ-l!?_|6uC8I75=r Date: Mon, 22 Dec 2008 19:39:41 +0000 Subject: [PATCH 129/342] Resurected project correction --- DotNetConnectors.sln | 90 ++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 90 insertions(+) create mode 100644 DotNetConnectors.sln diff --git a/DotNetConnectors.sln b/DotNetConnectors.sln new file mode 100644 index 00000000..2b646b0e --- /dev/null +++ b/DotNetConnectors.sln @@ -0,0 +1,90 @@ +Microsoft Visual Studio Solution File, Format Version 10.00 +# Visual Studio 2008 +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Service", "Service\Service.csproj", "{A9D6374A-D51F-4FA3-8C02-5B1D23FAA82E}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Framework", "Framework\Framework.csproj", "{8B24461B-456A-4032-89A1-CD418F7B5B62}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Common", "Common\Common.csproj", "{F140E8DA-52B4-4159-992A-9DA10EA8EEFB}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "FrameworkTests", "FrameworkTests\FrameworkTests.csproj", "{32804F5A-812C-4FA6-835C-BDAE5B24D355}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "TestBundleV1", "TestBundleV1\TestBundleV1.csproj", "{0BC2A013-56FE-46DD-90FC-2D2D57748FB6}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "TestBundleV2", "TestBundleV2\TestBundleV2.csproj", "{3E737796-3A83-4924-9FF1-DC542F21F59E}" +EndProject +Project("{930C7802-8A8C-48F9-8165-68863BCCD9DD}") = "ServiceInstall", "ServiceInstall\ServiceInstall.wixproj", "{1CBA8F74-050C-432B-8437-08BD13BDC684}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Console", "Console\Console.csproj", "{D09E6A1F-55F6-4661-8E5A-C485E4B08BB3}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "FrameworkInternal", "FrameworkInternal\FrameworkInternal.csproj", "{5B011775-B121-4EEE-A410-BA2D2F5BFB8B}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "BooScriptExecutorFactory", "BooScriptExecutorFactory\BooScriptExecutorFactory.csproj", "{0747C440-70E4-4E63-9F9D-03B3A010C991}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "ShellScriptExecutorFactory", "ShellScriptExecutorFactory\ShellScriptExecutorFactory.csproj", "{4700690A-2D29-40A0-86AC-E5A9F71A479A}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "ActiveDirectoryConnector", "ActiveDirectoryConnector\ActiveDirectoryConnector.csproj", "{BDF495CA-0FCD-4E51-A871-D467CDE3B43E}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "ActiveDirectoryConnectorTests", "ActiveDirectoryConnectorTests\ActiveDirectoryConnectorTests.csproj", "{DB8F7DA9-11A2-4BEB-8C14-25D8229EBE7E}" +EndProject +Global + GlobalSection(SolutionConfigurationPlatforms) = preSolution + Debug|Any CPU = Debug|Any CPU + Release|Any CPU = Release|Any CPU + EndGlobalSection + GlobalSection(ProjectConfigurationPlatforms) = postSolution + {A9D6374A-D51F-4FA3-8C02-5B1D23FAA82E}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {A9D6374A-D51F-4FA3-8C02-5B1D23FAA82E}.Debug|Any CPU.Build.0 = Debug|Any CPU + {A9D6374A-D51F-4FA3-8C02-5B1D23FAA82E}.Release|Any CPU.ActiveCfg = Release|Any CPU + {A9D6374A-D51F-4FA3-8C02-5B1D23FAA82E}.Release|Any CPU.Build.0 = Release|Any CPU + {8B24461B-456A-4032-89A1-CD418F7B5B62}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {8B24461B-456A-4032-89A1-CD418F7B5B62}.Debug|Any CPU.Build.0 = Debug|Any CPU + {8B24461B-456A-4032-89A1-CD418F7B5B62}.Release|Any CPU.ActiveCfg = Release|Any CPU + {8B24461B-456A-4032-89A1-CD418F7B5B62}.Release|Any CPU.Build.0 = Release|Any CPU + {F140E8DA-52B4-4159-992A-9DA10EA8EEFB}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {F140E8DA-52B4-4159-992A-9DA10EA8EEFB}.Debug|Any CPU.Build.0 = Debug|Any CPU + {F140E8DA-52B4-4159-992A-9DA10EA8EEFB}.Release|Any CPU.ActiveCfg = Release|Any CPU + {F140E8DA-52B4-4159-992A-9DA10EA8EEFB}.Release|Any CPU.Build.0 = Release|Any CPU + {32804F5A-812C-4FA6-835C-BDAE5B24D355}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {32804F5A-812C-4FA6-835C-BDAE5B24D355}.Debug|Any CPU.Build.0 = Debug|Any CPU + {32804F5A-812C-4FA6-835C-BDAE5B24D355}.Release|Any CPU.ActiveCfg = Release|Any CPU + {32804F5A-812C-4FA6-835C-BDAE5B24D355}.Release|Any CPU.Build.0 = Release|Any CPU + {0BC2A013-56FE-46DD-90FC-2D2D57748FB6}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {0BC2A013-56FE-46DD-90FC-2D2D57748FB6}.Debug|Any CPU.Build.0 = Debug|Any CPU + {0BC2A013-56FE-46DD-90FC-2D2D57748FB6}.Release|Any CPU.ActiveCfg = Release|Any CPU + {0BC2A013-56FE-46DD-90FC-2D2D57748FB6}.Release|Any CPU.Build.0 = Release|Any CPU + {3E737796-3A83-4924-9FF1-DC542F21F59E}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {3E737796-3A83-4924-9FF1-DC542F21F59E}.Debug|Any CPU.Build.0 = Debug|Any CPU + {3E737796-3A83-4924-9FF1-DC542F21F59E}.Release|Any CPU.ActiveCfg = Release|Any CPU + {3E737796-3A83-4924-9FF1-DC542F21F59E}.Release|Any CPU.Build.0 = Release|Any CPU + {1CBA8F74-050C-432B-8437-08BD13BDC684}.Debug|Any CPU.ActiveCfg = Debug|x86 + {1CBA8F74-050C-432B-8437-08BD13BDC684}.Release|Any CPU.ActiveCfg = Release|x86 + {D09E6A1F-55F6-4661-8E5A-C485E4B08BB3}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {D09E6A1F-55F6-4661-8E5A-C485E4B08BB3}.Debug|Any CPU.Build.0 = Debug|Any CPU + {D09E6A1F-55F6-4661-8E5A-C485E4B08BB3}.Release|Any CPU.ActiveCfg = Release|Any CPU + {D09E6A1F-55F6-4661-8E5A-C485E4B08BB3}.Release|Any CPU.Build.0 = Release|Any CPU + {5B011775-B121-4EEE-A410-BA2D2F5BFB8B}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {5B011775-B121-4EEE-A410-BA2D2F5BFB8B}.Debug|Any CPU.Build.0 = Debug|Any CPU + {5B011775-B121-4EEE-A410-BA2D2F5BFB8B}.Release|Any CPU.ActiveCfg = Release|Any CPU + {5B011775-B121-4EEE-A410-BA2D2F5BFB8B}.Release|Any CPU.Build.0 = Release|Any CPU + {0747C440-70E4-4E63-9F9D-03B3A010C991}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {0747C440-70E4-4E63-9F9D-03B3A010C991}.Debug|Any CPU.Build.0 = Debug|Any CPU + {0747C440-70E4-4E63-9F9D-03B3A010C991}.Release|Any CPU.ActiveCfg = Release|Any CPU + {0747C440-70E4-4E63-9F9D-03B3A010C991}.Release|Any CPU.Build.0 = Release|Any CPU + {4700690A-2D29-40A0-86AC-E5A9F71A479A}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {4700690A-2D29-40A0-86AC-E5A9F71A479A}.Debug|Any CPU.Build.0 = Debug|Any CPU + {4700690A-2D29-40A0-86AC-E5A9F71A479A}.Release|Any CPU.ActiveCfg = Release|Any CPU + {4700690A-2D29-40A0-86AC-E5A9F71A479A}.Release|Any CPU.Build.0 = Release|Any CPU + {BDF495CA-0FCD-4E51-A871-D467CDE3B43E}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {BDF495CA-0FCD-4E51-A871-D467CDE3B43E}.Debug|Any CPU.Build.0 = Debug|Any CPU + {BDF495CA-0FCD-4E51-A871-D467CDE3B43E}.Release|Any CPU.ActiveCfg = Release|Any CPU + {BDF495CA-0FCD-4E51-A871-D467CDE3B43E}.Release|Any CPU.Build.0 = Release|Any CPU + {DB8F7DA9-11A2-4BEB-8C14-25D8229EBE7E}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {DB8F7DA9-11A2-4BEB-8C14-25D8229EBE7E}.Debug|Any CPU.Build.0 = Debug|Any CPU + {DB8F7DA9-11A2-4BEB-8C14-25D8229EBE7E}.Release|Any CPU.ActiveCfg = Release|Any CPU + {DB8F7DA9-11A2-4BEB-8C14-25D8229EBE7E}.Release|Any CPU.Build.0 = Release|Any CPU + EndGlobalSection + GlobalSection(SolutionProperties) = preSolution + HideSolutionNode = FALSE + EndGlobalSection +EndGlobal + From c93c8ea75847bafcaa5b986a6804bdabb7c36b00 Mon Sep 17 00:00:00 2001 From: petrjung Date: Mon, 22 Dec 2008 20:09:08 +0000 Subject: [PATCH 130/342] issue 323 Contract tests need to be run for Active Directory during the Hudson build. --- ServiceInstall/ExtBuild.proj | 2 +- build.xml | 14 ++++++++++++-- 2 files changed, 13 insertions(+), 3 deletions(-) diff --git a/ServiceInstall/ExtBuild.proj b/ServiceInstall/ExtBuild.proj index df976a43..018a4252 100644 --- a/ServiceInstall/ExtBuild.proj +++ b/ServiceInstall/ExtBuild.proj @@ -44,7 +44,7 @@ Lines="@(AllFiles->'<File Source="%(Fullpath)" Name="%(Filename)%(Extension)" />')"/> - + - + @@ -47,6 +47,16 @@ + + + + + + + + + + @@ -74,7 +84,7 @@ - + From 0826ec01581327eea855a6e9c03e7cd729173a5f Mon Sep 17 00:00:00 2001 From: petrjung Date: Mon, 22 Dec 2008 20:33:22 +0000 Subject: [PATCH 131/342] ServiceInstall revisioned --- ServiceInstall/ExtBuild.proj | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/ServiceInstall/ExtBuild.proj b/ServiceInstall/ExtBuild.proj index 018a4252..8664c400 100644 --- a/ServiceInstall/ExtBuild.proj +++ b/ServiceInstall/ExtBuild.proj @@ -55,7 +55,11 @@ - + + + + + From df812c8297590584afabf7ec7b06889a5ef72345 Mon Sep 17 00:00:00 2001 From: petrjung Date: Mon, 22 Dec 2008 20:57:59 +0000 Subject: [PATCH 132/342] issue 323 --- ServiceInstall/ExtBuild.proj | 3 --- ServiceInstall/ServiceInstall.wixproj | 5 ++++- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/ServiceInstall/ExtBuild.proj b/ServiceInstall/ExtBuild.proj index 8664c400..81826820 100644 --- a/ServiceInstall/ExtBuild.proj +++ b/ServiceInstall/ExtBuild.proj @@ -55,10 +55,7 @@ - - - diff --git a/ServiceInstall/ServiceInstall.wixproj b/ServiceInstall/ServiceInstall.wixproj index 3012a219..55e35f96 100644 --- a/ServiceInstall/ServiceInstall.wixproj +++ b/ServiceInstall/ServiceInstall.wixproj @@ -64,4 +64,7 @@ - + + + + \ No newline at end of file From 9293d67590f2dd36f66c37d1a365a16f98446168 Mon Sep 17 00:00:00 2001 From: petrjung Date: Tue, 23 Dec 2008 09:24:49 +0000 Subject: [PATCH 133/342] 323 Contract tests need to be run for Active Directory during the Hudson build. AD part --- ActiveDirectoryConnector/build.xml | 2 +- build.xml | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/ActiveDirectoryConnector/build.xml b/ActiveDirectoryConnector/build.xml index 5972f10c..59ae80bd 100644 --- a/ActiveDirectoryConnector/build.xml +++ b/ActiveDirectoryConnector/build.xml @@ -20,7 +20,7 @@ "Portions Copyrighted [year] [name of copyright owner]" ==================== --> - + diff --git a/build.xml b/build.xml index f8c452e4..80598830 100644 --- a/build.xml +++ b/build.xml @@ -84,8 +84,8 @@ - - + + From b83e4938db35e7b8e337ce4fb0fd6e17e77cd4db Mon Sep 17 00:00:00 2001 From: petrjung Date: Tue, 23 Dec 2008 15:42:11 +0000 Subject: [PATCH 134/342] 323, the build performance --- DotNetConnectors.sln | 3 ++- Service/Service.csproj | 2 +- ServiceInstall/ServiceInstall.wixproj | 2 +- build.xml | 4 ++-- 4 files changed, 6 insertions(+), 5 deletions(-) diff --git a/DotNetConnectors.sln b/DotNetConnectors.sln index 2b646b0e..f10eadd6 100644 --- a/DotNetConnectors.sln +++ b/DotNetConnectors.sln @@ -57,7 +57,9 @@ Global {3E737796-3A83-4924-9FF1-DC542F21F59E}.Release|Any CPU.ActiveCfg = Release|Any CPU {3E737796-3A83-4924-9FF1-DC542F21F59E}.Release|Any CPU.Build.0 = Release|Any CPU {1CBA8F74-050C-432B-8437-08BD13BDC684}.Debug|Any CPU.ActiveCfg = Debug|x86 + {1CBA8F74-050C-432B-8437-08BD13BDC684}.Debug|Any CPU.Build.0 = Debug|x86 {1CBA8F74-050C-432B-8437-08BD13BDC684}.Release|Any CPU.ActiveCfg = Release|x86 + {1CBA8F74-050C-432B-8437-08BD13BDC684}.Release|Any CPU.Build.0 = Release|x86 {D09E6A1F-55F6-4661-8E5A-C485E4B08BB3}.Debug|Any CPU.ActiveCfg = Debug|Any CPU {D09E6A1F-55F6-4661-8E5A-C485E4B08BB3}.Debug|Any CPU.Build.0 = Debug|Any CPU {D09E6A1F-55F6-4661-8E5A-C485E4B08BB3}.Release|Any CPU.ActiveCfg = Release|Any CPU @@ -87,4 +89,3 @@ Global HideSolutionNode = FALSE EndGlobalSection EndGlobal - diff --git a/Service/Service.csproj b/Service/Service.csproj index ea668380..0033e118 100644 --- a/Service/Service.csproj +++ b/Service/Service.csproj @@ -98,7 +98,7 @@ - + diff --git a/ServiceInstall/ServiceInstall.wixproj b/ServiceInstall/ServiceInstall.wixproj index 55e35f96..dc8af861 100644 --- a/ServiceInstall/ServiceInstall.wixproj +++ b/ServiceInstall/ServiceInstall.wixproj @@ -67,4 +67,4 @@ - \ No newline at end of file + diff --git a/build.xml b/build.xml index 80598830..e8961e8d 100644 --- a/build.xml +++ b/build.xml @@ -84,8 +84,8 @@ - - + + From 2b8bdab8aa2e3e432d20b3917c66a188c8ed0c75 Mon Sep 17 00:00:00 2001 From: petrjung Date: Tue, 23 Dec 2008 18:02:38 +0000 Subject: [PATCH 135/342] 323, build fix, v2 --- DotNetConnectors.sln | 81 +++++++++++++++------------ Service/Service.csproj | 9 ++- ServiceInstall/ServiceInstall.wixproj | 6 +- 3 files changed, 56 insertions(+), 40 deletions(-) diff --git a/DotNetConnectors.sln b/DotNetConnectors.sln index f10eadd6..14b67eb8 100644 --- a/DotNetConnectors.sln +++ b/DotNetConnectors.sln @@ -1,5 +1,7 @@ + Microsoft Visual Studio Solution File, Format Version 10.00 # Visual Studio 2008 +# SharpDevelop 3.0.0.3437 Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Service", "Service\Service.csproj", "{A9D6374A-D51F-4FA3-8C02-5B1D23FAA82E}" EndProject Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Framework", "Framework\Framework.csproj", "{8B24461B-456A-4032-89A1-CD418F7B5B62}" @@ -12,7 +14,7 @@ Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "TestBundleV1", "TestBundleV EndProject Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "TestBundleV2", "TestBundleV2\TestBundleV2.csproj", "{3E737796-3A83-4924-9FF1-DC542F21F59E}" EndProject -Project("{930C7802-8A8C-48F9-8165-68863BCCD9DD}") = "ServiceInstall", "ServiceInstall\ServiceInstall.wixproj", "{1CBA8F74-050C-432B-8437-08BD13BDC684}" +Project("{CFEE4113-1246-4D54-95CB-156813CB8593}") = "ServiceInstall", "ServiceInstall\ServiceInstall.wixproj", "{1CBA8F74-050C-432B-8437-08BD13BDC684}" EndProject Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Console", "Console\Console.csproj", "{D09E6A1F-55F6-4661-8E5A-C485E4B08BB3}" EndProject @@ -32,60 +34,69 @@ Global Release|Any CPU = Release|Any CPU EndGlobalSection GlobalSection(ProjectConfigurationPlatforms) = postSolution + {A9D6374A-D51F-4FA3-8C02-5B1D23FAA82E}.Debug.ActiveCfg = Debug + {A9D6374A-D51F-4FA3-8C02-5B1D23FAA82E}.Debug.Build.0 = Debug {A9D6374A-D51F-4FA3-8C02-5B1D23FAA82E}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {A9D6374A-D51F-4FA3-8C02-5B1D23FAA82E}.Debug|Any CPU.Build.0 = Debug|Any CPU + {A9D6374A-D51F-4FA3-8C02-5B1D23FAA82E}.Release.ActiveCfg = Release + {A9D6374A-D51F-4FA3-8C02-5B1D23FAA82E}.Release.Build.0 = Release {A9D6374A-D51F-4FA3-8C02-5B1D23FAA82E}.Release|Any CPU.ActiveCfg = Release|Any CPU + {D09E6A1F-55F6-4661-8E5A-C485E4B08BB3}.Debug.ActiveCfg = Debug + {D09E6A1F-55F6-4661-8E5A-C485E4B08BB3}.Debug.Build.0 = Debug + {D09E6A1F-55F6-4661-8E5A-C485E4B08BB3}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {D09E6A1F-55F6-4661-8E5A-C485E4B08BB3}.Release.ActiveCfg = Release + {D09E6A1F-55F6-4661-8E5A-C485E4B08BB3}.Release.Build.0 = Release + {D09E6A1F-55F6-4661-8E5A-C485E4B08BB3}.Release|Any CPU.ActiveCfg = Release|Any CPU + {D09E6A1F-55F6-4661-8E5A-C485E4B08BB3}.Debug|Any CPU.Build.0 = Debug|Any CPU + {D09E6A1F-55F6-4661-8E5A-C485E4B08BB3}.Release|Any CPU.Build.0 = Release|Any CPU + {A9D6374A-D51F-4FA3-8C02-5B1D23FAA82E}.Debug|Any CPU.Build.0 = Debug|Any CPU {A9D6374A-D51F-4FA3-8C02-5B1D23FAA82E}.Release|Any CPU.Build.0 = Release|Any CPU - {8B24461B-456A-4032-89A1-CD418F7B5B62}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {8B24461B-456A-4032-89A1-CD418F7B5B62}.Debug.Build.0 = Debug + {8B24461B-456A-4032-89A1-CD418F7B5B62}.Debug.ActiveCfg = Debug + {8B24461B-456A-4032-89A1-CD418F7B5B62}.Release.Build.0 = Release + {8B24461B-456A-4032-89A1-CD418F7B5B62}.Release.ActiveCfg = Release {8B24461B-456A-4032-89A1-CD418F7B5B62}.Debug|Any CPU.Build.0 = Debug|Any CPU - {8B24461B-456A-4032-89A1-CD418F7B5B62}.Release|Any CPU.ActiveCfg = Release|Any CPU + {8B24461B-456A-4032-89A1-CD418F7B5B62}.Debug|Any CPU.ActiveCfg = Debug|Any CPU {8B24461B-456A-4032-89A1-CD418F7B5B62}.Release|Any CPU.Build.0 = Release|Any CPU - {F140E8DA-52B4-4159-992A-9DA10EA8EEFB}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {8B24461B-456A-4032-89A1-CD418F7B5B62}.Release|Any CPU.ActiveCfg = Release|Any CPU {F140E8DA-52B4-4159-992A-9DA10EA8EEFB}.Debug|Any CPU.Build.0 = Debug|Any CPU - {F140E8DA-52B4-4159-992A-9DA10EA8EEFB}.Release|Any CPU.ActiveCfg = Release|Any CPU + {F140E8DA-52B4-4159-992A-9DA10EA8EEFB}.Debug|Any CPU.ActiveCfg = Debug|Any CPU {F140E8DA-52B4-4159-992A-9DA10EA8EEFB}.Release|Any CPU.Build.0 = Release|Any CPU - {32804F5A-812C-4FA6-835C-BDAE5B24D355}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {F140E8DA-52B4-4159-992A-9DA10EA8EEFB}.Release|Any CPU.ActiveCfg = Release|Any CPU {32804F5A-812C-4FA6-835C-BDAE5B24D355}.Debug|Any CPU.Build.0 = Debug|Any CPU - {32804F5A-812C-4FA6-835C-BDAE5B24D355}.Release|Any CPU.ActiveCfg = Release|Any CPU + {32804F5A-812C-4FA6-835C-BDAE5B24D355}.Debug|Any CPU.ActiveCfg = Debug|Any CPU {32804F5A-812C-4FA6-835C-BDAE5B24D355}.Release|Any CPU.Build.0 = Release|Any CPU - {0BC2A013-56FE-46DD-90FC-2D2D57748FB6}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {32804F5A-812C-4FA6-835C-BDAE5B24D355}.Release|Any CPU.ActiveCfg = Release|Any CPU {0BC2A013-56FE-46DD-90FC-2D2D57748FB6}.Debug|Any CPU.Build.0 = Debug|Any CPU - {0BC2A013-56FE-46DD-90FC-2D2D57748FB6}.Release|Any CPU.ActiveCfg = Release|Any CPU + {0BC2A013-56FE-46DD-90FC-2D2D57748FB6}.Debug|Any CPU.ActiveCfg = Debug|Any CPU {0BC2A013-56FE-46DD-90FC-2D2D57748FB6}.Release|Any CPU.Build.0 = Release|Any CPU - {3E737796-3A83-4924-9FF1-DC542F21F59E}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {0BC2A013-56FE-46DD-90FC-2D2D57748FB6}.Release|Any CPU.ActiveCfg = Release|Any CPU {3E737796-3A83-4924-9FF1-DC542F21F59E}.Debug|Any CPU.Build.0 = Debug|Any CPU - {3E737796-3A83-4924-9FF1-DC542F21F59E}.Release|Any CPU.ActiveCfg = Release|Any CPU + {3E737796-3A83-4924-9FF1-DC542F21F59E}.Debug|Any CPU.ActiveCfg = Debug|Any CPU {3E737796-3A83-4924-9FF1-DC542F21F59E}.Release|Any CPU.Build.0 = Release|Any CPU - {1CBA8F74-050C-432B-8437-08BD13BDC684}.Debug|Any CPU.ActiveCfg = Debug|x86 - {1CBA8F74-050C-432B-8437-08BD13BDC684}.Debug|Any CPU.Build.0 = Debug|x86 - {1CBA8F74-050C-432B-8437-08BD13BDC684}.Release|Any CPU.ActiveCfg = Release|x86 - {1CBA8F74-050C-432B-8437-08BD13BDC684}.Release|Any CPU.Build.0 = Release|x86 - {D09E6A1F-55F6-4661-8E5A-C485E4B08BB3}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {D09E6A1F-55F6-4661-8E5A-C485E4B08BB3}.Debug|Any CPU.Build.0 = Debug|Any CPU - {D09E6A1F-55F6-4661-8E5A-C485E4B08BB3}.Release|Any CPU.ActiveCfg = Release|Any CPU - {D09E6A1F-55F6-4661-8E5A-C485E4B08BB3}.Release|Any CPU.Build.0 = Release|Any CPU - {5B011775-B121-4EEE-A410-BA2D2F5BFB8B}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {3E737796-3A83-4924-9FF1-DC542F21F59E}.Release|Any CPU.ActiveCfg = Release|Any CPU + {1CBA8F74-050C-432B-8437-08BD13BDC684}.Debug|Any CPU.Build.0 = Debug|Any CPU + {1CBA8F74-050C-432B-8437-08BD13BDC684}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {1CBA8F74-050C-432B-8437-08BD13BDC684}.Release|Any CPU.Build.0 = Release|Any CPU + {1CBA8F74-050C-432B-8437-08BD13BDC684}.Release|Any CPU.ActiveCfg = Release|Any CPU {5B011775-B121-4EEE-A410-BA2D2F5BFB8B}.Debug|Any CPU.Build.0 = Debug|Any CPU - {5B011775-B121-4EEE-A410-BA2D2F5BFB8B}.Release|Any CPU.ActiveCfg = Release|Any CPU + {5B011775-B121-4EEE-A410-BA2D2F5BFB8B}.Debug|Any CPU.ActiveCfg = Debug|Any CPU {5B011775-B121-4EEE-A410-BA2D2F5BFB8B}.Release|Any CPU.Build.0 = Release|Any CPU - {0747C440-70E4-4E63-9F9D-03B3A010C991}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {5B011775-B121-4EEE-A410-BA2D2F5BFB8B}.Release|Any CPU.ActiveCfg = Release|Any CPU {0747C440-70E4-4E63-9F9D-03B3A010C991}.Debug|Any CPU.Build.0 = Debug|Any CPU - {0747C440-70E4-4E63-9F9D-03B3A010C991}.Release|Any CPU.ActiveCfg = Release|Any CPU + {0747C440-70E4-4E63-9F9D-03B3A010C991}.Debug|Any CPU.ActiveCfg = Debug|Any CPU {0747C440-70E4-4E63-9F9D-03B3A010C991}.Release|Any CPU.Build.0 = Release|Any CPU - {4700690A-2D29-40A0-86AC-E5A9F71A479A}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {4700690A-2D29-40A0-86AC-E5A9F71A479A}.Debug|Any CPU.Build.0 = Debug|Any CPU - {4700690A-2D29-40A0-86AC-E5A9F71A479A}.Release|Any CPU.ActiveCfg = Release|Any CPU - {4700690A-2D29-40A0-86AC-E5A9F71A479A}.Release|Any CPU.Build.0 = Release|Any CPU - {BDF495CA-0FCD-4E51-A871-D467CDE3B43E}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {0747C440-70E4-4E63-9F9D-03B3A010C991}.Release|Any CPU.ActiveCfg = Release|Any CPU {BDF495CA-0FCD-4E51-A871-D467CDE3B43E}.Debug|Any CPU.Build.0 = Debug|Any CPU - {BDF495CA-0FCD-4E51-A871-D467CDE3B43E}.Release|Any CPU.ActiveCfg = Release|Any CPU + {BDF495CA-0FCD-4E51-A871-D467CDE3B43E}.Debug|Any CPU.ActiveCfg = Debug|Any CPU {BDF495CA-0FCD-4E51-A871-D467CDE3B43E}.Release|Any CPU.Build.0 = Release|Any CPU - {DB8F7DA9-11A2-4BEB-8C14-25D8229EBE7E}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {BDF495CA-0FCD-4E51-A871-D467CDE3B43E}.Release|Any CPU.ActiveCfg = Release|Any CPU + {4700690A-2D29-40A0-86AC-E5A9F71A479A}.Debug|Any CPU.Build.0 = Debug|Any CPU + {4700690A-2D29-40A0-86AC-E5A9F71A479A}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {4700690A-2D29-40A0-86AC-E5A9F71A479A}.Release|Any CPU.Build.0 = Release|Any CPU + {4700690A-2D29-40A0-86AC-E5A9F71A479A}.Release|Any CPU.ActiveCfg = Release|Any CPU {DB8F7DA9-11A2-4BEB-8C14-25D8229EBE7E}.Debug|Any CPU.Build.0 = Debug|Any CPU - {DB8F7DA9-11A2-4BEB-8C14-25D8229EBE7E}.Release|Any CPU.ActiveCfg = Release|Any CPU + {DB8F7DA9-11A2-4BEB-8C14-25D8229EBE7E}.Debug|Any CPU.ActiveCfg = Debug|Any CPU {DB8F7DA9-11A2-4BEB-8C14-25D8229EBE7E}.Release|Any CPU.Build.0 = Release|Any CPU - EndGlobalSection - GlobalSection(SolutionProperties) = preSolution - HideSolutionNode = FALSE + {DB8F7DA9-11A2-4BEB-8C14-25D8229EBE7E}.Release|Any CPU.ActiveCfg = Release|Any CPU EndGlobalSection EndGlobal diff --git a/Service/Service.csproj b/Service/Service.csproj index 0033e118..d44c53c4 100644 --- a/Service/Service.csproj +++ b/Service/Service.csproj @@ -1,4 +1,4 @@ - - - - - - - - - - + diff --git a/ActiveDirectoryConnectorTests/Properties/AssemblyInfo.cs b/ActiveDirectoryConnectorTests/Properties/AssemblyInfo.cs deleted file mode 100644 index 5b458efe..00000000 --- a/ActiveDirectoryConnectorTests/Properties/AssemblyInfo.cs +++ /dev/null @@ -1,58 +0,0 @@ -/* - * ==================== - * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER. - * - * Copyright 2007-2008 Sun Microsystems, Inc. All rights reserved. - * - * The contents of this file are subject to the terms of the Common Development - * and Distribution License("CDDL") (the "License"). You may not use this file - * except in compliance with the License. - * - * You can obtain a copy of the License at - * http://IdentityConnectors.dev.java.net/legal/license.txt - * See the License for the specific language governing permissions and limitations - * under the License. - * - * When distributing the Covered Code, include this CDDL Header Notice in each file - * and include the License file at identityconnectors/legal/license.txt. - * If applicable, add the following below this CDDL Header, with the fields - * enclosed by brackets [] replaced by your own identifying information: - * "Portions Copyrighted [year] [name of copyright owner]" - * ==================== - */ -using System.Reflection; -using System.Runtime.CompilerServices; -using System.Runtime.InteropServices; - -// General Information about an assembly is controlled through the following -// set of attributes. Change these attribute values to modify the information -// associated with an assembly. -[assembly: AssemblyTitle("ActiveDirectoryConnectorTests")] -[assembly: AssemblyDescription("")] -[assembly: AssemblyConfiguration("")] -[assembly: AssemblyCompany("Sun Microsystems")] -[assembly: AssemblyProduct("ActiveDirectoryConnectorTests")] -[assembly: AssemblyCopyright("Copyright Sun Microsystems 2008")] -[assembly: AssemblyTrademark("")] -[assembly: AssemblyCulture("")] - -// Setting ComVisible to false makes the types in this assembly not visible -// to COM components. If you need to access a type in this assembly from -// COM, set the ComVisible attribute to true on that type. -[assembly: ComVisible(false)] - -// The following GUID is for the ID of the typelib if this project is exposed to COM -[assembly: Guid("bdb18bb6-ec8d-4a9b-a56b-7c600534f7f5")] - -// Version information for an assembly consists of the following four values: -// -// Major Version -// Minor Version -// Build Number -// Revision -// -// You can specify all the values or you can default the Build and Revision Numbers -// by using the '*' as shown below: -// [assembly: AssemblyVersion("1.0.*")] -[assembly: AssemblyVersion("1.0.0.0")] -[assembly: AssemblyFileVersion("1.0.0.0")] diff --git a/ActiveDirectoryConnectorTests/version.txt b/ActiveDirectoryConnectorTests/version.txt new file mode 100644 index 00000000..bd2666ab --- /dev/null +++ b/ActiveDirectoryConnectorTests/version.txt @@ -0,0 +1 @@ +1.0.0.0 \ No newline at end of file diff --git a/BooScriptExecutorFactory/AssemblyInfo.cs b/BooScriptExecutorFactory/AssemblyInfo.cs deleted file mode 100644 index 95eb3457..00000000 --- a/BooScriptExecutorFactory/AssemblyInfo.cs +++ /dev/null @@ -1,53 +0,0 @@ -/* - * ==================== - * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER. - * - * Copyright 2007-2008 Sun Microsystems, Inc. All rights reserved. - * - * The contents of this file are subject to the terms of the Common Development - * and Distribution License("CDDL") (the "License"). You may not use this file - * except in compliance with the License. - * - * You can obtain a copy of the License at - * http://IdentityConnectors.dev.java.net/legal/license.txt - * See the License for the specific language governing permissions and limitations - * under the License. - * - * When distributing the Covered Code, include this CDDL Header Notice in each file - * and include the License file at identityconnectors/legal/license.txt. - * If applicable, add the following below this CDDL Header, with the fields - * enclosed by brackets [] replaced by your own identifying information: - * "Portions Copyrighted [year] [name of copyright owner]" - * ==================== - */ -#region Using directives - -using System; -using System.Reflection; -using System.Runtime.InteropServices; - -#endregion - -// General Information about an assembly is controlled through the following -// set of attributes. Change these attribute values to modify the information -// associated with an assembly. -[assembly: AssemblyTitle("BooScriptExecutorFactory")] -[assembly: AssemblyDescription("")] -[assembly: AssemblyConfiguration("")] -[assembly: AssemblyCompany("")] -[assembly: AssemblyProduct("BooScriptExecutorFactory")] -[assembly: AssemblyCopyright("")] -[assembly: AssemblyTrademark("")] -[assembly: AssemblyCulture("")] - -// This sets the default COM visibility of types in the assembly to invisible. -// If you need to expose a type to COM, use [ComVisible(true)] on that type. -[assembly: ComVisible(false)] - -// The assembly version has following format : -// -// Major.Minor.Build.Revision -// -// You can specify all the values or you can use the default the Revision and -// Build Numbers by using the '*' as shown below: -[assembly: AssemblyVersion("1.0.*")] diff --git a/BooScriptExecutorFactory/BooScriptExecutorFactory.csproj b/BooScriptExecutorFactory/BooScriptExecutorFactory.csproj index 469a1f3b..9b8da6f1 100644 --- a/BooScriptExecutorFactory/BooScriptExecutorFactory.csproj +++ b/BooScriptExecutorFactory/BooScriptExecutorFactory.csproj @@ -51,8 +51,7 @@ False True False - - + lib\Boo.Lang.dll @@ -88,6 +87,7 @@ Common + diff --git a/BooScriptExecutorFactory/version.txt b/BooScriptExecutorFactory/version.txt new file mode 100644 index 00000000..bd2666ab --- /dev/null +++ b/BooScriptExecutorFactory/version.txt @@ -0,0 +1 @@ +1.0.0.0 \ No newline at end of file diff --git a/Common/AssemblyInfo.cs b/Common/AssemblyInfo.cs deleted file mode 100644 index 59a263db..00000000 --- a/Common/AssemblyInfo.cs +++ /dev/null @@ -1,53 +0,0 @@ -/* - * ==================== - * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER. - * - * Copyright 2007-2008 Sun Microsystems, Inc. All rights reserved. - * - * The contents of this file are subject to the terms of the Common Development - * and Distribution License("CDDL") (the "License"). You may not use this file - * except in compliance with the License. - * - * You can obtain a copy of the License at - * http://IdentityConnectors.dev.java.net/legal/license.txt - * See the License for the specific language governing permissions and limitations - * under the License. - * - * When distributing the Covered Code, include this CDDL Header Notice in each file - * and include the License file at identityconnectors/legal/license.txt. - * If applicable, add the following below this CDDL Header, with the fields - * enclosed by brackets [] replaced by your own identifying information: - * "Portions Copyrighted [year] [name of copyright owner]" - * ==================== - */ -#region Using directives - -using System; -using System.Reflection; -using System.Runtime.InteropServices; - -#endregion - -// General Information about an assembly is controlled through the following -// set of attributes. Change these attribute values to modify the information -// associated with an assembly. -[assembly: AssemblyTitle("Open Connectors Common")] -[assembly: AssemblyDescription("")] -[assembly: AssemblyConfiguration("")] -[assembly: AssemblyCompany("")] -[assembly: AssemblyProduct("Open Connectors Common")] -[assembly: AssemblyCopyright("")] -[assembly: AssemblyTrademark("")] -[assembly: AssemblyCulture("")] - -// This sets the default COM visibility of types in the assembly to invisible. -// If you need to expose a type to COM, use [ComVisible(true)] on that type. -[assembly: ComVisible(false)] - -// The assembly version has following format : -// -// Major.Minor.Build.Revision -// -// You can specify all the values or you can use the default the Revision and -// Build Numbers by using the '*' as shown below: -[assembly: AssemblyVersion("1.0.0.0")] diff --git a/Common/Common.csproj b/Common/Common.csproj index abf5a07e..0a98fa53 100644 --- a/Common/Common.csproj +++ b/Common/Common.csproj @@ -56,8 +56,8 @@ 4194304 AnyCPU 4096 - - + + diff --git a/Common/version.txt b/Common/version.txt new file mode 100644 index 00000000..bd2666ab --- /dev/null +++ b/Common/version.txt @@ -0,0 +1 @@ +1.0.0.0 \ No newline at end of file diff --git a/Console/AssemblyInfo.cs b/Console/AssemblyInfo.cs deleted file mode 100644 index f8507a15..00000000 --- a/Console/AssemblyInfo.cs +++ /dev/null @@ -1,53 +0,0 @@ -/* - * ==================== - * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER. - * - * Copyright 2007-2008 Sun Microsystems, Inc. All rights reserved. - * - * The contents of this file are subject to the terms of the Common Development - * and Distribution License("CDDL") (the "License"). You may not use this file - * except in compliance with the License. - * - * You can obtain a copy of the License at - * http://IdentityConnectors.dev.java.net/legal/license.txt - * See the License for the specific language governing permissions and limitations - * under the License. - * - * When distributing the Covered Code, include this CDDL Header Notice in each file - * and include the License file at identityconnectors/legal/license.txt. - * If applicable, add the following below this CDDL Header, with the fields - * enclosed by brackets [] replaced by your own identifying information: - * "Portions Copyrighted [year] [name of copyright owner]" - * ==================== - */ -#region Using directives - -using System.Reflection; -using System.Runtime.CompilerServices; -using System.Runtime.InteropServices; - -#endregion - -// General Information about an assembly is controlled through the following -// set of attributes. Change these attribute values to modify the information -// associated with an assembly. -[assembly: AssemblyTitle("Console")] -[assembly: AssemblyDescription("")] -[assembly: AssemblyConfiguration("")] -[assembly: AssemblyCompany("")] -[assembly: AssemblyProduct("Console")] -[assembly: AssemblyCopyright("")] -[assembly: AssemblyTrademark("")] -[assembly: AssemblyCulture("")] - -// This sets the default COM visibility of types in the assembly to invisible. -// If you need to expose a type to COM, use [ComVisible(true)] on that type. -[assembly: ComVisible(false)] - -// The assembly version has following format : -// -// Major.Minor.Build.Revision -// -// You can specify all the values or you can use the default the Revision and -// Build Numbers by using the '*' as shown below: -[assembly: AssemblyVersion("1.0.0.0")] diff --git a/Console/Console.csproj b/Console/Console.csproj index 1192908e..02e6ec98 100644 --- a/Console/Console.csproj +++ b/Console/Console.csproj @@ -57,10 +57,10 @@ False 4 - + - ..\..\..\..\..\Program Files\SharpDevelop\3.0\AddIns\AddIns\BackendBindings\BooBinding\Boo.Lang.dll + ..\..\..\..\..\..\..\Program Files\SharpDevelop\3.0\AddIns\AddIns\BackendBindings\BooBinding\Boo.Lang.dll diff --git a/Console/version.txt b/Console/version.txt new file mode 100644 index 00000000..bd2666ab --- /dev/null +++ b/Console/version.txt @@ -0,0 +1 @@ +1.0.0.0 \ No newline at end of file diff --git a/DotNetCommonBuild.Targets b/DotNetCommonBuild.Targets new file mode 100644 index 00000000..3ba320b3 --- /dev/null +++ b/DotNetCommonBuild.Targets @@ -0,0 +1,60 @@ + + + + 0 + $(MSBuildProjectDirectory)\version.txt + Sun Microsystems, Inc. + Copyright 2007-2008 Sun Microsystems, Inc. All rights reserved. + $(MSBuildProjectDirectory)\..\Build + + + + + + + + + + + + + + + + + + + + + + + + + CommonBeforeBuild; + $(BuildDependsOn); + CommonAfterBuild + + + $(CleanDependsOn); + CommonClean + + + + + + + + + + + + + diff --git a/ExchangeConnector/ExchangeConnector.csproj b/ExchangeConnector/ExchangeConnector.csproj index 7ba60d80..d4d6634e 100644 --- a/ExchangeConnector/ExchangeConnector.csproj +++ b/ExchangeConnector/ExchangeConnector.csproj @@ -33,7 +33,7 @@ Exchange.Connector v3.5 512 - C:\Documents and Settings\Administrator\Application Data\ICSharpCode/SharpDevelop3.0\Settings.SourceAnalysis + true true @@ -69,14 +69,14 @@ - + {BDF495CA-0FCD-4E51-A871-D467CDE3B43E} ActiveDirectoryConnector - False + True {F140E8DA-52B4-4159-992A-9DA10EA8EEFB} @@ -86,6 +86,7 @@ {8B24461B-456A-4032-89A1-CD418F7B5B62} Framework + False @@ -97,12 +98,5 @@ Designer - - - + + \ No newline at end of file diff --git a/ExchangeConnector/Properties/AssemblyInfo.cs b/ExchangeConnector/Properties/AssemblyInfo.cs deleted file mode 100644 index d1492654..00000000 --- a/ExchangeConnector/Properties/AssemblyInfo.cs +++ /dev/null @@ -1,58 +0,0 @@ -/* - * ==================== - * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER. - * - * Copyright 2007-2008 Sun Microsystems, Inc. All rights reserved. - * - * The contents of this file are subject to the terms of the Common Development - * and Distribution License("CDDL") (the "License"). You may not use this file - * except in compliance with the License. - * - * You can obtain a copy of the License at - * http://IdentityConnectors.dev.java.net/legal/license.txt - * See the License for the specific language governing permissions and limitations - * under the License. - * - * When distributing the Covered Code, include this CDDL Header Notice in each file - * and include the License file at identityconnectors/legal/license.txt. - * If applicable, add the following below this CDDL Header, with the fields - * enclosed by brackets [] replaced by your own identifying information: - * "Portions Copyrighted [year] [name of copyright owner]" - * ==================== - */ -using System.Reflection; -using System.Runtime.CompilerServices; -using System.Runtime.InteropServices; - -// General Information about an assembly is controlled through the following -// set of attributes. Change these attribute values to modify the information -// associated with an assembly. -[assembly: AssemblyTitle("ExchangeConnector")] -[assembly: AssemblyDescription("")] -[assembly: AssemblyConfiguration("")] -[assembly: AssemblyCompany("IDM")] -[assembly: AssemblyProduct("ExchangeConnector")] -[assembly: AssemblyCopyright("Copyright IDM 2008")] -[assembly: AssemblyTrademark("")] -[assembly: AssemblyCulture("")] - -// Setting ComVisible to false makes the types in this assembly not visible -// to COM components. If you need to access a type in this assembly from -// COM, set the ComVisible attribute to true on that type. -[assembly: ComVisible(false)] - -// The following GUID is for the ID of the typelib if this project is exposed to COM -[assembly: Guid("d23ec8a7-e491-4f91-bda5-8900423a410e")] - -// Version information for an assembly consists of the following four values: -// -// Major Version -// Minor Version -// Build Number -// Revision -// -// You can specify all the values or you can default the Build and Revision Numbers -// by using the '*' as shown below: -// [assembly: AssemblyVersion("1.0.*")] -[assembly: AssemblyVersion("1.0.0.0")] -[assembly: AssemblyFileVersion("1.0.0.0")] diff --git a/ExchangeConnector/build.xml b/ExchangeConnector/build.xml index f51519a7..630f6a7e 100644 --- a/ExchangeConnector/build.xml +++ b/ExchangeConnector/build.xml @@ -1,28 +1,44 @@ - - - - - - - + + + + + + + diff --git a/ExchangeConnector/version.txt b/ExchangeConnector/version.txt new file mode 100644 index 00000000..bd2666ab --- /dev/null +++ b/ExchangeConnector/version.txt @@ -0,0 +1 @@ +1.0.0.0 \ No newline at end of file diff --git a/Framework/AssemblyInfo.cs b/Framework/AssemblyInfo.cs deleted file mode 100644 index c33629ae..00000000 --- a/Framework/AssemblyInfo.cs +++ /dev/null @@ -1,53 +0,0 @@ -/* - * ==================== - * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER. - * - * Copyright 2007-2008 Sun Microsystems, Inc. All rights reserved. - * - * The contents of this file are subject to the terms of the Common Development - * and Distribution License("CDDL") (the "License"). You may not use this file - * except in compliance with the License. - * - * You can obtain a copy of the License at - * http://IdentityConnectors.dev.java.net/legal/license.txt - * See the License for the specific language governing permissions and limitations - * under the License. - * - * When distributing the Covered Code, include this CDDL Header Notice in each file - * and include the License file at identityconnectors/legal/license.txt. - * If applicable, add the following below this CDDL Header, with the fields - * enclosed by brackets [] replaced by your own identifying information: - * "Portions Copyrighted [year] [name of copyright owner]" - * ==================== - */ -#region Using directives - -using System.Reflection; -using System.Runtime.CompilerServices; -using System.Runtime.InteropServices; - -#endregion - -// General Information about an assembly is controlled through the following -// set of attributes. Change these attribute values to modify the information -// associated with an assembly. -[assembly: AssemblyTitle("Open Connectors Framework")] -[assembly: AssemblyDescription("")] -[assembly: AssemblyConfiguration("")] -[assembly: AssemblyCompany("")] -[assembly: AssemblyProduct("Open Connectors Framework")] -[assembly: AssemblyCopyright("")] -[assembly: AssemblyTrademark("")] -[assembly: AssemblyCulture("")] - -// This sets the default COM visibility of types in the assembly to invisible. -// If you need to expose a type to COM, use [ComVisible(true)] on that type. -[assembly: ComVisible(false)] - -// The assembly version has following format : -// -// Major.Minor.Build.Revision -// -// You can specify all the values or you can use the default the Revision and -// Build Numbers by using the '*' as shown below: -[assembly: AssemblyVersion("1.0.0.0")] diff --git a/Framework/Framework.csproj b/Framework/Framework.csproj index 4cecaa9d..2a8b6dd3 100644 --- a/Framework/Framework.csproj +++ b/Framework/Framework.csproj @@ -62,7 +62,7 @@ AnyCPU 4096 - + diff --git a/Framework/version.txt b/Framework/version.txt new file mode 100644 index 00000000..bd2666ab --- /dev/null +++ b/Framework/version.txt @@ -0,0 +1 @@ +1.0.0.0 \ No newline at end of file diff --git a/FrameworkInternal/AssemblyInfo.cs b/FrameworkInternal/AssemblyInfo.cs deleted file mode 100644 index 5c1c75d7..00000000 --- a/FrameworkInternal/AssemblyInfo.cs +++ /dev/null @@ -1,53 +0,0 @@ -/* - * ==================== - * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER. - * - * Copyright 2007-2008 Sun Microsystems, Inc. All rights reserved. - * - * The contents of this file are subject to the terms of the Common Development - * and Distribution License("CDDL") (the "License"). You may not use this file - * except in compliance with the License. - * - * You can obtain a copy of the License at - * http://IdentityConnectors.dev.java.net/legal/license.txt - * See the License for the specific language governing permissions and limitations - * under the License. - * - * When distributing the Covered Code, include this CDDL Header Notice in each file - * and include the License file at identityconnectors/legal/license.txt. - * If applicable, add the following below this CDDL Header, with the fields - * enclosed by brackets [] replaced by your own identifying information: - * "Portions Copyrighted [year] [name of copyright owner]" - * ==================== - */ -#region Using directives - -using System; -using System.Reflection; -using System.Runtime.InteropServices; - -#endregion - -// General Information about an assembly is controlled through the following -// set of attributes. Change these attribute values to modify the information -// associated with an assembly. -[assembly: AssemblyTitle("FrameworkInternal")] -[assembly: AssemblyDescription("")] -[assembly: AssemblyConfiguration("")] -[assembly: AssemblyCompany("")] -[assembly: AssemblyProduct("FrameworkInternal")] -[assembly: AssemblyCopyright("")] -[assembly: AssemblyTrademark("")] -[assembly: AssemblyCulture("")] - -// This sets the default COM visibility of types in the assembly to invisible. -// If you need to expose a type to COM, use [ComVisible(true)] on that type. -[assembly: ComVisible(false)] - -// The assembly version has following format : -// -// Major.Minor.Build.Revision -// -// You can specify all the values or you can use the default the Revision and -// Build Numbers by using the '*' as shown below: -[assembly: AssemblyVersion("1.0.0.0")] diff --git a/FrameworkInternal/FrameworkInternal.csproj b/FrameworkInternal/FrameworkInternal.csproj index 34ca21fd..58896f1c 100644 --- a/FrameworkInternal/FrameworkInternal.csproj +++ b/FrameworkInternal/FrameworkInternal.csproj @@ -61,7 +61,7 @@ AnyCPU 4096 - + diff --git a/FrameworkInternal/version.txt b/FrameworkInternal/version.txt new file mode 100644 index 00000000..bd2666ab --- /dev/null +++ b/FrameworkInternal/version.txt @@ -0,0 +1 @@ +1.0.0.0 \ No newline at end of file diff --git a/FrameworkTests/AssemblyInfo.cs b/FrameworkTests/AssemblyInfo.cs deleted file mode 100644 index 61f6c635..00000000 --- a/FrameworkTests/AssemblyInfo.cs +++ /dev/null @@ -1,53 +0,0 @@ -/* - * ==================== - * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER. - * - * Copyright 2007-2008 Sun Microsystems, Inc. All rights reserved. - * - * The contents of this file are subject to the terms of the Common Development - * and Distribution License("CDDL") (the "License"). You may not use this file - * except in compliance with the License. - * - * You can obtain a copy of the License at - * http://IdentityConnectors.dev.java.net/legal/license.txt - * See the License for the specific language governing permissions and limitations - * under the License. - * - * When distributing the Covered Code, include this CDDL Header Notice in each file - * and include the License file at identityconnectors/legal/license.txt. - * If applicable, add the following below this CDDL Header, with the fields - * enclosed by brackets [] replaced by your own identifying information: - * "Portions Copyrighted [year] [name of copyright owner]" - * ==================== - */ -#region Using directives - -using System; -using System.Reflection; -using System.Runtime.InteropServices; - -#endregion - -// General Information about an assembly is controlled through the following -// set of attributes. Change these attribute values to modify the information -// associated with an assembly. -[assembly: AssemblyTitle("FrameworkTests")] -[assembly: AssemblyDescription("")] -[assembly: AssemblyConfiguration("")] -[assembly: AssemblyCompany("")] -[assembly: AssemblyProduct("FrameworkTests")] -[assembly: AssemblyCopyright("")] -[assembly: AssemblyTrademark("")] -[assembly: AssemblyCulture("")] - -// This sets the default COM visibility of types in the assembly to invisible. -// If you need to expose a type to COM, use [ComVisible(true)] on that type. -[assembly: ComVisible(false)] - -// The assembly version has following format : -// -// Major.Minor.Build.Revision -// -// You can specify all the values or you can use the default the Revision and -// Build Numbers by using the '*' as shown below: -[assembly: AssemblyVersion("1.0.0.0")] diff --git a/FrameworkTests/FrameworkTests.csproj b/FrameworkTests/FrameworkTests.csproj index 8137402f..1411362e 100644 --- a/FrameworkTests/FrameworkTests.csproj +++ b/FrameworkTests/FrameworkTests.csproj @@ -46,7 +46,7 @@ False TRACE - + @@ -98,8 +98,7 @@ {5B011775-B121-4EEE-A410-BA2D2F5BFB8B} FrameworkInternal - - + diff --git a/FrameworkTests/version.txt b/FrameworkTests/version.txt new file mode 100644 index 00000000..bd2666ab --- /dev/null +++ b/FrameworkTests/version.txt @@ -0,0 +1 @@ +1.0.0.0 \ No newline at end of file diff --git a/Service/AssemblyInfo.cs b/Service/AssemblyInfo.cs deleted file mode 100644 index b5a7e910..00000000 --- a/Service/AssemblyInfo.cs +++ /dev/null @@ -1,53 +0,0 @@ -/* - * ==================== - * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER. - * - * Copyright 2007-2008 Sun Microsystems, Inc. All rights reserved. - * - * The contents of this file are subject to the terms of the Common Development - * and Distribution License("CDDL") (the "License"). You may not use this file - * except in compliance with the License. - * - * You can obtain a copy of the License at - * http://IdentityConnectors.dev.java.net/legal/license.txt - * See the License for the specific language governing permissions and limitations - * under the License. - * - * When distributing the Covered Code, include this CDDL Header Notice in each file - * and include the License file at identityconnectors/legal/license.txt. - * If applicable, add the following below this CDDL Header, with the fields - * enclosed by brackets [] replaced by your own identifying information: - * "Portions Copyrighted [year] [name of copyright owner]" - * ==================== - */ -#region Using directives - -using System.Reflection; -using System.Runtime.CompilerServices; -using System.Runtime.InteropServices; - -#endregion - -// General Information about an assembly is controlled through the following -// set of attributes. Change these attribute values to modify the information -// associated with an assembly. -[assembly: AssemblyTitle("Service")] -[assembly: AssemblyDescription("")] -[assembly: AssemblyConfiguration("")] -[assembly: AssemblyCompany("")] -[assembly: AssemblyProduct("Service")] -[assembly: AssemblyCopyright("")] -[assembly: AssemblyTrademark("")] -[assembly: AssemblyCulture("")] - -// This sets the default COM visibility of types in the assembly to invisible. -// If you need to expose a type to COM, use [ComVisible(true)] on that type. -[assembly: ComVisible(false)] - -// The assembly version has following format : -// -// Major.Minor.Build.Revision -// -// You can specify all the values or you can use the default the Revision and -// Build Numbers by using the '*' as shown below: -[assembly: AssemblyVersion("1.0.0.0")] diff --git a/Service/Service.csproj b/Service/Service.csproj index d44c53c4..3293e127 100644 --- a/Service/Service.csproj +++ b/Service/Service.csproj @@ -1,4 +1,4 @@ - - - - + + - - - - - - - - - + @@ -59,34 +50,25 @@ - - + + + - - - - - - - - + + - - - - - - - - - + + + + - - + + + From 4532752c5e80fc0ea19179966af8a8a9a09fad61 Mon Sep 17 00:00:00 2001 From: tknappek Date: Thu, 15 Jan 2009 21:47:07 +0000 Subject: [PATCH 143/342] Assembly name generating fixed --- ActiveDirectoryConnector/ActiveDirectoryConnector.csproj | 1 + BooScriptExecutorFactory/BooScriptExecutorFactory.csproj | 1 + Common/Common.csproj | 1 + Console/Console.csproj | 1 + DotNetCommonBuild.Targets | 4 +++- Framework/Framework.csproj | 1 + FrameworkInternal/FrameworkInternal.csproj | 1 + FrameworkTests/FrameworkTests.csproj | 1 + Service/Service.csproj | 1 + ShellScriptExecutorFactory/ShellScriptExecutorFactory.csproj | 1 + 10 files changed, 12 insertions(+), 1 deletion(-) diff --git a/ActiveDirectoryConnector/ActiveDirectoryConnector.csproj b/ActiveDirectoryConnector/ActiveDirectoryConnector.csproj index baa055a3..18df4ff2 100644 --- a/ActiveDirectoryConnector/ActiveDirectoryConnector.csproj +++ b/ActiveDirectoryConnector/ActiveDirectoryConnector.csproj @@ -28,6 +28,7 @@ Library Org.IdentityConnectors.ActiveDirectory ActiveDirectory.Connector + ActiveDirectoryConnector v3.5 true diff --git a/BooScriptExecutorFactory/BooScriptExecutorFactory.csproj b/BooScriptExecutorFactory/BooScriptExecutorFactory.csproj index 9b8da6f1..b560a68e 100644 --- a/BooScriptExecutorFactory/BooScriptExecutorFactory.csproj +++ b/BooScriptExecutorFactory/BooScriptExecutorFactory.csproj @@ -28,6 +28,7 @@ Library Org.IdentityConnectors.Common.Script.Boo Boo.ScriptExecutorFactory + BooScriptExecutorFactory v3.5 diff --git a/Common/Common.csproj b/Common/Common.csproj index 0a98fa53..ec840c45 100644 --- a/Common/Common.csproj +++ b/Common/Common.csproj @@ -28,6 +28,7 @@ Library Org.IdentityConnectors.Common Common + Open Connectors Common v3.5 True False diff --git a/Console/Console.csproj b/Console/Console.csproj index 02e6ec98..4132bb5e 100644 --- a/Console/Console.csproj +++ b/Console/Console.csproj @@ -28,6 +28,7 @@ WinExe Console Console + Console v3.5 diff --git a/DotNetCommonBuild.Targets b/DotNetCommonBuild.Targets index 3ba320b3..fed23f9a 100644 --- a/DotNetCommonBuild.Targets +++ b/DotNetCommonBuild.Targets @@ -18,7 +18,9 @@ Library Org.IdentityConnectors.Framework Framework + Open Connectors Framework v3.5 False False diff --git a/FrameworkInternal/FrameworkInternal.csproj b/FrameworkInternal/FrameworkInternal.csproj index 58896f1c..f007c6ae 100644 --- a/FrameworkInternal/FrameworkInternal.csproj +++ b/FrameworkInternal/FrameworkInternal.csproj @@ -28,6 +28,7 @@ Library Org.IdentityConnectors FrameworkInternal + FrameworkInternal v3.5 True False diff --git a/FrameworkTests/FrameworkTests.csproj b/FrameworkTests/FrameworkTests.csproj index 1411362e..a5af2546 100644 --- a/FrameworkTests/FrameworkTests.csproj +++ b/FrameworkTests/FrameworkTests.csproj @@ -28,6 +28,7 @@ Library FrameworkTests FrameworkTests + FrameworkTests v3.5 diff --git a/Service/Service.csproj b/Service/Service.csproj index 3293e127..d8271ae6 100644 --- a/Service/Service.csproj +++ b/Service/Service.csproj @@ -28,6 +28,7 @@ Exe Sun.OpenConnectors.Framework.Service ConnectorServer + Service v3.5 False false diff --git a/ShellScriptExecutorFactory/ShellScriptExecutorFactory.csproj b/ShellScriptExecutorFactory/ShellScriptExecutorFactory.csproj index cab70035..53200b04 100644 --- a/ShellScriptExecutorFactory/ShellScriptExecutorFactory.csproj +++ b/ShellScriptExecutorFactory/ShellScriptExecutorFactory.csproj @@ -28,6 +28,7 @@ Library Sun.OpenConnectors.Common.Script.Shell Shell.ScriptExecutorFactory + ShellScriptExecutorFactory v3.5 From b543ec84804d4cc1c7500db0568d467ebebef88f Mon Sep 17 00:00:00 2001 From: dvernon Date: Thu, 15 Jan 2009 23:37:33 +0000 Subject: [PATCH 144/342] Issue #392 - Removed GROUP_NAME and ObjectClass.GROUP --- .../ActiveDirectoryConnector.cs | 9 ++- .../ActiveDirectoryUtils.cs | 8 +-- .../CustomAttributeHandlers.cs | 4 +- ActiveDirectoryConnector/ObjectClasses.xml | 2 +- .../ActiveDirectoryConnectorTest.cs | 61 ++++++++++--------- 5 files changed, 44 insertions(+), 40 deletions(-) diff --git a/ActiveDirectoryConnector/ActiveDirectoryConnector.cs b/ActiveDirectoryConnector/ActiveDirectoryConnector.cs index 1672321b..0181781f 100644 --- a/ActiveDirectoryConnector/ActiveDirectoryConnector.cs +++ b/ActiveDirectoryConnector/ActiveDirectoryConnector.cs @@ -84,8 +84,11 @@ public class ActiveDirectoryConnector : CreateOp, Connector, SchemaOp, DeleteOp, public static readonly string ATT_USER_ACOUNT_CONTROL = "userAccountControl"; public static readonly string ATT_PASSWORD_NEVER_EXPIRES = "PasswordNeverExpires"; public static readonly string OBJECTCLASS_OU = "Organizational Unit"; + public static readonly string OBJECTCLASS_GROUP = "Group"; public static readonly ObjectClass ouObjectClass = new ObjectClass(OBJECTCLASS_OU); + public static readonly ObjectClass groupObjectClass = new ObjectClass(OBJECTCLASS_GROUP); + private static readonly string OLD_SEARCH_FILTER_STRING = "Search Filter String"; private static readonly string OLD_SEARCH_FILTER = "searchFilter"; @@ -148,7 +151,7 @@ public virtual Uid Create(ObjectClass oclass, ActiveDirectoryUtils.GetRelativeName(nameAttribute), _utils.GetADObjectClass(oclass)); - if (oclass.Equals(ObjectClass.GROUP)) + if (oclass.Equals(ActiveDirectoryConnector.groupObjectClass)) { ConnectorAttribute groupAttribute = ConnectorAttributeUtil.Find(ActiveDirectoryConnector.ATT_GROUP_TYPE, attributes); @@ -347,7 +350,7 @@ protected virtual IList> GetSupportedOperations(ObjectCla /// protected virtual IList> GetUnSupportedOperations(ObjectClass oc) { - if (oc.Equals(ObjectClass.GROUP) || oc.Equals(ouObjectClass)) + if (oc.Equals(ActiveDirectoryConnector.groupObjectClass) || oc.Equals(ouObjectClass)) { return new List> { SafeType.Get(), @@ -842,7 +845,7 @@ public virtual void Delete(ObjectClass objClass, Uid uid, OperationOptions optio DirectoryEntry parent = de.Parent; parent.Children.Remove(de); } - else if (objClass.Equals(ObjectClass.GROUP) || objClass.Equals(ouObjectClass)) + else if (objClass.Equals(ActiveDirectoryConnector.groupObjectClass) || objClass.Equals(ouObjectClass)) { // if it's a group or ou (container), delete this // entry and all it's children diff --git a/ActiveDirectoryConnector/ActiveDirectoryUtils.cs b/ActiveDirectoryConnector/ActiveDirectoryUtils.cs index 6804677c..45413f27 100644 --- a/ActiveDirectoryConnector/ActiveDirectoryUtils.cs +++ b/ActiveDirectoryConnector/ActiveDirectoryUtils.cs @@ -263,7 +263,7 @@ internal void UpdateADObject(ObjectClass oclass, HandleNameChange(type, directoryEntry, attributes); HandleContainerChange(type, directoryEntry, attributes, config); } - else if (oclass.Equals(ObjectClass.GROUP)) + else if (oclass.Equals(ActiveDirectoryConnector.groupObjectClass)) { // translate attribute passed in foreach (ConnectorAttribute attribute in attributes) @@ -473,13 +473,13 @@ internal String GetADObjectClass(ObjectClass oclass) if (oclass.Equals(ObjectClass.ACCOUNT)) { - return "User"; + return _configuration.ObjectClass; } - else if (oclass.Equals(ObjectClass.GROUP)) + else if (ActiveDirectoryConnector.groupObjectClass.Equals(oclass)) { return "Group"; } - else if ("ORGANIZATIONAL UNIT".Equals(oclass.GetObjectClassValue(), StringComparison.CurrentCultureIgnoreCase)) + else if (ActiveDirectoryConnector.ouObjectClass.Equals(oclass)) { return "organizationalUnit"; } diff --git a/ActiveDirectoryConnector/CustomAttributeHandlers.cs b/ActiveDirectoryConnector/CustomAttributeHandlers.cs index a9a5df8a..f700bc34 100644 --- a/ActiveDirectoryConnector/CustomAttributeHandlers.cs +++ b/ActiveDirectoryConnector/CustomAttributeHandlers.cs @@ -227,7 +227,7 @@ internal void UpdateDeFromCa(ObjectClass oclass, if(oclass.Equals(ObjectClass.ACCOUNT)) { ignoreList = IgnoreConnectorAttributeNames_account; } - else if (oclass.Equals(ObjectClass.GROUP)) + else if (oclass.Equals(ActiveDirectoryConnector.groupObjectClass)) { ignoreList = IgnoreConnectorAttributeNames_group; } @@ -393,7 +393,7 @@ internal void UpdateDeFromCa_OpAtt_Accounts(ObjectClass oclass, UpdateType type, DirectoryEntry directoryEntry, ConnectorAttribute attribute) { - if (ObjectClass.GROUP.Equals(oclass)) + if (ActiveDirectoryConnector.groupObjectClass.Equals(oclass)) { // create an 'attribute' with the real name, and then call the // generic version diff --git a/ActiveDirectoryConnector/ObjectClasses.xml b/ActiveDirectoryConnector/ObjectClasses.xml index c8a7ec29..98c3db2f 100644 --- a/ActiveDirectoryConnector/ObjectClasses.xml +++ b/ActiveDirectoryConnector/ObjectClasses.xml @@ -157,7 +157,7 @@ - + diff --git a/ActiveDirectoryConnectorTests/ActiveDirectoryConnectorTest.cs b/ActiveDirectoryConnectorTests/ActiveDirectoryConnectorTest.cs index 8b9d562a..c5546de3 100644 --- a/ActiveDirectoryConnectorTests/ActiveDirectoryConnectorTest.cs +++ b/ActiveDirectoryConnectorTests/ActiveDirectoryConnectorTest.cs @@ -118,7 +118,7 @@ public void TestSchema() foreach(ObjectClassInfo ocInfo in schema.ObjectClassInfo) { Assert.IsNotNull(ocInfo); Assert.That((ocInfo.ObjectType == ObjectClass.ACCOUNT.GetObjectClassValue()) - || (ocInfo.ObjectType == ObjectClass.GROUP.GetObjectClassValue()) + || (ocInfo.ObjectType == ActiveDirectoryConnector.OBJECTCLASS_GROUP) || (ocInfo.ObjectType == ActiveDirectoryConnector.OBJECTCLASS_OU)); Trace.WriteLine("****** " + ocInfo.ObjectType); @@ -207,18 +207,18 @@ public void TestBasics_Group() CreateGroupMember(connector))); // create object - uidToDelete = connector.Create(ObjectClass.GROUP, createAttributes, null); + uidToDelete = connector.Create(ActiveDirectoryConnector.groupObjectClass, createAttributes, null); Uid createUid = uidToDelete; Assert.IsNotNull(createUid); // find new object ... have to add groups to list of things to return OperationOptionsBuilder optionsBuilder = new OperationOptionsBuilder(); - ICollection attributesToGet = GetDefaultAttributesToGet(ObjectClass.GROUP); + ICollection attributesToGet = GetDefaultAttributesToGet(ActiveDirectoryConnector.groupObjectClass); attributesToGet.Add(PredefinedAttributes.ACCOUNTS_NAME); optionsBuilder.AttributesToGet = attributesToGet.ToArray(); ConnectorObject newObject = GetConnectorObjectFromUid(connector, - ObjectClass.GROUP, createUid, optionsBuilder.Build()); + ActiveDirectoryConnector.groupObjectClass, createUid, optionsBuilder.Build()); VerifyObject(createAttributes, newObject); // update the group - replace ICollection updateReplaceAttrs = @@ -233,7 +233,7 @@ public void TestBasics_Group() updateReplaceAttrs.Add(ConnectorAttributeBuilder.Build( "description", "New description")); uidToDelete = UpdateReplaceAndVerifyObject(connector, - ObjectClass.GROUP, createUid, updateReplaceAttrs); + ActiveDirectoryConnector.groupObjectClass, createUid, updateReplaceAttrs); Uid updateReplaceUid = uidToDelete; // update the group - add @@ -243,14 +243,14 @@ public void TestBasics_Group() CreateGroupMember(connector), CreateGroupMember(connector))); uidToDelete = UpdateAddAndVerifyUser(connector, - ObjectClass.GROUP, updateReplaceUid, updateAddAttrs, optionsBuilder.Build()); + ActiveDirectoryConnector.groupObjectClass, updateReplaceUid, updateAddAttrs, optionsBuilder.Build()); } finally { if (uidToDelete != null) { // delete user - DeleteAndVerifyObject(connector, ObjectClass.GROUP, uidToDelete, true, true); + DeleteAndVerifyObject(connector, ActiveDirectoryConnector.groupObjectClass, uidToDelete, true, true); } } } @@ -288,14 +288,14 @@ public void TestCreate_Group() try { ICollection createAttributes = GetNormalAttributes_Group(); - createUid = CreateAndVerifyObject(connector, ObjectClass.GROUP, createAttributes); + createUid = CreateAndVerifyObject(connector, ActiveDirectoryConnector.groupObjectClass, createAttributes); } finally { if (createUid != null) { //remove the one we created - DeleteAndVerifyObject(connector, ObjectClass.GROUP, + DeleteAndVerifyObject(connector, ActiveDirectoryConnector.groupObjectClass, createUid, false, true); } } @@ -539,12 +539,12 @@ public void TestSearchNoFilter_Group() int numCreated = 0; for (numCreated = 0; numCreated < 5; numCreated++) { - createdUids.Add(CreateAndVerifyObject(connector, - ObjectClass.GROUP, GetNormalAttributes_Group())); + createdUids.Add(CreateAndVerifyObject(connector, + ActiveDirectoryConnector.groupObjectClass, GetNormalAttributes_Group())); } ICollection results = TestHelpers.SearchToList(connector, - ObjectClass.GROUP, null); + ActiveDirectoryConnector.groupObjectClass, null); // not sure how many should be found ... it should find everything // it's hard to say how many that is, but it should at least find the @@ -562,7 +562,8 @@ public void TestSearchNoFilter_Group() if ((o is String) && (o != null)) { String stringValue = (String)o; - if (stringValue.ToUpper().Trim().Equals("GROUP")) + if (stringValue.ToUpper().Trim().Equals( + ActiveDirectoryConnector.OBJECTCLASS_GROUP, StringComparison.CurrentCultureIgnoreCase)) { foundCorrectObjectClass = true; } @@ -577,7 +578,7 @@ public void TestSearchNoFilter_Group() { if (uid != null) { - DeleteAndVerifyObject(connector, ObjectClass.GROUP, uid, false, true); + DeleteAndVerifyObject(connector, ActiveDirectoryConnector.groupObjectClass, uid, false, true); } } } @@ -664,18 +665,18 @@ public void TestSearchByName_group() try { - createUid = CreateAndVerifyObject(connector, ObjectClass.GROUP, + createUid = CreateAndVerifyObject(connector, ActiveDirectoryConnector.groupObjectClass, GetNormalAttributes_Group()); // find out what the name was ConnectorObject newObject = GetConnectorObjectFromUid(connector, - ObjectClass.GROUP, createUid); + ActiveDirectoryConnector.groupObjectClass, createUid); Name nameAttr = newObject.Name; Assert.IsNotNull(nameAttr); //search normally ICollection results = TestHelpers.SearchToList(connector, - ObjectClass.GROUP, FilterBuilder.EqualTo(nameAttr)); + ActiveDirectoryConnector.groupObjectClass, FilterBuilder.EqualTo(nameAttr)); // there really should only be one Assert.AreEqual(results.Count, 1); @@ -689,7 +690,7 @@ public void TestSearchByName_group() ConnectorAttribute nameUpper = ConnectorAttributeBuilder.Build( nameAttr.Name, nameAttr.GetNameValue().ToUpper()); results = TestHelpers.SearchToList(connector, - ObjectClass.GROUP, FilterBuilder.EqualTo(nameUpper)); + ActiveDirectoryConnector.groupObjectClass, FilterBuilder.EqualTo(nameUpper)); // there really should only be one Assert.AreEqual(results.Count, 1); @@ -703,7 +704,7 @@ public void TestSearchByName_group() ConnectorAttribute nameLower = ConnectorAttributeBuilder.Build( nameAttr.Name, nameAttr.GetNameValue().ToLower()); results = TestHelpers.SearchToList(connector, - ObjectClass.GROUP, FilterBuilder.EqualTo(nameLower)); + ActiveDirectoryConnector.groupObjectClass, FilterBuilder.EqualTo(nameLower)); // there really should only be one Assert.AreEqual(results.Count, 1); @@ -719,7 +720,7 @@ public void TestSearchByName_group() if (createUid != null) { //remove the one we created - DeleteAndVerifyObject(connector, ObjectClass.GROUP, + DeleteAndVerifyObject(connector, ActiveDirectoryConnector.groupObjectClass, createUid, false, true); } } @@ -1003,10 +1004,10 @@ public void TestAddGroup_Account() Assert.AreEqual(1, foundUserObjects.Count); groupUid = CreateAndVerifyObject(connector, - ObjectClass.GROUP, GetNormalAttributes_Group()); + ActiveDirectoryConnector.groupObjectClass, GetNormalAttributes_Group()); Filter groupUidFilter = FilterBuilder.EqualTo(groupUid); IList foundGroupObjects = - TestHelpers.SearchToList(connector, ObjectClass.GROUP, groupUidFilter); + TestHelpers.SearchToList(connector, ActiveDirectoryConnector.groupObjectClass, groupUidFilter); Assert.AreEqual(1, foundGroupObjects.Count); String groupName = foundGroupObjects[0].Name.GetNameValue(); @@ -1021,7 +1022,7 @@ public void TestAddGroup_Account() } finally { DeleteAndVerifyObject(connector, ObjectClass.ACCOUNT, userUid, false, false); - DeleteAndVerifyObject(connector, ObjectClass.GROUP, groupUid, false, false); + DeleteAndVerifyObject(connector, ActiveDirectoryConnector.groupObjectClass, groupUid, false, false); } } @@ -1103,9 +1104,9 @@ public void TestContainerChange_account() // create container for this test ICollection groupAttributes = GetNormalAttributes_Group(); createGroupUid = CreateAndVerifyObject(connector, - ObjectClass.GROUP, groupAttributes); + ActiveDirectoryConnector.groupObjectClass, groupAttributes); ICollection groupResults = TestHelpers.SearchToList( - connector, ObjectClass.GROUP, FilterBuilder.EqualTo(createGroupUid)); + connector, ActiveDirectoryConnector.groupObjectClass, FilterBuilder.EqualTo(createGroupUid)); Assert.AreEqual(1, groupResults.Count); Assert.AreEqual(createGroupUid, groupResults.ElementAt(0).Uid); ConnectorAttribute groupDnAttr = @@ -1147,7 +1148,7 @@ public void TestContainerChange_account() if (createGroupUid != null) { //remove the one we created - DeleteAndVerifyObject(connector, ObjectClass.GROUP, + DeleteAndVerifyObject(connector, ActiveDirectoryConnector.groupObjectClass, createGroupUid, false, true); } } @@ -1523,10 +1524,10 @@ public void TestShortnameAndDescription() "name", accountObject.GetAttributes())); Assert.AreEqual(accountShortName, accountDisplayName); - uidGroup = CreateObject(connector, ObjectClass.GROUP, groupAttributes); + uidGroup = CreateObject(connector, ActiveDirectoryConnector.groupObjectClass, groupAttributes); OperationOptionsBuilder groupOptionsBuilder = new OperationOptionsBuilder(); - ICollection groupAttributesToGet = GetDefaultAttributesToGet(ObjectClass.GROUP); + ICollection groupAttributesToGet = GetDefaultAttributesToGet(ActiveDirectoryConnector.groupObjectClass); groupAttributesToGet.Add(PredefinedAttributes.DESCRIPTION); groupAttributesToGet.Add(PredefinedAttributes.SHORT_NAME); groupAttributesToGet.Add("name"); @@ -1534,7 +1535,7 @@ public void TestShortnameAndDescription() groupOptionsBuilder.AttributesToGet = groupAttributesToGet.ToArray(); ConnectorObject groupObject = GetConnectorObjectFromUid(connector, - ObjectClass.GROUP, uidGroup, groupOptionsBuilder.Build()); + ActiveDirectoryConnector.groupObjectClass, uidGroup, groupOptionsBuilder.Build()); // compare description string foundGroupDescription = ConnectorAttributeUtil.GetStringValue( @@ -1587,7 +1588,7 @@ public void TestShortnameAndDescription() if (uidGroup != null) { //remove the one we created - DeleteAndVerifyObject(connector, ObjectClass.GROUP, + DeleteAndVerifyObject(connector, ActiveDirectoryConnector.groupObjectClass, uidGroup, false, true); } if (uidOu != null) From 677c6f047b9695c1039ac2a21de0f019fb624f16 Mon Sep 17 00:00:00 2001 From: dvernon Date: Fri, 16 Jan 2009 02:03:30 +0000 Subject: [PATCH 145/342] Check in --- ActiveDirectoryConnector/ActiveDirectoryUtils.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/ActiveDirectoryConnector/ActiveDirectoryUtils.cs b/ActiveDirectoryConnector/ActiveDirectoryUtils.cs index 45413f27..a1c51567 100644 --- a/ActiveDirectoryConnector/ActiveDirectoryUtils.cs +++ b/ActiveDirectoryConnector/ActiveDirectoryUtils.cs @@ -628,7 +628,7 @@ public static SecureString GetSecureString(String stringToSecure) } return secure; - } + } internal static string GetDnFromPath(string fullPath) { From 78f21a05bf23a8f7036c33045042bd9294dd93c7 Mon Sep 17 00:00:00 2001 From: tknappek Date: Fri, 16 Jan 2009 11:26:01 +0000 Subject: [PATCH 146/342] dotnet - setting appropriate bundle version for contract tests --- ActiveDirectoryConnector/build.xml | 2 +- DotNetCommonBuild.Targets | 25 +++++++++++++++++++++++++ build.xml | 13 +++++++------ 3 files changed, 33 insertions(+), 7 deletions(-) diff --git a/ActiveDirectoryConnector/build.xml b/ActiveDirectoryConnector/build.xml index 59ae80bd..9b6bed6d 100644 --- a/ActiveDirectoryConnector/build.xml +++ b/ActiveDirectoryConnector/build.xml @@ -22,7 +22,7 @@ --> - + diff --git a/DotNetCommonBuild.Targets b/DotNetCommonBuild.Targets index fed23f9a..987e510c 100644 --- a/DotNetCommonBuild.Targets +++ b/DotNetCommonBuild.Targets @@ -1,3 +1,25 @@ + @@ -17,6 +39,9 @@ + + - + + - + @@ -50,9 +51,9 @@ - - - + + + @@ -61,7 +62,7 @@ - + From 5419ca15e9a74c310230e27ed8cb6cebcecb6ed5 Mon Sep 17 00:00:00 2001 From: dvernon Date: Fri, 16 Jan 2009 18:14:00 +0000 Subject: [PATCH 147/342] Issue #371 - ObjectClass name should not be case sensitive --- Framework/CommonObjects.cs | 108 ++++++++++++++++++++++++++++--------- 1 file changed, 84 insertions(+), 24 deletions(-) diff --git a/Framework/CommonObjects.cs b/Framework/CommonObjects.cs index 3130ccfb..3084659e 100644 --- a/Framework/CommonObjects.cs +++ b/Framework/CommonObjects.cs @@ -1701,17 +1701,50 @@ public String GetObjectClassValue() { public String GetDisplayNameKey() { return "MESSAGE_OBJECT_CLASS_"+_type.ToUpper(); } + + /** + * Determines if the 'name' matches this {@link ObjectClass}. + * + * @param name + * case-insensitive string representation of the ObjectClass's + * type. + * @return true if the case-insensitive name is equal to + * that of the one in this {@link ObjectClass}. + */ + public bool Is(String name) { + return this._type.equalsIgnoreCase(name); + } public override int GetHashCode() { return _type.ToUpper().GetHashCode(); } - public override bool Equals(object o) { - if ( o is ObjectClass ) { - ObjectClass other = (ObjectClass)o; - return _type.Equals(other._type, StringComparison.CurrentCultureIgnoreCase); + public override bool Equals(object obj) { + // test identity + if(this == obj) + { + return true; } - return false; + + // test for null.. + if(obj == null) + { + return false; + } + + // test that the exact class matches + if(!(GetType().Equals(obj.GetType()))) + { + return false; + } + + ObjectClass other = (ObjectClass) obj; + + if(!Is(other._type)) { + return false; + } + + return true; } public override string ToString() @@ -1731,10 +1764,7 @@ public sealed class ObjectClassInfo { public ObjectClassInfo(String type, ICollection attrInfo, bool isContainer) { - if ( type == null ) { - throw new ArgumentException("Type cannot be null."); - } - + Assertions.NullCheck(type, "type"); _type = type; _info = CollectionUtil.NewReadOnlySet(attrInfo); _isContainer = isContainer; @@ -1758,6 +1788,19 @@ public String ObjectType { return this._type; } } + + /** + * Determines if the 'name' matches this {@link ObjectClassInfo}. + * + * @param name + * case-insensitive string representation of the ObjectClassInfo's + * type. + * @return true if the case insensitive type is equal to + * that of the one in this {@link ObjectClassInfo}. + */ + public bool Is(String name) { + return this._type.Equals(name, StringComparison.CurrentCultureIgnoreCase); + } public bool IsContainer { get { @@ -1769,22 +1812,39 @@ public override int GetHashCode() { return _type.ToUpper().GetHashCode(); } - public override bool Equals(Object o) { - ObjectClassInfo other = o as ObjectClassInfo; - if ( other != null ) { - if (!ObjectType.Equals(other.ObjectType, StringComparison.CurrentCultureIgnoreCase)) { - return false; - } - if (!CollectionUtil.Equals(ConnectorAttributeInfos, - other.ConnectorAttributeInfos)) { - return false; - } - if (_isContainer != other._isContainer) { - return false; - } + public override bool Equals(Object obj) { + // test identity + if (this == obj) { return true; } - return false; + + // test for null.. + if(obj == null) + { + return false; + } + + if (obj.GetType().Equals(this.GetType())) + { + return false; + } + + ObjectClassInfo other = obj as ObjectClassInfo; + + if (!ObjectType.Equals(other.ObjectType, StringComparison.CurrentCultureIgnoreCase)) { + return false; + } + + if (!CollectionUtil.Equals(ConnectorAttributeInfos, + other.ConnectorAttributeInfos)) { + return false; + } + + if (_isContainer != other._isContainer) { + return false; + } + + return true; } public override string ToString() @@ -2681,7 +2741,7 @@ public ICollection ObjectClassInfo { */ public ObjectClassInfo FindObjectClassInfo(String type) { foreach (ObjectClassInfo info in _declaredObjectClasses) { - if ( info.ObjectType.Equals(type, StringComparison.CurrentCultureIgnoreCase) ) { + if ( info.Is(type) ) { return info; } } From 5c8714968de9ae799a1749cb0bbccc8e08eac575 Mon Sep 17 00:00:00 2001 From: dvernon Date: Fri, 16 Jan 2009 19:51:21 +0000 Subject: [PATCH 148/342] Issue #371 - ObjectClass name should not be case sensitive - correcting error noted in build. --- Framework/CommonObjects.cs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/Framework/CommonObjects.cs b/Framework/CommonObjects.cs index 3084659e..1f68d33d 100644 --- a/Framework/CommonObjects.cs +++ b/Framework/CommonObjects.cs @@ -1712,7 +1712,7 @@ public String GetDisplayNameKey() { * that of the one in this {@link ObjectClass}. */ public bool Is(String name) { - return this._type.equalsIgnoreCase(name); + return this._type.Equals(name, StringComparison.CurrentCultureIgnoreCase); } public override int GetHashCode() { @@ -1824,7 +1824,7 @@ public override bool Equals(Object obj) { return false; } - if (obj.GetType().Equals(this.GetType())) + if (!obj.GetType().Equals(this.GetType())) { return false; } From 09fee784a08f9edbf4af239e4482f1904f5ac1c6 Mon Sep 17 00:00:00 2001 From: dvernon Date: Tue, 20 Jan 2009 20:33:09 +0000 Subject: [PATCH 149/342] Issue 403 - Fixes for passing Contract Tests. Handle dates properly, and also added Attribute normalization for groups predefined attribute. --- ActiveDirectoryConnector/ActiveDirectoryConnector.cs | 9 +++++++++ ActiveDirectoryConnector/CustomAttributeHandlers.cs | 6 ++++-- ActiveDirectoryConnector/ObjectClasses.xml | 2 +- 3 files changed, 14 insertions(+), 3 deletions(-) diff --git a/ActiveDirectoryConnector/ActiveDirectoryConnector.cs b/ActiveDirectoryConnector/ActiveDirectoryConnector.cs index 0181781f..c3110fab 100644 --- a/ActiveDirectoryConnector/ActiveDirectoryConnector.cs +++ b/ActiveDirectoryConnector/ActiveDirectoryConnector.cs @@ -1179,6 +1179,15 @@ public virtual ConnectorAttribute NormalizeAttribute(ObjectClass oclass, Connect return ConnectorAttributeBuilder.Build(attribute.Name, ActiveDirectoryUtils.NormalizeLdapString(nameValue)); } + else if (attribute.Name.Equals(PredefinedAttributes.GROUPS_NAME)) + { + IList groupValues = new List(); + foreach(String groupname in attribute.Value) + { + groupValues.Add(ActiveDirectoryUtils.NormalizeLdapString(groupname)); + } + return ConnectorAttributeBuilder.Build(attribute.Name, groupValues); + } return attribute; } diff --git a/ActiveDirectoryConnector/CustomAttributeHandlers.cs b/ActiveDirectoryConnector/CustomAttributeHandlers.cs index f700bc34..b2e170b0 100644 --- a/ActiveDirectoryConnector/CustomAttributeHandlers.cs +++ b/ActiveDirectoryConnector/CustomAttributeHandlers.cs @@ -982,9 +982,11 @@ private ConnectorAttribute GetCaFromDe_OpAtt_PasswordExpireDate( if (accountExpireAttribute != null) { long? expireValue = ConnectorAttributeUtil.GetLongValue(accountExpireAttribute); - if (expireValue != null) + // if value present and not set to never expires + if ((expireValue != null) && (!expireValue.Value.Equals(9223372036854775807))) { - return ConnectorAttributeBuilder.BuildPasswordExpirationDate(expireValue.Value); + DateTime expireDate = DateTime.FromFileTime(expireValue.Value); + return ConnectorAttributeBuilder.BuildPasswordExpirationDate(expireDate); } else { diff --git a/ActiveDirectoryConnector/ObjectClasses.xml b/ActiveDirectoryConnector/ObjectClasses.xml index 98c3db2f..f2824446 100644 --- a/ActiveDirectoryConnector/ObjectClasses.xml +++ b/ActiveDirectoryConnector/ObjectClasses.xml @@ -133,7 +133,7 @@ - + AttributeInfo name="@@ENABLE_DATE@@" type="long"/--> From 0fc693f0ecde8956694071401a958a40fc831b54 Mon Sep 17 00:00:00 2001 From: dvernon Date: Tue, 20 Jan 2009 20:55:42 +0000 Subject: [PATCH 150/342] Issue# 404 - Clean up failed creates. --- .../ActiveDirectoryConnector.cs | 24 +++++++++++++++++-- 1 file changed, 22 insertions(+), 2 deletions(-) diff --git a/ActiveDirectoryConnector/ActiveDirectoryConnector.cs b/ActiveDirectoryConnector/ActiveDirectoryConnector.cs index c3110fab..69cf1972 100644 --- a/ActiveDirectoryConnector/ActiveDirectoryConnector.cs +++ b/ActiveDirectoryConnector/ActiveDirectoryConnector.cs @@ -107,6 +107,8 @@ public virtual Uid Create(ObjectClass oclass, ICollection attributes, OperationOptions options) { Uid uid = null; + bool created = false; + DirectoryEntry newDe = null; // I had lots of problems here. Here are the things // that seemed to make everything work: @@ -147,7 +149,7 @@ public virtual Uid Create(ObjectClass oclass, // Get the correct container, and put the new user in it DirectoryEntry containerDe = new DirectoryEntry(ldapContainerPath, _configuration.DirectoryAdminName, _configuration.DirectoryAdminPassword); - DirectoryEntry newDe = containerDe.Children.Add( + newDe = containerDe.Children.Add( ActiveDirectoryUtils.GetRelativeName(nameAttribute), _utils.GetADObjectClass(oclass)); @@ -166,7 +168,7 @@ public virtual Uid Create(ObjectClass oclass, } newDe.CommitChanges(); - + created = true; // default to creating users enabled if ((ObjectClass.ACCOUNT.Equals(oclass)) && (ConnectorAttributeUtil.Find(OperationalAttributes.ENABLE_NAME, attributes) == null)) @@ -199,6 +201,24 @@ public virtual Uid Create(ObjectClass oclass, // the case of error Console.WriteLine("caught exception:" + exception); Trace.TraceError(exception.Message); + if (created) + { + // In the case of an exception, make sure we + // don't leave any partial objects around + newDe.DeleteTree(); + } + throw; + } + catch(Exception exception) + { + Console.WriteLine("caught exception:" + exception); + Trace.TraceError(exception.Message); + if (created) + { + // In the case of an exception, make sure we + // don't leave any partial objects around + newDe.DeleteTree(); + } throw; } From 4641196b040a6e75c49ca7ef1d70881808b2e0a6 Mon Sep 17 00:00:00 2001 From: dvernon Date: Tue, 20 Jan 2009 21:58:09 +0000 Subject: [PATCH 151/342] Remove ACCOUNTS from schema --- ActiveDirectoryConnector/ObjectClasses.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/ActiveDirectoryConnector/ObjectClasses.xml b/ActiveDirectoryConnector/ObjectClasses.xml index f2824446..ad869ee3 100644 --- a/ActiveDirectoryConnector/ObjectClasses.xml +++ b/ActiveDirectoryConnector/ObjectClasses.xml @@ -182,7 +182,7 @@ - + From 6ebc8242e4f9bc2d93d27b8ea99ca14134fe9ca5 Mon Sep 17 00:00:00 2001 From: dvernon Date: Wed, 21 Jan 2009 17:19:34 +0000 Subject: [PATCH 152/342] Issue #406 - Fixes for contract tests. --- ActiveDirectoryConnector/ObjectClasses.xml | 5 ++++- ActiveDirectoryConnector/TerminalServicesUtils.cs | 3 +-- ActiveDirectoryConnector/UserAccountControl.cs | 8 ++++++-- 3 files changed, 11 insertions(+), 5 deletions(-) diff --git a/ActiveDirectoryConnector/ObjectClasses.xml b/ActiveDirectoryConnector/ObjectClasses.xml index ad869ee3..65c2713a 100644 --- a/ActiveDirectoryConnector/ObjectClasses.xml +++ b/ActiveDirectoryConnector/ObjectClasses.xml @@ -118,7 +118,10 @@ - + + + + /> diff --git a/ActiveDirectoryConnector/TerminalServicesUtils.cs b/ActiveDirectoryConnector/TerminalServicesUtils.cs index 599506d2..fbf749d0 100644 --- a/ActiveDirectoryConnector/TerminalServicesUtils.cs +++ b/ActiveDirectoryConnector/TerminalServicesUtils.cs @@ -115,8 +115,7 @@ internal static void SetValue(UpdateType type, if (value == null) { - // just ignore a null - return; + throw new ArgumentException(); } // invoke set on 'name' with 'value' diff --git a/ActiveDirectoryConnector/UserAccountControl.cs b/ActiveDirectoryConnector/UserAccountControl.cs index 086b462b..e0aed510 100644 --- a/ActiveDirectoryConnector/UserAccountControl.cs +++ b/ActiveDirectoryConnector/UserAccountControl.cs @@ -87,8 +87,12 @@ private static void SetUAC(PropertyValueCollection pvc, int value) internal static void Set(PropertyValueCollection pvc, int flag, bool? isSet) { int uac = GetUAC(pvc); - // boolean false (null is same as false) - if ((isSet == null) || (isSet.Value.Equals(false))) + if(isSet == null) + { + throw new ArgumentException(); + } + // boolean false + if (isSet.Value.Equals(false)) { uac &= (~flag); } From 861cfb911969220367379b81d2c1f37bfa9d38a1 Mon Sep 17 00:00:00 2001 From: dvernon Date: Wed, 21 Jan 2009 19:21:59 +0000 Subject: [PATCH 153/342] Issue #406 - Fixes for contract tests. --- .../CustomAttributeHandlers.cs | 14 ++++++-------- ActiveDirectoryConnector/ObjectClasses.xml | 5 ++++- 2 files changed, 10 insertions(+), 9 deletions(-) diff --git a/ActiveDirectoryConnector/CustomAttributeHandlers.cs b/ActiveDirectoryConnector/CustomAttributeHandlers.cs index b2e170b0..1603e8ed 100644 --- a/ActiveDirectoryConnector/CustomAttributeHandlers.cs +++ b/ActiveDirectoryConnector/CustomAttributeHandlers.cs @@ -544,14 +544,12 @@ internal void UpdateDeFromCa_PasswordNeverExpires(ObjectClass oclass, ConnectorAttribute attribute) { bool? passwordNeverExpires = ConnectorAttributeUtil.GetBooleanValue(attribute); - if (passwordNeverExpires.HasValue) - { - PropertyValueCollection pvc = - directoryEntry.Properties[ActiveDirectoryConnector.ATT_USER_ACOUNT_CONTROL]; - UserAccountControl.Set(pvc, - UserAccountControl.DONT_EXPIRE_PASSWORD, - passwordNeverExpires); - } + + PropertyValueCollection pvc = + directoryEntry.Properties[ActiveDirectoryConnector.ATT_USER_ACOUNT_CONTROL]; + UserAccountControl.Set(pvc, + UserAccountControl.DONT_EXPIRE_PASSWORD, + passwordNeverExpires); } // supporting class not implemented in the framework /* diff --git a/ActiveDirectoryConnector/ObjectClasses.xml b/ActiveDirectoryConnector/ObjectClasses.xml index 65c2713a..9a0f0ee0 100644 --- a/ActiveDirectoryConnector/ObjectClasses.xml +++ b/ActiveDirectoryConnector/ObjectClasses.xml @@ -184,7 +184,10 @@ - + + + + /> From ec472925596a41ae5469d5ad27add8cbfd5a6825 Mon Sep 17 00:00:00 2001 From: abadea Date: Thu, 22 Jan 2009 10:09:22 +0000 Subject: [PATCH 154/342] Issue #394 - Problems in implementations of hashCode() and equals() - .NET side --- Framework/CommonObjects.cs | 50 +++++++++++++++++++++++++++++--------- 1 file changed, 38 insertions(+), 12 deletions(-) diff --git a/Framework/CommonObjects.cs b/Framework/CommonObjects.cs index 1f68d33d..d3443ce3 100644 --- a/Framework/CommonObjects.cs +++ b/Framework/CommonObjects.cs @@ -649,7 +649,8 @@ public IList Value { } public bool Is(string name) { - return Name.ToUpper().Equals(name.ToUpper()); + return Name.ToUpper(CultureInfoCache.Instance).Equals( + name.ToUpper(CultureInfoCache.Instance)); } public sealed override bool Equals(Object obj) { @@ -667,7 +668,7 @@ public sealed override bool Equals(Object obj) { } // test name field.. ConnectorAttribute other = (ConnectorAttribute) obj; - if (!_name.ToUpper().Equals(other._name.ToUpper())) { + if (!Is(other._name)) { return false; } @@ -678,7 +679,7 @@ public sealed override bool Equals(Object obj) { } public sealed override int GetHashCode() { - return _name.ToUpper().GetHashCode(); + return _name.ToUpper(CultureInfoCache.Instance).GetHashCode(); } @@ -1259,7 +1260,8 @@ public Flags InfoFlags { public bool Is(string name) { - return Name.ToUpper().Equals(name.ToUpper()); + return Name.ToUpper(CultureInfoCache.Instance).Equals( + name.ToUpper(CultureInfoCache.Instance)); } /** @@ -1336,7 +1338,7 @@ public bool IsReturnedByDefault { public override bool Equals(Object o) { ConnectorAttributeInfo other = o as ConnectorAttributeInfo; if ( other != null ) { - if (!Name.ToUpper().Equals(other.Name.ToUpper())) { + if (!Is(other.Name)) { return false; } if (!ValueType.Equals(other.ValueType)) { @@ -1351,7 +1353,7 @@ public override bool Equals(Object o) { } public override int GetHashCode() { - return _name.ToUpper().GetHashCode(); + return _name.ToUpper(CultureInfoCache.Instance).GetHashCode(); } public override string ToString() { @@ -1699,7 +1701,7 @@ public String GetObjectClassValue() { * @return The display name key. */ public String GetDisplayNameKey() { - return "MESSAGE_OBJECT_CLASS_"+_type.ToUpper(); + return "MESSAGE_OBJECT_CLASS_"+_type.ToUpper(CultureInfo.GetCultureInfo("en-US")); } /** @@ -1712,11 +1714,12 @@ public String GetDisplayNameKey() { * that of the one in this {@link ObjectClass}. */ public bool Is(String name) { - return this._type.Equals(name, StringComparison.CurrentCultureIgnoreCase); + return this._type.ToUpper(CultureInfoCache.Instance).Equals( + name.ToUpper(CultureInfoCache.Instance)); } public override int GetHashCode() { - return _type.ToUpper().GetHashCode(); + return _type.ToUpper(CultureInfoCache.Instance).GetHashCode(); } public override bool Equals(object obj) { @@ -1799,7 +1802,8 @@ public String ObjectType { * that of the one in this {@link ObjectClassInfo}. */ public bool Is(String name) { - return this._type.Equals(name, StringComparison.CurrentCultureIgnoreCase); + return this._type.ToUpper(CultureInfoCache.Instance).Equals( + name.ToUpper(CultureInfoCache.Instance)); } public bool IsContainer { @@ -1809,7 +1813,7 @@ public bool IsContainer { } public override int GetHashCode() { - return _type.ToUpper().GetHashCode(); + return _type.ToUpper(CultureInfoCache.Instance).GetHashCode(); } public override bool Equals(Object obj) { @@ -1831,7 +1835,7 @@ public override bool Equals(Object obj) { ObjectClassInfo other = obj as ObjectClassInfo; - if (!ObjectType.Equals(other.ObjectType, StringComparison.CurrentCultureIgnoreCase)) { + if (!Is(other.ObjectType)) { return false; } @@ -3954,4 +3958,26 @@ public String FindString(String name) { } } #endregion + + #region CultureInfoCache + internal static class CultureInfoCache { + + private static readonly object LOCK = new Object(); + private static CultureInfo _instance; + + public static CultureInfo Instance { + get { + lock (LOCK) { + if (_instance == null) { + _instance = CultureInfo.CurrentCulture; + if (_instance == null) { + _instance = CultureInfo.InstalledUICulture; + } + } + return _instance; + } + } + } + } + #endregion } From fabf33c136bd1faa52007584f269f30f0bad12a0 Mon Sep 17 00:00:00 2001 From: tknappek Date: Mon, 26 Jan 2009 10:47:48 +0000 Subject: [PATCH 155/342] Issue #411: added waiting for Connector server to be started --- build.xml | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/build.xml b/build.xml index d9bf8a32..fe46e1c6 100644 --- a/build.xml +++ b/build.xml @@ -56,7 +56,13 @@ - + + + + + + + From fc5d6fad9a4fdda6bdd36cd17e64af236fa4f6b8 Mon Sep 17 00:00:00 2001 From: dvernon Date: Tue, 27 Jan 2009 22:50:18 +0000 Subject: [PATCH 156/342] Issue# 412 - Better handling of objects that are not in schema. --- .../ActiveDirectoryConnector.cs | 35 +------ .../ActiveDirectoryUtils.cs | 93 ++++++++++++++++--- .../CustomAttributeHandlers.cs | 48 ++++++++++ ActiveDirectoryConnector/ObjectClasses.xml | 2 +- 4 files changed, 132 insertions(+), 46 deletions(-) diff --git a/ActiveDirectoryConnector/ActiveDirectoryConnector.cs b/ActiveDirectoryConnector/ActiveDirectoryConnector.cs index 69cf1972..d9a2696c 100644 --- a/ActiveDirectoryConnector/ActiveDirectoryConnector.cs +++ b/ActiveDirectoryConnector/ActiveDirectoryConnector.cs @@ -83,7 +83,7 @@ public class ActiveDirectoryConnector : CreateOp, Connector, SchemaOp, DeleteOp, public static readonly string ATT_DISPLAY_NAME = "displayName"; public static readonly string ATT_USER_ACOUNT_CONTROL = "userAccountControl"; public static readonly string ATT_PASSWORD_NEVER_EXPIRES = "PasswordNeverExpires"; - public static readonly string OBJECTCLASS_OU = "Organizational Unit"; + public static readonly string OBJECTCLASS_OU = "organizationalUnit"; public static readonly string OBJECTCLASS_GROUP = "Group"; public static readonly ObjectClass ouObjectClass = new ObjectClass(OBJECTCLASS_OU); @@ -380,37 +380,6 @@ protected virtual IList> GetUnSupportedOperations(ObjectC return null; } - - private ActiveDirectorySchema GetADSchema() - { - String serverName = _configuration.LDAPHostName; - Forest forest = null; - - if ((serverName == null) || (serverName.Length == 0)) - { - // get the active directory schema - DirectoryContext context = new DirectoryContext( - DirectoryContextType.Domain, - _configuration.DomainName, - _configuration.DirectoryAdminName, - _configuration.DirectoryAdminPassword); - DomainController dc = DomainController.FindOne(context); - forest = dc.Forest; - } - else - { - DirectoryContext context = new DirectoryContext( - DirectoryContextType.DirectoryServer, - _configuration.LDAPHostName, - _configuration.DirectoryAdminName, - _configuration.DirectoryAdminPassword); - forest = Forest.GetForest(context); - } - - ActiveDirectorySchema ADSchema = forest.Schema; - return ADSchema; - } - #endregion #region SearchOp Members @@ -749,7 +718,7 @@ public virtual void Test() bool objectFound = true; // now make sure they specified a valid value for the User Object Class - ActiveDirectorySchema ADSchema = GetADSchema(); + ActiveDirectorySchema ADSchema = _utils.GetADSchema(); ActiveDirectorySchemaClass ADSchemaClass = null; try { diff --git a/ActiveDirectoryConnector/ActiveDirectoryUtils.cs b/ActiveDirectoryConnector/ActiveDirectoryUtils.cs index a1c51567..a0852641 100644 --- a/ActiveDirectoryConnector/ActiveDirectoryUtils.cs +++ b/ActiveDirectoryConnector/ActiveDirectoryUtils.cs @@ -45,6 +45,7 @@ public class ActiveDirectoryUtils { ActiveDirectoryConfiguration _configuration = null; private CustomAttributeHandlers _customHandlers = null; + private ICollection _knownObjectClasses = new HashSet(StringComparer.CurrentCultureIgnoreCase); /// /// Constructor @@ -275,7 +276,7 @@ internal void UpdateADObject(ObjectClass oclass, directoryEntry, attribute, type); // Uncommenting the next line is very helpful in // finding mysterious errors. - directoryEntry.CommitChanges(); + // directoryEntry.CommitChanges(); } directoryEntry.CommitChanges(); @@ -294,7 +295,7 @@ internal void UpdateADObject(ObjectClass oclass, directoryEntry, attribute, type); // Uncommenting the next line is very helpful in // finding mysterious errors. - directoryEntry.CommitChanges(); + // directoryEntry.CommitChanges(); } directoryEntry.CommitChanges(); @@ -303,10 +304,24 @@ internal void UpdateADObject(ObjectClass oclass, } else { - throw new ConnectorException( - _configuration.ConnectorMessages.Format("ex_InvalidObjectClass", - "Invalid object class: {0}", oclass.GetObjectClassValue())); - } + String objectClassName = GetADObjectClass(oclass); + // translate attribute passed in + foreach (ConnectorAttribute attribute in attributes) + { + // Temporary + // Trace.TraceInformation(String.Format("Setting attribute {0} to {1}", + // attribute.Name, attribute.Value)); + AddConnectorAttributeToADProperties(oclass, + directoryEntry, attribute, type); + // Uncommenting the next line is very helpful in + // finding mysterious errors. + // directoryEntry.CommitChanges(); + } + + directoryEntry.CommitChanges(); + HandleNameChange(type, directoryEntry, attributes); + HandleContainerChange(type, directoryEntry, attributes, config); + } } internal ConnectorAttribute GetConnectorAttributeFromADEntry(ObjectClass oclass, @@ -484,12 +499,35 @@ internal String GetADObjectClass(ObjectClass oclass) return "organizationalUnit"; } else - { - String msg = _configuration.ConnectorMessages.Format( - "ex_ObjectClassInvalidForConnector", - "ObjectClass \'{0}\' is not valid for this connector", - oclass.GetObjectClassValue()); - throw new ConnectorException(msg); + { + // It's not something I know about, so I'll consult the AD schema. + // if it's there, fine, but if not throw an exception. + + //first check to see if we have seen it before. + String objectClassName = oclass.GetObjectClassValue(); + if(_knownObjectClasses.Contains(objectClassName)) + { + return objectClassName; + } + + // if we havent seen it before, consult AD's schema + ActiveDirectorySchema ADSchema = GetADSchema(); + ActiveDirectorySchemaClass ADSchemaClass = null; + try + { + ADSchemaClass = ADSchema.FindClass(objectClassName); + _knownObjectClasses.Add(objectClassName); + return objectClassName; + } + catch (ActiveDirectoryObjectNotFoundException exception) + { + String msg = _configuration.ConnectorMessages.Format( + "ex_ObjectClassInvalidForConnector", + "ObjectClass \'{0}\' is not valid for this connector", + objectClassName); + throw new ConnectorException(msg); + } + } } @@ -664,6 +702,37 @@ internal static DomainController GetDomainController(ActiveDirectoryConfiguratio return controller; } + + internal ActiveDirectorySchema GetADSchema() + { + String serverName = _configuration.LDAPHostName; + Forest forest = null; + + if ((serverName == null) || (serverName.Length == 0)) + { + // get the active directory schema + DirectoryContext context = new DirectoryContext( + DirectoryContextType.Domain, + _configuration.DomainName, + _configuration.DirectoryAdminName, + _configuration.DirectoryAdminPassword); + DomainController dc = DomainController.FindOne(context); + forest = dc.Forest; + } + else + { + DirectoryContext context = new DirectoryContext( + DirectoryContextType.DirectoryServer, + _configuration.LDAPHostName, + _configuration.DirectoryAdminName, + _configuration.DirectoryAdminPassword); + forest = Forest.GetForest(context); + } + + ActiveDirectorySchema ADSchema = forest.Schema; + return ADSchema; + } + } } diff --git a/ActiveDirectoryConnector/CustomAttributeHandlers.cs b/ActiveDirectoryConnector/CustomAttributeHandlers.cs index 1603e8ed..0e968dac 100644 --- a/ActiveDirectoryConnector/CustomAttributeHandlers.cs +++ b/ActiveDirectoryConnector/CustomAttributeHandlers.cs @@ -64,6 +64,7 @@ internal class CustomAttributeHandlers IList IgnoreConnectorAttributeNames_account = new List(); IList IgnoreConnectorAttributeNames_group = new List(); IList IgnoreConnectorAttributeNames_ou = new List(); + IList IgnoreConnectorAttributeNames_generic = new List(); // method to update a directory entry from a connector attribute Dictionary @@ -100,6 +101,10 @@ internal CustomAttributeHandlers(ActiveDirectoryConfiguration configuration) { IgnoreConnectorAttributeNames_ou.Add(Name.NAME); IgnoreConnectorAttributeNames_ou.Add(Uid.NAME); + // Connector attributes names to ignore for everything else + IgnoreConnectorAttributeNames_generic.Add(Name.NAME); + IgnoreConnectorAttributeNames_generic.Add(Uid.NAME); + // methods to update a directory entry from a connectorattribute UpdateDeFromCaDelegates.Add(PredefinedAttributes.ACCOUNTS_NAME, UpdateDeFromCa_OpAtt_Accounts); @@ -235,6 +240,10 @@ internal void UpdateDeFromCa(ObjectClass oclass, { ignoreList = IgnoreConnectorAttributeNames_ou; } + else + { + ignoreList = IgnoreConnectorAttributeNames_generic; + } // if it's an ignored attribute, we're done if ((ignoreList != null) && @@ -717,6 +726,45 @@ internal void UpdateDeFromCa_Att_Generic(ObjectClass oclass, // null out the values if we are replacing attributes. if (type.Equals(UpdateType.REPLACE)) { + // There is a problem where some attributes cant be set + // even if the value is just being set to the same as + // before. This especially comes up for RDN (such as + // cn and ou). As a workaround, and for backward compatibility + // with IDM, if the value is the same, just ignore it. + + // check equality + IList attributeValue = attribute.Value; + PropertyValueCollection pvc = directoryEntry.Properties[attribute.Name]; + if(attributeValue.Count == pvc.Count) + { + Boolean valueEqual = true; + + if (attributeValue != null) + { + + foreach (Object attValueObj in attributeValue) + { + if (pvc == null) + { + if (attValueObj != null) + { + valueEqual = false; + break; + } + } + if (!pvc.Contains(attValueObj)) + { + valueEqual = false; + break; + } + } + if (valueEqual) + { + // the value is already set, so just return without doing anything + return; + } + } + } directoryEntry.Properties[attribute.Name].Value = null; } diff --git a/ActiveDirectoryConnector/ObjectClasses.xml b/ActiveDirectoryConnector/ObjectClasses.xml index 9a0f0ee0..1f408588 100644 --- a/ActiveDirectoryConnector/ObjectClasses.xml +++ b/ActiveDirectoryConnector/ObjectClasses.xml @@ -202,7 +202,7 @@ - + From 23309ba7ea5af1f5e64de6879a63293104ff4940 Mon Sep 17 00:00:00 2001 From: tknappek Date: Wed, 28 Jan 2009 15:57:58 +0000 Subject: [PATCH 157/342] Added ShellScriptExecutorFactory dependency --- ActiveDirectoryConnector/ActiveDirectoryConnector.csproj | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/ActiveDirectoryConnector/ActiveDirectoryConnector.csproj b/ActiveDirectoryConnector/ActiveDirectoryConnector.csproj index 18df4ff2..02c001fc 100644 --- a/ActiveDirectoryConnector/ActiveDirectoryConnector.csproj +++ b/ActiveDirectoryConnector/ActiveDirectoryConnector.csproj @@ -1,4 +1,4 @@ -AttributeInfo name="@@ENABLE_DATE@@" type="long"/--> - From a7bbcef33b3e6b7341fe7735cac79467c0582ab1 Mon Sep 17 00:00:00 2001 From: dvernon Date: Thu, 5 Feb 2009 18:59:58 +0000 Subject: [PATCH 167/342] Checking in message changes for translation. --- ActiveDirectoryConnector/Messages.resx | 15 +++++++++------ 1 file changed, 9 insertions(+), 6 deletions(-) diff --git a/ActiveDirectoryConnector/Messages.resx b/ActiveDirectoryConnector/Messages.resx index 098e4713..4628828d 100644 --- a/ActiveDirectoryConnector/Messages.resx +++ b/ActiveDirectoryConnector/Messages.resx @@ -142,7 +142,7 @@ Search Child Domains - Search Container + Base Container Sync Domain Controller @@ -150,8 +150,8 @@ Sync Global Catalog Server - - Sync Search Context + + Search Context Specify whether or not the home directory for the user will be created. @@ -175,7 +175,7 @@ Select if you want searches of Active Directory to include child domains. In addition, the Search Container and Sync Search Context (see sync settings) attributes must be set to the top of the parent domain, e.g. DC=mydomain,DC=com. - Specify a container object which will be the default root of all searches. Unless a search explicitly passes in other criteria, only objects under this container will be searched. For example, if you want to retrieve users from the Users container, enter CN=Users,DC=MYDOMAIN,DC=COM. + Distinguished name of the base context object. This is used as the parent when creating new resource objects. Domain controller to use during active sync. Only used if not searching child domains. @@ -183,8 +183,8 @@ Name of the global catalog server. This is needed only if searching child domains. - - Distinguished name of the object under which to search for changes during sync operation. + + Specify a container object which will be the default root of all searches. Unless a search explicitly passes in other criteria, only objects under this container will be searched. For example, if you want to retrieve users from the Users container, enter CN=Users,DC=MYDOMAIN,DC=COM. If no value is specified, the value of the 'Base Container' attribute is used. Connector has not been configured @@ -276,4 +276,7 @@ User account expired for user {0} + + An invalid search context was supplied: {0} + \ No newline at end of file From 787774cb41a572725106d06ce01c89d8df12e33a Mon Sep 17 00:00:00 2001 From: dvernon Date: Thu, 5 Feb 2009 21:21:37 +0000 Subject: [PATCH 168/342] Issue #417 - Search Context should default to Container. SearchContainer was renamed to Container, SyncSearchContext was renamed to SearchContext. --- .../ActiveDirectoryConfiguration.cs | 33 ++++++++--- .../ActiveDirectoryConnector.cs | 55 +++++++++++++++---- .../ActiveDirectoryUtils.cs | 4 +- ActiveDirectoryConnector/Messages.resx | 12 ++-- 4 files changed, 76 insertions(+), 28 deletions(-) diff --git a/ActiveDirectoryConnector/ActiveDirectoryConfiguration.cs b/ActiveDirectoryConnector/ActiveDirectoryConfiguration.cs index 20d662b5..63aafbea 100644 --- a/ActiveDirectoryConnector/ActiveDirectoryConfiguration.cs +++ b/ActiveDirectoryConnector/ActiveDirectoryConfiguration.cs @@ -46,9 +46,9 @@ public String DirectoryAdminPassword public String ObjectClass { get; set; } - [ConfigurationProperty(Confidential = false, DisplayMessageKey = "display_SearchContainer", - Required=true, HelpMessageKey = "help_SearchContainer", Order = 4)] - public String SearchContainer + [ConfigurationProperty(Confidential = false, DisplayMessageKey = "display_Container", + Required=true, HelpMessageKey = "help_Container", Order = 4)] + public String Container { get; set; } [ConfigurationProperty(Confidential = false, DisplayMessageKey = "display_CreateHomeDirectory", HelpMessageKey = "help_CreateHomeDirectory", Order = 5)] @@ -74,14 +74,29 @@ public String SyncGlobalCatalogServer public String SyncDomainController { get; set; } - [ConfigurationProperty(OperationTypes = new Type[] { typeof(SyncOp) }, Confidential = false, DisplayMessageKey = "display_SyncSearchContext", HelpMessageKey = "help_SyncSearchContext", Order=11)] - public String SyncSearchContext - { get; set; } + private string _searchContext = string.Empty; + [ConfigurationProperty(Confidential = false, DisplayMessageKey = "display_SearchContext", HelpMessageKey = "help_SearchContext", Order=11)] + public String SearchContext + { + get + { + if((_searchContext == null) || (_searchContext.Length == 0)) + { + return Container; + } + return _searchContext; + } + + set + { + _searchContext = value; + } + } public ActiveDirectoryConfiguration() { DomainName = ""; - SearchContainer = ""; + Container = ""; DirectoryAdminName = "administrator"; ObjectClass = "User"; CreateHomeDirectory = true; @@ -123,10 +138,10 @@ public override void Validate() foundError = true; } - if ((SearchContainer == null) || (SearchContainer.Length == 0)) + if ((Container == null) || (Container.Length == 0)) { message += ConnectorMessages.Format( - "confReqParam_searchContainer", "Search Container was not supplied "); + "confReqParam_Container", "Container was not supplied "); foundError = true; } diff --git a/ActiveDirectoryConnector/ActiveDirectoryConnector.cs b/ActiveDirectoryConnector/ActiveDirectoryConnector.cs index 2b2e8833..cbe848fa 100644 --- a/ActiveDirectoryConnector/ActiveDirectoryConnector.cs +++ b/ActiveDirectoryConnector/ActiveDirectoryConnector.cs @@ -85,6 +85,8 @@ public class ActiveDirectoryConnector : CreateOp, Connector, SchemaOp, DeleteOp, public static readonly string ATT_PASSWORD_NEVER_EXPIRES = "PasswordNeverExpires"; public static readonly string OBJECTCLASS_OU = "organizationalUnit"; public static readonly string OBJECTCLASS_GROUP = "Group"; + public static readonly string OPTION_DOMAIN = "w2k_domain"; + public static readonly string OPTION_RETURN_UID_ONLY = "returnUidOnly"; public static readonly ObjectClass ouObjectClass = new ObjectClass(OBJECTCLASS_OU); public static readonly ObjectClass groupObjectClass = new ObjectClass(OBJECTCLASS_GROUP); @@ -453,7 +455,7 @@ public string GetADSearchContextFromOptions(OperationOptions options) } } - return _configuration.SearchContainer; + return _configuration.SearchContext; } public SearchScope GetADSearchScopeFromOptions(OperationOptions options) @@ -658,11 +660,12 @@ private void ExecuteQuery(ObjectClass oclass, string query, } } - private string GetSearchContainerPath() + // this is the path that all searches come from unless otherwise directed + private string GetSearchContextPath() { - return GetSearchContainerPath(UseGlobalCatalog(), _configuration.LDAPHostName, _configuration.SearchContainer); + return GetSearchContainerPath(UseGlobalCatalog(), _configuration.LDAPHostName, _configuration.SearchContext); } - + private string GetSearchContainerPath(bool useGC, string hostname, string searchContainer) { String path; @@ -737,14 +740,26 @@ public virtual void Test() _configuration.ObjectClass)); } - // see if search container is valid - if (!DirectoryEntry.Exists(GetSearchContainerPath())) + // see if SearchContext is valid + if (!DirectoryEntry.Exists(GetSearchContextPath())) { throw new ConnectorException( _configuration.ConnectorMessages.Format( - "ex_InvalidSearchContainerInConfiguration", - "An invalid search container was supplied: {0}", - _configuration.SearchContainer)); + "ex_InvalidSearchContextInConfiguration", + "An invalid search context was supplied: {0}", + _configuration.SearchContext)); + } + + // see if the Container exists + + if (!DirectoryEntry.Exists(GetSearchContainerPath(UseGlobalCatalog(), + _configuration.LDAPHostName, _configuration.Container))) + { + throw new ConnectorException( + _configuration.ConnectorMessages.Format( + "ex_InvalidContainerInConfiguration", + "An invalid container was supplied: {0}", + _configuration.Container)); } } @@ -991,7 +1006,7 @@ public virtual void Sync(ObjectClass objClass, SyncToken token, // find modified usn's ExecuteQuery(objClass, modifiedQuery, syncResults.SyncHandler, builder.Build(), false, new SortOption(ATT_USN_CHANGED, SortDirection.Ascending), - serverName, UseGlobalCatalog(), _configuration.SyncSearchContext, SearchScope.Subtree); + serverName, UseGlobalCatalog(), GetADSearchContextFromOptions(null), SearchScope.Subtree); // find deleted usn's DirectoryContext domainContext = new DirectoryContext(DirectoryContextType.DirectoryServer, @@ -1125,8 +1140,26 @@ public Uid Authenticate(ObjectClass objectClass, string username, Org.IdentityConnectors.Common.Security.GuardedString password, OperationOptions options) { + bool returnUidOnly = false; + + if (options != null) + { + if (options.Options.ContainsKey(OPTION_DOMAIN)) + { + string domainName = options.Options[OPTION_DOMAIN].ToString(); + if ((domainName != null) && (domainName.Length > 0)) + { + username = string.Format("{0}@{1}", username, options.Options["w2k_domain"]); + } + } + else if (options.Options.ContainsKey(OPTION_RETURN_UID_ONLY)) + { + returnUidOnly = true; + } + } + PasswordChangeHandler handler = new PasswordChangeHandler(_configuration); - return handler.Authenticate(username, password); + return handler.Authenticate(username, password, returnUidOnly); } #endregion diff --git a/ActiveDirectoryConnector/ActiveDirectoryUtils.cs b/ActiveDirectoryConnector/ActiveDirectoryUtils.cs index c91d94c7..06763cf7 100644 --- a/ActiveDirectoryConnector/ActiveDirectoryUtils.cs +++ b/ActiveDirectoryConnector/ActiveDirectoryUtils.cs @@ -89,9 +89,9 @@ internal static String ConvertUIDBytesToGUIDString(Byte[] guidBytes) return ConvertBytesToADSpecialString("GUID", guidBytes); } - internal static String ConvertSIDBytesToGUIDString(Byte[] guidBytes) + internal static String ConvertSIDBytesToGUIDString(Byte[] sidBytes) { - return ConvertBytesToADSpecialString("SID", guidBytes); + return ConvertBytesToADSpecialString("SID", sidBytes); } internal static String ConvertBytesToADSpecialString(string attribute, Byte[] bytes) diff --git a/ActiveDirectoryConnector/Messages.resx b/ActiveDirectoryConnector/Messages.resx index 4628828d..eb6b5a72 100644 --- a/ActiveDirectoryConnector/Messages.resx +++ b/ActiveDirectoryConnector/Messages.resx @@ -141,8 +141,8 @@ Search Child Domains - - Base Container + + Container Sync Domain Controller @@ -174,7 +174,7 @@ Select if you want searches of Active Directory to include child domains. In addition, the Search Container and Sync Search Context (see sync settings) attributes must be set to the top of the parent domain, e.g. DC=mydomain,DC=com. - + Distinguished name of the base context object. This is used as the parent when creating new resource objects. @@ -252,14 +252,14 @@ ObjectClass was not supplied. - + Search Container was not supplied. Using Identity Manger Resource Adapter style query '{0}'. This should be updated to use the new connector query syntax. - - An invalid search container was supplied: {0} + + An invalid container was supplied: {0} Shell Script Variable Prefix From 1e3265901c9e9a3596af69bd58e9364998dba375 Mon Sep 17 00:00:00 2001 From: dvernon Date: Fri, 6 Feb 2009 00:42:56 +0000 Subject: [PATCH 169/342] Issue #417 - Should use Container unless told otherwise as the root of searches. --- .../ActiveDirectoryConnector.cs | 26 +++++++++---------- .../ActiveDirectoryConnectorTest.cs | 19 +++++++------- 2 files changed, 23 insertions(+), 22 deletions(-) diff --git a/ActiveDirectoryConnector/ActiveDirectoryConnector.cs b/ActiveDirectoryConnector/ActiveDirectoryConnector.cs index cbe848fa..ee36548f 100644 --- a/ActiveDirectoryConnector/ActiveDirectoryConnector.cs +++ b/ActiveDirectoryConnector/ActiveDirectoryConnector.cs @@ -407,7 +407,7 @@ public virtual void ExecuteQuery(ObjectClass oclass, string query, IDictionarysearchOptions = options.Options; SearchScope searchScope = GetADSearchScopeFromOptions(options); - string searchContext = GetADSearchContextFromOptions(options); + string searchContainer = GetADSearchContainerFromOptions(options); // for backward compatibility, support old query style from resource adapters // but log a warning @@ -435,7 +435,7 @@ public virtual void ExecuteQuery(ObjectClass oclass, string query, } ExecuteQuery(oclass, query, handler, options, - false, null, _configuration.LDAPHostName, useGC, searchContext, searchScope); + false, null, _configuration.LDAPHostName, useGC, searchContainer, searchScope); } catch (Exception e) { @@ -444,7 +444,7 @@ public virtual void ExecuteQuery(ObjectClass oclass, string query, } } - public string GetADSearchContextFromOptions(OperationOptions options) + public string GetADSearchContainerFromOptions(OperationOptions options) { if (options != null) { @@ -455,7 +455,7 @@ public string GetADSearchContextFromOptions(OperationOptions options) } } - return _configuration.SearchContext; + return _configuration.Container; } public SearchScope GetADSearchScopeFromOptions(OperationOptions options) @@ -661,9 +661,9 @@ private void ExecuteQuery(ObjectClass oclass, string query, } // this is the path that all searches come from unless otherwise directed - private string GetSearchContextPath() + private string GetSearchContainerPath() { - return GetSearchContainerPath(UseGlobalCatalog(), _configuration.LDAPHostName, _configuration.SearchContext); + return GetSearchContainerPath(UseGlobalCatalog(), _configuration.LDAPHostName, _configuration.Container); } private string GetSearchContainerPath(bool useGC, string hostname, string searchContainer) @@ -740,17 +740,17 @@ public virtual void Test() _configuration.ObjectClass)); } - // see if SearchContext is valid - if (!DirectoryEntry.Exists(GetSearchContextPath())) + // see if SearchContainer is valid + if (!DirectoryEntry.Exists(GetSearchContainerPath())) { throw new ConnectorException( _configuration.ConnectorMessages.Format( - "ex_InvalidSearchContextInConfiguration", - "An invalid search context was supplied: {0}", - _configuration.SearchContext)); + "ex_InvalidSearchContainerInConfiguration", + "An invalid search container was supplied: {0}", + _configuration.Container)); } - // see if the Container exists + // see if the Context exists if (!DirectoryEntry.Exists(GetSearchContainerPath(UseGlobalCatalog(), _configuration.LDAPHostName, _configuration.Container))) @@ -1006,7 +1006,7 @@ public virtual void Sync(ObjectClass objClass, SyncToken token, // find modified usn's ExecuteQuery(objClass, modifiedQuery, syncResults.SyncHandler, builder.Build(), false, new SortOption(ATT_USN_CHANGED, SortDirection.Ascending), - serverName, UseGlobalCatalog(), GetADSearchContextFromOptions(null), SearchScope.Subtree); + serverName, UseGlobalCatalog(), GetADSearchContainerFromOptions(null), SearchScope.Subtree); // find deleted usn's DirectoryContext domainContext = new DirectoryContext(DirectoryContextType.DirectoryServer, diff --git a/ActiveDirectoryConnectorTests/ActiveDirectoryConnectorTest.cs b/ActiveDirectoryConnectorTests/ActiveDirectoryConnectorTest.cs index 82ce9f55..ff25ee5b 100644 --- a/ActiveDirectoryConnectorTests/ActiveDirectoryConnectorTest.cs +++ b/ActiveDirectoryConnectorTests/ActiveDirectoryConnectorTest.cs @@ -58,8 +58,8 @@ public class ActiveDirectoryConnectorTest public static readonly string CONFIG_PROPERTY_SCRIPT_PASSWORD_DOMAIN = "config_script_password_domain"; public static readonly string CONFIG_PROPERTY_LDAPHOSTNAME = "config_ldap_hostname"; public static readonly string CONFIG_PROPERTY_SEARCH_CONTEXT = "config_search_context"; - public static readonly string CONFIG_PROPERTY_SYNC_SEARCH_CONTEXT_ROOT = "config_sync_search_context_root"; - public static readonly string CONFIG_PROPERTY_SYNC_SEARCH_CONTEXT_CHILD = "config_sync_search_context_child"; + public static readonly string config_PROPERTY_SYNC_CONTAINER_ROOT = "config_sync_container_root"; + public static readonly string config_PROPERTY_SYNC_CONTAINER_CHILD = "config_sync_container_child"; public static readonly string CONFIG_PROPERTY_DOMAIN_NAME = "config_domain_name"; public static readonly string CONFIG_PROPERTY_SYNC_DOMAIN_CONTROLLER = "config_sync_domain_controller"; public static readonly string CONFIG_PROPERTY_GC_DOMAIN_CONTROLLER = "config_sync_gc_domain_controller"; @@ -1258,8 +1258,8 @@ public void TestDisableDate() public void TestSyncGC() { // test with searchChildDomain (uses GC) - TestSync(true, GetProperty(CONFIG_PROPERTY_SYNC_SEARCH_CONTEXT_ROOT)); - TestSync(true, GetProperty(CONFIG_PROPERTY_SYNC_SEARCH_CONTEXT_CHILD)); + TestSync(true, GetProperty(config_PROPERTY_SYNC_CONTAINER_ROOT)); + TestSync(true, GetProperty(config_PROPERTY_SYNC_CONTAINER_CHILD)); } // test sync @@ -1267,8 +1267,8 @@ public void TestSyncGC() public void TestSyncDC() { // test withouth searchChildDomains (uses DC) - TestSync(false, GetProperty(CONFIG_PROPERTY_SYNC_SEARCH_CONTEXT_ROOT)); - TestSync(false, GetProperty(CONFIG_PROPERTY_SYNC_SEARCH_CONTEXT_CHILD)); + TestSync(false, GetProperty(config_PROPERTY_SYNC_CONTAINER_ROOT)); + TestSync(false, GetProperty(config_PROPERTY_SYNC_CONTAINER_CHILD)); } [Test] @@ -1803,13 +1803,13 @@ public void TestAccountLocked() } } - public void TestSync(bool searchChildDomains, String syncSearchContext) + public void TestSync(bool searchChildDomains, String container) { //Initialize Connector ActiveDirectoryConnector connector = new ActiveDirectoryConnector(); ActiveDirectoryConfiguration configuration = (ActiveDirectoryConfiguration)GetConfiguration(); - configuration.SyncSearchContext = syncSearchContext; + configuration.SearchContext = container; configuration.SearchChildDomains = searchChildDomains; connector.Init(configuration); @@ -2319,7 +2319,8 @@ public Configuration GetConfiguration() config.LDAPHostName = GetProperty(CONFIG_PROPERTY_LDAPHOSTNAME); config.DirectoryAdminName = GetProperty(CONFIG_PROPERTY_USER); config.DirectoryAdminPassword = GetProperty(CONFIG_PROPERTY_PASSWORD); - config.SearchContainer = GetProperty(CONFIG_PROPERTY_SEARCH_CONTEXT); + config.Container = GetProperty(CONFIG_PROPERTY_CONTAINER); + config.SearchContext = GetProperty(CONFIG_PROPERTY_SEARCH_CONTEXT); config.SyncDomainController = GetProperty(CONFIG_PROPERTY_SYNC_DOMAIN_CONTROLLER); config.SyncGlobalCatalogServer = GetProperty(CONFIG_PROPERTY_GC_DOMAIN_CONTROLLER); config.ConnectorMessages = TestHelpers.CreateDummyMessages(); From 4100607e0011d56a10256a6cb5ff73e2b8dd602b Mon Sep 17 00:00:00 2001 From: dvernon Date: Fri, 6 Feb 2009 00:47:06 +0000 Subject: [PATCH 170/342] Issue #418 - adding option for returning a guid without Authenticating in the Authenticate method. --- ActiveDirectoryConnector/PasswordChangeHandler.cs | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/ActiveDirectoryConnector/PasswordChangeHandler.cs b/ActiveDirectoryConnector/PasswordChangeHandler.cs index 84f259b1..e050170a 100644 --- a/ActiveDirectoryConnector/PasswordChangeHandler.cs +++ b/ActiveDirectoryConnector/PasswordChangeHandler.cs @@ -270,11 +270,16 @@ internal void changePassword(DirectoryEntry directoryEntry, /// /// internal Uid Authenticate(/*DirectoryEntry directoryEntry,*/ string username, - Org.IdentityConnectors.Common.Security.GuardedString password) + Org.IdentityConnectors.Common.Security.GuardedString password, bool returnUidOnly) { AuthenticationHelper authHelper = new AuthenticationHelper(_configuration); + if(returnUidOnly) + { + return authHelper.GetUidFromSamAccountName(username); + } password.Access(setCurrentPassword); return authHelper.ValidateUserCredentials(username, _currentPassword); } + } } From 3df1049e3a2d9d315adadeacd29c92cab7f57e02 Mon Sep 17 00:00:00 2001 From: dvernon Date: Fri, 6 Feb 2009 01:16:07 +0000 Subject: [PATCH 171/342] Issue #417 - Should use Container unless told otherwise as the root of searches. --- ActiveDirectoryConnector/Messages.resx | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/ActiveDirectoryConnector/Messages.resx b/ActiveDirectoryConnector/Messages.resx index eb6b5a72..61cebd2b 100644 --- a/ActiveDirectoryConnector/Messages.resx +++ b/ActiveDirectoryConnector/Messages.resx @@ -175,7 +175,7 @@ Select if you want searches of Active Directory to include child domains. In addition, the Search Container and Sync Search Context (see sync settings) attributes must be set to the top of the parent domain, e.g. DC=mydomain,DC=com. - Distinguished name of the base context object. This is used as the parent when creating new resource objects. + Specify a container object which will be the default root of all searches. Unless a search explicitly passes in other criteria, only objects under this container will be searched. For example, if you want to retrieve users from the Users container, enter CN=Users,DC=MYDOMAIN,DC=COM. If no value is specified, the value of the 'Base Container' attribute is used. Domain controller to use during active sync. Only used if not searching child domains. @@ -184,7 +184,7 @@ Name of the global catalog server. This is needed only if searching child domains. - Specify a container object which will be the default root of all searches. Unless a search explicitly passes in other criteria, only objects under this container will be searched. For example, if you want to retrieve users from the Users container, enter CN=Users,DC=MYDOMAIN,DC=COM. If no value is specified, the value of the 'Base Container' attribute is used. + Specify where to look in the ADSI directory when attempting to resolve the attribute specified by searchAttributes to the native dn format. Connector has not been configured From ae7d0100fa53866e817c13302e1c6aac905bdbda Mon Sep 17 00:00:00 2001 From: kyarbro Date: Fri, 6 Feb 2009 21:39:17 +0000 Subject: [PATCH 172/342] issue # 420 - change default install dir --- ServiceInstall/File.top | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/ServiceInstall/File.top b/ServiceInstall/File.top index 84086191..1f3f3346 100644 --- a/ServiceInstall/File.top +++ b/ServiceInstall/File.top @@ -3,7 +3,8 @@ - + + Date: Fri, 6 Feb 2009 21:44:59 +0000 Subject: [PATCH 173/342] issue # 420 - change default install dir --- ServiceInstall/File.bottom | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/ServiceInstall/File.bottom b/ServiceInstall/File.bottom index 8de6ef93..9e4c6103 100644 --- a/ServiceInstall/File.bottom +++ b/ServiceInstall/File.bottom @@ -1,6 +1,7 @@ + - \ No newline at end of file + From b459ea3ddbeb6c7a2d6339861801ebd96626098e Mon Sep 17 00:00:00 2001 From: kyarbro Date: Fri, 6 Feb 2009 22:07:43 +0000 Subject: [PATCH 174/342] issue # 420 - change default install dir --- ServiceInstall/File.top | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/ServiceInstall/File.top b/ServiceInstall/File.top index 1f3f3346..591c53d5 100644 --- a/ServiceInstall/File.top +++ b/ServiceInstall/File.top @@ -4,7 +4,7 @@ - + Date: Sat, 7 Feb 2009 19:42:59 +0000 Subject: [PATCH 175/342] issue # 420 - change default install dir --- ServiceInstall/File.top | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/ServiceInstall/File.top b/ServiceInstall/File.top index 591c53d5..c046613c 100644 --- a/ServiceInstall/File.top +++ b/ServiceInstall/File.top @@ -3,8 +3,8 @@ - - + + Date: Mon, 9 Feb 2009 10:55:25 +0000 Subject: [PATCH 176/342] Issue #414: ObjectPoolTests.TestsWithManyThreads fixed --- FrameworkInternal/Server.cs | 15 +++++++-------- FrameworkTests/ObjectPoolTests.cs | 12 ++++++++---- 2 files changed, 15 insertions(+), 12 deletions(-) diff --git a/FrameworkInternal/Server.cs b/FrameworkInternal/Server.cs index 70f3a7a3..25ed9432 100644 --- a/FrameworkInternal/Server.cs +++ b/FrameworkInternal/Server.cs @@ -657,6 +657,7 @@ public ConnectionListener(ConnectorServerImpl server, TcpListener socket) { _server = server; _socket = socket; + _thisThread = new Thread(Run) {Name = "ConnectionListener", IsBackground = false}; //TODO: thread pool /* _threadPool = new ThreadPoolExecutor @@ -669,11 +670,12 @@ public ConnectionListener(ConnectorServerImpl server, true)); //fair*/ } + public void Start() { + _thisThread.Start(); + } - - public void Run(Object o) { - Trace.TraceInformation("Server started on port: "+_server.Port); - _thisThread = (Thread)o; + public void Run() { + Trace.TraceInformation("Server started on port: "+_server.Port); while (!IsStopped()) { try { TcpClient connection = null; @@ -866,10 +868,7 @@ public override void Start() { TcpListener socket = CreateServerSocket(); ConnectionListener listener = new ConnectionListener(this,socket); - Thread thread = new Thread(listener.Run); - thread.Name="ConnectionListener"; - thread.Start(thread); - thread.IsBackground = false; + listener.Start(); _listener = listener; } diff --git a/FrameworkTests/ObjectPoolTests.cs b/FrameworkTests/ObjectPoolTests.cs index 3c09f8ec..c34afc48 100644 --- a/FrameworkTests/ObjectPoolTests.cs +++ b/FrameworkTests/ObjectPoolTests.cs @@ -98,9 +98,14 @@ public MyTestThread(ObjectPool pool, int numIterations) { _pool = pool; _numIterations = numIterations; + _thisThread = new Thread(Run); } - public void Run(object o) { - _thisThread = (Thread)o; + + public void Start() { + _thisThread.Start(); + } + + public void Run() { try { for ( int i = 0; i < _numIterations; i++ ) { MyTestConnection con = @@ -141,8 +146,7 @@ public void TestWithManyThreads() MyTestThread [] threads = new MyTestThread[NUM_THREADS]; for (int i = 0; i < threads.Length; i++) { threads[i] = new MyTestThread(pool,NUM_ITERATIONS); - Thread thread = new Thread(threads[i].Run); - thread.Start(thread); + threads[i].Start(); } foreach (MyTestThread thread in threads) { From cd3510559ab6cff271c2521b9ad6dd8126cb1fa9 Mon Sep 17 00:00:00 2001 From: abadea Date: Mon, 9 Feb 2009 15:23:48 +0000 Subject: [PATCH 177/342] Issue 388: Reserved ObjectClass names should be have '@@' prefix/suffix --- .../ActiveDirectoryConnector.cs | 1 + .../CustomAttributeHandlers.cs | 8 +- .../ActiveDirectoryConnectorTest.cs | 8 +- Framework/CommonObjects.cs | 109 +++++++++--------- FrameworkTests/FrameworkTests.csproj | 3 +- FrameworkTests/ObjectClassUtilTests.cs | 39 +++++++ FrameworkTests/ObjectSerializationTests.cs | 4 +- 7 files changed, 106 insertions(+), 66 deletions(-) create mode 100755 FrameworkTests/ObjectClassUtilTests.cs diff --git a/ActiveDirectoryConnector/ActiveDirectoryConnector.cs b/ActiveDirectoryConnector/ActiveDirectoryConnector.cs index ee36548f..611d9347 100644 --- a/ActiveDirectoryConnector/ActiveDirectoryConnector.cs +++ b/ActiveDirectoryConnector/ActiveDirectoryConnector.cs @@ -83,6 +83,7 @@ public class ActiveDirectoryConnector : CreateOp, Connector, SchemaOp, DeleteOp, public static readonly string ATT_DISPLAY_NAME = "displayName"; public static readonly string ATT_USER_ACOUNT_CONTROL = "userAccountControl"; public static readonly string ATT_PASSWORD_NEVER_EXPIRES = "PasswordNeverExpires"; + public static readonly string ATT_ACCOUNTS = ConnectorAttributeUtil.CreateSpecialName("ACCOUNTS"); public static readonly string OBJECTCLASS_OU = "organizationalUnit"; public static readonly string OBJECTCLASS_GROUP = "Group"; public static readonly string OPTION_DOMAIN = "w2k_domain"; diff --git a/ActiveDirectoryConnector/CustomAttributeHandlers.cs b/ActiveDirectoryConnector/CustomAttributeHandlers.cs index a144df64..4d25cba9 100644 --- a/ActiveDirectoryConnector/CustomAttributeHandlers.cs +++ b/ActiveDirectoryConnector/CustomAttributeHandlers.cs @@ -106,7 +106,7 @@ internal CustomAttributeHandlers(ActiveDirectoryConfiguration configuration) { IgnoreConnectorAttributeNames_generic.Add(Uid.NAME); // methods to update a directory entry from a connectorattribute - UpdateDeFromCaDelegates.Add(PredefinedAttributes.ACCOUNTS_NAME, + UpdateDeFromCaDelegates.Add(ActiveDirectoryConnector.ATT_ACCOUNTS, UpdateDeFromCa_OpAtt_Accounts); UpdateDeFromCaDelegates.Add(PredefinedAttributes.GROUPS_NAME, UpdateDeFromCa_OpAtt_Groups); @@ -166,7 +166,7 @@ internal CustomAttributeHandlers(ActiveDirectoryConfiguration configuration) { GetCaFromDeDelegates.Add(Uid.NAME, GetCaFromDe_OpAtt_Uid); GetCaFromDeDelegates.Add(ActiveDirectoryConnector.ATT_CONTAINER, GetCaFromDe_Att_Container); - GetCaFromDeDelegates.Add(PredefinedAttributes.ACCOUNTS_NAME, + GetCaFromDeDelegates.Add(ActiveDirectoryConnector.ATT_ACCOUNTS, GetCaFromDe_OpAtt_Accounts); GetCaFromDeDelegates.Add(PredefinedAttributes.GROUPS_NAME, GetCaFromDe_OpAtt_Groups); @@ -414,7 +414,7 @@ internal void UpdateDeFromCa_OpAtt_Accounts(ObjectClass oclass, { throw new ConnectorException( String.Format("'{0}' is an invalid attribute for object class '{1}'", - PredefinedAttributeInfos.ACCOUNTS, oclass.GetObjectClassValue())); + ActiveDirectoryConnector.ATT_ACCOUNTS, oclass.GetObjectClassValue())); } } @@ -935,7 +935,7 @@ private ConnectorAttribute GetCaFromDe_OpAtt_Accounts( } else { - return ConnectorAttributeBuilder.Build(PredefinedAttributes.ACCOUNTS_NAME, + return ConnectorAttributeBuilder.Build(ActiveDirectoryConnector.ATT_ACCOUNTS, realAttribute.Value); } } diff --git a/ActiveDirectoryConnectorTests/ActiveDirectoryConnectorTest.cs b/ActiveDirectoryConnectorTests/ActiveDirectoryConnectorTest.cs index ff25ee5b..62f75a74 100644 --- a/ActiveDirectoryConnectorTests/ActiveDirectoryConnectorTest.cs +++ b/ActiveDirectoryConnectorTests/ActiveDirectoryConnectorTest.cs @@ -203,7 +203,7 @@ public void TestBasics_Group() { // create group ICollection createAttributes = GetNormalAttributes_Group(); - createAttributes.Add(ConnectorAttributeBuilder.Build(PredefinedAttributes.ACCOUNTS_NAME, + createAttributes.Add(ConnectorAttributeBuilder.Build(ActiveDirectoryConnector.ATT_ACCOUNTS, CreateGroupMember(connector))); // create object @@ -214,7 +214,7 @@ public void TestBasics_Group() // find new object ... have to add groups to list of things to return OperationOptionsBuilder optionsBuilder = new OperationOptionsBuilder(); ICollection attributesToGet = GetDefaultAttributesToGet(ActiveDirectoryConnector.groupObjectClass); - attributesToGet.Add(PredefinedAttributes.ACCOUNTS_NAME); + attributesToGet.Add(ActiveDirectoryConnector.ATT_ACCOUNTS); optionsBuilder.AttributesToGet = attributesToGet.ToArray(); ConnectorObject newObject = GetConnectorObjectFromUid(connector, @@ -239,7 +239,7 @@ public void TestBasics_Group() // update the group - add ICollection updateAddAttrs = new List(); - updateAddAttrs.Add(ConnectorAttributeBuilder.Build(PredefinedAttributes.ACCOUNTS_NAME, + updateAddAttrs.Add(ConnectorAttributeBuilder.Build(ActiveDirectoryConnector.ATT_ACCOUNTS, CreateGroupMember(connector), CreateGroupMember(connector))); uidToDelete = UpdateAddAndVerifyUser(connector, @@ -2592,7 +2592,7 @@ private static void VerifyObject(ICollection requestedAttrib ldapStringAttributes.Add("AD_CONTAINER"); ldapStringAttributes.Add(Name.NAME); ldapStringAttributes.Add(PredefinedAttributes.GROUPS_NAME); - ldapStringAttributes.Add(PredefinedAttributes.ACCOUNTS_NAME); + ldapStringAttributes.Add(ActiveDirectoryConnector.ATT_ACCOUNTS); // for each attribute in the connector object ... foreach (ConnectorAttribute attribute in requestedAttributes) diff --git a/Framework/CommonObjects.cs b/Framework/CommonObjects.cs index d3443ce3..0896566b 100644 --- a/Framework/CommonObjects.cs +++ b/Framework/CommonObjects.cs @@ -37,6 +37,23 @@ using Org.IdentityConnectors.Framework.Common.Objects.Filters; namespace Org.IdentityConnectors.Framework.Common.Objects { + #region NameUtil + internal static class NameUtil { + + public static bool IsSpecialName(String name) { + return (name.StartsWith("@@") && name.EndsWith("@@")); + } + + public static string CreateSpecialName(string name) { + if (StringUtil.IsBlank(name)) { + const string ERR = "Name parameter must not be blank!"; + throw new ArgumentException(ERR); + } + return "@@" + name + "@@"; + } + } + #endregion + #region ConnectorAttributeUtil public static class ConnectorAttributeUtil { @@ -298,16 +315,12 @@ public static bool IsSpecial(ConnectorAttribute attr) { * iff the attribute parameter is null. */ public static bool IsSpecial(ConnectorAttributeInfo attr) { - // note this is dangerous because we need to be consistent - // in the naming of special attributes. String name = attr.Name; return IsSpecialName(name); } private static bool IsSpecialName(String name) { - // note this is dangerous because we need to be consistent - // in the naming of special attributes. - return (name.StartsWith("@@") && name.EndsWith("@@")); + return NameUtil.IsSpecialName(name); } /// @@ -316,11 +329,7 @@ private static bool IsSpecialName(String name) { /// string to make special /// name constructed for use as an operational attribute. public static string CreateSpecialName(string name) { - if (StringUtil.IsBlank(name)) { - const string ERR = "Name parameter must not be blank!"; - throw new ArgumentException(ERR); - } - return "@@" + name + "@@"; + return NameUtil.CreateSpecialName(name); } /// @@ -1659,28 +1668,51 @@ public String GetNameValue() { } #endregion + #region ObjectClassUtil + public static class ObjectClassUtil { + + /// + /// Determines if this object class is a special object class. + /// Special object classes include the predefined ones, such as + /// and . + /// + /// the object class to test + /// true iff the object class is special + /// if the object class parameter is null + public static bool IsSpecial(ObjectClass oclass) { + String name = oclass.GetObjectClassValue(); + return IsSpecialName(name); + } + + private static bool IsSpecialName(String name) { + return NameUtil.IsSpecialName(name); + } + + /// + /// Create a special name from the specified name. Add the @@ + /// string as both prefix and suffix. This indicates that a name + /// identifies a special object class such as a predefined one. + /// + /// object class name to make special + /// name constructed for use as a special name + public static string CreateSpecialName(string name) { + return NameUtil.CreateSpecialName(name); + } + } + #endregion + #region ObjectClass public sealed class ObjectClass { - public const String ACCOUNT_NAME = "account"; - public const String PERSON_NAME = "person"; - public const String GROUP_NAME = "group"; - public const String ORGANIZATION_NAME = "organization"; + public static readonly String ACCOUNT_NAME = ObjectClassUtil.CreateSpecialName("account"); + public static readonly String GROUP_NAME = ObjectClassUtil.CreateSpecialName("group"); /** * Denotes an account based object. */ public static readonly ObjectClass ACCOUNT = new ObjectClass(ACCOUNT_NAME); - /** - * Denotes a person based object. - */ - public static readonly ObjectClass PERSON = new ObjectClass(PERSON_NAME); /** * Denotes a group based object. */ public static readonly ObjectClass GROUP = new ObjectClass(GROUP_NAME); - /** - * Denotes a organization based object. - */ - public static readonly ObjectClass ORGANIZATION = new ObjectClass(ORGANIZATION_NAME); private readonly String _type; @@ -2096,16 +2128,6 @@ public static class PredefinedAttributes { * Groups an account object belongs to. */ public static readonly string GROUPS_NAME = ConnectorAttributeUtil.CreateSpecialName("GROUPS"); - - /** - * Accounts that belong to a group or organization. - */ - public static readonly string ACCOUNTS_NAME = ConnectorAttributeUtil.CreateSpecialName("ACCOUNTS"); - - /** - * An organization that that an account/person belongs to. - */ - public static readonly string ORGANIZATION_NAME = ConnectorAttributeUtil.CreateSpecialName("ORGANIZATION"); } #endregion @@ -2165,29 +2187,6 @@ public static class PredefinedAttributeInfos { typeof(String), ConnectorAttributeInfo.Flags.MULTIVALUED | ConnectorAttributeInfo.Flags.NOT_RETURNED_BY_DEFAULT); - - - /** - * Accounts that are members of a group or organization. The Attribute - * values are the UID value of each account the has a group or organization - * membership. - */ - public static readonly ConnectorAttributeInfo ACCOUNTS = - ConnectorAttributeInfoBuilder.Build(PredefinedAttributes.ACCOUNTS_NAME, - typeof(String), - ConnectorAttributeInfo.Flags.MULTIVALUED| - ConnectorAttributeInfo.Flags.NOT_RETURNED_BY_DEFAULT); - - - /** - * Organizations that an account or person is a member of. The Attribute - * values are the UID value of each organization that an account or person is - * a member of. - */ - public static readonly ConnectorAttributeInfo ORGANIZATION = - ConnectorAttributeInfoBuilder.Build(PredefinedAttributes.ORGANIZATION_NAME, - typeof(String), - ConnectorAttributeInfo.Flags.NOT_RETURNED_BY_DEFAULT); } #endregion diff --git a/FrameworkTests/FrameworkTests.csproj b/FrameworkTests/FrameworkTests.csproj index a5af2546..0c77e4cf 100644 --- a/FrameworkTests/FrameworkTests.csproj +++ b/FrameworkTests/FrameworkTests.csproj @@ -66,6 +66,7 @@ + @@ -99,7 +100,7 @@ {5B011775-B121-4EEE-A410-BA2D2F5BFB8B} FrameworkInternal - + diff --git a/FrameworkTests/ObjectClassUtilTests.cs b/FrameworkTests/ObjectClassUtilTests.cs new file mode 100755 index 00000000..fbf426fd --- /dev/null +++ b/FrameworkTests/ObjectClassUtilTests.cs @@ -0,0 +1,39 @@ +/* + * ==================== + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER. + * + * Copyright 2007-2008 Sun Microsystems, Inc. All rights reserved. + * + * The contents of this file are subject to the terms of the Common Development + * and Distribution License("CDDL") (the "License"). You may not use this file + * except in compliance with the License. + * + * You can obtain a copy of the License at + * http://IdentityConnectors.dev.java.net/legal/license.txt + * See the License for the specific language governing permissions and limitations + * under the License. + * + * When distributing the Covered Code, include this CDDL Header Notice in each file + * and include the License file at identityconnectors/legal/license.txt. + * If applicable, add the following below this CDDL Header, with the fields + * enclosed by brackets [] replaced by your own identifying information: + * "Portions Copyrighted [year] [name of copyright owner]" + * ==================== + */ +using NUnit.Framework; +using Org.IdentityConnectors.Framework.Common.Objects; + +namespace FrameworkTests +{ + [TestFixture] + public class ObjectClassUtilTests + { + + [Test] + public void TestIsSpecial() + { + Assert.IsTrue(ObjectClassUtil.IsSpecial(ObjectClass.ACCOUNT)); + Assert.IsFalse(ObjectClassUtil.IsSpecial(new ObjectClass("o"))); + } + } +} diff --git a/FrameworkTests/ObjectSerializationTests.cs b/FrameworkTests/ObjectSerializationTests.cs index 657a71c0..5e35fc4a 100644 --- a/FrameworkTests/ObjectSerializationTests.cs +++ b/FrameworkTests/ObjectSerializationTests.cs @@ -606,7 +606,7 @@ public void TestObjectClassInfo() { builder.Updateable=(true); builder.MultiValued=(true); ObjectClassInfoBuilder obld = new ObjectClassInfoBuilder(); - obld.ObjectType = ObjectClass.ORGANIZATION_NAME; + obld.ObjectType = ObjectClass.ACCOUNT_NAME; obld.IsContainer = true; obld.AddAttributeInfo(builder.Build()); ObjectClassInfo v1 = obld.Build(); @@ -636,7 +636,7 @@ public void TestSchema() { OperationOptionInfo opInfo = new OperationOptionInfo("name",typeof(int?)); ObjectClassInfoBuilder bld = new ObjectClassInfoBuilder(); - bld.ObjectType = ObjectClass.ORGANIZATION_NAME; + bld.ObjectType = ObjectClass.ACCOUNT_NAME; ObjectClassInfo info = bld.Build(); ICollection temp = CollectionUtil.NewSet(info); IDictionary,ICollection> map = From fa0b8bc3354172e39e5ca84a538fd6b04094a8b8 Mon Sep 17 00:00:00 2001 From: abadea Date: Mon, 9 Feb 2009 20:29:26 +0000 Subject: [PATCH 178/342] Issue 388: Reserved object classes need to be in upper case --- Framework/CommonObjects.cs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/Framework/CommonObjects.cs b/Framework/CommonObjects.cs index 0896566b..7f6c4d5a 100644 --- a/Framework/CommonObjects.cs +++ b/Framework/CommonObjects.cs @@ -1703,8 +1703,8 @@ public static string CreateSpecialName(string name) { #region ObjectClass public sealed class ObjectClass { - public static readonly String ACCOUNT_NAME = ObjectClassUtil.CreateSpecialName("account"); - public static readonly String GROUP_NAME = ObjectClassUtil.CreateSpecialName("group"); + public static readonly String ACCOUNT_NAME = ObjectClassUtil.CreateSpecialName("ACCOUNT"); + public static readonly String GROUP_NAME = ObjectClassUtil.CreateSpecialName("GROUP"); /** * Denotes an account based object. */ From 954fab44f43acfe63b94be70b764193fd91c060c Mon Sep 17 00:00:00 2001 From: kyarbro Date: Mon, 9 Feb 2009 20:54:12 +0000 Subject: [PATCH 179/342] Support for connector DLLs with a version in their filename --- FrameworkInternal/ApiLocal.cs | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/FrameworkInternal/ApiLocal.cs b/FrameworkInternal/ApiLocal.cs index 6f78e65e..2485e132 100644 --- a/FrameworkInternal/ApiLocal.cs +++ b/FrameworkInternal/ApiLocal.cs @@ -334,6 +334,14 @@ public LocalConnectorInfoManagerImpl() Assembly.LoadFrom(file.ToString()); CollectionUtil.AddAll(_connectorInfo, ProcessAssembly(lib)); + } + // also handle connector DLL file names with a version + FileInfo[] versionedFiles = directory.GetFiles("*.Connector-*.dll"); + foreach (FileInfo versionedFile in versionedFiles) { + Assembly lib = + Assembly.LoadFrom(versionedFile.ToString()); + CollectionUtil.AddAll(_connectorInfo, + ProcessAssembly(lib)); } } From e8ab719d2fb13119f1e2f2b2c705cec9a23a55ee Mon Sep 17 00:00:00 2001 From: abadea Date: Mon, 9 Feb 2009 21:45:48 +0000 Subject: [PATCH 180/342] Issue 388: Changing the marker of special names to '__' --- ActiveDirectoryConnector/ObjectClasses.xml | 34 +++++++++---------- .../ActiveDirectoryConnectorTest.cs | 4 +-- ExchangeConnector/ObjectClasses.xml | 4 +-- Framework/CommonObjects.cs | 6 ++-- 4 files changed, 24 insertions(+), 24 deletions(-) diff --git a/ActiveDirectoryConnector/ObjectClasses.xml b/ActiveDirectoryConnector/ObjectClasses.xml index fc1d22c1..9e9b0097 100644 --- a/ActiveDirectoryConnector/ObjectClasses.xml +++ b/ActiveDirectoryConnector/ObjectClasses.xml @@ -135,26 +135,26 @@ - - AttributeInfo name="@@ENABLE_DATE@@" type="long"/--> - - - - + + AttributeInfo name="__ENABLE_DATE__" type="long"/--> + + + + - - + + - + - + @@ -187,16 +187,16 @@ /> - - + + - + - + @@ -211,15 +211,15 @@ - + - + - + diff --git a/ActiveDirectoryConnectorTests/ActiveDirectoryConnectorTest.cs b/ActiveDirectoryConnectorTests/ActiveDirectoryConnectorTest.cs index 62f75a74..9fcab2f7 100644 --- a/ActiveDirectoryConnectorTests/ActiveDirectoryConnectorTest.cs +++ b/ActiveDirectoryConnectorTests/ActiveDirectoryConnectorTest.cs @@ -2329,8 +2329,8 @@ public Configuration GetConfiguration() /** * NOTES: - * - cn and @@NAME@@ should be the same. Test if they are not - * test for proper behavior if @@name@@ is not supplied + * - cn and __NAME__ should be the same. Test if they are not + * test for proper behavior if __name__ is not supplied * - test bogus attributes to like attribut named BogusAttr = hello * or something * - test writing to a read only attribute diff --git a/ExchangeConnector/ObjectClasses.xml b/ExchangeConnector/ObjectClasses.xml index f16c47ad..f8a8100b 100644 --- a/ExchangeConnector/ObjectClasses.xml +++ b/ExchangeConnector/ObjectClasses.xml @@ -39,7 +39,7 @@ - + @@ -107,7 +107,7 @@ - + diff --git a/Framework/CommonObjects.cs b/Framework/CommonObjects.cs index 7f6c4d5a..bd9927e2 100644 --- a/Framework/CommonObjects.cs +++ b/Framework/CommonObjects.cs @@ -41,7 +41,7 @@ namespace Org.IdentityConnectors.Framework.Common.Objects internal static class NameUtil { public static bool IsSpecialName(String name) { - return (name.StartsWith("@@") && name.EndsWith("@@")); + return (name.StartsWith("__") && name.EndsWith("__")); } public static string CreateSpecialName(string name) { @@ -49,7 +49,7 @@ public static string CreateSpecialName(string name) { const string ERR = "Name parameter must not be blank!"; throw new ArgumentException(ERR); } - return "@@" + name + "@@"; + return "__" + name + "__"; } } #endregion @@ -1689,7 +1689,7 @@ private static bool IsSpecialName(String name) { } /// - /// Create a special name from the specified name. Add the @@ + /// Create a special name from the specified name. Add the __ /// string as both prefix and suffix. This indicates that a name /// identifies a special object class such as a predefined one. /// From 775eaa8fc6496f6ed1df1058510f95bbce63d829 Mon Sep 17 00:00:00 2001 From: dvernon Date: Mon, 9 Feb 2009 23:09:20 +0000 Subject: [PATCH 181/342] Issue #422 - Added code to exclude computer accounts during account search. Also, added code so that if password expired attribute is not set, we dont get a null pointer exception. Added code to catch all exceptions when returning results, so that if any exception is thrown, we log an error, but continue to return results. --- .../ActiveDirectoryConnector.cs | 13 +++++++++++++ .../CustomAttributeHandlers.cs | 16 +++++++++++----- ActiveDirectoryConnector/ObjectClasses.xml | 2 +- 3 files changed, 25 insertions(+), 6 deletions(-) diff --git a/ActiveDirectoryConnector/ActiveDirectoryConnector.cs b/ActiveDirectoryConnector/ActiveDirectoryConnector.cs index 611d9347..a6a23a0f 100644 --- a/ActiveDirectoryConnector/ActiveDirectoryConnector.cs +++ b/ActiveDirectoryConnector/ActiveDirectoryConnector.cs @@ -513,6 +513,7 @@ private void ExecuteQuery(ObjectClass oclass, string query, fullQueryBuilder.Append(query); fullQueryBuilder.Append(")"); } + query = fullQueryBuilder.ToString(); if (query == null) @@ -521,6 +522,12 @@ private void ExecuteQuery(ObjectClass oclass, string query, } else { + // for backward compatibility ... + if((ObjectClass.ACCOUNT.Equals(oclass)) && (!includeDeleted)) + { + query = String.Format("(&(ObjectCategory=Person){0})", query); + } + Trace.TraceInformation("Setting search string to \'{0}\'", query); } @@ -658,6 +665,12 @@ private void ExecuteQuery(ObjectClass oclass, string query, Trace.TraceWarning("Error in creating ConnectorObject from DirectoryEntry. It may have been deleted during search."); Trace.TraceWarning(e.Message); } + catch (Exception e) + { + // In that case, of any error, try to continue + Trace.TraceWarning("Error in creating ConnectorObject from DirectoryEntry."); + Trace.TraceWarning(e.Message); + } } } diff --git a/ActiveDirectoryConnector/CustomAttributeHandlers.cs b/ActiveDirectoryConnector/CustomAttributeHandlers.cs index 4d25cba9..0cc4658a 100644 --- a/ActiveDirectoryConnector/CustomAttributeHandlers.cs +++ b/ActiveDirectoryConnector/CustomAttributeHandlers.cs @@ -960,13 +960,19 @@ private ConnectorAttribute GetCaFromDe_OpAtt_PasswordExpired( { ConnectorAttribute realAttribute = GetCaFromDe_Att_Generic( oclass, ActiveDirectoryConnector.ATT_PWD_LAST_SET, searchResult); - long? lastSetDate = ConnectorAttributeUtil.GetLongValue(realAttribute); - if ((lastSetDate.HasValue) && (lastSetDate.Value != 0)) + if (realAttribute != null) { - return ConnectorAttributeBuilder.BuildPasswordExpired(false); + long? lastSetDate = ConnectorAttributeUtil.GetLongValue(realAttribute); + if ((lastSetDate.HasValue) && (lastSetDate.Value != 0)) + { + return ConnectorAttributeBuilder.BuildPasswordExpired(false); + } + else + { + return ConnectorAttributeBuilder.BuildPasswordExpired(true); + } } - - return ConnectorAttributeBuilder.BuildPasswordExpired(true); + return null; } private ConnectorAttribute GetCaFromDe_OpAtt_Description( diff --git a/ActiveDirectoryConnector/ObjectClasses.xml b/ActiveDirectoryConnector/ObjectClasses.xml index 9e9b0097..a57aa21c 100644 --- a/ActiveDirectoryConnector/ObjectClasses.xml +++ b/ActiveDirectoryConnector/ObjectClasses.xml @@ -37,7 +37,7 @@ "Portions Copyrighted [year] [name of copyright owner]" --> - + From 84a667400da3e0afef6a23e2c750ca504a0ebd4d Mon Sep 17 00:00:00 2001 From: tknappek Date: Wed, 11 Feb 2009 11:42:29 +0000 Subject: [PATCH 182/342] Issue #427: added NUnit target to DotNetCommonBuild.Targets --- DotNetCommonBuild.Targets | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/DotNetCommonBuild.Targets b/DotNetCommonBuild.Targets index 98330c18..9f609826 100644 --- a/DotNetCommonBuild.Targets +++ b/DotNetCommonBuild.Targets @@ -68,6 +68,13 @@ WorkingDirectory="$(OutputPath)" ZipFileName="$(CommonBuildDir)\$(AssemblyName)-$(Major).$(Minor).$(Build).$(SVN_Revision).zip" ZipLevel="9" /> + + + + + + + From de5e963ac3ed6264b0e6d3a4f5b3551949a78c8d Mon Sep 17 00:00:00 2001 From: tknappek Date: Wed, 11 Feb 2009 13:57:15 +0000 Subject: [PATCH 183/342] Issue #427: make AD unit tests part of Hudson build --- .../ActiveDirectoryConnectorTests.csproj | 43 ++++++------------- 1 file changed, 13 insertions(+), 30 deletions(-) diff --git a/ActiveDirectoryConnectorTests/ActiveDirectoryConnectorTests.csproj b/ActiveDirectoryConnectorTests/ActiveDirectoryConnectorTests.csproj index 5b047e8f..c8b2a110 100644 --- a/ActiveDirectoryConnectorTests/ActiveDirectoryConnectorTests.csproj +++ b/ActiveDirectoryConnectorTests/ActiveDirectoryConnectorTests.csproj @@ -29,7 +29,7 @@ {DB8F7DA9-11A2-4BEB-8C14-25D8229EBE7E} Library Properties - Sun.OpenConnectors.ActiveDirectory + Org.IdentityConnectors.ActiveDirectory ActiveDirectoryConnectorTests v3.5 512 @@ -57,26 +57,8 @@ Project - - - - - - - - - - - - - - - - - - - - + + 3.5 @@ -102,20 +84,21 @@ {F140E8DA-52B4-4159-992A-9DA10EA8EEFB} - Common + Common {8B24461B-456A-4032-89A1-CD418F7B5B62} - Framework + Framework - + {5B011775-B121-4EEE-A410-BA2D2F5BFB8B} - FrameworkInternal + FrameworkInternal + + + {4700690A-2D29-40A0-86AC-E5A9F71A479A} + ShellScriptExecutorFactory + False - - {4700690A-2D29-40A0-86AC-E5A9F71A479A} - ShellScriptExecutorFactory - @@ -123,4 +106,4 @@ - + \ No newline at end of file From 45300d907329d1a00eae75eca79d52e7f990663f Mon Sep 17 00:00:00 2001 From: tknappek Date: Wed, 11 Feb 2009 14:20:45 +0000 Subject: [PATCH 184/342] Issue 74: ExchangeConnector implementation --- ExchangeConnector/{ => Data}/CommandInfos.xml | 111 ++-- ExchangeConnector/Data/PersistenceUtility.cs | 72 +++ .../Data/SerializableCommandInfo.cs | 83 +++ ExchangeConnector/ExchangeConfiguration.cs | 56 +- ExchangeConnector/ExchangeConnector.FxCop | 46 ++ ExchangeConnector/ExchangeConnector.cs | 442 ++++++++------ ExchangeConnector/ExchangeConnector.csproj | 10 +- ExchangeConnector/ExchangeUtility.cs | 344 +++++++++++ ExchangeConnector/ExchangeUtils.cs | 375 ------------ ExchangeConnector/LegacyExchangeConnector.cs | 556 ++++++++++-------- ExchangeConnector/ObjectClasses.xml | 326 ++++++---- ExchangeConnector/RunSpaceInstance.cs | 385 ++++++------ ExchangeConnector/build.xml | 42 +- 13 files changed, 1647 insertions(+), 1201 deletions(-) rename ExchangeConnector/{ => Data}/CommandInfos.xml (75%) create mode 100644 ExchangeConnector/Data/PersistenceUtility.cs create mode 100644 ExchangeConnector/Data/SerializableCommandInfo.cs create mode 100644 ExchangeConnector/ExchangeConnector.FxCop create mode 100644 ExchangeConnector/ExchangeUtility.cs delete mode 100644 ExchangeConnector/ExchangeUtils.cs diff --git a/ExchangeConnector/CommandInfos.xml b/ExchangeConnector/Data/CommandInfos.xml similarity index 75% rename from ExchangeConnector/CommandInfos.xml rename to ExchangeConnector/Data/CommandInfos.xml index 2690e95f..021f7250 100644 --- a/ExchangeConnector/CommandInfos.xml +++ b/ExchangeConnector/Data/CommandInfos.xml @@ -1,47 +1,32 @@  - + - + New-MailUser Name - + ExternalEmailAddress OrganizationalUnit Password @@ -59,12 +44,12 @@ SamAccountName TemplateInstance UsePreferMessageFormat - - - + + + Set-MailUser Identity - + AcceptMessagesOnlyFrom AcceptMessagesOnlyFromDLMembers Alias @@ -113,16 +98,16 @@ WindowsEmailAddress MaxReceiveSize MaxSendSize - - - + + + Get-MailUser - + Identity OrganizationalUnit ReadFromDomainController - + Filter AcceptMessagesOnlyFrom @@ -169,11 +154,11 @@ WhenCreated WindowsEmailAddress - - + + Enable-MailUser Identity - + ExternalEmailAddress Alias DomainController @@ -181,18 +166,18 @@ MessageBodyFormat MessageFormat UsePreferMessageFormat - - + + - + Get-User Identity - - + + Set-User Identity - + AssistantName City Company @@ -231,14 +216,14 @@ UserPrincipalName WebPage WindowsEmailAddress - - + + - + Enable-Mailbox Identity - + Database Equipment Alias @@ -251,7 +236,7 @@ LinkedCredential ManagedFolderMailboxPolicy ManagedFolderMailboxPolicyAllowed - - + + - \ No newline at end of file + \ No newline at end of file diff --git a/ExchangeConnector/Data/PersistenceUtility.cs b/ExchangeConnector/Data/PersistenceUtility.cs new file mode 100644 index 00000000..f7cc3fe1 --- /dev/null +++ b/ExchangeConnector/Data/PersistenceUtility.cs @@ -0,0 +1,72 @@ +// +// ==================== +// DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER. +// +// Copyright 2007-2008 Sun Microsystems, Inc. All rights reserved. +// +// The contents of this file are subject to the terms of the Common Development +// and Distribution License("CDDL") (the "License"). You may not use this file +// except in compliance with the License. +// +// You can obtain a copy of the License at +// http://IdentityConnectors.dev.java.net/legal/license.txt +// See the License for the specific language governing permissions and limitations +// under the License. +// +// When distributing the Covered Code, include this CDDL Header Notice in each file +// and include the License file at identityconnectors/legal/license.txt. +// If applicable, add the following below this CDDL Header, with the fields +// enclosed by brackets [] replaced by your own identifying information: +// "Portions Copyrighted [year] [name of copyright owner]" +// ==================== +// +// Tomas Knappek + +namespace Org.IdentityConnectors.Exchange.Data +{ + using System.Collections.Generic; + using System.Globalization; + using System.IO; + using System.Reflection; + using System.Xml.Serialization; + + /// + /// Persitence helper class, it uses to read the persistent data + /// + internal sealed class PersistenceUtility + { + /// + /// Prevents a default instance of the class from being created. + /// + private PersistenceUtility() + { + } + + /// + /// Reads the from persistent store (xml file) + /// + /// List of + /// if not able to read from persistent store + internal static IList ReadCommandInfo() + { + // persistent file + const string PersistFile = "Org.IdentityConnectors.Exchange.Data.CommandInfos.xml"; + + Assembly assembly = Assembly.GetExecutingAssembly(); + Stream stream = assembly.GetManifestResourceStream(PersistFile); + if (stream == null) + { + throw new IOException( + string.Format(CultureInfo.CurrentCulture, "Unable to read the {0} file from Assembly", PersistFile)); + } + + // we just read + using (TextReader streamReader = new StreamReader(stream)) + { + XmlSerializer ser = new XmlSerializer(typeof(List)); + List commandInfos = (List)ser.Deserialize(streamReader); + return commandInfos; + } + } + } +} \ No newline at end of file diff --git a/ExchangeConnector/Data/SerializableCommandInfo.cs b/ExchangeConnector/Data/SerializableCommandInfo.cs new file mode 100644 index 00000000..e6d74015 --- /dev/null +++ b/ExchangeConnector/Data/SerializableCommandInfo.cs @@ -0,0 +1,83 @@ +// +// ==================== +// DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER. +// +// Copyright 2007-2008 Sun Microsystems, Inc. All rights reserved. +// +// The contents of this file are subject to the terms of the Common Development +// and Distribution License("CDDL") (the "License"). You may not use this file +// except in compliance with the License. +// +// You can obtain a copy of the License at +// http://IdentityConnectors.dev.java.net/legal/license.txt +// See the License for the specific language governing permissions and limitations +// under the License. +// +// When distributing the Covered Code, include this CDDL Header Notice in each file +// and include the License file at identityconnectors/legal/license.txt. +// If applicable, add the following below this CDDL Header, with the fields +// enclosed by brackets [] replaced by your own identifying information: +// "Portions Copyrighted [year] [name of copyright owner]" +// ==================== +// +// Tomas Knappek +namespace Org.IdentityConnectors.Exchange.Data +{ + using System.Collections.Generic; + + /// + /// Command metadata data object, it has to be public in order to be + /// used by , + /// can be also made "struct" however we don't expect huge amount of data here + /// + public class SerializableCommandInfo + { + /// + /// Command parameters local variable + /// + private readonly List parameters; + + /// + /// Initializes a new instance of the class. + /// + public SerializableCommandInfo() + { + this.parameters = new List(); + } + + /// + /// Gets or sets Command name + /// + public string Name { get; set; } + + /// + /// Gets or sets special parameter name used as id for this command + /// + public string NameParameter { get; set; } + + /// + /// Gets Command parameters - only string type is supported + /// + /// Note: Cann't be IList because it is not supported by + /// + /// + [System.Xml.Serialization.XmlArray("Parameters")] + [System.Xml.Serialization.XmlArrayItem("string", typeof(string))] + public List Parameters + { + get + { + return this.parameters; + } + } + + /// + /// Adds parameter to command parameter list + /// + /// string to be added as parameter + public void AddParameter(string parameter) + { + this.parameters.Add(parameter); + } + } +} \ No newline at end of file diff --git a/ExchangeConnector/ExchangeConfiguration.cs b/ExchangeConnector/ExchangeConfiguration.cs index 9d5e5477..a963d667 100644 --- a/ExchangeConnector/ExchangeConfiguration.cs +++ b/ExchangeConnector/ExchangeConfiguration.cs @@ -1,37 +1,35 @@ -/* - * ==================== - * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER. - * - * Copyright 2007-2008 Sun Microsystems, Inc. All rights reserved. - * - * The contents of this file are subject to the terms of the Common Development - * and Distribution License("CDDL") (the "License"). You may not use this file - * except in compliance with the License. - * - * You can obtain a copy of the License at - * http://IdentityConnectors.dev.java.net/legal/license.txt - * See the License for the specific language governing permissions and limitations - * under the License. - * - * When distributing the Covered Code, include this CDDL Header Notice in each file - * and include the License file at identityconnectors/legal/license.txt. - * If applicable, add the following below this CDDL Header, with the fields - * enclosed by brackets [] replaced by your own identifying information: - * "Portions Copyrighted [year] [name of copyright owner]" - * ==================== - */ -using System; -using Org.IdentityConnectors.ActiveDirectory; -using Org.IdentityConnectors.Framework.Spi; +// +// ==================== +// DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER. +// +// Copyright 2007-2008 Sun Microsystems, Inc. All rights reserved. +// +// The contents of this file are subject to the terms of the Common Development +// and Distribution License("CDDL") (the "License"). You may not use this file +// except in compliance with the License. +// +// You can obtain a copy of the License at +// http://IdentityConnectors.dev.java.net/legal/license.txt +// See the License for the specific language governing permissions and limitations +// under the License. +// +// When distributing the Covered Code, include this CDDL Header Notice in each file +// and include the License file at identityconnectors/legal/license.txt. +// If applicable, add the following below this CDDL Header, with the fields +// enclosed by brackets [] replaced by your own identifying information: +// "Portions Copyrighted [year] [name of copyright owner]" +// ==================== +// +// Tomas Knappek namespace Org.IdentityConnectors.Exchange { + using Org.IdentityConnectors.ActiveDirectory; + /// /// MS Exchange specific configuration /// public class ExchangeConfiguration : ActiveDirectoryConfiguration - { - - } - + { + } } diff --git a/ExchangeConnector/ExchangeConnector.FxCop b/ExchangeConnector/ExchangeConnector.FxCop new file mode 100644 index 00000000..916e0ca8 --- /dev/null +++ b/ExchangeConnector/ExchangeConnector.FxCop @@ -0,0 +1,46 @@ + + + + True + $(FxCopDir)\Xml\FxCopReport.xsl + + + + + + True + True + True + 10 + 1 + + False + + False + 120 + False + + + + $(ProjectDir)/../../../../Program Files/Reference Assemblies/Microsoft/WindowsPowerShell/v1.0/ + $(ProjectDir)/../Framework/bin/Release/ + + + + + + + + + + + + + + + + + + + + diff --git a/ExchangeConnector/ExchangeConnector.cs b/ExchangeConnector/ExchangeConnector.cs index 9ca43a69..c3cf2560 100644 --- a/ExchangeConnector/ExchangeConnector.cs +++ b/ExchangeConnector/ExchangeConnector.cs @@ -1,100 +1,128 @@ -/* - * ==================== - * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER. - * - * Copyright 2007-2008 Sun Microsystems, Inc. All rights reserved. - * - * The contents of this file are subject to the terms of the Common Development - * and Distribution License("CDDL") (the "License"). You may not use this file - * except in compliance with the License. - * - * You can obtain a copy of the License at - * http://IdentityConnectors.dev.java.net/legal/license.txt - * See the License for the specific language governing permissions and limitations - * under the License. - * - * When distributing the Covered Code, include this CDDL Header Notice in each file - * and include the License file at identityconnectors/legal/license.txt. - * If applicable, add the following below this CDDL Header, with the fields - * enclosed by brackets [] replaced by your own identifying information: - * "Portions Copyrighted [year] [name of copyright owner]" - * ==================== - */ -using System; -using System.Collections.Generic; -using System.Diagnostics; -using System.Management.Automation.Runspaces; -using Org.IdentityConnectors.ActiveDirectory; -using Org.IdentityConnectors.Common; -using Org.IdentityConnectors.Framework.Common.Objects; -using Org.IdentityConnectors.Framework.Spi; +// +// ==================== +// DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER. +// +// Copyright 2007-2008 Sun Microsystems, Inc. All rights reserved. +// +// The contents of this file are subject to the terms of the Common Development +// and Distribution License("CDDL") (the "License"). You may not use this file +// except in compliance with the License. +// +// You can obtain a copy of the License at +// http://IdentityConnectors.dev.java.net/legal/license.txt +// See the License for the specific language governing permissions and limitations +// under the License. +// +// When distributing the Covered Code, include this CDDL Header Notice in each file +// and include the License file at identityconnectors/legal/license.txt. +// If applicable, add the following below this CDDL Header, with the fields +// enclosed by brackets [] replaced by your own identifying information: +// "Portions Copyrighted [year] [name of copyright owner]" +// ==================== +// +// Tomas Knappek namespace Org.IdentityConnectors.Exchange { + using System; + using System.Collections.Generic; + using System.Diagnostics; + using System.Management.Automation.Runspaces; + + using Data; + + using Org.IdentityConnectors.ActiveDirectory; + using Org.IdentityConnectors.Common; + using Org.IdentityConnectors.Framework.Common.Objects; + using Org.IdentityConnectors.Framework.Spi; + /// /// MS Exchange extension of Active Directory connector. - /// Full featured connector, see LegacyExchangeConnector for limited functionality connector. + /// Full featured connector, for limited functionality connector. /// LegacyExchangeConnector will be extension of this class, once ready. - /// + /// public class ExchangeConnector : ActiveDirectoryConnector - { - private static readonly string CLASS = typeof(ExchangeConnector).ToString(); + { + /// + /// MailBox object class name + /// + public const string MailboxName = "mailbox"; + + /// + /// MailUser object class name + /// + public const string MailUserName = "mailuser"; + + /// + /// MailBox object class, based on + /// + public static readonly ObjectClass Mailbox = new ObjectClass(MailboxName); + + /// + /// MailUser object class, based on + /// + public static readonly ObjectClass MailUser = new ObjectClass(MailUserName); - //object class names - public const string MAILBOX_NAME = "mailbox"; - public const string MAILUSER_NAME = "mailuser"; + /// + /// This Class name - used for logging purposes + /// + private static readonly string ClassName = typeof(ExchangeConnector).ToString(); - //object classes - public static readonly ObjectClass MAILBOX = new ObjectClass(MAILBOX_NAME); - public static readonly ObjectClass MAILUSER = new ObjectClass(MAILUSER_NAME); + /// + /// Configuration instance variable, method for assignment + /// + private ExchangeConfiguration configuration; - //local vars - private ExchangeConfiguration _configuration = null; - private RunSpaceInstance _runspace = null; - private bool _disposed = false; - private IDictionary _mapOcInfo = null; + /// + /// Runspace instance variable, it is managed resource - has to be released + /// + private RunSpaceInstance runspace; + /// + /// Map of object class infos, used for generating + /// + private IDictionary mapOcInfo; /// /// Implementation of CreateOp.Create /// - /// (oc - /// - /// - /// - public override Uid Create(ObjectClass oclass, - ICollection attributes, OperationOptions options) + /// Object class(oc + /// Object attributes + /// Operation options + /// of the created object + public override Uid Create( + ObjectClass oclass, + ICollection attributes, + OperationOptions options) { const string METHOD = "Create"; - - Debug.WriteLine(METHOD + ":entry", CLASS); - + Debug.WriteLine(METHOD + ":entry", ClassName); - //first create the object in AD + // first create the object in AD Uid uid = base.Create(oclass, attributes, options); try - { - if (oclass.Equals(MAILBOX)) + { + if (oclass.Is(MailboxName)) { - //enable mailbox for person - Command cmd = ExchangeUtils.GetCommand(CommandInfo.ENABLE_MAILBOX, attributes); - _runspace.InvokePipeline(cmd); - } else if (oclass.Equals(MAILUSER)) + // enable mailbox for person + Command cmd = ExchangeUtility.GetCommand(CommandInfo.EnableMailbox, attributes); + this.runspace.InvokePipeline(cmd); + } + else if (oclass.Is(MailUserName)) { - //enable mailuser - Command cmd = ExchangeUtils.GetCommand(CommandInfo.ENABLE_MAILUSER, attributes); - _runspace.InvokePipeline(cmd); - + // enable mailuser + Command cmd = ExchangeUtility.GetCommand(CommandInfo.EnableMailUser, attributes); + this.runspace.InvokePipeline(cmd); } - Debug.WriteLine(METHOD + ":exit", CLASS); - + Debug.WriteLine(METHOD + ":exit", ClassName); } - catch (Exception) + catch { - //do the rollback - delete the uid - base.Delete(oclass, uid, options); + // do the rollback - delete the object by uid + // no need to check the uid is null, ensured by the create contract + this.Delete(oclass, uid, options); throw; } @@ -104,15 +132,18 @@ public override Uid Create(ObjectClass oclass, /// /// Implementation of UpdateOp.Update /// - /// - /// - /// - /// - /// - public override Uid Update(UpdateType type, ObjectClass oclass, - ICollection attributes, OperationOptions options) + /// Update type + /// Object class + /// Object attributes + /// Operation options + /// of the updated object + public override Uid Update( + UpdateType type, + ObjectClass oclass, + ICollection attributes, + OperationOptions options) { - //TODO: Implement Update + // TODO: Implement Update return base.Update(type, oclass, attributes, options); } @@ -121,98 +152,115 @@ public override Uid Update(UpdateType type, ObjectClass oclass, /// public override void Test() { - //validate the configuration first, this will check AD configuration too - _configuration.Validate(); - //AD validation (includes configuration validation too) + // validate the configuration first, this will check AD configuration too + this.configuration.Validate(); + + // AD validation (includes configuration validation too) base.Test(); - //runspace check - _runspace.Test(); + + // runspace check + this.runspace.Test(); } /// /// Implementation of SynOp.Sync /// - /// - /// - /// - /// - public override void Sync(ObjectClass objClass, SyncToken token, - SyncResultsHandler handler, OperationOptions options) + /// Object class + /// Syncronization token + /// Handler for syncronization results + /// Operation options, can be null + public override void Sync( + ObjectClass objClass, + SyncToken token, + SyncResultsHandler handler, + OperationOptions options) { - //TODO: implement Sync + // TODO: implement Sync base.Sync(objClass, token, handler, options); } /// /// Implementation of SynOp.GetLatestSyncToken /// - /// - public override SyncToken GetLatestSyncToken(ObjectClass oclass) + /// Object class + /// of the last sync + public override SyncToken GetLatestSyncToken(ObjectClass objectClass) { - //TODO: Implement GetLatestSyncToken - return base.GetLatestSyncToken(oclass); + // TODO: Implement GetLatestSyncToken + return base.GetLatestSyncToken(objectClass); } /// /// Implementation of SearchOp.ExecuteQuery /// - /// - /// - /// - /// - public override void ExecuteQuery(ObjectClass oclass, string query, - ResultsHandler handler, OperationOptions options) + /// Object class + /// Query to execute + /// Result handler + /// Operation options + public override void ExecuteQuery( + ObjectClass oclass, + string query, + ResultsHandler handler, + OperationOptions options) { - //TODO: Implement ExecuteQuery + // TODO: Implement ExecuteQuery base.ExecuteQuery(oclass, query, handler, options); } - /// /// Implementation of SearchOp.CreateFilterTranslator /// - /// - /// - /// - public override Org.IdentityConnectors.Framework.Common.Objects.Filters.FilterTranslator CreateFilterTranslator(ObjectClass oclass, OperationOptions options) + /// Object class + /// Operation options + /// Exchange specific Filter translator + public override Org.IdentityConnectors.Framework.Common.Objects.Filters.FilterTranslator CreateFilterTranslator( + ObjectClass oclass, + OperationOptions options) { - //TODO: Implement CreateFilterTranslator + // TODO: Implement CreateFilterTranslator return base.CreateFilterTranslator(oclass, options); } /// /// Inits the connector with configuration injected /// - /// + /// Initialized Exchange configuration public override void Init(Configuration configuration) { base.Init(configuration); - _configuration = (ExchangeConfiguration)configuration; - _runspace = new RunSpaceInstance(RunSpaceInstance.SnapIn.Exchange); - _mapOcInfo = ExchangeUtils.GetOCInfo(); + this.configuration = (ExchangeConfiguration)configuration; + + // create runspace instance, will be alive as long as the connector instance is alive + this.runspace = new RunSpaceInstance(RunSpaceInstance.SnapIn.Exchange); + + // read the object class info definitions + this.mapOcInfo = ExchangeUtility.GetOCInfo(); } - - + /// - /// Dispose resources + /// Dispose resources, /// - public override void Dispose() + public sealed override void Dispose() { - //lock is probably not necessary - lock (this) + this.Dispose(true); + GC.SuppressFinalize(this); + } + + /// + /// Dispose the resources we use + /// + /// true if called from + protected virtual void Dispose(bool disposing) + { + if (disposing) { - if (_disposed) - { - return; - } - if (_runspace != null) + // free managed resources + if (this.runspace != null) { - _runspace.Dispose(); + this.runspace.Dispose(); + this.runspace = null; } - base.Dispose(); - _disposed = true; - } - + } } /// @@ -221,11 +269,15 @@ public override void Dispose() /// List of supported object classes protected override ICollection GetSupportedObjectClasses() { - ICollection ocList = base.GetSupportedObjectClasses(); - Assertions.NullCheck(ocList, "ocList"); - ocList.Add(MAILBOX); - ocList.Add(MAILUSER); - return ocList; + ICollection objectClasses = base.GetSupportedObjectClasses(); + Assertions.NullCheck(objectClasses, "ocList"); + + ICollection ourObjectClass = new List(objectClasses); + + // add our object classes + ourObjectClass.Add(Mailbox); + ourObjectClass.Add(MailUser); + return ourObjectClass; } /// @@ -235,68 +287,108 @@ protected override ICollection GetSupportedObjectClasses() /// ObjectClass' ObjectClassInfo protected override ObjectClassInfo GetObjectClassInfo(ObjectClass oc) { - ObjectClassInfo ret = CollectionUtil.GetValue(_mapOcInfo, oc, null) ?? base.GetObjectClassInfo(oc); + ObjectClassInfo ret = CollectionUtil.GetValue(this.mapOcInfo, oc, null) ?? base.GetObjectClassInfo(oc); Assertions.NullCheck(ret, "ret"); return ret; } - } + /// + /// Command definition object, uses internally , + /// intended to be one place for all the PowerShell commands definition + /// + internal sealed class CommandInfo + { + /// + /// Enable-Mailbox command meta info + /// + internal static readonly CommandInfo EnableMailbox = new CommandInfo("Enable-Mailbox"); + /// + /// Enable-MailUser command meta info + /// + internal static readonly CommandInfo EnableMailUser = new CommandInfo("Enable-MailUser"); - /// - /// Command definition object - /// - internal sealed class CommandInfo - { - private static IDictionary cinfos = null; + /// + /// Set-MailUser command meta info + /// + internal static readonly CommandInfo SetMailUser = new CommandInfo("Set-MailUser"); - internal static readonly CommandInfo ENABLE_MAILBOX = new CommandInfo("Enable-Mailbox"); - internal static readonly CommandInfo ENABLE_MAILUSER = new CommandInfo("Enable-MailUser"); - internal static readonly CommandInfo SET_MAILUSER = new CommandInfo("Set-MailUser"); + /// + /// List of SerializableCommandInfo object - will be read from persistence + /// + private static IList serCmdInfos; + /// + /// Private placeholder for concrete + /// + private SerializableCommandInfo serCmdInfo; - private CommandInfo(string name) - { - Name = name; - if (cinfos == null) + /// + /// Initializes a new instance of the class. + /// , made private to be immutable + /// + /// Command name + private CommandInfo(string name) { - cinfos = ExchangeUtils.GetCommandInfo(); + this.Name = name; } - } - - /// - /// Comamnd Name - /// - internal string Name { get; private set; } + /// + /// Gets Comamnd Name + /// + internal string Name { get; private set; } - /// - /// Comand Parameters - /// - internal string[] Parameters - { - get + /// + /// Gets Comand Parameters + /// + internal IList Parameters { - var ret = CollectionUtil.GetValue(cinfos, Name, null); - Assertions.NullCheck(ret, "ret"); + get + { + return this.SerCmdInfo.Parameters; + } + } - return ret.Parameter; + /// + /// Gets Name parameter + /// + internal string NameParameter + { + get + { + return this.SerCmdInfo.NameParameter; + } } - } - /// - /// Name parameter - /// - internal string NameParameter - { - get + /// + /// Gets SerCmdInfo. + /// + private SerializableCommandInfo SerCmdInfo { - var ret = CollectionUtil.GetValue(cinfos, Name, null); - Assertions.NullCheck(ret, "ret"); + get + { + // lazy init + if (serCmdInfos == null) + { + serCmdInfos = PersistenceUtility.ReadCommandInfo(); + } - return ret.NameParameter; - } + if (this.serCmdInfo == null) + { + foreach (SerializableCommandInfo info in serCmdInfos) + { + if (info.Name.Equals(this.Name)) + { + this.serCmdInfo = info; + break; + } + } + } + + return this.serCmdInfo; + } + } } - } + } } diff --git a/ExchangeConnector/ExchangeConnector.csproj b/ExchangeConnector/ExchangeConnector.csproj index d4d6634e..6be03cbf 100644 --- a/ExchangeConnector/ExchangeConnector.csproj +++ b/ExchangeConnector/ExchangeConnector.csproj @@ -31,9 +31,12 @@ Properties Org.IdentityConnectors.Exchange Exchange.Connector + Exchange v3.5 512 true + false + ExchangeConnectorTests true @@ -58,6 +61,7 @@ 3.5 + False ..\..\..\..\Program Files\Reference Assemblies\Microsoft\WindowsPowerShell\v1.0\System.Management.Automation.dll @@ -65,10 +69,12 @@ + + - + @@ -93,7 +99,7 @@ - + Designer diff --git a/ExchangeConnector/ExchangeUtility.cs b/ExchangeConnector/ExchangeUtility.cs new file mode 100644 index 00000000..ea463f06 --- /dev/null +++ b/ExchangeConnector/ExchangeUtility.cs @@ -0,0 +1,344 @@ +// +// ==================== +// DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER. +// +// Copyright 2007-2008 Sun Microsystems, Inc. All rights reserved. +// +// The contents of this file are subject to the terms of the Common Development +// and Distribution License("CDDL") (the "License"). You may not use this file +// except in compliance with the License. +// +// You can obtain a copy of the License at +// http://IdentityConnectors.dev.java.net/legal/license.txt +// See the License for the specific language governing permissions and limitations +// under the License. +// +// When distributing the Covered Code, include this CDDL Header Notice in each file +// and include the License file at identityconnectors/legal/license.txt. +// If applicable, add the following below this CDDL Header, with the fields +// enclosed by brackets [] replaced by your own identifying information: +// "Portions Copyrighted [year] [name of copyright owner]" +// ==================== +// +// Tomas Knappek + +namespace Org.IdentityConnectors.Exchange +{ + using System; + using System.Collections; + using System.Collections.Generic; + using System.Diagnostics; + using System.Globalization; + using System.IO; + using System.Management.Automation.Runspaces; + using System.Reflection; + using Microsoft.Win32; + using Org.IdentityConnectors.ActiveDirectory; + using Org.IdentityConnectors.Common; + using Org.IdentityConnectors.Framework.Common.Objects; + + /// + /// Description of ExchangeUtility. + /// + public sealed class ExchangeUtility : CommonUtils + { + /// + /// class name, used for logging purposes + /// + private static readonly string ClassName = typeof(ExchangeUtility).ToString(); + + /// + /// Embedded xml resource file containg the object class definitions + /// + private const string FileObjectClassDef = "Org.IdentityConnectors.Exchange.ObjectClasses.xml"; + + /// + /// Exchange registry key, used for building the exchange assembly resolver + /// + private const string ExchangeRegKey = "Software\\Microsoft\\Exchange\\v8.0\\Setup\\"; + + /// + /// Exchange registry value name, used together with + /// + private const string ExchangeRegValueName = "MsiInstallPath"; + + /// + /// Prevents a default instance of the class from being created. + /// + private ExchangeUtility() + { + } + + /// + /// Creates Exchange Assembly Resolver, + /// + /// The source of the event + /// A System.ResolveEventArgs that contains the event data + /// Assembly resolver that resolves Exchange assemblies + internal static Assembly AssemblyResolver(object sender, ResolveEventArgs args) + { + // Add path for the Exchange 2007 DLLs + if (args.Name.Contains("Microsoft.Exchange")) + { + string installPath = GetRegistryStringValue(ExchangeRegKey, ExchangeRegValueName); + installPath += "\\bin\\" + args.Name.Split(',')[0] + ".dll"; + return Assembly.LoadFrom(installPath); + } + + return null; + } + + /// + /// Get registry value, which is expected to be a string + /// + /// Registry Key Name + /// Registry Value Name + /// Registry value + /// If is null + /// If some problem with the registry value + internal static string GetRegistryStringValue(string keyName, string valName) + { + const string MethodName = "GetRegistryStringValue"; + Debug.WriteLine(MethodName + "(" + keyName + ", " + valName + ")" + ":entry", ClassName); + + // argument check + if (keyName == null) + { + keyName = string.Empty; + } + + if (valName == null) + { + throw new ArgumentNullException("valName"); + } + + RegistryKey regKey = Registry.LocalMachine.OpenSubKey(keyName, false); + try + { + if (regKey != null) + { + object val = regKey.GetValue(valName); + if (val != null) + { + RegistryValueKind regType = regKey.GetValueKind(valName); + if (!regType.Equals(RegistryValueKind.String)) + { + throw new InvalidDataException(String.Format( + CultureInfo.CurrentCulture, + "Invalid Registry data type, key name: {0} value name: {1} should be String", + keyName, + valName)); + } + + return Convert.ToString(val, CultureInfo.CurrentCulture); + } + else + { + throw new InvalidDataException(String.Format( + CultureInfo.CurrentCulture, + "Missing value for key name: {0} value name: {1}", + keyName, + valName)); + } + } + else + { + throw new InvalidDataException(String.Format( + CultureInfo.CurrentCulture, + "Unable to open registry for key: {0}", + keyName)); + } + } + finally + { + if (regKey != null) + { + regKey.Close(); + } + + Debug.WriteLine(MethodName + ":exit", ClassName); + } + } + + /// + /// reads the object class info definitions from xml + /// + /// Dictionary of object classes + internal static IDictionary GetOCInfo() + { + return GetOCInfo(FileObjectClassDef); + } + + /// + /// Creates command based on the commanf info, reading the calues from attributes + /// + /// Command defition + /// Attribute values + /// Ready to execute Command + /// if some of the param is null + internal static Command GetCommand(ExchangeConnector.CommandInfo cmdInfo, ICollection attributes) + { + Assertions.NullCheck(cmdInfo, "cmdInfo"); + Assertions.NullCheck(attributes, "attributes"); + + // create command + Command cmd = new Command(cmdInfo.Name); + + // map name attribute, if mapping specified + if (!string.IsNullOrEmpty(cmdInfo.NameParameter)) + { + object val = GetAttValue(Name.NAME, attributes); + if (val != null) + { + cmd.Parameters.Add(cmdInfo.NameParameter, val); + } + } + + foreach (string attName in cmdInfo.Parameters) + { + object val = GetAttValue(attName, attributes); + if (val != null) + { + cmd.Parameters.Add(attName, val); + } + } + + return cmd; + } + + /// + /// Helper method: Gets attribute value from the attribute collection + /// + /// attribute name + /// collection of attribute + /// Attribute value as object, null if not found + /// If some of the params is null + internal static object GetAttValue(string attName, ICollection attributes) + { + Assertions.NullCheck(attName, "attName"); + Assertions.NullCheck(attributes, "attributes"); + + object value = null; + ConnectorAttribute attribute = ConnectorAttributeUtil.Find(attName, attributes); + + if (attribute != null) + { + value = ConnectorAttributeUtil.GetSingleValue(attribute); + } + + return value; + } + + /// + /// Helper method for filtering the specified attributes from collection of attributes + /// + /// Collection of attributes + /// Attribute names to be filtered out + /// Filtered collection of attributes + internal static ICollection FilterOut(ICollection attributes, params string[] attName) + { + Assertions.NullCheck(attributes, "attributes"); + if (attName == null || attName.Length == 0) + { + return attributes; + } + + IList names = new ArrayList(attName); + ICollection filtered = new List(); + foreach (ConnectorAttribute attribute in attributes) + { + if (!names.Contains(attribute.Name)) + { + filtered.Add(attribute); + } + } + + return filtered; + } + + /// + /// Helper method - Replaces specified collection Items + /// TODO: reimplement not using arrays! + /// + /// Input to be searched for replacement + /// Replace mappings + /// Replaced + /// If some of the params is null + internal static ArrayList FilterReplace(ArrayList col, string[,] replace) + { + Assertions.NullCheck(col, "col"); + Assertions.NullCheck(replace, "replace"); + + ArrayList newcol = (ArrayList) col.Clone(); + for (int i = 0; i < replace.GetLength(0); i++) + { + if (newcol.Contains(replace[i, 0])) + { + newcol.Remove(replace[i, 0]); + newcol.Add(replace[i, 1]); + } + } + + return newcol; + } + + /// + /// Finds the attributes in connector object and rename it according to input array of names, but only + /// if the atribute name is in attributes to get + /// + /// ConnectorObject which attributes should be replaced + /// Attributes to get list + /// Replace mapping + /// ConnectorObject with replaced attributes + /// If some of the params is null + internal static ConnectorObject ReplaceAttributes(ConnectorObject cobject, IList attsToGet, string[,] replace) + { + Assertions.NullCheck(cobject, "cobject"); + Assertions.NullCheck(attsToGet, "attsToGet"); + Assertions.NullCheck(replace, "replace"); + + var attributes = cobject.GetAttributes(); + var builder = new ConnectorObjectBuilder(); + foreach (ConnectorAttribute attribute in attributes) + { + for (int i = 0; i < replace.GetLength(0); i++) + { + string oldName = replace[i, 1]; + string newName = replace[i, 0]; + if (attsToGet.Contains(newName) && attribute.Name == oldName) + { + var newAttribute = RenameAttribute(attribute, replace[i, 0]); + builder.AddAttribute(newAttribute); + break; + } + } + + builder.AddAttribute(attribute); + } + + builder.AddAttributes(attributes); + builder.ObjectClass = cobject.ObjectClass; + builder.SetName(cobject.Name); + builder.SetUid(cobject.Uid); + return builder.Build(); + } + + /// + /// Renames the connector attribute to new name + /// + /// ConnectorAttribute to be renamed + /// New attribute name + /// Renamed ConnectorAttribute + /// If some of the params is null + internal static ConnectorAttribute RenameAttribute(ConnectorAttribute cattribute, string newName) + { + Assertions.NullCheck(cattribute, "cattribute"); + Assertions.NullCheck(newName, "newName"); + + var attBuilder = new ConnectorAttributeBuilder(); + attBuilder.AddValue(cattribute.Value); + attBuilder.Name = newName; + return attBuilder.Build(); + } + } +} diff --git a/ExchangeConnector/ExchangeUtils.cs b/ExchangeConnector/ExchangeUtils.cs deleted file mode 100644 index 6f4bcaf8..00000000 --- a/ExchangeConnector/ExchangeUtils.cs +++ /dev/null @@ -1,375 +0,0 @@ -/* - * ==================== - * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER. - * - * Copyright 2007-2008 Sun Microsystems, Inc. All rights reserved. - * - * The contents of this file are subject to the terms of the Common Development - * and Distribution License("CDDL") (the "License"). You may not use this file - * except in compliance with the License. - * - * You can obtain a copy of the License at - * http://IdentityConnectors.dev.java.net/legal/license.txt - * See the License for the specific language governing permissions and limitations - * under the License. - * - * When distributing the Covered Code, include this CDDL Header Notice in each file - * and include the License file at identityconnectors/legal/license.txt. - * If applicable, add the following below this CDDL Header, with the fields - * enclosed by brackets [] replaced by your own identifying information: - * "Portions Copyrighted [year] [name of copyright owner]" - * ==================== - */ -using System; -using System.Collections; -using System.Collections.Generic; -using System.Diagnostics; -using System.IO; -using System.Management.Automation.Runspaces; -using System.Reflection; - -using Microsoft.Win32; -using System.Xml.Serialization; -using Org.IdentityConnectors.ActiveDirectory; -using Org.IdentityConnectors.Common; -using Org.IdentityConnectors.Framework.Common.Objects; - -namespace Org.IdentityConnectors.Exchange -{ - /// - /// Description of ExchangeUtils. - /// - public class ExchangeUtils : CommonUtils - { - private static readonly string CLASS = typeof(ExchangeUtils).ToString(); - - private const string OC_DEF_FILE = "Org.IdentityConnectors.Exchange.ObjectClasses.xml"; - private const string EXCHANGE_REG_KEY = "Software\\Microsoft\\Exchange\\v8.0\\Setup\\"; - private const string EXCHANGE_REG_VALUE = "MsiInstallPath"; - - /// use reflection to load the Exchange assembly - internal static Assembly AssemblyResolver(object p, ResolveEventArgs args) - { - //Add path for the Exchange 2007 DLLs - if (args.Name.Contains("Microsoft.Exchange")) - { - String installPath = GetRegistryStringValue(EXCHANGE_REG_KEY, EXCHANGE_REG_VALUE); - installPath += "\\bin\\" + args.Name.Split(',')[0] + ".dll"; - return Assembly.LoadFrom(installPath); - - } - - return null; - } - - /// - /// Get registry value, which is expected to be a string - /// - /// Registry Key Name - /// Registry Value Name - /// - internal static String GetRegistryStringValue(string keyName, string valName) - { - const string METHOD = "GetRegistryStringValue"; - Debug.WriteLine(METHOD + ":entry", CLASS); - //argument check - if (keyName == null) - { - keyName = ""; - } - if (valName == null) - { - throw new ArgumentNullException("valName"); - } - - RegistryKey regKey = Registry.LocalMachine.OpenSubKey(keyName, false); - try - { - Object val = regKey.GetValue(valName); - if (val != null) - { - RegistryValueKind regType = regKey.GetValueKind(valName); - if (!regType.Equals(RegistryValueKind.String)) - { - throw new InvalidDataException(String.Format("Invalid Registry data type, key name: {0} value name: {1} should be String", keyName, valName)); - } - return Convert.ToString(val); - } - else - { - throw new InvalidDataException(String.Format("Missing value for key name: {0} value name: {1}", keyName, valName)); - } - } - finally - { - if (regKey != null) - { - regKey.Close(); - } - Debug.WriteLine(METHOD + ":exit", CLASS); - } - } - - - - /// - /// reads the object class info definitions from xml - /// - ///Dictionary of object classes - internal static IDictionary GetOCInfo() - { - return GetOCInfo(OC_DEF_FILE); - } - - /// - /// - /// - internal static IDictionary GetCommandInfo () - { - Assembly assembly = Assembly.GetExecutingAssembly(); - Stream stream = assembly.GetManifestResourceStream("Org.IdentityConnectors.Exchange.CommandInfos.xml"); - - Assertions.NullCheck(stream, "stream"); - - //we just read - TextReader streamReader = new StreamReader(stream); - - XmlSerializer ser = new XmlSerializer(typeof(XCommandInfos)); - XCommandInfos cInfos = (XCommandInfos)ser.Deserialize(streamReader); - streamReader.Close(); - - Assertions.NullCheck(cInfos, "cInfos"); - - //create map of command infos - var map = new Dictionary(cInfos.XCommandInfo.Length); - foreach (XCommandInfo o in cInfos.XCommandInfo) - { - map.Add(o.Name, o); - } - - return map; - - } - - /// - /// creates command based on the commanf info, reading the calues from attributes - /// - /// Command defition - /// Attribute values - /// Ready to execute Command - internal static Command GetCommand(CommandInfo cmdInfo, ICollection attributes) - { - Assertions.NullCheck(cmdInfo, "cmdInfo"); - Assertions.NullCheck(attributes, "attributes"); - - //create command - Command cmd = new Command(cmdInfo.Name); - - //map name attribute, if mapping specified - if (!string.IsNullOrEmpty(cmdInfo.NameParameter)) - { - object val = GetAttValue(Name.NAME, attributes); - if (val != null) - { - cmd.Parameters.Add(cmdInfo.NameParameter, val); - } - } - - foreach (string attName in cmdInfo.Parameters) - { - object val = GetAttValue(attName, attributes); - if (val != null) - { - cmd.Parameters.Add(attName, val); - } - } - return cmd; - } - - /// - /// Helper method: Gets attribute value from the attribute collection - /// - /// attribute name - /// collection of attribute - /// attribute value as object, null if not found - internal static object GetAttValue(String attName, ICollection attributes) - { - Assertions.NullCheck(attName, "attName"); - Assertions.NullCheck(attributes, "attributes"); - - object value = null; - ConnectorAttribute attribute = ConnectorAttributeUtil.Find(attName, attributes); - - if (attribute != null) - { - value = ConnectorAttributeUtil.GetSingleValue(attribute); - } - - return value; - } - - /// - /// Helper method for filtering the specified attributes from collection of attributes - /// - /// Collection of attributes - /// Attribute names to be filtered out - /// Filtered collection of attributes - internal static ICollection FilterOut(ICollection attributes, params string[] attName) - { - Assertions.NullCheck(attributes, "attributes"); - if (attName == null || attName.Length == 0) - { - return attributes; - } - - IList names = new ArrayList(attName); - ICollection filtered = new List(); - foreach (ConnectorAttribute attribute in attributes) - { - if (!names.Contains(attribute.Name)) - { - filtered.Add(attribute); - } - } - return filtered; - } - - /// - /// Helper method - Replaces specified collection Items - /// - /// - /// - /// - internal static ArrayList FilterReplace(ArrayList col, string[,] replace) - { - Assertions.NullCheck(col, "col"); - Assertions.NullCheck(replace, "replace"); - - ArrayList newcol = (ArrayList) col.Clone(); - for (int i = 0; i < replace.GetLength(0); i++) - { - if (newcol.Contains(replace[i,0])) - { - newcol.Remove(replace[i, 0]); - newcol.Add(replace[i,1]); - } - } - return newcol; - } - - /// - /// finds the attributes in connector object and rename it according to input array of names, but only - /// if the aatribute name is in attributes to get - /// - /// - /// - /// - /// - internal static ConnectorObject ReplaceAttributes(ConnectorObject cobject, IList attsToGet, string[,] replace) - { - Assertions.NullCheck(cobject, "cobject"); - Assertions.NullCheck(attsToGet, "attsToGet"); - Assertions.NullCheck(replace, "replace"); - - var attributes = cobject.GetAttributes(); - var builder = new ConnectorObjectBuilder(); - foreach (ConnectorAttribute attribute in attributes) - { - for (int i = 0; i < replace.GetLength(0); i++) - { - string oldName = replace[i, 1]; - string newName = replace[i, 0]; - if (attsToGet.Contains(newName) && attribute.Name == oldName) - { - var newAttribute = RenameAttribute(attribute, replace[i, 0]); - builder.AddAttribute(newAttribute); - break; - } - } - - builder.AddAttribute(attribute); - } - builder.AddAttributes(attributes); - builder.ObjectClass = cobject.ObjectClass; - builder.SetName(cobject.Name); - builder.SetUid(cobject.Uid); - return builder.Build(); - } - - /// - /// Renames the connector attribute to new name - /// - /// - /// - /// - internal static ConnectorAttribute RenameAttribute(ConnectorAttribute cattribute, string newName) - { - Assertions.NullCheck(cattribute, "cattribute"); - Assertions.NullCheck(newName, "newName"); - - var caBuilder = new ConnectorAttributeBuilder(); - caBuilder.AddValue(cattribute.Value); - caBuilder.Name = newName; - return caBuilder.Build(); - } - - - } - - /// - /// DAO class for getting serialized data from xml - /// - [XmlRoot("CommandInfos")] - public class XCommandInfos - { - private readonly ArrayList lstCommandInfos = new ArrayList(); - - /// - /// Command info array - /// - [XmlElement("CommandInfo")] - public XCommandInfo[] XCommandInfo - { - get - { - var items = new XCommandInfo[lstCommandInfos.Count]; - lstCommandInfos.CopyTo(items); - return items; - - } - set - { - if (value == null) return; - var items = (XCommandInfo[])value; - lstCommandInfos.Clear(); - foreach (XCommandInfo item in items) - lstCommandInfos.Add(item); - - } - } - } - - /// - /// DO class for getting serialized data from XML - /// - public class XCommandInfo - { - - /// - /// Command name - /// - public string Name { get; set; } - - /// - /// Special parameter name used as id for this command - /// - public string NameParameter { get; set; } - - /// - /// Command parameters - /// - public string[] Parameter { get; set; } - } - - -} diff --git a/ExchangeConnector/LegacyExchangeConnector.cs b/ExchangeConnector/LegacyExchangeConnector.cs index 8b0aab56..8efa4d34 100644 --- a/ExchangeConnector/LegacyExchangeConnector.cs +++ b/ExchangeConnector/LegacyExchangeConnector.cs @@ -1,194 +1,258 @@ -/* - * ==================== - * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER. - * - * Copyright 2007-2008 Sun Microsystems, Inc. All rights reserved. - * - * The contents of this file are subject to the terms of the Common Development - * and Distribution License("CDDL") (the "License"). You may not use this file - * except in compliance with the License. - * - * You can obtain a copy of the License at - * http://IdentityConnectors.dev.java.net/legal/license.txt - * See the License for the specific language governing permissions and limitations - * under the License. - * - * When distributing the Covered Code, include this CDDL Header Notice in each file - * and include the License file at identityconnectors/legal/license.txt. - * If applicable, add the following below this CDDL Header, with the fields - * enclosed by brackets [] replaced by your own identifying information: - * "Portions Copyrighted [year] [name of copyright owner]" - * ==================== - */ -using System.Collections.Generic; -using System.Diagnostics; -using System.Management.Automation.Runspaces; -using Org.IdentityConnectors.ActiveDirectory; -using Org.IdentityConnectors.Common; -using Org.IdentityConnectors.Framework.Common.Exceptions; -using Org.IdentityConnectors.Framework.Common.Objects; -using Org.IdentityConnectors.Framework.Common.Objects.Filters; -using Org.IdentityConnectors.Framework.Spi; -using System.Collections; +// +// ==================== +// DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER. +// +// Copyright 2007-2008 Sun Microsystems, Inc. All rights reserved. +// +// The contents of this file are subject to the terms of the Common Development +// and Distribution License("CDDL") (the "License"). You may not use this file +// except in compliance with the License. +// +// You can obtain a copy of the License at +// http://IdentityConnectors.dev.java.net/legal/license.txt +// See the License for the specific language governing permissions and limitations +// under the License. +// +// When distributing the Covered Code, include this CDDL Header Notice in each file +// and include the License file at identityconnectors/legal/license.txt. +// If applicable, add the following below this CDDL Header, with the fields +// enclosed by brackets [] replaced by your own identifying information: +// "Portions Copyrighted [year] [name of copyright owner]" +// ==================== +// +// Tomas Knappek namespace Org.IdentityConnectors.Exchange { + using System; + using System.Collections; + using System.Collections.Generic; + using System.Diagnostics; + using System.Globalization; + using System.Management.Automation.Runspaces; + using Org.IdentityConnectors.ActiveDirectory; + using Org.IdentityConnectors.Common; + using Org.IdentityConnectors.Framework.Common.Exceptions; + using Org.IdentityConnectors.Framework.Common.Objects; + using Org.IdentityConnectors.Framework.Common.Objects.Filters; + using Org.IdentityConnectors.Framework.Spi; + /// /// MS Exchange connector - build to have the same functionality as Exchange resource adapter - /// /// [ConnectorClass("connector_displayName", - typeof(ExchangeConfiguration), - MessageCatalogPaths = new[] { "Org.IdentityConnectors.Exchange.Messages", - "Org.IdentityConnectors.ActiveDirectory.Messages" } - )] + typeof(ExchangeConfiguration), + MessageCatalogPaths = new[] { "Org.IdentityConnectors.Exchange.Messages", + "Org.IdentityConnectors.ActiveDirectory.Messages" })] public class LegacyExchangeConnector : ActiveDirectoryConnector { - private static readonly string CLASS = typeof(LegacyExchangeConnector).ToString(); + #region Fields Definition - //hardcoded stuff - internal const string ATT_RECIPIENT_TYPE = "RecipientType"; - internal const string ATT_EXTERNAL_MAIL = "ExternalEmailAddress"; - internal const string ATT_DATABASE = "Database"; + /// + /// Recipient Type attribute name + /// + internal const string AttRecipientType = "RecipientType"; - internal const string ATT_EXTERNAL_MAIL_AD_NAME = "targetAddress"; - internal const string ATT_DATABASE_AD_NAME = "homeMDB"; + /// + /// External Mail Address attribute name + /// + internal const string AttExternalMail = "ExternalEmailAddress"; - internal static readonly string[,] ATT_MAPPING = new[,] - { - {ATT_DATABASE, ATT_DATABASE_AD_NAME}, - {ATT_EXTERNAL_MAIL, ATT_EXTERNAL_MAIL_AD_NAME} - }; + /// + /// Database attribute name + /// + internal const string AttDatabase = "Database"; - private static readonly ConnectorAttributeInfo ATTINFO_RECIPIENT_TYPE = - ConnectorAttributeInfoBuilder.Build(ATT_RECIPIENT_TYPE, typeof(string), ConnectorAttributeInfo.Flags.REQUIRED | - ConnectorAttributeInfo.Flags.NOT_UPDATEABLE | ConnectorAttributeInfo.Flags.NOT_RETURNED_BY_DEFAULT); + /// + /// External Mail attribute name as in AD + /// + internal const string AttExternalMailADName = "targetAddress"; - + /// + /// Database attribute name as in AD + /// + internal const string AttDatabaseADName = "homeMDB"; - private static readonly ConnectorAttributeInfo ATTINFO_EXTERNAL_MAIL = - ConnectorAttributeInfoBuilder.Build(ATT_EXTERNAL_MAIL, typeof(string), ConnectorAttributeInfo.Flags.NOT_RETURNED_BY_DEFAULT | - ConnectorAttributeInfo.Flags.MULTIVALUED); + /// + /// Attribute mapping constant + /// + internal static readonly string[,] AttMapping = new[,] + { + { AttDatabase, AttDatabaseADName }, + { AttExternalMail, AttExternalMailADName } + }; - private static readonly ConnectorAttributeInfo ATTINFO_DATABASE = - ConnectorAttributeInfoBuilder.Build(ATT_DATABASE, typeof(string), ConnectorAttributeInfo.Flags.NOT_RETURNED_BY_DEFAULT); + /// + /// ClassName - used for debugging purposes + /// + private static readonly string ClassName = typeof(LegacyExchangeConnector).ToString(); - private const string RCPT_TYPE_MAIL_BOX = "mailbox"; - private const string RCPT_TYPE_MAIL_USER = "mailuser"; + /// + /// Recipient type attribute info + /// + private static readonly ConnectorAttributeInfo AttInfoRecipientType = + ConnectorAttributeInfoBuilder.Build( + AttRecipientType, + typeof(string), + ConnectorAttributeInfo.Flags.REQUIRED | ConnectorAttributeInfo.Flags.NOT_UPDATEABLE | ConnectorAttributeInfo.Flags.NOT_RETURNED_BY_DEFAULT); + + /// + /// External Mail attribute info + /// + private static readonly ConnectorAttributeInfo AttInfoExternalMail = + ConnectorAttributeInfoBuilder.Build( + AttExternalMail, + typeof(string), + ConnectorAttributeInfo.Flags.NOT_RETURNED_BY_DEFAULT | ConnectorAttributeInfo.Flags.MULTIVALUED); + + /// + /// Database attribute info + /// + private static readonly ConnectorAttributeInfo AttInfoDatabase = + ConnectorAttributeInfoBuilder.Build( + AttDatabase, + typeof(string), + ConnectorAttributeInfo.Flags.NOT_RETURNED_BY_DEFAULT); + /// + /// Recipient type attribute for Mailbox + /// + private const string RcptTypeMailBox = "mailbox"; + + /// + /// Recipient type attribute for MailUser + /// + private const string RcptTypeMailUser = "mailuser"; + + /// + /// Configuration instance + /// + private ExchangeConfiguration configuration; - //local vars - private ExchangeConfiguration _configuration = null; - private RunSpaceInstance _runspace = null; - private bool _disposed = false; + /// + /// Runspace instance + /// + private RunSpaceInstance runspace; + #endregion #region CreateOp.Create implementation + /// /// Implementation of CreateOp.Create /// - /// (oc - /// - /// - /// - public override Uid Create(ObjectClass oclass, - ICollection attributes, OperationOptions options) + /// Object class(oc + /// Object attributes + /// Operation options + /// Uid of the created object + public override Uid Create( + ObjectClass oclass, ICollection attributes, OperationOptions options) { const string METHOD = "Create"; - Debug.WriteLine(METHOD + ":entry", CLASS); + Debug.WriteLine(METHOD + ":entry", ClassName); - //get recipient type - string rcptType = ExchangeUtils.GetAttValue(ATT_RECIPIENT_TYPE, attributes) as string; + // get recipient type + string rcptType = ExchangeUtility.GetAttValue(AttRecipientType, attributes) as string; - if (rcptType != RCPT_TYPE_MAIL_BOX && rcptType != RCPT_TYPE_MAIL_USER) + if (rcptType != RcptTypeMailBox && rcptType != RcptTypeMailUser) { - //AD account only, we do nothing + // AD account only, we do nothing return base.Create(oclass, attributes, options); } - //first create the object in AD + // first create the object in AD Uid uid = base.Create(oclass, FilterOut(attributes), options); - //prepare the command - CommandInfo cmdInfo = rcptType == RCPT_TYPE_MAIL_BOX ? CommandInfo.ENABLE_MAILBOX : CommandInfo.ENABLE_MAILUSER; - Command cmd = ExchangeUtils.GetCommand(cmdInfo, attributes); + // prepare the command + ExchangeConnector.CommandInfo cmdInfo = rcptType == RcptTypeMailBox + ? ExchangeConnector.CommandInfo.EnableMailbox + : ExchangeConnector.CommandInfo.EnableMailUser; + Command cmd = ExchangeUtility.GetCommand(cmdInfo, attributes); try { - //execute the command - _runspace.InvokePipeline(cmd); + // execute the command + this.runspace.InvokePipeline(cmd); } catch { Trace.TraceWarning("Rolling back AD create for UID: " + uid.GetUidValue()); - //rollback AD create + + // rollback AD create try { - base.Delete(oclass, uid, options); + Delete(oclass, uid, options); } catch { - //ignore delete error Trace.TraceWarning("Not able to rollback AD create for UID: " + uid.GetUidValue()); + throw; } - //rethrow original exception + + // rethrow original exception throw; } - Debug.WriteLine(METHOD + ":exit", CLASS); + Debug.WriteLine(METHOD + ":exit", ClassName); return uid; } + #endregion /// /// Implementation of UpdateOp.Update /// - /// - /// - /// - /// - /// - public override Uid Update(UpdateType type, ObjectClass oclass, - ICollection attributes, OperationOptions options) + /// Update type + /// Object class + /// Object attributes + /// Operation options + /// Uid of the updated object + public override Uid Update( + UpdateType type, + ObjectClass oclass, + ICollection attributes, + OperationOptions options) { const string METHOD = "Update"; - Debug.WriteLine(METHOD + ":entry", CLASS); + Debug.WriteLine(METHOD + ":entry", ClassName); Assertions.NullCheck(type, "updatetype"); Assertions.NullCheck(oclass, "oclass"); Assertions.NullCheck(attributes, "attributes"); - //update in AD first + // update in AD first Uid uid = base.Update(type, oclass, FilterOut(attributes), options); - //get recipient type - string rcptType = ExchangeUtils.GetAttValue(ATT_RECIPIENT_TYPE, attributes) as string; - //update is possible for mailuser's external email only - if (rcptType == RCPT_TYPE_MAIL_USER) + // get recipient type + string rcptType = ExchangeUtility.GetAttValue(AttRecipientType, attributes) as string; + + // update is possible for mailuser's external email only + if (rcptType == RcptTypeMailUser) { if (type == UpdateType.REPLACE) { - //get name attribute - string name = ExchangeUtils.GetAttValue(Name.NAME, attributes) as string; + // get name attribute + string name = ExchangeUtility.GetAttValue(Name.NAME, attributes) as string; if (name == null) { - //we don't know name, but we need it - NOTE: searching for all the default attributes, we need only Name here, it can be improved - ConnectorObject co = ADSearchByUid(uid, oclass, null); + // we don't know name, but we need it - NOTE: searching for all the default attributes, we need only Name here, it can be improved + ConnectorObject co = this.ADSearchByUid(uid, oclass, null); Assertions.NullCheck(co, "co"); - //add to attributes + + // add to attributes attributes.Add(co.Name); } - Command cmd = ExchangeUtils.GetCommand(CommandInfo.SET_MAILUSER, attributes); - _runspace.InvokePipeline(cmd); + Command cmd = ExchangeUtility.GetCommand(ExchangeConnector.CommandInfo.SetMailUser, attributes); + this.runspace.InvokePipeline(cmd); } else { - throw new ConnectorException(string.Format("Update type [{0}] not supported", type)); + throw new ConnectorException(string.Format(CultureInfo.CurrentCulture, "Update type [{0}] not supported", type)); } } - Debug.WriteLine(METHOD + ":exit", CLASS); + Debug.WriteLine(METHOD + ":exit", ClassName); return uid; } @@ -197,87 +261,95 @@ public override Uid Update(UpdateType type, ObjectClass oclass, /// public override void Test() { - //validate the configuration first, this will check AD configuration too - _configuration.Validate(); - //AD validation (includes configuration validation too) + // validate the configuration first, this will check AD configuration too + this.configuration.Validate(); + + // AD validation (includes configuration validation too) base.Test(); - //runspace check - _runspace.Test(); + + // runspace check + this.runspace.Test(); } /// /// Implementation of SynOp.Sync /// - /// - /// - /// - /// - public override void Sync(ObjectClass objClass, SyncToken token, - SyncResultsHandler handler, OperationOptions options) + /// Object class + /// Sync token + /// Sync results handler + /// Operation options + public override void Sync( + ObjectClass objClass, SyncToken token, SyncResultsHandler handler, OperationOptions options) { - //TODO: implement Sync + // TODO: implement Sync base.Sync(objClass, token, handler, options); } /// /// Implementation of SynOp.GetLatestSyncToken /// - /// - public override SyncToken GetLatestSyncToken(ObjectClass oclass) + /// Object class + /// Last sync token + public override SyncToken GetLatestSyncToken(ObjectClass objectClass) { - //TODO: Implement GetLatestSyncToken - return base.GetLatestSyncToken(oclass); + // TODO: Implement GetLatestSyncToken + return base.GetLatestSyncToken(objectClass); } /// /// Implementation of SearchOp.ExecuteQuery /// - /// - /// - /// - /// - public override void ExecuteQuery(ObjectClass oclass, string query, - ResultsHandler handler, OperationOptions options) + /// Object class + /// Query to execute + /// Results handler + /// Operation options + public override void ExecuteQuery( + ObjectClass oclass, string query, ResultsHandler handler, OperationOptions options) { ArrayList attsToGet = null; if (options != null && options.AttributesToGet != null) { attsToGet = new ArrayList(options.AttributesToGet); } - //delegate to get the exchange attributes if requested + + // delegate to get the exchange attributes if requested ResultsHandler filter = delegate(ConnectorObject cobject) - { - ConnectorObject filtered = ExchangeUtils.ReplaceAttributes(cobject, attsToGet, ATT_MAPPING); - return handler(filtered); - }; + { + ConnectorObject filtered = ExchangeUtility.ReplaceAttributes( + cobject, attsToGet, AttMapping); + return handler(filtered); + }; ResultsHandler handler2use = handler; OperationOptions options2use = options; if (options != null && options.AttributesToGet != null) { - if (attsToGet.Contains(ATT_DATABASE) || attsToGet.Contains(ATT_EXTERNAL_MAIL) || - attsToGet.Contains(ATT_RECIPIENT_TYPE)) + if (attsToGet.Contains(AttDatabase) || attsToGet.Contains(AttExternalMail) + || attsToGet.Contains(AttRecipientType)) { - //replace Exchange attributes with AD names - var newAttsToGet = ExchangeUtils.FilterReplace(attsToGet, ATT_MAPPING); - //we have to remove recipient type, as it is unknown to AD - newAttsToGet.Remove(ATT_RECIPIENT_TYPE); - //build new op options + // replace Exchange attributes with AD names + var newAttsToGet = ExchangeUtility.FilterReplace(attsToGet, AttMapping); + + // we have to remove recipient type, as it is unknown to AD + newAttsToGet.Remove(AttRecipientType); + + // build new op options var builder = new OperationOptionsBuilder(options); - builder.AttributesToGet = (string[]) newAttsToGet.ToArray(typeof(string)); + builder.AttributesToGet = (string[])newAttsToGet.ToArray(typeof(string)); options2use = builder.Build(); handler2use = filter; } } + base.ExecuteQuery(oclass, query, handler2use, options2use); } /// /// Implementation of SearchOp.CreateFilterTranslator /// - /// - /// - /// + /// Object class + /// Operation options + /// Filter translator public override FilterTranslator CreateFilterTranslator(ObjectClass oclass, OperationOptions options) { return new LegacyExchangeConnectorFilterTranslator(); @@ -286,64 +358,21 @@ public override FilterTranslator CreateFilterTranslator(ObjectClass ocla /// /// Inits the connector with configuration injected /// - /// + /// Connector configuration public override void Init(Configuration configuration) { - base.Init(configuration); - _configuration = (ExchangeConfiguration)configuration; - _runspace = new RunSpaceInstance(RunSpaceInstance.SnapIn.Exchange); + this.configuration = (ExchangeConfiguration)configuration; + base.Init(configuration); + this.runspace = new RunSpaceInstance(RunSpaceInstance.SnapIn.Exchange); } - /// - /// Dispose resources + /// Dispose resources, /// - public override void Dispose() + public sealed override void Dispose() { - //lock is probably not necessary - lock (this) - { - if (_disposed) - { - return; - } - if (_runspace != null) - { - _runspace.Dispose(); - } - base.Dispose(); - _disposed = true; - } - - } - - /// - /// Gets the object class info for specified object class, used for schema building - /// - /// ObjectClass to get info for - /// ObjectClass' ObjectClassInfo - protected override ObjectClassInfo GetObjectClassInfo(ObjectClass oc) - { - //get the object class from base - ObjectClassInfo oinfo = base.GetObjectClassInfo(oc); - - //add additional attributes for ACCOUNT - if (oc == ObjectClass.ACCOUNT) - { - - var oiBuilder = new ObjectClassInfoBuilder - { - IsContainer = oinfo.IsContainer, - ObjectType = oinfo.ObjectType - }; - oiBuilder.AddAllAttributeInfo(oinfo.ConnectorAttributeInfos); - oiBuilder.AddAttributeInfo(ATTINFO_DATABASE); - oiBuilder.AddAttributeInfo(ATTINFO_RECIPIENT_TYPE); - oiBuilder.AddAttributeInfo(ATTINFO_EXTERNAL_MAIL); - } - - //return - return oinfo; + this.Dispose(true); + GC.SuppressFinalize(this); } /// @@ -351,13 +380,14 @@ protected override ObjectClassInfo GetObjectClassInfo(ObjectClass oc) /// /// Object class /// Attribute to be normalized - /// normalized attribute + /// Normalized attribute public override ConnectorAttribute NormalizeAttribute(ObjectClass oclass, ConnectorAttribute attribute) { - //normalize the attribute using AD connector first + // normalize the attribute using AD connector first attribute = base.NormalizeAttribute(oclass, attribute); - //normalize external mail value - if (attribute.Name == ATT_EXTERNAL_MAIL && attribute.Value != null) + + // normalize external mail value + if (attribute.Name == AttExternalMail && attribute.Value != null) { IList normAttributes = new List(); bool normalized = false; @@ -369,45 +399,87 @@ public override ConnectorAttribute NormalizeAttribute(ObjectClass oclass, Connec string[] split = strVal.Split(':'); if (split.Length == 2) { - //it contains delimiter, use the second part + // it contains delimiter, use the second part normAttributes.Add(split[1]); normalized = true; - } else + } + else { - //put the original value + // put the original value normAttributes.Add(val); } } } + if (normalized) { - //build the attribute again - return ConnectorAttributeBuilder.Build(attribute.Name, normAttributes); + // build the attribute again + return ConnectorAttributeBuilder.Build(attribute.Name, normAttributes); } - } - //return the original attribute - return attribute; + + // return the original attribute + return attribute; + } + + /// + /// Dispose the resources we use + /// + /// true if called from + protected virtual void Dispose(bool disposing) + { + if (disposing) + { + // free managed resources + if (this.runspace != null) + { + this.runspace.Dispose(); + this.runspace = null; + } + } + } + + /// + /// Gets the object class info for specified object class, used for schema building + /// + /// ObjectClass to get info for + /// ObjectClass' ObjectClassInfo + protected override ObjectClassInfo GetObjectClassInfo(ObjectClass oc) + { + // get the object class from base + ObjectClassInfo oinfo = base.GetObjectClassInfo(oc); + + // add additional attributes for ACCOUNT + if (oc == ObjectClass.ACCOUNT) + { + var classInfoBuilder = new ObjectClassInfoBuilder { IsContainer = oinfo.IsContainer, ObjectType = oinfo.ObjectType }; + classInfoBuilder.AddAllAttributeInfo(oinfo.ConnectorAttributeInfos); + classInfoBuilder.AddAttributeInfo(AttInfoDatabase); + classInfoBuilder.AddAttributeInfo(AttInfoRecipientType); + classInfoBuilder.AddAttributeInfo(AttInfoExternalMail); + } + + // return + return oinfo; } - /// /// helper method to filter out all attributes used in LegacyExchangeConnector only /// - /// - /// + /// Connector attributes + /// Filtered connector attributes private static ICollection FilterOut(ICollection attributes) { - return ExchangeUtils.FilterOut(attributes, ATT_RECIPIENT_TYPE, ATT_DATABASE, ATT_EXTERNAL_MAIL); + return ExchangeUtility.FilterOut(attributes, AttRecipientType, AttDatabase, AttExternalMail); } /// /// helper method for searching object in AD by UID /// - /// - /// - /// - /// + /// Uid of the searched + /// Object class + /// Operation options + /// Connector object found by the Uid private ConnectorObject ADSearchByUid(Uid uid, ObjectClass oclass, OperationOptions options) { Assertions.NullCheck(uid, "uid"); @@ -419,44 +491,46 @@ private ConnectorObject ADSearchByUid(Uid uid, ObjectClass oclass, OperationOpti ConnectorObject ret = null; Filter filter = FilterBuilder.EqualTo(uid); - var translator = - base.CreateFilterTranslator(oclass, options); + var translator = base.CreateFilterTranslator(oclass, options); IList queries = translator.Translate(filter); if (queries.Count == 1) { ResultsHandler handler = delegate(ConnectorObject cobject) - { - ret = cobject; - return false; - }; + { + ret = cobject; + return false; + }; base.ExecuteQuery(oclass, queries[0], handler, options); } - return ret; } - } - - /// - /// Filter translator which does MS Exchange specific things - /// - public class LegacyExchangeConnectorFilterTranslator : ActiveDirectoryFilterTranslator - { - protected override string[] GetLdapNamesForAttribute(ConnectorAttribute attr) + /// + /// Filter translator which does MS Exchange specific translation + /// + private class LegacyExchangeConnectorFilterTranslator : ActiveDirectoryFilterTranslator { - - if (attr.Is(LegacyExchangeConnector.ATT_DATABASE)) - { - return new string[] { LegacyExchangeConnector.ATT_DATABASE_AD_NAME }; - } - if (attr.Is(LegacyExchangeConnector.ATT_EXTERNAL_MAIL)) + /// + /// Translates the connector attribute name to LDAP name + /// + /// Connector attribute name + /// Translated string array + protected override string[] GetLdapNamesForAttribute(ConnectorAttribute attr) { - return new string[] { LegacyExchangeConnector.ATT_EXTERNAL_MAIL_AD_NAME }; + if (attr.Is(AttDatabase)) + { + return new[] { AttDatabaseADName }; + } + + if (attr.Is(AttExternalMail)) + { + return new[] { AttExternalMailADName }; + } + + return base.GetLdapNamesForAttribute(attr); } - return base.GetLdapNamesForAttribute(attr); } } - } diff --git a/ExchangeConnector/ObjectClasses.xml b/ExchangeConnector/ObjectClasses.xml index f8a8100b..e03c70e9 100644 --- a/ExchangeConnector/ObjectClasses.xml +++ b/ExchangeConnector/ObjectClasses.xml @@ -1,114 +1,238 @@  + - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + - - + + + + + + + + diff --git a/ExchangeConnector/RunSpaceInstance.cs b/ExchangeConnector/RunSpaceInstance.cs index 21a0ff76..7a907ca4 100644 --- a/ExchangeConnector/RunSpaceInstance.cs +++ b/ExchangeConnector/RunSpaceInstance.cs @@ -1,45 +1,49 @@ -/* - * ==================== - * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER. - * - * Copyright 2007-2008 Sun Microsystems, Inc. All rights reserved. - * - * The contents of this file are subject to the terms of the Common Development - * and Distribution License("CDDL") (the "License"). You may not use this file - * except in compliance with the License. - * - * You can obtain a copy of the License at - * http://IdentityConnectors.dev.java.net/legal/license.txt - * See the License for the specific language governing permissions and limitations - * under the License. - * - * When distributing the Covered Code, include this CDDL Header Notice in each file - * and include the License file at identityconnectors/legal/license.txt. - * If applicable, add the following below this CDDL Header, with the fields - * enclosed by brackets [] replaced by your own identifying information: - * "Portions Copyrighted [year] [name of copyright owner]" - * ==================== - */ -using System; -using System.Collections; -using System.Collections.Generic; -using System.Collections.ObjectModel; -using System.Diagnostics; -using System.Management.Automation; -using System.Management.Automation.Runspaces; -using System.Text; - -using Org.IdentityConnectors.Framework.Common.Exceptions; +// +// ==================== +// DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER. +// +// Copyright 2007-2008 Sun Microsystems, Inc. All rights reserved. +// +// The contents of this file are subject to the terms of the Common Development +// and Distribution License("CDDL") (the "License"). You may not use this file +// except in compliance with the License. +// +// You can obtain a copy of the License at +// http://IdentityConnectors.dev.java.net/legal/license.txt +// See the License for the specific language governing permissions and limitations +// under the License. +// +// When distributing the Covered Code, include this CDDL Header Notice in each file +// and include the License file at identityconnectors/legal/license.txt. +// If applicable, add the following below this CDDL Header, with the fields +// enclosed by brackets [] replaced by your own identifying information: +// "Portions Copyrighted [year] [name of copyright owner]" +// ==================== +// +// Tomas Knappek namespace Org.IdentityConnectors.Exchange { + using System; + using System.Collections; + using System.Collections.Generic; + using System.Collections.ObjectModel; + using System.Diagnostics; + using System.Globalization; + using System.Management.Automation; + using System.Management.Automation.Runspaces; + using System.Text; + + using Org.IdentityConnectors.Framework.Common.Exceptions; /// + /// /// The implementation of the run space. This wraps the real run space object /// from powershell for use in the pool /// First written for the exchange adapter, the snapin is not needed if you do /// not use it for exchange - /// + /// + /// /// Two possible ways of executing a command using different access point to /// the Runspace: /// - RunspaceInvoke: simple commands in string form, the command string can @@ -48,145 +52,75 @@ namespace Org.IdentityConnectors.Exchange /// shell /// - PipelineInvoke: complex (multi) command structured pipelines which also /// allow complex parameters, like objects, to be passed in. + /// /// - public sealed class RunSpaceInstance : IDisposable + internal sealed class RunSpaceInstance : IDisposable { - private static readonly string CLASS = typeof(RunSpaceInstance).ToString(); - - // the exchange snap in which needs to be loaded - private const string EXCHANGE_SNAPIN = "Microsoft.Exchange.Management.PowerShell.Admin"; - - private RunspaceConfiguration _runSpaceConfig = null; - private Runspace _runSpace = null; - private RunspaceInvoke _runSpaceInvoke = null; - - //SnapIn type enum - /// - /// Snapin type to load - /// - public enum SnapIn - { - /// - /// None - /// - None, - /// - /// MS Exchange snapin - /// - Exchange - }; + /// + /// This class name, used for logging purposes + /// + private static readonly string ClassName = typeof(RunSpaceInstance).ToString(); /// - /// RunSpaceInstance Constructor + /// The exchange snap in name which needs to be loaded /// - /// Type of snapin to be loaded - public RunSpaceInstance(SnapIn snapin) - { - // initialize this - InitRunSpace(snapin); - } + private const string ExchangeSnapIn = "Microsoft.Exchange.Management.PowerShell.Admin"; /// - /// Implementation of IDisposable + /// Instance variable keeping the /// - public void Dispose() - { - Dispose(true); - GC.SuppressFinalize(this); - } + private RunspaceConfiguration runSpaceConfig; /// - /// Dispose/Finalize pattern + /// instance, managed resource /// - /// - private void Dispose(bool disposing) - { - if (disposing) - { - // Free other state (managed objects). - // anything we should do? - } - // clean up the runspace with attached things: - // the API docs show that the RunspaceInvoke will call Dispose on - // the Runspace which in turn calls Close on the Runspace. - if (_runSpaceInvoke != null) - { - _runSpaceInvoke.Dispose(); - } - else - { - if (_runSpace != null) - { - _runSpace.Dispose(); - } - } - } + private Runspace runSpace; /// - /// Finalize + /// instance, managed resource /// - ~RunSpaceInstance() + private RunspaceInvoke runSpaceInvoke; + + /// + /// Initializes a new instance of the class. + /// + /// Type of snapin to be loaded + public RunSpaceInstance(SnapIn snapin) { - // Simply call Dispose(false). - Dispose(false); + // initialize this + this.InitRunSpace(snapin); } - - /// - /// main initialisation routine for the Runspace + /// Snapin type to load /// - private void InitRunSpace(SnapIn snapin) + internal enum SnapIn { - string METHOD = "InitRunSpace with snapin " + snapin; - Debug.WriteLine(METHOD + ":entry", CLASS); - + /// + /// None - not defined + /// + None, - // create a new config from scratch - PSSnapInException snapOutput = null; - _runSpaceConfig = RunspaceConfiguration.Create(); - - switch (snapin) - { - case SnapIn.Exchange: - // used for force load of the exchange dll's - AppDomain.CurrentDomain.AssemblyResolve += - new ResolveEventHandler(ExchangeUtils.AssemblyResolver); - - PSSnapInInfo info = _runSpaceConfig.AddPSSnapIn(EXCHANGE_SNAPIN, - out snapOutput); - break; - } - - //check snapOutput - if (snapOutput != null) - { - throw snapOutput; - } - - // create the real Runspace and open it for processing - _runSpace = RunspaceFactory.CreateRunspace(_runSpaceConfig); - _runSpace.Open(); - _runSpaceInvoke = new RunspaceInvoke(_runSpace); - - Debug.WriteLine(METHOD + ":exit", CLASS); + /// + /// MS Exchange snapin + /// + Exchange } - - + /// - /// Test the state of this RunspaceInstance, throws InvalidRunspaceStateException if in incorrect state + /// Test the state of this , throws if in incorrect state /// public void Test() { - const string METHOD = "Test"; - Debug.WriteLine(METHOD + ":entry", CLASS); + const string MethodName = "Test"; + Debug.WriteLine(MethodName + ":entry", ClassName); // compare the state against the passed in state - if (_runSpace != null - && _runSpace.RunspaceStateInfo.State == RunspaceState.Opened) + if (this.runSpace != null + && this.runSpace.RunspaceStateInfo.State == RunspaceState.Opened) { - Debug.WriteLine(METHOD + ":exit", CLASS); - return ; + Debug.WriteLine(MethodName + ":exit", ClassName); + return; } throw new InvalidRunspaceStateException("Runspace is not in Opened state"); @@ -197,9 +131,9 @@ public void Test() /// collection of objects with the result /// if no command is passed in return null /// if no output/errors from the invoke return an empty collection - public ICollection InvokeCommand(String commandString) + public ICollection InvokeCommand(string command) { - return InvokeCommand(commandString, null); + return this.InvokeCommand(command, null); } /// @@ -210,21 +144,23 @@ public ICollection InvokeCommand(String commandString) /// inputEnum in the example could be the output of an earlier /// invokeCommand call (and thus a complex set of objects) /// - /// command string to execute + /// command string to execute /// input passed in as $input in the execution /// environment /// collection of objects with the result /// if no command is passed in return null /// if no output from the invoke return an empty collection - public ICollection InvokeCommand(String commandString, - IEnumerable input) + public ICollection InvokeCommand( + string command, + IEnumerable input) { - const string METHOD = "InvokeCommand"; - Debug.WriteLine(METHOD + ":entry", CLASS); + const string MethodName = "InvokeCommand"; + Debug.WriteLine(MethodName + "(" + command + ")" + ":entry", ClassName); - IList errors = null; + IList errors = null; + // trim the spaces and check the length - if (commandString == null || commandString.Trim().Length == 0) + if (command == null || command.Trim().Length == 0) { Trace.TraceError("CommandString argument can't be null or empty"); throw new ArgumentException("CommandString argument can't be null or empty"); @@ -232,24 +168,25 @@ public ICollection InvokeCommand(String commandString, // run the command Collection returns = - _runSpaceInvoke.Invoke(commandString, input, out errors); - //check for errors - checkErrors(errors); + this.runSpaceInvoke.Invoke(command, input, out errors); + + // check for errors + CheckErrors(errors); // an empty collection instead of null when we have executed if (returns == null) { - Debug.WriteLine(METHOD + ":exit", CLASS); + Debug.WriteLine(MethodName + ":exit", ClassName); returns = new Collection(); - } //if returns - Trace.WriteLine(String.Format("{0} results returned", returns.Count), CLASS); - Debug.WriteLine(METHOD + ":exit", CLASS); - return returns; + } + Trace.WriteLine(String.Format(CultureInfo.CurrentCulture, "{0} results returned", returns.Count), ClassName); + Debug.WriteLine(MethodName + ":exit", ClassName); + return returns; } /// - /// invoke the pipeline + /// invoke the powershell pipeline /// /// a collection of commands to execute /// collection of objects with the result @@ -257,14 +194,13 @@ public ICollection InvokeCommand(String commandString, /// if no output from the invoke return an empty collection public ICollection InvokePipeline(Collection commands) { - const string METHOD = "InvokePipeline"; - Debug.WriteLine(METHOD + ":entry", CLASS); + const string MethodName = "InvokePipeline"; + Debug.WriteLine(MethodName + ":entry", ClassName); IList errors = null; if (commands == null || commands.Count == 0) - { - Trace.TraceInformation("Commands argument is null or empty", CLASS); + { throw new ArgumentException("Commands argument is null or empty"); } @@ -273,33 +209,37 @@ public ICollection InvokePipeline(Collection commands) Collection results; // create the pipeline - Pipeline pipe = _runSpace.CreatePipeline(); + Pipeline pipe = this.runSpace.CreatePipeline(); + // add the commands to the pipeline foreach (Command item in commands) { pipe.Commands.Add(item); - } // foreach item + } + // run the pipeline if we have something to execute results = pipe.Invoke(); - PipelineReader reader = pipe.Error; + PipelineReader reader = pipe.Error; errors = (IList)reader.ReadToEnd(); - //check for errors - checkErrors(errors); + + // check for errors + CheckErrors(errors); + // an empty collection instead of null when we have executed if (results == null) { - Trace.TraceInformation("NO result returned"); + Debug.WriteLine("NO result returned"); results = new Collection(); - } //if results - Debug.WriteLine(METHOD + ":exit", CLASS); - return results; + } + Debug.WriteLine(MethodName + ":exit", ClassName); + return results; } /// /// invoke the pipeline /// - /// a command to execute + /// a command to execute /// collection of objects with the result /// if no command is passed in return null /// if no output from the invoke return an empty collection @@ -309,27 +249,100 @@ public ICollection InvokePipeline(Command item) // specifically not a CommandCollection: that will not work here Collection commands = new Collection(); commands.Add(item); - return InvokePipeline(commands); + return this.InvokePipeline(commands); } - + /// - /// Checks whether errors List contains some error, if so the errors are concatenated and exception is thrown + /// Implementation of + /// + public void Dispose() + { + this.Dispose(true); + GC.SuppressFinalize(this); + } + + /// + /// Checks whether errors List contains some error, if so the errors are concatenated and exception is thrown, + /// throws if the parameter is not empty /// /// List of error messages - private void checkErrors(IList errors) - { + private static void CheckErrors(IList errors) + { StringBuilder builder = new StringBuilder(); - foreach (Object error in errors) + foreach (object error in errors) { builder.Append(error.ToString()); - builder.Append("\n"); + builder.Append("\n"); } - + if (builder.Length > 0) { - throw new ConnectorException("Exception when executing PowerShell: " + builder.ToString()); - } + throw new ConnectorException("Exception when executing PowerShell: " + builder); + } + } + + /// + /// Dispose/Finalize pattern + /// + /// true if called from + private void Dispose(bool disposing) + { + if (disposing) + { + // Free other state (managed objects). + // clean up the runspace with attached things: + // the API docs show that the RunspaceInvoke will call Dispose on + // the Runspace which in turn calls Close on the Runspace. + if (this.runSpaceInvoke != null) + { + this.runSpaceInvoke.Dispose(); + } + else + { + if (this.runSpace != null) + { + this.runSpace.Dispose(); + } + } + } } - } // class RunSpaceInstance + /// + /// main initialisation routine for the + /// + /// to be initialized together with the + private void InitRunSpace(SnapIn snapin) + { + const string MethodName = "InitRunSpace"; + Debug.WriteLine(MethodName + "(" + snapin + ")" + ":entry", ClassName); + + // create a new config from scratch + PSSnapInException snapOutput = null; + this.runSpaceConfig = RunspaceConfiguration.Create(); + + switch (snapin) + { + case SnapIn.Exchange: + // used for force load of the exchange dll's + AppDomain.CurrentDomain.AssemblyResolve += + new ResolveEventHandler(ExchangeUtility.AssemblyResolver); + + this.runSpaceConfig.AddPSSnapIn(ExchangeSnapIn, out snapOutput); + break; + } + + // check snapOutput + if (snapOutput != null) + { + throw snapOutput; + } + + // create the real Runspace and open it for processing + this.runSpace = RunspaceFactory.CreateRunspace(this.runSpaceConfig); + this.runSpace.Open(); + this.runSpaceInvoke = new RunspaceInvoke(this.runSpace); + + Debug.WriteLine(MethodName + ":exit", ClassName); + } + } } diff --git a/ExchangeConnector/build.xml b/ExchangeConnector/build.xml index 630f6a7e..06f6f903 100644 --- a/ExchangeConnector/build.xml +++ b/ExchangeConnector/build.xml @@ -1,44 +1,28 @@ - + From f6035b8ba77ca99ce70a8a732686471f57e27ab9 Mon Sep 17 00:00:00 2001 From: abadea Date: Wed, 11 Feb 2009 14:30:01 +0000 Subject: [PATCH 185/342] Issue 379: Need a method to compare attribute names --- Framework/CommonObjects.cs | 51 ++++++++++++++----- FrameworkTests/ConnectorAttributeUtilTests.cs | 38 ++++++++++++++ FrameworkTests/FrameworkTests.csproj | 1 + FrameworkTests/ObjectClassUtilTests.cs | 6 +++ 4 files changed, 83 insertions(+), 13 deletions(-) create mode 100644 FrameworkTests/ConnectorAttributeUtilTests.cs diff --git a/Framework/CommonObjects.cs b/Framework/CommonObjects.cs index bd9927e2..3961ec8f 100644 --- a/Framework/CommonObjects.cs +++ b/Framework/CommonObjects.cs @@ -51,6 +51,15 @@ public static string CreateSpecialName(string name) { } return "__" + name + "__"; } + + public static bool NamesEqual(string name1, string name2) { + return name1.ToUpper(CultureInfoCache.Instance).Equals( + name2.ToUpper(CultureInfoCache.Instance)); + } + + public static int GetNameHashCode(string name) { + return name.ToUpper(CultureInfoCache.Instance).GetHashCode(); + } } #endregion @@ -331,7 +340,17 @@ private static bool IsSpecialName(String name) { public static string CreateSpecialName(string name) { return NameUtil.CreateSpecialName(name); } - + + /// + /// Compares two attribute names for equality. + /// + /// the first attribute name + /// the second attribute name + /// true iff the two attribute names are equal + public static bool NamesEqual(string name1, string name2) { + return NameUtil.NamesEqual(name2, name2); + } + /// /// Gets the 'Name' attribute from a set of ConnectorAttributes. /// @@ -658,8 +677,7 @@ public IList Value { } public bool Is(string name) { - return Name.ToUpper(CultureInfoCache.Instance).Equals( - name.ToUpper(CultureInfoCache.Instance)); + return NameUtil.NamesEqual(_name, name); } public sealed override bool Equals(Object obj) { @@ -688,7 +706,7 @@ public sealed override bool Equals(Object obj) { } public sealed override int GetHashCode() { - return _name.ToUpper(CultureInfoCache.Instance).GetHashCode(); + return NameUtil.GetNameHashCode(_name); } @@ -1269,8 +1287,7 @@ public Flags InfoFlags { public bool Is(string name) { - return Name.ToUpper(CultureInfoCache.Instance).Equals( - name.ToUpper(CultureInfoCache.Instance)); + return NameUtil.NamesEqual(_name, name); } /** @@ -1362,7 +1379,7 @@ public override bool Equals(Object o) { } public override int GetHashCode() { - return _name.ToUpper(CultureInfoCache.Instance).GetHashCode(); + return NameUtil.GetNameHashCode(_name); } public override string ToString() { @@ -1698,6 +1715,16 @@ private static bool IsSpecialName(String name) { public static string CreateSpecialName(string name) { return NameUtil.CreateSpecialName(name); } + + /// + /// Compares two object class names for equality. + /// + /// the first object class name + /// the second object class name + /// true iff the two object class names are equal + public static bool NamesEqual(string name1, string name2) { + return NameUtil.NamesEqual(name2, name2); + } } #endregion @@ -1746,12 +1773,11 @@ public String GetDisplayNameKey() { * that of the one in this {@link ObjectClass}. */ public bool Is(String name) { - return this._type.ToUpper(CultureInfoCache.Instance).Equals( - name.ToUpper(CultureInfoCache.Instance)); + return NameUtil.NamesEqual(_type, name); } public override int GetHashCode() { - return _type.ToUpper(CultureInfoCache.Instance).GetHashCode(); + return NameUtil.GetNameHashCode(_type); } public override bool Equals(object obj) { @@ -1834,8 +1860,7 @@ public String ObjectType { * that of the one in this {@link ObjectClassInfo}. */ public bool Is(String name) { - return this._type.ToUpper(CultureInfoCache.Instance).Equals( - name.ToUpper(CultureInfoCache.Instance)); + return NameUtil.NamesEqual(_type, name); } public bool IsContainer { @@ -1845,7 +1870,7 @@ public bool IsContainer { } public override int GetHashCode() { - return _type.ToUpper(CultureInfoCache.Instance).GetHashCode(); + return NameUtil.GetNameHashCode(_type); } public override bool Equals(Object obj) { diff --git a/FrameworkTests/ConnectorAttributeUtilTests.cs b/FrameworkTests/ConnectorAttributeUtilTests.cs new file mode 100644 index 00000000..8b9d6c51 --- /dev/null +++ b/FrameworkTests/ConnectorAttributeUtilTests.cs @@ -0,0 +1,38 @@ +/* + * ==================== + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER. + * + * Copyright 2007-2008 Sun Microsystems, Inc. All rights reserved. + * + * The contents of this file are subject to the terms of the Common Development + * and Distribution License("CDDL") (the "License"). You may not use this file + * except in compliance with the License. + * + * You can obtain a copy of the License at + * http://IdentityConnectors.dev.java.net/legal/license.txt + * See the License for the specific language governing permissions and limitations + * under the License. + * + * When distributing the Covered Code, include this CDDL Header Notice in each file + * and include the License file at identityconnectors/legal/license.txt. + * If applicable, add the following below this CDDL Header, with the fields + * enclosed by brackets [] replaced by your own identifying information: + * "Portions Copyrighted [year] [name of copyright owner]" + * ==================== + */ +using NUnit.Framework; +using Org.IdentityConnectors.Framework.Common.Objects; + +namespace FrameworkTests +{ + [TestFixture] + public class ConnectorAttributeUtilTests + { + + [Test] + public void TestNamesEqual() + { + Assert.IsTrue(ConnectorAttributeUtil.NamesEqual("givenName", "givenname")); + } + } +} diff --git a/FrameworkTests/FrameworkTests.csproj b/FrameworkTests/FrameworkTests.csproj index 0c77e4cf..208b5611 100644 --- a/FrameworkTests/FrameworkTests.csproj +++ b/FrameworkTests/FrameworkTests.csproj @@ -66,6 +66,7 @@ + diff --git a/FrameworkTests/ObjectClassUtilTests.cs b/FrameworkTests/ObjectClassUtilTests.cs index fbf426fd..27e771d3 100755 --- a/FrameworkTests/ObjectClassUtilTests.cs +++ b/FrameworkTests/ObjectClassUtilTests.cs @@ -35,5 +35,11 @@ public void TestIsSpecial() Assert.IsTrue(ObjectClassUtil.IsSpecial(ObjectClass.ACCOUNT)); Assert.IsFalse(ObjectClassUtil.IsSpecial(new ObjectClass("o"))); } + + [Test] + public void TestNamesEqual() + { + Assert.IsTrue(ObjectClassUtil.NamesEqual("ACCOUNT", "account")); + } } } From a8eb9bbca7079181bb4c4e58df4aa398da232737 Mon Sep 17 00:00:00 2001 From: abadea Date: Wed, 11 Feb 2009 17:22:13 +0000 Subject: [PATCH 186/342] Issue 378: Make AttributeUtil.isSpecialName() public --- Framework/CommonObjects.cs | 16 ++++++++++++++-- 1 file changed, 14 insertions(+), 2 deletions(-) diff --git a/Framework/CommonObjects.cs b/Framework/CommonObjects.cs index 3961ec8f..bfd8c908 100644 --- a/Framework/CommonObjects.cs +++ b/Framework/CommonObjects.cs @@ -328,7 +328,13 @@ public static bool IsSpecial(ConnectorAttributeInfo attr) { return IsSpecialName(name); } - private static bool IsSpecialName(String name) { + /// + /// Determines whether the specified attribute name is special in the + /// sense of . + /// + /// the name of the attribute to test + /// true iff the attribute name is special + public static bool IsSpecialName(String name) { return NameUtil.IsSpecialName(name); } @@ -1701,7 +1707,13 @@ public static bool IsSpecial(ObjectClass oclass) { return IsSpecialName(name); } - private static bool IsSpecialName(String name) { + /// + /// Determines whether the specified object class name is special in the + /// sense of . + /// + /// the name of the object class to test + /// true iff the object class name is special + public static bool IsSpecialName(String name) { return NameUtil.IsSpecialName(name); } From f647434cefe407a7b927f10c44b6dc841caf8aba Mon Sep 17 00:00:00 2001 From: tknappek Date: Thu, 12 Feb 2009 10:46:20 +0000 Subject: [PATCH 187/342] Issue #421: TestHelpers in dotnet - not able to override property in xml property files --- Common/CollectionUtil.cs | 22 ++++++++++++++++++++++ Framework/Test.cs | 30 +++++++++++++++--------------- 2 files changed, 37 insertions(+), 15 deletions(-) diff --git a/Common/CollectionUtil.cs b/Common/CollectionUtil.cs index 5e57742e..12dcb3b6 100644 --- a/Common/CollectionUtil.cs +++ b/Common/CollectionUtil.cs @@ -327,6 +327,28 @@ public static void AddAll(ICollection collection, } } } + + /// + /// Adds all the elements from the given enumerable to the given collection. + /// Replace the element value if already stored in the collection. + /// + /// IDictionary key type + /// IDictionary value type + /// Enumeration key type, has to extend IDictionary key type + /// Enumeration value type, has to extend IDictionary value type + /// The collection to add to + /// The enumerable to get from + public static void AddOrReplaceAll(IDictionary collection, + IEnumerable> enumerable) + where UKey : TKey + where UValue : TValue { + if (enumerable != null) { + foreach (KeyValuePair obj in enumerable) { + collection[obj.Key] = obj.Value; + } + } + } + /// /// Adds all the elements from the given enumerable to the given collection. /// diff --git a/Framework/Test.cs b/Framework/Test.cs index ea50962a..1e2f0aeb 100644 --- a/Framework/Test.cs +++ b/Framework/Test.cs @@ -21,13 +21,13 @@ * ==================== */ using System; - -using System.IO; -using System.Xml; using System.Collections; using System.Collections.Generic; +using System.Diagnostics; +using System.IO; using System.Reflection; using System.Runtime.CompilerServices; +using System.Xml; using Org.IdentityConnectors.Common; using Org.IdentityConnectors.Framework.Api; using Org.IdentityConnectors.Framework.Api.Operations; @@ -203,7 +203,7 @@ private static IDictionary GetProperties(Assembly asm) { } private static IDictionary LoadProperties(Assembly asm) { - const string ERR = "Unable to load optional XML properties file: "; + const string ERR = "TestHelpers: Unable to load optional XML properties file \"{0}\""; string fn = null; IDictionary props = null; IDictionary ret = new Dictionary(); @@ -212,9 +212,9 @@ private static IDictionary LoadProperties(Assembly asm) { try { fn = Path.Combine(PREFIX, GLOBAL_PROPS); props = LoadPropertiesFile(fn); - CollectionUtil.AddAll(ret, props); - } catch (Exception e) { - TraceUtil.TraceException(ERR + fn, e); + CollectionUtil.AddOrReplaceAll(ret, props); + } catch (Exception) { + Trace.TraceInformation(ERR, fn); } // load the project properties file @@ -222,8 +222,8 @@ private static IDictionary LoadProperties(Assembly asm) { fn = Path.Combine(Environment.CurrentDirectory, "project.xml"); props = LoadPropertiesFile(fn); CollectionUtil.AddAll(ret, props); - } catch (Exception e) { - TraceUtil.TraceException(ERR + fn, e); + } catch (Exception) { + Trace.TraceInformation(ERR, fn); } // private settings are in the "assembly name" folder, as defined in the assembly @@ -233,9 +233,9 @@ private static IDictionary LoadProperties(Assembly asm) { try { fn = Path.Combine(PREFIX, prjName + "/project.xml"); props = LoadPropertiesFile(fn); - CollectionUtil.AddAll(ret, props); - } catch (IOException e) { - TraceUtil.TraceException(ERR + fn, e); + CollectionUtil.AddOrReplaceAll(ret, props); + } catch (IOException) { + Trace.TraceInformation(ERR, fn); } string cfg = Environment.GetEnvironmentVariable("configuration"); @@ -244,9 +244,9 @@ private static IDictionary LoadProperties(Assembly asm) { // load a config-specific properties file fn = Path.Combine(PREFIX, prjName + "/" + cfg + "/project.xml"); props = LoadPropertiesFile(fn); - CollectionUtil.AddAll(ret, props); - } catch (IOException e) { - TraceUtil.TraceException(ERR + fn, e); + CollectionUtil.AddOrReplaceAll(ret, props); + } catch (IOException) { + Trace.TraceInformation(ERR, fn); } } } else { From 923b01f3f2da2c96b1aca13251b134415736885f Mon Sep 17 00:00:00 2001 From: kyarbro Date: Thu, 12 Feb 2009 18:24:04 +0000 Subject: [PATCH 188/342] License notices --- LICENSE.txt | 1 - 1 file changed, 1 deletion(-) delete mode 100644 LICENSE.txt diff --git a/LICENSE.txt b/LICENSE.txt deleted file mode 100644 index 8478fd41..00000000 --- a/LICENSE.txt +++ /dev/null @@ -1 +0,0 @@ -license file \ No newline at end of file From 66ee2c004d81125594b3548775ad193a14d1fdd8 Mon Sep 17 00:00:00 2001 From: kyarbro Date: Thu, 12 Feb 2009 18:24:21 +0000 Subject: [PATCH 189/342] License notices --- THIRDPARTYREADME.txt | 3591 ------------------------------------------ 1 file changed, 3591 deletions(-) delete mode 100644 THIRDPARTYREADME.txt diff --git a/THIRDPARTYREADME.txt b/THIRDPARTYREADME.txt deleted file mode 100644 index 797aa123..00000000 --- a/THIRDPARTYREADME.txt +++ /dev/null @@ -1,3591 +0,0 @@ -DO NOT TRANSLATE OR LOCALIZE -*************************************************************************** -%%The following software may be included in this product: -Groovy Scripting Language -Use of any of this software is governed by the terms of the license below: -Apache License -Version 2.0, January 2004 -http://www.apache.org/licenses/ - -TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION - -1. Definitions. - -"License" shall mean the terms and conditions for use, reproduction, and -distribution as defined by Sections 1 through 9 of this document. - -"Licensor" shall mean the copyright owner or entity authorized by the copyright -owner that is granting the License. - -"Legal Entity" shall mean the union of the acting entity and all other entities -that control, are controlled by, or are under common control with that entity. -For the purposes of this definition, "control" means (i) the power, direct or -indirect, to cause the direction or management of such entity, whether by -contract or otherwise, or (ii) ownership of fifty percent (50%) or more of the -outstanding shares, or (iii) beneficial ownership of such entity. - -"You" (or "Your") shall mean an individual or Legal Entity exercising -permissions granted by this License. - -"Source" form shall mean the preferred form for making modifications, including -but not limited to software source code, documentation source, and configuration -files. - -"Object" form shall mean any form resulting from mechanical transformation or -translation of a Source form, including but not limited to compiled object code, -generated documentation, and conversions to other media types. - -"Work" shall mean the work of authorship, whether in Source or Object form, made -available under the License, as indicated by a copyright notice that is included -in or attached to the work (an example is provided in the Appendix below). - -"Derivative Works" shall mean any work, whether in Source or Object form, that -is based on (or derived from) the Work and for which the editorial revisions, -annotations, elaborations, or other modifications represent, as a whole, an -original work of authorship. For the purposes of this License, Derivative Works -shall not include works that remain separable from, or merely link (or bind by -name) to the interfaces of, the Work and Derivative Works thereof. - -"Contribution" shall mean any work of authorship, including the original version -of the Work and any modifications or additions to that Work or Derivative Works -thereof, that is intentionally submitted to Licensor for inclusion in the Work -by the copyright owner or by an individual or Legal Entity authorized to submit -on behalf of the copyright owner. For the purposes of this definition, -"submitted" means any form of electronic, verbal, or written communication sent -to the Licensor or its representatives, including but not limited to -communication on electronic mailing lists, source code control systems, and -issue tracking systems that are managed by, or on behalf of, the Licensor for -the purpose of discussing and improving the Work, but excluding communication -that is conspicuously marked or otherwise designated in writing by the copyright -owner as "Not a Contribution." - -"Contributor" shall mean Licensor and any individual or Legal Entity on behalf -of whom a Contribution has been received by Licensor and subsequently -incorporated within the Work. - -2. Grant of Copyright License. Subject to the terms and conditions of this -License, each Contributor hereby grants to You a perpetual, worldwide, -non-exclusive, no-charge, royalty-free, irrevocable copyright license to -reproduce, prepare Derivative Works of, publicly display, publicly perform, -sublicense, and distribute the Work and such Derivative Works in Source or -Object form. - -3. Grant of Patent License. Subject to the terms and conditions of this License, -each Contributor hereby grants to You a perpetual, worldwide, non-exclusive, -no-charge, royalty-free, irrevocable (except as stated in this section) patent -license to make, have made, use, offer to sell, sell, import, and otherwise -transfer the Work, where such license applies only to those patent claims -licensable by such Contributor that are necessarily infringed by their -Contribution(s) alone or by combination of their Contribution(s) with the Work -to which such Contribution(s) was submitted. If You institute patent litigation -against any entity (including a cross-claim or counterclaim in a lawsuit) -alleging that the Work or a Contribution incorporated within the Work -constitutes direct or contributory patent infringement, then any patent licenses -granted to You under this License for that Work shall terminate as of the date -such litigation is filed. - -4. Redistribution. You may reproduce and distribute copies of the Work or -Derivative Works thereof in any medium, with or without modifications, and in -Source or Object form, provided that You meet the following conditions: - -1. You must give any other recipients of the Work or Derivative Works a copy -of this License; and - -2. You must cause any modified files to carry prominent notices stating that -You changed the files; and - -3. You must retain, in the Source form of any Derivative Works that You -distribute, all copyright, patent, trademark, and attribution notices from the -Source form of the Work, excluding those notices that do not pertain to any part -of the Derivative Works; and - -4. If the Work includes a "NOTICE" text file as part of its distribution, -then any Derivative Works that You distribute must include a readable copy of -the attribution notices contained within such NOTICE file, excluding those -notices that do not pertain to any part of the Derivative Works, in at least one -of the following places: within a NOTICE text file distributed as part of the -Derivative Works; within the Source form or documentation, if provided along -with the Derivative Works; or, within a display generated by the Derivative -Works, if and wherever such third-party notices normally appear. The contents of -the NOTICE file are for informational purposes only and do not modify the -License. You may add Your own attribution notices within Derivative Works that -You distribute, alongside or as an addendum to the NOTICE text from the Work, -provided that such additional attribution notices cannot be construed as -modifying the License. - -You may add Your own copyright statement to Your modifications and may provide -additional or different license terms and conditions for use, reproduction, or -distribution of Your modifications, or for any such Derivative Works as a whole, -provided Your use, reproduction, and distribution of the Work otherwise complies -with the conditions stated in this License. - -5. Submission of Contributions. Unless You explicitly state otherwise, any -Contribution intentionally submitted for inclusion in the Work by You to the -Licensor shall be under the terms and conditions of this License, without any -additional terms or conditions. Notwithstanding the above, nothing herein shall -supersede or modify the terms of any separate license agreement you may have -executed with Licensor regarding such Contributions. - -6. Trademarks. This License does not grant permission to use the trade names, -trademarks, service marks, or product names of the Licensor, except as required -for reasonable and customary use in describing the origin of the Work and -reproducing the content of the NOTICE file. - -7. Disclaimer of Warranty. Unless required by applicable law or agreed to in -writing, Licensor provides the Work (and each Contributor provides its -Contributions) on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY -KIND, either express or implied, including, without limitation, any warranties -or conditions of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A -PARTICULAR PURPOSE. You are solely responsible for determining the -appropriateness of using or redistributing the Work and assume any risks -associated with Your exercise of permissions under this License. - -8. Limitation of Liability. In no event and under no legal theory, whether in -tort (including negligence), contract, or otherwise, unless required by -applicable law (such as deliberate and grossly negligent acts) or agreed to in -writing, shall any Contributor be liable to You for damages, including any -direct, indirect, special, incidental, or consequential damages of any character -arising as a result of this License or out of the use or inability to use the -Work (including but not limited to damages for loss of goodwill, work stoppage, -computer failure or malfunction, or any and all other commercial damages or -losses), even if such Contributor has been advised of the possibility of such -damages. - -9. Accepting Warranty or Additional Liability. While redistributing the Work or -Derivative Works thereof, You may choose to offer, and charge a fee for, -acceptance of support, warranty, indemnity, or other liability obligations -and/or rights consistent with this License. However, in accepting such -obligations, You may act only on Your own behalf and on Your sole -responsibility, not on behalf of any other Contributor, and only if You agree to -indemnify, defend, and hold each Contributor harmless for any liability incurred -by, or claims asserted against, such Contributor by reason of your accepting any -such warranty or additional liability. - -END OF TERMS AND CONDITIONS -APPENDIX: How to apply the Apache License to your work - -To apply the Apache License to your work, attach the following boilerplate -notice, with the fields enclosed by brackets "[]" replaced with your own -identifying information. (Don't include the brackets!) The text should be -enclosed in the appropriate comment syntax for the file format. We also -recommend that a file or class name and description of purpose be included on -the same "printed page" as the copyright notice for easier identification within -third-party archives. -Copyright [yyyy] [name of copyright owner] 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. -*************************************************************************** -%%The following software may be included in this product: -Apache Commons Pool -Use of any of this software is governed by the terms of the license below: -Apache License -Version 2.0, January 2004 -http://www.apache.org/licenses/ - -TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION - -1. Definitions. - -"License" shall mean the terms and conditions for use, reproduction, -and distribution as defined by Sections 1 through 9 of this document. - -"Licensor" shall mean the copyright owner or entity authorized by -the copyright owner that is granting the License. - -"Legal Entity" shall mean the union of the acting entity and all -other entities that control, are controlled by, or are under common -control with that entity. For the purposes of this definition, -"control" means (i) the power, direct or indirect, to cause the -direction or management of such entity, whether by contract or -otherwise, or (ii) ownership of fifty percent (50%) or more of the -outstanding shares, or (iii) beneficial ownership of such entity. - -"You" (or "Your") shall mean an individual or Legal Entity -exercising permissions granted by this License. - -"Source" form shall mean the preferred form for making modifications, -including but not limited to software source code, documentation -source, and configuration files. - -"Object" form shall mean any form resulting from mechanical -transformation or translation of a Source form, including but -not limited to compiled object code, generated documentation, -and conversions to other media types. - -"Work" shall mean the work of authorship, whether in Source or -Object form, made available under the License, as indicated by a -copyright notice that is included in or attached to the work -(an example is provided in the Appendix below). - -"Derivative Works" shall mean any work, whether in Source or Object -form, that is based on (or derived from) the Work and for which the -editorial revisions, annotations, elaborations, or other modifications -represent, as a whole, an original work of authorship. For the purposes -of this License, Derivative Works shall not include works that remain -separable from, or merely link (or bind by name) to the interfaces of, -the Work and Derivative Works thereof. - -"Contribution" shall mean any work of authorship, including -the original version of the Work and any modifications or additions -to that Work or Derivative Works thereof, that is intentionally -submitted to Licensor for inclusion in the Work by the copyright owner -or by an individual or Legal Entity authorized to submit on behalf of -the copyright owner. For the purposes of this definition, "submitted" -means any form of electronic, verbal, or written communication sent -to the Licensor or its representatives, including but not limited to -communication on electronic mailing lists, source code control systems, -and issue tracking systems that are managed by, or on behalf of, the -Licensor for the purpose of discussing and improving the Work, but -excluding communication that is conspicuously marked or otherwise -designated in writing by the copyright owner as "Not a Contribution." - -"Contributor" shall mean Licensor and any individual or Legal Entity -on behalf of whom a Contribution has been received by Licensor and -subsequently incorporated within the Work. - -2. Grant of Copyright License. Subject to the terms and conditions of -this License, each Contributor hereby grants to You a perpetual, -worldwide, non-exclusive, no-charge, royalty-free, irrevocable -copyright license to reproduce, prepare Derivative Works of, -publicly display, publicly perform, sublicense, and distribute the -Work and such Derivative Works in Source or Object form. - -3. Grant of Patent License. Subject to the terms and conditions of -this License, each Contributor hereby grants to You a perpetual, -worldwide, non-exclusive, no-charge, royalty-free, irrevocable -(except as stated in this section) patent license to make, have made, -use, offer to sell, sell, import, and otherwise transfer the Work, -where such license applies only to those patent claims licensable -by such Contributor that are necessarily infringed by their -Contribution(s) alone or by combination of their Contribution(s) -with the Work to which such Contribution(s) was submitted. If You -institute patent litigation against any entity (including a -cross-claim or counterclaim in a lawsuit) alleging that the Work -or a Contribution incorporated within the Work constitutes direct -or contributory patent infringement, then any patent licenses -granted to You under this License for that Work shall terminate -as of the date such litigation is filed. - -4. Redistribution. You may reproduce and distribute copies of the -Work or Derivative Works thereof in any medium, with or without -modifications, and in Source or Object form, provided that You -meet the following conditions: - -(a) You must give any other recipients of the Work or -Derivative Works a copy of this License; and - -(b) You must cause any modified files to carry prominent notices -stating that You changed the files; and - -(c) You must retain, in the Source form of any Derivative Works -that You distribute, all copyright, patent, trademark, and -attribution notices from the Source form of the Work, -excluding those notices that do not pertain to any part of -the Derivative Works; and - -(d) If the Work includes a "NOTICE" text file as part of its -distribution, then any Derivative Works that You distribute must -include a readable copy of the attribution notices contained -within such NOTICE file, excluding those notices that do not -pertain to any part of the Derivative Works, in at least one -of the following places: within a NOTICE text file distributed -as part of the Derivative Works; within the Source form or -documentation, if provided along with the Derivative Works; or, -within a display generated by the Derivative Works, if and -wherever such third-party notices normally appear. The contents -of the NOTICE file are for informational purposes only and -do not modify the License. You may add Your own attribution -notices within Derivative Works that You distribute, alongside -or as an addendum to the NOTICE text from the Work, provided -that such additional attribution notices cannot be construed -as modifying the License. - -You may add Your own copyright statement to Your modifications and -may provide additional or different license terms and conditions -for use, reproduction, or distribution of Your modifications, or -for any such Derivative Works as a whole, provided Your use, -reproduction, and distribution of the Work otherwise complies with -the conditions stated in this License. - -5. Submission of Contributions. Unless You explicitly state otherwise, -any Contribution intentionally submitted for inclusion in the Work -by You to the Licensor shall be under the terms and conditions of -this License, without any additional terms or conditions. -Notwithstanding the above, nothing herein shall supersede or modify -the terms of any separate license agreement you may have executed -with Licensor regarding such Contributions. - -6. Trademarks. This License does not grant permission to use the trade -names, trademarks, service marks, or product names of the Licensor, -except as required for reasonable and customary use in describing the -origin of the Work and reproducing the content of the NOTICE file. - -7. Disclaimer of Warranty. Unless required by applicable law or -agreed to in writing, Licensor provides the Work (and each -Contributor provides its Contributions) on an "AS IS" BASIS, -WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or -implied, including, without limitation, any warranties or conditions -of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A -PARTICULAR PURPOSE. You are solely responsible for determining the -appropriateness of using or redistributing the Work and assume any -risks associated with Your exercise of permissions under this License. - -8. Limitation of Liability. In no event and under no legal theory, -whether in tort (including negligence), contract, or otherwise, -unless required by applicable law (such as deliberate and grossly -negligent acts) or agreed to in writing, shall any Contributor be -liable to You for damages, including any direct, indirect, special, -incidental, or consequential damages of any character arising as a -result of this License or out of the use or inability to use the -Work (including but not limited to damages for loss of goodwill, -work stoppage, computer failure or malfunction, or any and all -other commercial damages or losses), even if such Contributor -has been advised of the possibility of such damages. - -9. Accepting Warranty or Additional Liability. While redistributing -the Work or Derivative Works thereof, You may choose to offer, -and charge a fee for, acceptance of support, warranty, indemnity, -or other liability obligations and/or rights consistent with this -License. However, in accepting such obligations, You may act only -on Your own behalf and on Your sole responsibility, not on behalf -of any other Contributor, and only if You agree to indemnify, -defend, and hold each Contributor harmless for any liability -incurred by, or claims asserted against, such Contributor by reason -of your accepting any such warranty or additional liability. - -END OF TERMS AND CONDITIONS - -APPENDIX: How to apply the Apache License to your work. - -To apply the Apache License to your work, attach the following -boilerplate notice, with the fields enclosed by brackets "[]" -replaced with your own identifying information. (Don't include -the brackets!) The text should be enclosed in the appropriate -comment syntax for the file format. We also recommend that a -file or class name and description of purpose be included on the -same "printed page" as the copyright notice for easier -identification within third-party archives. - -Copyright [yyyy] [name of copyright owner] - -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. -*************************************************************************** -%%The following software may be included in this product: -HSQL Database Engine -Use of any of this software is governed by the terms of the license below: -COPYRIGHTS AND LICENSES - -ORIGINAL LICENSE (a.k.a. "hypersonic_lic.txt") - -For content, code, and products originally developed by Thomas Mueller and the -Hypersonic SQL Group: - -Copyright (c) 1995-2000 by the Hypersonic SQL Group. -All rights reserved. - - -Redistribution and use in source and binary forms, with or without -modification, are permitted provided that the following conditions are met: - -Redistributions of source code must retain the above copyright notice, this -list of conditions and the following disclaimer. - -Redistributions in binary form must reproduce the above copyright notice, -this list of conditions and the following disclaimer in the documentation -and/or other materials provided with the distribution. - -Neither the name of the Hypersonic SQL Group nor the names of its -contributors may be used to endorse or promote products derived from this -software without specific prior written permission. - -THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" -AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE -IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE -ARE DISCLAIMED. IN NO EVENT SHALL THE HYPERSONIC SQL GROUP, -OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, -EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, -PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; -LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND -ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT -(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS -SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - -This software consists of voluntary contributions made by many individuals on -behalf of the -Hypersonic SQL Group. - - - -For work added by the HSQL Development Group (a.k.a. hsqldb_lic.txt): -Copyright (c) 2001-2005, The HSQL Development Group -All rights reserved. - -Redistribution and use in source and binary forms, with or without -modification, are permitted provided that the following conditions are met: - -Redistributions of source code must retain the above copyright notice, this -list of conditions and the following disclaimer. - -Redistributions in binary form must reproduce the above copyright notice, -this list of conditions and the following disclaimer in the documentation -and/or other materials provided with the distribution. - -Neither the name of the HSQL Development Group nor the names of its -contributors may be used to endorse or promote products derived from this -software without specific prior written permission. - -THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" -AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE -IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE -ARE DISCLAIMED. IN NO EVENT SHALL HSQL DEVELOPMENT GROUP, HSQLDB.ORG, -OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, -EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, -PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; -LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND -ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT -(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS -SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. -*************************************************************************** -%%The following software may be included in this product: -Derby -Use of any of this software is governed by the terms of the license below: -Apache License, Version 2.0 - -Apache License -Version 2.0, January 2004 -http://www.apache.org/licenses/ - -TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION - -1. Definitions. - -"License" shall mean the terms and conditions for use, reproduction, and -distribution as defined by Sections 1 through 9 of this document. - -"Licensor" shall mean the copyright owner or entity authorized by the copyright -owner that is granting the License. - -"Legal Entity" shall mean the union of the acting entity and all other entities -that control, are controlled by, or are under common control with that entity. -For the purposes of this definition, "control" means (i) the power, direct or -indirect, to cause the direction or management of such entity, whether by -contract or otherwise, or (ii) ownership of fifty percent (50%) or more of the -outstanding shares, or (iii) beneficial ownership of such entity. - -"You" (or "Your") shall mean an individual or Legal Entity exercising -permissions granted by this License. - -"Source" form shall mean the preferred form for making modifications, including -but not limited to software source code, documentation source, and configuration -files. - -"Object" form shall mean any form resulting from mechanical transformation or -translation of a Source form, including but not limited to compiled object code, -generated documentation, and conversions to other media types. - -"Work" shall mean the work of authorship, whether in Source or Object form, made -available under the License, as indicated by a copyright notice that is included -in or attached to the work (an example is provided in the Appendix below). - -"Derivative Works" shall mean any work, whether in Source or Object form, that -is based on (or derived from) the Work and for which the editorial revisions, -annotations, elaborations, or other modifications represent, as a whole, an -original work of authorship. For the purposes of this License, Derivative Works -shall not include works that remain separable from, or merely link (or bind by -name) to the interfaces of, the Work and Derivative Works thereof. - -"Contribution" shall mean any work of authorship, including the original version -of the Work and any modifications or additions to that Work or Derivative Works -thereof, that is intentionally submitted to Licensor for inclusion in the Work -by the copyright owner or by an individual or Legal Entity authorized to submit -on behalf of the copyright owner. For the purposes of this definition, -"submitted" means any form of electronic, verbal, or written communication sent -to the Licensor or its representatives, including but not limited to -communication on electronic mailing lists, source code control systems, and -issue tracking systems that are managed by, or on behalf of, the Licensor for -the purpose of discussing and improving the Work, but excluding communication -that is conspicuously marked or otherwise designated in writing by the copyright -owner as "Not a Contribution." - -"Contributor" shall mean Licensor and any individual or Legal Entity on behalf -of whom a Contribution has been received by Licensor and subsequently -incorporated within the Work. - -2. Grant of Copyright License. Subject to the terms and conditions of this -License, each Contributor hereby grants to You a perpetual, worldwide, -non-exclusive, no-charge, royalty-free, irrevocable copyright license to -reproduce, prepare Derivative Works of, publicly display, publicly perform, -sublicense, and distribute the Work and such Derivative Works in Source or -Object form. - -3. Grant of Patent License. Subject to the terms and conditions of this License, -each Contributor hereby grants to You a perpetual, worldwide, non-exclusive, -no-charge, royalty-free, irrevocable (except as stated in this section) patent -license to make, have made, use, offer to sell, sell, import, and otherwise -transfer the Work, where such license applies only to those patent claims -licensable by such Contributor that are necessarily infringed by their -Contribution(s) alone or by combination of their Contribution(s) with the Work -to which such Contribution(s) was submitted. If You institute patent litigation -against any entity (including a cross-claim or counterclaim in a lawsuit) -alleging that the Work or a Contribution incorporated within the Work -constitutes direct or contributory patent infringement, then any patent licenses -granted to You under this License for that Work shall terminate as of the date -such litigation is filed. - -4. Redistribution. You may reproduce and distribute copies of the Work or -Derivative Works thereof in any medium, with or without modifications, and in -Source or Object form, provided that You meet the following conditions: - -1. You must give any other recipients of the Work or Derivative Works a copy -of this License; and - -2. You must cause any modified files to carry prominent notices stating that -You changed the files; and - -3. You must retain, in the Source form of any Derivative Works that You -distribute, all copyright, patent, trademark, and attribution notices from the -Source form of the Work, excluding those notices that do not pertain to any part -of the Derivative Works; and - -4. If the Work includes a "NOTICE" text file as part of its distribution, -then any Derivative Works that You distribute must include a readable copy of -the attribution notices contained within such NOTICE file, excluding those -notices that do not pertain to any part of the Derivative Works, in at least one -of the following places: within a NOTICE text file distributed as part of the -Derivative Works; within the Source form or documentation, if provided along -with the Derivative Works; or, within a display generated by the Derivative -Works, if and wherever such third-party notices normally appear. The contents of -the NOTICE file are for informational purposes only and do not modify the -License. You may add Your own attribution notices within Derivative Works that -You distribute, alongside or as an addendum to the NOTICE text from the Work, -provided that such additional attribution notices cannot be construed as -modifying the License. - -You may add Your own copyright statement to Your modifications and may provide -additional or different license terms and conditions for use, reproduction, or -distribution of Your modifications, or for any such Derivative Works as a whole, -provided Your use, reproduction, and distribution of the Work otherwise complies -with the conditions stated in this License. - -5. Submission of Contributions. Unless You explicitly state otherwise, any -Contribution intentionally submitted for inclusion in the Work by You to the -Licensor shall be under the terms and conditions of this License, without any -additional terms or conditions. Notwithstanding the above, nothing herein shall -supersede or modify the terms of any separate license agreement you may have -executed with Licensor regarding such Contributions. - -6. Trademarks. This License does not grant permission to use the trade names, -trademarks, service marks, or product names of the Licensor, except as required -for reasonable and customary use in describing the origin of the Work and -reproducing the content of the NOTICE file. - -7. Disclaimer of Warranty. Unless required by applicable law or agreed to in -writing, Licensor provides the Work (and each Contributor provides its -Contributions) on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY -KIND, either express or implied, including, without limitation, any warranties -or conditions of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A -PARTICULAR PURPOSE. You are solely responsible for determining the -appropriateness of using or redistributing the Work and assume any risks -associated with Your exercise of permissions under this License. - -8. Limitation of Liability. In no event and under no legal theory, whether in -tort (including negligence), contract, or otherwise, unless required by -applicable law (such as deliberate and grossly negligent acts) or agreed to in -writing, shall any Contributor be liable to You for damages, including any -direct, indirect, special, incidental, or consequential damages of any character -arising as a result of this License or out of the use or inability to use the -Work (including but not limited to damages for loss of goodwill, work stoppage, -computer failure or malfunction, or any and all other commercial damages or -losses), even if such Contributor has been advised of the possibility of such -damages. - -9. Accepting Warranty or Additional Liability. While redistributing the Work or -Derivative Works thereof, You may choose to offer, and charge a fee for, -acceptance of support, warranty, indemnity, or other liability obligations -and/or rights consistent with this License. However, in accepting such -obligations, You may act only on Your own behalf and on Your sole -responsibility, not on behalf of any other Contributor, and only if You agree to -indemnify, defend, and hold each Contributor harmless for any liability incurred -by, or claims asserted against, such Contributor by reason of your accepting any -such warranty or additional liability. - -END OF TERMS AND CONDITIONS -APPENDIX: How to apply the Apache License to your work - -To apply the Apache License to your work, attach the following boilerplate -notice, with the fields enclosed by brackets "[]" replaced with your own -identifying information. (Don't include the brackets!) The text should be -enclosed in the appropriate comment syntax for the file format. We also -recommend that a file or class name and description of purpose be included on -the same "printed page" as the copyright notice for easier identification within -third-party archives. -Copyright [yyyy] [name of copyright owner] 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. -*************************************************************************** -%%The following software may be included in this product: -Apache Commons - commons-net-1.4.1.jar -Use of any of this software is governed by the terms of the license below: -Apache License -Version 2.0, January 2004 -http://www.apache.org/licenses/ - -TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION - -1. Definitions. - -"License" shall mean the terms and conditions for use, reproduction, -and distribution as defined by Sections 1 through 9 of this document. - -"Licensor" shall mean the copyright owner or entity authorized by -the copyright owner that is granting the License. - -"Legal Entity" shall mean the union of the acting entity and all -other entities that control, are controlled by, or are under common -control with that entity. For the purposes of this definition, -"control" means (i) the power, direct or indirect, to cause the -direction or management of such entity, whether by contract or -otherwise, or (ii) ownership of fifty percent (50%) or more of the -outstanding shares, or (iii) beneficial ownership of such entity. - -"You" (or "Your") shall mean an individual or Legal Entity -exercising permissions granted by this License. - -"Source" form shall mean the preferred form for making modifications, -including but not limited to software source code, documentation -source, and configuration files. - -"Object" form shall mean any form resulting from mechanical -transformation or translation of a Source form, including but -not limited to compiled object code, generated documentation, -and conversions to other media types. - -"Work" shall mean the work of authorship, whether in Source or -Object form, made available under the License, as indicated by a -copyright notice that is included in or attached to the work -(an example is provided in the Appendix below). - -"Derivative Works" shall mean any work, whether in Source or Object -form, that is based on (or derived from) the Work and for which the -editorial revisions, annotations, elaborations, or other modifications -represent, as a whole, an original work of authorship. For the purposes -of this License, Derivative Works shall not include works that remain -separable from, or merely link (or bind by name) to the interfaces of, -the Work and Derivative Works thereof. - -"Contribution" shall mean any work of authorship, including -the original version of the Work and any modifications or additions -to that Work or Derivative Works thereof, that is intentionally -submitted to Licensor for inclusion in the Work by the copyright owner -or by an individual or Legal Entity authorized to submit on behalf of -the copyright owner. For the purposes of this definition, "submitted" -means any form of electronic, verbal, or written communication sent -to the Licensor or its representatives, including but not limited to -communication on electronic mailing lists, source code control systems, -and issue tracking systems that are managed by, or on behalf of, the -Licensor for the purpose of discussing and improving the Work, but -excluding communication that is conspicuously marked or otherwise -designated in writing by the copyright owner as "Not a Contribution." - -"Contributor" shall mean Licensor and any individual or Legal Entity -on behalf of whom a Contribution has been received by Licensor and -subsequently incorporated within the Work. - -2. Grant of Copyright License. Subject to the terms and conditions of -this License, each Contributor hereby grants to You a perpetual, -worldwide, non-exclusive, no-charge, royalty-free, irrevocable -copyright license to reproduce, prepare Derivative Works of, -publicly display, publicly perform, sublicense, and distribute the -Work and such Derivative Works in Source or Object form. - -3. Grant of Patent License. Subject to the terms and conditions of -this License, each Contributor hereby grants to You a perpetual, -worldwide, non-exclusive, no-charge, royalty-free, irrevocable -(except as stated in this section) patent license to make, have made, -use, offer to sell, sell, import, and otherwise transfer the Work, -where such license applies only to those patent claims licensable -by such Contributor that are necessarily infringed by their -Contribution(s) alone or by combination of their Contribution(s) -with the Work to which such Contribution(s) was submitted. If You -institute patent litigation against any entity (including a -cross-claim or counterclaim in a lawsuit) alleging that the Work -or a Contribution incorporated within the Work constitutes direct -or contributory patent infringement, then any patent licenses -granted to You under this License for that Work shall terminate -as of the date such litigation is filed. - -4. Redistribution. You may reproduce and distribute copies of the -Work or Derivative Works thereof in any medium, with or without -modifications, and in Source or Object form, provided that You -meet the following conditions: - -(a) You must give any other recipients of the Work or -Derivative Works a copy of this License; and - -(b) You must cause any modified files to carry prominent notices -stating that You changed the files; and - -(c) You must retain, in the Source form of any Derivative Works -that You distribute, all copyright, patent, trademark, and -attribution notices from the Source form of the Work, -excluding those notices that do not pertain to any part of -the Derivative Works; and - -(d) If the Work includes a "NOTICE" text file as part of its -distribution, then any Derivative Works that You distribute must -include a readable copy of the attribution notices contained -within such NOTICE file, excluding those notices that do not -pertain to any part of the Derivative Works, in at least one -of the following places: within a NOTICE text file distributed -as part of the Derivative Works; within the Source form or -documentation, if provided along with the Derivative Works; or, -within a display generated by the Derivative Works, if and -wherever such third-party notices normally appear. The contents -of the NOTICE file are for informational purposes only and -do not modify the License. You may add Your own attribution -notices within Derivative Works that You distribute, alongside -or as an addendum to the NOTICE text from the Work, provided -that such additional attribution notices cannot be construed -as modifying the License. - -You may add Your own copyright statement to Your modifications and -may provide additional or different license terms and conditions -for use, reproduction, or distribution of Your modifications, or -for any such Derivative Works as a whole, provided Your use, -reproduction, and distribution of the Work otherwise complies with -the conditions stated in this License. - -5. Submission of Contributions. Unless You explicitly state otherwise, -any Contribution intentionally submitted for inclusion in the Work -by You to the Licensor shall be under the terms and conditions of -this License, without any additional terms or conditions. -Notwithstanding the above, nothing herein shall supersede or modify -the terms of any separate license agreement you may have executed -with Licensor regarding such Contributions. - -6. Trademarks. This License does not grant permission to use the trade -names, trademarks, service marks, or product names of the Licensor, -except as required for reasonable and customary use in describing the -origin of the Work and reproducing the content of the NOTICE file. - -7. Disclaimer of Warranty. Unless required by applicable law or -agreed to in writing, Licensor provides the Work (and each -Contributor provides its Contributions) on an "AS IS" BASIS, -WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or -implied, including, without limitation, any warranties or conditions -of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A -PARTICULAR PURPOSE. You are solely responsible for determining the -appropriateness of using or redistributing the Work and assume any -risks associated with Your exercise of permissions under this License. - -8. Limitation of Liability. In no event and under no legal theory, -whether in tort (including negligence), contract, or otherwise, -unless required by applicable law (such as deliberate and grossly -negligent acts) or agreed to in writing, shall any Contributor be -liable to You for damages, including any direct, indirect, special, -incidental, or consequential damages of any character arising as a -result of this License or out of the use or inability to use the -Work (including but not limited to damages for loss of goodwill, -work stoppage, computer failure or malfunction, or any and all -other commercial damages or losses), even if such Contributor -has been advised of the possibility of such damages. - -9. Accepting Warranty or Additional Liability. While redistributing -the Work or Derivative Works thereof, You may choose to offer, -and charge a fee for, acceptance of support, warranty, indemnity, -or other liability obligations and/or rights consistent with this -License. However, in accepting such obligations, You may act only -on Your own behalf and on Your sole responsibility, not on behalf -of any other Contributor, and only if You agree to indemnify, -defend, and hold each Contributor harmless for any liability -incurred by, or claims asserted against, such Contributor by reason -of your accepting any such warranty or additional liability. - -END OF TERMS AND CONDITIONS - -APPENDIX: How to apply the Apache License to your work. - -To apply the Apache License to your work, attach the following -boilerplate notice, with the fields enclosed by brackets "[]" -replaced with your own identifying information. (Don't include -the brackets!) The text should be enclosed in the appropriate -comment syntax for the file format. We also recommend that a -file or class name and description of purpose be included on the -same "printed page" as the copyright notice for easier -identification within third-party archives. - -Copyright [yyyy] [name of copyright owner] - -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. -*************************************************************************** -%%The following software may be included in this product: -Jakarta Oro -Use of any of this software is governed by the terms of the license below: -Apache License -Version 2.0, January 2004 -http://www.apache.org/licenses/ - -TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION - -1. Definitions. - -"License" shall mean the terms and conditions for use, reproduction, -and distribution as defined by Sections 1 through 9 of this document. - -"Licensor" shall mean the copyright owner or entity authorized by -the copyright owner that is granting the License. - -"Legal Entity" shall mean the union of the acting entity and all -other entities that control, are controlled by, or are under common -control with that entity. For the purposes of this definition, -"control" means (i) the power, direct or indirect, to cause the -direction or management of such entity, whether by contract or -otherwise, or (ii) ownership of fifty percent (50%) or more of the -outstanding shares, or (iii) beneficial ownership of such entity. - -"You" (or "Your") shall mean an individual or Legal Entity -exercising permissions granted by this License. - -"Source" form shall mean the preferred form for making modifications, -including but not limited to software source code, documentation -source, and configuration files. - -"Object" form shall mean any form resulting from mechanical -transformation or translation of a Source form, including but -not limited to compiled object code, generated documentation, -and conversions to other media types. - -"Work" shall mean the work of authorship, whether in Source or -Object form, made available under the License, as indicated by a -copyright notice that is included in or attached to the work -(an example is provided in the Appendix below). - -"Derivative Works" shall mean any work, whether in Source or Object -form, that is based on (or derived from) the Work and for which the -editorial revisions, annotations, elaborations, or other modifications -represent, as a whole, an original work of authorship. For the purposes -of this License, Derivative Works shall not include works that remain -separable from, or merely link (or bind by name) to the interfaces of, -the Work and Derivative Works thereof. - -"Contribution" shall mean any work of authorship, including -the original version of the Work and any modifications or additions -to that Work or Derivative Works thereof, that is intentionally -submitted to Licensor for inclusion in the Work by the copyright owner -or by an individual or Legal Entity authorized to submit on behalf of -the copyright owner. For the purposes of this definition, "submitted" -means any form of electronic, verbal, or written communication sent -to the Licensor or its representatives, including but not limited to -communication on electronic mailing lists, source code control systems, -and issue tracking systems that are managed by, or on behalf of, the -Licensor for the purpose of discussing and improving the Work, but -excluding communication that is conspicuously marked or otherwise -designated in writing by the copyright owner as "Not a Contribution." - -"Contributor" shall mean Licensor and any individual or Legal Entity -on behalf of whom a Contribution has been received by Licensor and -subsequently incorporated within the Work. - -2. Grant of Copyright License. Subject to the terms and conditions of -this License, each Contributor hereby grants to You a perpetual, -worldwide, non-exclusive, no-charge, royalty-free, irrevocable -copyright license to reproduce, prepare Derivative Works of, -publicly display, publicly perform, sublicense, and distribute the -Work and such Derivative Works in Source or Object form. - -3. Grant of Patent License. Subject to the terms and conditions of -this License, each Contributor hereby grants to You a perpetual, -worldwide, non-exclusive, no-charge, royalty-free, irrevocable -(except as stated in this section) patent license to make, have made, -use, offer to sell, sell, import, and otherwise transfer the Work, -where such license applies only to those patent claims licensable -by such Contributor that are necessarily infringed by their -Contribution(s) alone or by combination of their Contribution(s) -with the Work to which such Contribution(s) was submitted. If You -institute patent litigation against any entity (including a -cross-claim or counterclaim in a lawsuit) alleging that the Work -or a Contribution incorporated within the Work constitutes direct -or contributory patent infringement, then any patent licenses -granted to You under this License for that Work shall terminate -as of the date such litigation is filed. - -4. Redistribution. You may reproduce and distribute copies of the -Work or Derivative Works thereof in any medium, with or without -modifications, and in Source or Object form, provided that You -meet the following conditions: - -(a) You must give any other recipients of the Work or -Derivative Works a copy of this License; and - -(b) You must cause any modified files to carry prominent notices -stating that You changed the files; and - -(c) You must retain, in the Source form of any Derivative Works -that You distribute, all copyright, patent, trademark, and -attribution notices from the Source form of the Work, -excluding those notices that do not pertain to any part of -the Derivative Works; and - -(d) If the Work includes a "NOTICE" text file as part of its -distribution, then any Derivative Works that You distribute must -include a readable copy of the attribution notices contained -within such NOTICE file, excluding those notices that do not -pertain to any part of the Derivative Works, in at least one -of the following places: within a NOTICE text file distributed -as part of the Derivative Works; within the Source form or -documentation, if provided along with the Derivative Works; or, -within a display generated by the Derivative Works, if and -wherever such third-party notices normally appear. The contents -of the NOTICE file are for informational purposes only and -do not modify the License. You may add Your own attribution -notices within Derivative Works that You distribute, alongside -or as an addendum to the NOTICE text from the Work, provided -that such additional attribution notices cannot be construed -as modifying the License. - -You may add Your own copyright statement to Your modifications and -may provide additional or different license terms and conditions -for use, reproduction, or distribution of Your modifications, or -for any such Derivative Works as a whole, provided Your use, -reproduction, and distribution of the Work otherwise complies with -the conditions stated in this License. - -5. Submission of Contributions. Unless You explicitly state otherwise, -any Contribution intentionally submitted for inclusion in the Work -by You to the Licensor shall be under the terms and conditions of -this License, without any additional terms or conditions. -Notwithstanding the above, nothing herein shall supersede or modify -the terms of any separate license agreement you may have executed -with Licensor regarding such Contributions. - -6. Trademarks. This License does not grant permission to use the trade -names, trademarks, service marks, or product names of the Licensor, -except as required for reasonable and customary use in describing the -origin of the Work and reproducing the content of the NOTICE file. - -7. Disclaimer of Warranty. Unless required by applicable law or -agreed to in writing, Licensor provides the Work (and each -Contributor provides its Contributions) on an "AS IS" BASIS, -WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or -implied, including, without limitation, any warranties or conditions -of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A -PARTICULAR PURPOSE. You are solely responsible for determining the -appropriateness of using or redistributing the Work and assume any -risks associated with Your exercise of permissions under this License. - -8. Limitation of Liability. In no event and under no legal theory, -whether in tort (including negligence), contract, or otherwise, -unless required by applicable law (such as deliberate and grossly -negligent acts) or agreed to in writing, shall any Contributor be -liable to You for damages, including any direct, indirect, special, -incidental, or consequential damages of any character arising as a -result of this License or out of the use or inability to use the -Work (including but not limited to damages for loss of goodwill, -work stoppage, computer failure or malfunction, or any and all -other commercial damages or losses), even if such Contributor -has been advised of the possibility of such damages. - -9. Accepting Warranty or Additional Liability. While redistributing -the Work or Derivative Works thereof, You may choose to offer, -and charge a fee for, acceptance of support, warranty, indemnity, -or other liability obligations and/or rights consistent with this -License. However, in accepting such obligations, You may act only -on Your own behalf and on Your sole responsibility, not on behalf -of any other Contributor, and only if You agree to indemnify, -defend, and hold each Contributor harmless for any liability -incurred by, or claims asserted against, such Contributor by reason -of your accepting any such warranty or additional liability. - -END OF TERMS AND CONDITIONS - -APPENDIX: How to apply the Apache License to your work. - -To apply the Apache License to your work, attach the following -boilerplate notice, with the fields enclosed by brackets "[]" -replaced with your own identifying information. (Don't include -the brackets!) The text should be enclosed in the appropriate -comment syntax for the file format. We also recommend that a -file or class name and description of purpose be included on the -same "printed page" as the copyright notice for easier -identification within third-party archives. - -Copyright [yyyy] [name of copyright owner] - -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. -*************************************************************************** -%%The following software may be included in this product: -Jsch -Use of any of this software is governed by the terms of the license below: -JSch 0.0.* was released under the GNU LGPL license. Later, we have switched -over to a BSD-style license. - ------------------------------------------------------------------------------- -Copyright (c) 2002,2003,2004,2005,2006,2007,2008 Atsuhiko Yamanaka, JCraft,Inc. -All rights reserved. - -Redistribution and use in source and binary forms, with or without -modification, are permitted provided that the following conditions are met: - -1. Redistributions of source code must retain the above copyright notice, -this list of conditions and the following disclaimer. - -2. Redistributions in binary form must reproduce the above copyright -notice, this list of conditions and the following disclaimer in -the documentation and/or other materials provided with the distribution. - -3. The names of the authors may not be used to endorse or promote products -derived from this software without specific prior written permission. - -THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED OR IMPLIED WARRANTIES, -INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND -FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL JCRAFT, -INC. OR ANY CONTRIBUTORS TO THIS SOFTWARE BE LIABLE FOR ANY DIRECT, INDIRECT, -INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT -LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, -OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF -LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING -NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, -EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. -Additional License(s) -LICENSE.txt:JSch 0.0.* was released under the GNU LGPL license. -Later, we have switched -LICENSE.txt:over to a BSD-style license. -*************************************************************************** -%%The following software may be included in this product: -Expect4j -Use of any of this software is governed by the terms of the license below: -Apache License, Version 2.0 - -Apache License -Version 2.0, January 2004 -http://www.apache.org/licenses/ - -TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION - -1. Definitions. - -"License" shall mean the terms and conditions for use, reproduction, and -distribution as defined by Sections 1 through 9 of this document. - -"Licensor" shall mean the copyright owner or entity authorized by the copyright -owner that is granting the License. - -"Legal Entity" shall mean the union of the acting entity and all other entities -that control, are controlled by, or are under common control with that entity. -For the purposes of this definition, "control" means (i) the power, direct or -indirect, to cause the direction or management of such entity, whether by -contract or otherwise, or (ii) ownership of fifty percent (50%) or more of the -outstanding shares, or (iii) beneficial ownership of such entity. - -"You" (or "Your") shall mean an individual or Legal Entity exercising -permissions granted by this License. - -"Source" form shall mean the preferred form for making modifications, including -but not limited to software source code, documentation source, and configuration -files. - -"Object" form shall mean any form resulting from mechanical transformation or -translation of a Source form, including but not limited to compiled object code, -generated documentation, and conversions to other media types. - -"Work" shall mean the work of authorship, whether in Source or Object form, made -available under the License, as indicated by a copyright notice that is included -in or attached to the work (an example is provided in the Appendix below). - -"Derivative Works" shall mean any work, whether in Source or Object form, that -is based on (or derived from) the Work and for which the editorial revisions, -annotations, elaborations, or other modifications represent, as a whole, an -original work of authorship. For the purposes of this License, Derivative Works -shall not include works that remain separable from, or merely link (or bind by -name) to the interfaces of, the Work and Derivative Works thereof. - -"Contribution" shall mean any work of authorship, including the original version -of the Work and any modifications or additions to that Work or Derivative Works -thereof, that is intentionally submitted to Licensor for inclusion in the Work -by the copyright owner or by an individual or Legal Entity authorized to submit -on behalf of the copyright owner. For the purposes of this definition, -"submitted" means any form of electronic, verbal, or written communication sent -to the Licensor or its representatives, including but not limited to -communication on electronic mailing lists, source code control systems, and -issue tracking systems that are managed by, or on behalf of, the Licensor for -the purpose of discussing and improving the Work, but excluding communication -that is conspicuously marked or otherwise designated in writing by the copyright -owner as "Not a Contribution." - -"Contributor" shall mean Licensor and any individual or Legal Entity on behalf -of whom a Contribution has been received by Licensor and subsequently -incorporated within the Work. - -2. Grant of Copyright License. Subject to the terms and conditions of this -License, each Contributor hereby grants to You a perpetual, worldwide, -non-exclusive, no-charge, royalty-free, irrevocable copyright license to -reproduce, prepare Derivative Works of, publicly display, publicly perform, -sublicense, and distribute the Work and such Derivative Works in Source or -Object form. - -3. Grant of Patent License. Subject to the terms and conditions of this License, -each Contributor hereby grants to You a perpetual, worldwide, non-exclusive, -no-charge, royalty-free, irrevocable (except as stated in this section) patent -license to make, have made, use, offer to sell, sell, import, and otherwise -transfer the Work, where such license applies only to those patent claims -licensable by such Contributor that are necessarily infringed by their -Contribution(s) alone or by combination of their Contribution(s) with the Work -to which such Contribution(s) was submitted. If You institute patent litigation -against any entity (including a cross-claim or counterclaim in a lawsuit) -alleging that the Work or a Contribution incorporated within the Work -constitutes direct or contributory patent infringement, then any patent licenses -granted to You under this License for that Work shall terminate as of the date -such litigation is filed. - -4. Redistribution. You may reproduce and distribute copies of the Work or -Derivative Works thereof in any medium, with or without modifications, and in -Source or Object form, provided that You meet the following conditions: - -1. You must give any other recipients of the Work or Derivative Works a copy -of this License; and - -2. You must cause any modified files to carry prominent notices stating that -You changed the files; and - -3. You must retain, in the Source form of any Derivative Works that You -distribute, all copyright, patent, trademark, and attribution notices from the -Source form of the Work, excluding those notices that do not pertain to any part -of the Derivative Works; and - -4. If the Work includes a "NOTICE" text file as part of its distribution, -then any Derivative Works that You distribute must include a readable copy of -the attribution notices contained within such NOTICE file, excluding those -notices that do not pertain to any part of the Derivative Works, in at least one -of the following places: within a NOTICE text file distributed as part of the -Derivative Works; within the Source form or documentation, if provided along -with the Derivative Works; or, within a display generated by the Derivative -Works, if and wherever such third-party notices normally appear. The contents of -the NOTICE file are for informational purposes only and do not modify the -License. You may add Your own attribution notices within Derivative Works that -You distribute, alongside or as an addendum to the NOTICE text from the Work, -provided that such additional attribution notices cannot be construed as -modifying the License. - -You may add Your own copyright statement to Your modifications and may provide -additional or different license terms and conditions for use, reproduction, or -distribution of Your modifications, or for any such Derivative Works as a whole, -provided Your use, reproduction, and distribution of the Work otherwise complies -with the conditions stated in this License. - -5. Submission of Contributions. Unless You explicitly state otherwise, any -Contribution intentionally submitted for inclusion in the Work by You to the -Licensor shall be under the terms and conditions of this License, without any -additional terms or conditions. Notwithstanding the above, nothing herein shall -supersede or modify the terms of any separate license agreement you may have -executed with Licensor regarding such Contributions. - -6. Trademarks. This License does not grant permission to use the trade names, -trademarks, service marks, or product names of the Licensor, except as required -for reasonable and customary use in describing the origin of the Work and -reproducing the content of the NOTICE file. - -7. Disclaimer of Warranty. Unless required by applicable law or agreed to in -writing, Licensor provides the Work (and each Contributor provides its -Contributions) on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY -KIND, either express or implied, including, without limitation, any warranties -or conditions of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A -PARTICULAR PURPOSE. You are solely responsible for determining the -appropriateness of using or redistributing the Work and assume any risks -associated with Your exercise of permissions under this License. - -8. Limitation of Liability. In no event and under no legal theory, whether in -tort (including negligence), contract, or otherwise, unless required by -applicable law (such as deliberate and grossly negligent acts) or agreed to in -writing, shall any Contributor be liable to You for damages, including any -direct, indirect, special, incidental, or consequential damages of any character -arising as a result of this License or out of the use or inability to use the -Work (including but not limited to damages for loss of goodwill, work stoppage, -computer failure or malfunction, or any and all other commercial damages or -losses), even if such Contributor has been advised of the possibility of such -damages. - -9. Accepting Warranty or Additional Liability. While redistributing the Work or -Derivative Works thereof, You may choose to offer, and charge a fee for, -acceptance of support, warranty, indemnity, or other liability obligations -and/or rights consistent with this License. However, in accepting such -obligations, You may act only on Your own behalf and on Your sole -responsibility, not on behalf of any other Contributor, and only if You agree to -indemnify, defend, and hold each Contributor harmless for any liability incurred -by, or claims asserted against, such Contributor by reason of your accepting any -such warranty or additional liability. - -END OF TERMS AND CONDITIONS -APPENDIX: How to apply the Apache License to your work - -To apply the Apache License to your work, attach the following boilerplate -notice, with the fields enclosed by brackets "[]" replaced with your own -identifying information. (Don't include the brackets!) The text should be -enclosed in the appropriate comment syntax for the file format. We also -recommend that a file or class name and description of purpose be included on -the same "printed page" as the copyright notice for easier identification within -third-party archives. -Copyright [yyyy] [name of copyright owner] 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. -Apache Projects - -* HTTP Server -* ActiveMQ -* Ant -* APR -* Archiva -* Beehive -* Cayenne -* Cocoon -* Commons -* Continuum -* CXF -* DB -* Directory -* Excalibur -* Felix -* Forrest -* Geronimo -* Gump -* Hadoop -* Harmony -* HiveMind -* HttpComponents -* iBATIS -* Incubator -* Jackrabbit -* Jakarta -* James -* Labs -* Lenya -* Logging -* Lucene -* Maven -* Mina -* MyFaces -* ODE -* OFBiz -* OpenEJB -* OpenJPA -* Perl -* POI -* Portals -* Roller -* Santuario -* ServiceMix -* Shale -* SpamAssassin -* STDCXX -* Struts -* Synapse -* Tapestry -* TCL -* Tiles -* Tomcat -* Turbine -* Velocity -* Wicket -* Web Services -* Xalan -* Xerces -* XML -* XMLBeans -* XML Graphics - -Foundation - -* FAQ -* Licenses -* News -* Public Records -* Sponsorship -* Donations -* Thanks -* Contact - -Foundation Projects - -* Conferences -* Infrastructure -* JCP - -How it works - -* Introduction -* Meritocracy -* Structure -* Roles -* Collaboration -* Infrastructure -* Incubator -* Other entities -* Glossary -* Voting - -Get Involved - -* Mailing Lists -* Version Control -* Developer Info - -Download - -* from a mirror - -Related Sites - -* ApacheCon -* Bookstore -* Feathercast -* PlanetApache - -Copyright © 2008 The Apache Software Foundation, Licensed under the Apache -License, Version 2.0. -*************************************************************************** -%%The following software may be included in this product: -FreeHost3270 -Use of any of this software is governed by the terms of the license below: -GNU LESSER GENERAL PUBLIC LICENSE - -Version 2.1, February 1999 - -Copyright (C) 1991, 1999 Free Software Foundation, Inc. -51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA -Everyone is permitted to copy and distribute verbatim copies -of this license document, but changing it is not allowed. - -[This is the first released version of the Lesser GPL. It also counts -as the successor of the GNU Library Public License, version 2, hence -the version number 2.1.] -Preamble - -The licenses for most software are designed to take away your freedom to share and change it. By -contrast, the GNU General Public Licenses are intended to guarantee your freedom to share and change -free software--to make sure the software is free for all its users. - -This license, the Lesser General Public License, applies to some specially designated software -packages--typically libraries--of the Free Software Foundation and other authors who decide to use it. -You can use it too, but we suggest you first think carefully about whether this license or the ordinary -General Public License is the better strategy to use in any particular case, based on the explanations -below. - -When we speak of free software, we are referring to freedom of use, not price. Our General Public -Licenses are designed to make sure that you have the freedom to distribute copies of free software (and -charge for this service if you wish); that you receive source code or can get it if you want it; that you -can change the software and use pieces of it in new free programs; and that you are informed that you -can do these things. - -To protect your rights, we need to make restrictions that forbid distributors to deny you these rights or -to ask you to surrender these rights. These restrictions translate to certain responsibilities for you if -you distribute copies of the library or if you modify it. - -For example, if you distribute copies of the library, whether gratis or for a fee, you must give the -recipients all the rights that we gave you. You must make sure that they, too, receive or can get the -source code. If you link other code with the library, you must provide complete object files to the -recipients, so that they can relink them with the library after making changes to the library and -recompiling it. And you must show them these terms so they know their rights. - -We protect your rights with a two-step method: (1) we copyright the library, and (2) we offer you this -license, which gives you legal permission to copy, distribute and/or modify the library. - -To protect each distributor, we want to make it very clear that there is no warranty for the free library. -Also, if the library is modified by someone else and passed on, the recipients should know that what -they have is not the original version, so that the original author's reputation will not be affected by -problems that might be introduced by others. - -Finally, software patents pose a constant threat to the existence of any free program. We wish to make -sure that a company cannot effectively restrict the users of a free program by obtaining a restrictive -license from a patent holder. Therefore, we insist that any patent license obtained for a version of the -library must be consistent with the full freedom of use specified in this license. - -Most GNU software, including some libraries, is covered by the ordinary GNU General Public License. -This license, the GNU Lesser General Public License, applies to certain designated libraries, and is quite -different from the ordinary General Public License. We use this license for certain libraries in order to -permit linking those libraries into non-free programs. - -When a program is linked with a library, whether statically or using a shared library, the combination of -the two is legally speaking a combined work, a derivative of the original library. The ordinary General -Public License therefore permits such linking only if the entire combination fits its criteria of freedom. -The Lesser General Public License permits more lax criteria for linking other code with the library. - -We call this license the "Lesser" General Public License because it does Less to protect the user's -freedom than the ordinary General Public License. It also provides other free software developers Less -of an advantage over competing non-free programs. These disadvantages are the reason we use the -ordinary General Public License for many libraries. However, the Lesser license provides advantages in -certain special circumstances. - -For example, on rare occasions, there may be a special need to encourage the widest possible use of a -certain library, so that it becomes a de-facto standard. To achieve this, non-free programs must be -allowed to use the library. A more frequent case is that a free library does the same job as widely used -non-free libraries. In this case, there is little to gain by limiting the free library to free software only, so -we use the Lesser General Public License. - -In other cases, permission to use a particular library in non-free programs enables a greater number of -people to use a large body of free software. For example, permission to use the GNU C Library in non- -free programs enables many more people to use the whole GNU operating system, as well as its variant, -the GNU/Linux operating system. - -Although the Lesser General Public License is Less protective of the users' freedom, it does ensure that -the user of a program that is linked with the Library has the freedom and the wherewithal to run that -program using a modified version of the Library. - -The precise terms and conditions for copying, distribution and modification follow. Pay close attention -to the difference between a "work based on the library" and a "work that uses the library". The former -contains code derived from the library, whereas the latter must be combined with the library in order to -run. - -TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION - -0. This License Agreement applies to any software library or other program which contains a notice -placed by the copyright holder or other authorized party saying it may be distributed under the terms -of this Lesser General Public License (also called "this License"). Each licensee is addressed as "you". - -A "library" means a collection of software functions and/or data prepared so as to be conveniently -linked with application programs (which use some of those functions and data) to form executables. - -The "Library", below, refers to any such software library or work which has been distributed under these -terms. A "work based on the Library" means either the Library or any derivative work under copyright -law: that is to say, a work containing the Library or a portion of it, either verbatim or with modifications -and/or translated straightforwardly into another language. (Hereinafter, translation is included without -limitation in the term "modification".) - -"Source code" for a work means the preferred form of the work for making modifications to it. For a -library, complete source code means all the source code for all modules it contains, plus any associated -interface definition files, plus the scripts used to control compilation and installation of the library. - -Activities other than copying, distribution and modification are not covered by this License; they are -outside its scope. The act of running a program using the Library is not restricted, and output from -such a program is covered only if its contents constitute a work based on the Library (independent of -the use of the Library in a tool for writing it). Whether that is true depends on what the Library does -and what the program that uses the Library does. - -1. You may copy and distribute verbatim copies of the Library's complete source code as you receive it, -in any medium, provided that you conspicuously and appropriately publish on each copy an -appropriate copyright notice and disclaimer of warranty; keep intact all the notices that refer to this -License and to the absence of any warranty; and distribute a copy of this License along with the Library. - -You may charge a fee for the physical act of transferring a copy, and you may at your option offer -warranty protection in exchange for a fee. - -2. You may modify your copy or copies of the Library or any portion of it, thus forming a work based -on the Library, and copy and distribute such modifications or work under the terms of Section 1 above, -provided that you also meet all of these conditions: - -a) The modified work must itself be a software library. -b) You must cause the files modified to carry prominent notices stating that you changed the files and -the date of any change. -c) You must cause the whole of the work to be licensed at no charge to all third parties under the terms -of this License. -d) If a facility in the modified Library refers to a function or a table of data to be supplied by an -application program that uses the facility, other than as an argument passed when the facility is -invoked, then you must make a good faith effort to ensure that, in the event an application does not -supply such function or table, the facility still operates, and performs whatever part of its purpose -remains meaningful. -(For example, a function in a library to compute square roots has a purpose that is entirely well- -defined independent of the application. Therefore, Subsection 2d requires that any application- -supplied function or table used by this function must be optional: if the application does not supply it, -the square root function must still compute square roots.) - -These requirements apply to the modified work as a whole. If identifiable sections of that work are not -derived from the Library, and can be reasonably considered independent and separate works in -themselves, then this License, and its terms, do not apply to those sections when you distribute them as -separate works. But when you distribute the same sections as part of a whole which is a work based on -the Library, the distribution of the whole must be on the terms of this License, whose permissions for -other licensees extend to the entire whole, and thus to each and every part regardless of who wrote it. -Thus, it is not the intent of this section to claim rights or contest your rights to work written entirely by -you; rather, the intent is to exercise the right to control the distribution of derivative or collective works -based on the Library. - -In addition, mere aggregation of another work not based on the Library with the Library (or with a work -based on the Library) on a volume of a storage or distribution medium does not bring the other work -under the scope of this License. - -3. You may opt to apply the terms of the ordinary GNU General Public License instead of this License to -a given copy of the Library. To do this, you must alter all the notices that refer to this License, so that -they refer to the ordinary GNU General Public License, version 2, instead of to this License. (If a newer -version than version 2 of the ordinary GNU General Public License has appeared, then you can specify -that version instead if you wish.) Do not make any other change in these notices. - -Once this change is made in a given copy, it is irreversible for that copy, so the ordinary GNU General -Public License applies to all subsequent copies and derivative works made from that copy. - -This option is useful when you wish to copy part of the code of the Library into a program that is not a -library. - -4. You may copy and distribute the Library (or a portion or derivative of it, under Section 2) in object -code or executable form under the terms of Sections 1 and 2 above provided that you accompany it -with the complete corresponding machine-readable source code, which must be distributed under the -terms of Sections 1 and 2 above on a medium customarily used for software interchange. - -If distribution of object code is made by offering access to copy from a designated place, then offering -equivalent access to copy the source code from the same place satisfies the requirement to distribute -the source code, even though third parties are not compelled to copy the source along with the object -code. - -5. A program that contains no derivative of any portion of the Library, but is designed to work with the -Library by being compiled or linked with it, is called a "work that uses the Library". Such a work, in -isolation, is not a derivative work of the Library, and therefore falls outside the scope of this License. - -However, linking a "work that uses the Library" with the Library creates an executable that is a -derivative of the Library (because it contains portions of the Library), rather than a "work that uses the -library". The executable is therefore covered by this License. Section 6 states terms for distribution of -such executables. - -When a "work that uses the Library" uses material from a header file that is part of the Library, the -object code for the work may be a derivative work of the Library even though the source code is not. -Whether this is true is especially significant if the work can be linked without the Library, or if the work -is itself a library. The threshold for this to be true is not precisely defined by law. - -If such an object file uses only numerical parameters, data structure layouts and accessors, and small -macros and small inline functions (ten lines or less in length), then the use of the object file is -unrestricted, regardless of whether it is legally a derivative work. (Executables containing this object -code plus portions of the Library will still fall under Section 6.) - -Otherwise, if the work is a derivative of the Library, you may distribute the object code for the work -under the terms of Section 6. Any executables containing that work also fall under Section 6, whether -or not they are linked directly with the Library itself. - -6. As an exception to the Sections above, you may also combine or link a "work that uses the Library" -with the Library to produce a work containing portions of the Library, and distribute that work under -terms of your choice, provided that the terms permit modification of the work for the customer's own -use and reverse engineering for debugging such modifications. - -You must give prominent notice with each copy of the work that the Library is used in it and that the -Library and its use are covered by this License. You must supply a copy of this License. If the work -during execution displays copyright notices, you must include the copyright notice for the Library -among them, as well as a reference directing the user to the copy of this License. Also, you must do -one of these things: - -a) Accompany the work with the complete corresponding machine-readable source code for the Library -including whatever changes were used in the work (which must be distributed under Sections 1 and 2 -above); and, if the work is an executable linked with the Library, with the complete machine-readable -"work that uses the Library", as object code and/or source code, so that the user can modify the Library -and then relink to produce a modified executable containing the modified Library. (It is understood that -the user who changes the contents of definitions files in the Library will not necessarily be able to -recompile the application to use the modified definitions.) -b) Use a suitable shared library mechanism for linking with the Library. A suitable mechanism is one -that (1) uses at run time a copy of the library already present on the user's computer system, rather -than copying library functions into the executable, and (2) will operate properly with a modified version -of the library, if the user installs one, as long as the modified version is interface-compatible with the -version that the work was made with. -c) Accompany the work with a written offer, valid for at least three years, to give the same user the -materials specified in Subsection 6a, above, for a charge no more than the cost of performing this -distribution. -d) If distribution of the work is made by offering access to copy from a designated place, offer -equivalent access to copy the above specified materials from the same place. -e) Verify that the user has already received a copy of these materials or that you have already sent this -user a copy. -For an executable, the required form of the "work that uses the Library" must include any data and -utility programs needed for reproducing the executable from it. However, as a special exception, the -materials to be distributed need not include anything that is normally distributed (in either source or -binary form) with the major components (compiler, kernel, and so on) of the operating system on which -the executable runs, unless that component itself accompanies the executable. - -It may happen that this requirement contradicts the license restrictions of other proprietary libraries -that do not normally accompany the operating system. Such a contradiction means you cannot use both -them and the Library together in an executable that you distribute. - -7. You may place library facilities that are a work based on the Library side-by-side in a single library -together with other library facilities not covered by this License, and distribute such a combined library, -provided that the separate distribution of the work based on the Library and of the other library -facilities is otherwise permitted, and provided that you do these two things: - -a) Accompany the combined library with a copy of the same work based on the Library, uncombined -with any other library facilities. This must be distributed under the terms of the Sections above. -b) Give prominent notice with the combined library of the fact that part of it is a work based on the -Library, and explaining where to find the accompanying uncombined form of the same work. -8. You may not copy, modify, sublicense, link with, or distribute the Library except as expressly -provided under this License. Any attempt otherwise to copy, modify, sublicense, link with, or distribute -the Library is void, and will automatically terminate your rights under this License. However, parties -who have received copies, or rights, from you under this License will not have their licenses terminated -so long as such parties remain in full compliance. - -9. You are not required to accept this License, since you have not signed it. However, nothing else -grants you permission to modify or distribute the Library or its derivative works. These actions are -prohibited by law if you do not accept this License. Therefore, by modifying or distributing the Library -(or any work based on the Library), you indicate your acceptance of this License to do so, and all its -terms and conditions for copying, distributing or modifying the Library or works based on it. - -10. Each time you redistribute the Library (or any work based on the Library), the recipient -automatically receives a license from the original licensor to copy, distribute, link with or modify the -Library subject to these terms and conditions. You may not impose any further restrictions on the -recipients' exercise of the rights granted herein. You are not responsible for enforcing compliance by -third parties with this License. - -11. If, as a consequence of a court judgment or allegation of patent infringement or for any other -reason (not limited to patent issues), conditions are imposed on you (whether by court order, -agreement or otherwise) that contradict the conditions of this License, they do not excuse you from the -conditions of this License. If you cannot distribute so as to satisfy simultaneously your obligations -under this License and any other pertinent obligations, then as a consequence you may not distribute -the Library at all. For example, if a patent license would not permit royalty-free redistribution of the -Library by all those who receive copies directly or indirectly through you, then the only way you could -satisfy both it and this License would be to refrain entirely from distribution of the Library. - -If any portion of this section is held invalid or unenforceable under any particular circumstance, the -balance of the section is intended to apply, and the section as a whole is intended to apply in other -circumstances. - -It is not the purpose of this section to induce you to infringe any patents or other property right claims -or to contest validity of any such claims; this section has the sole purpose of protecting the integrity of -the free software distribution system which is implemented by public license practices. Many people -have made generous contributions to the wide range of software distributed through that system in -reliance on consistent application of that system; it is up to the author/donor to decide if he or she is -willing to distribute software through any other system and a licensee cannot impose that choice. - -This section is intended to make thoroughly clear what is believed to be a consequence of the rest of -this License. - -12. If the distribution and/or use of the Library is restricted in certain countries either by patents or by -copyrighted interfaces, the original copyright holder who places the Library under this License may add -an explicit geographical distribution limitation excluding those countries, so that distribution is -permitted only in or among countries not thus excluded. In such case, this License incorporates the -limitation as if written in the body of this License. - -13. The Free Software Foundation may publish revised and/or new versions of the Lesser General Public -License from time to time. Such new versions will be similar in spirit to the present version, but may -differ in detail to address new problems or concerns. - -Each version is given a distinguishing version number. If the Library specifies a version number of this -License which applies to it and "any later version", you have the option of following the terms and -conditions either of that version or of any later version published by the Free Software Foundation. If -the Library does not specify a license version number, you may choose any version ever published by -the Free Software Foundation. - -14. If you wish to incorporate parts of the Library into other free programs whose distribution -conditions are incompatible with these, write to the author to ask for permission. For software which is -copyrighted by the Free Software Foundation, write to the Free Software Foundation; we sometimes -make exceptions for this. Our decision will be guided by the two goals of preserving the free status of -all derivatives of our free software and of promoting the sharing and reuse of software generally. - -NO WARRANTY - -15. BECAUSE THE LIBRARY IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY FOR THE LIBRARY, -TO THE EXTENT PERMITTED BY APPLICABLE LAW. EXCEPT WHEN OTHERWISE STATED IN WRITING THE -COPYRIGHT HOLDERS AND/OR OTHER PARTIES PROVIDE THE LIBRARY "AS IS" WITHOUT WARRANTY OF -ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES -OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE ENTIRE RISK AS TO THE QUALITY -AND PERFORMANCE OF THE LIBRARY IS WITH YOU. SHOULD THE LIBRARY PROVE DEFECTIVE, YOU -ASSUME THE COST OF ALL NECESSARY SERVICING, REPAIR OR CORRECTION. - -16. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING WILL ANY -COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY AND/OR REDISTRIBUTE THE LIBRARY AS -PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL -OR CONSEQUENTIAL DAMAGES ARISING OUT OF THE USE OR INABILITY TO USE THE LIBRARY -(INCLUDING BUT NOT LIMITED TO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR LOSSES -SUSTAINED BY YOU OR THIRD PARTIES OR A FAILURE OF THE LIBRARY TO OPERATE WITH ANY OTHER -SOFTWARE), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH -DAMAGES. - -END OF TERMS AND CONDITIONS - -How to Apply These Terms to Your New Libraries - -If you develop a new library, and you want it to be of the greatest possible use to the public, we -recommend making it free software that everyone can redistribute and change. You can do so by -permitting redistribution under these terms (or, alternatively, under the terms of the ordinary General -Public License). - -To apply these terms, attach the following notices to the library. It is safest to attach them to the start -of each source file to most effectively convey the exclusion of warranty; and each file should have at -least the "copyright" line and a pointer to where the full notice is found. - -one line to give the library's name and an idea of what it does. -Copyright (C) year name of author - -This library is free software; you can redistribute it and/or -modify it under the terms of the GNU Lesser General Public -License as published by the Free Software Foundation; either -version 2.1 of the License, or (at your option) any later version. - -This library is distributed in the hope that it will be useful, -but WITHOUT ANY WARRANTY; without even the implied warranty of -MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU -Lesser General Public License for more details. - -You should have received a copy of the GNU Lesser General Public -License along with this library; if not, write to the Free Software -Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA -Also add information on how to contact you by electronic and paper mail. - -You should also get your employer (if you work as a programmer) or your school, if any, to sign a -"copyright disclaimer" for the library, if necessary. Here is a sample; alter the names: - -Yoyodyne, Inc., hereby disclaims all copyright interest in -the library `Frob' (a library for tweaking knobs) written -by James Random Hacker. - -signature of Ty Coon, 1 April 1990 -Ty Coon, President of Vice -That's all there is to it! -*************************************************************************** -%%The following software may be included in this product: -Boo -Use of any of this software is governed by the terms of the license below: -Copyright (c) 2003, 2004, Rodrigo B. de Oliveira (rbo@acm.org) -All rights reserved. - -Redistribution and use in source and binary forms, with or without modification, -are permitted provided that the following conditions are met: - -* Redistributions of source code must retain the above copyright notice, -this list of conditions and the following disclaimer. -* Redistributions in binary form must reproduce the above copyright notice, -this list of conditions and the following disclaimer in the documentation -and/or other materials provided with the distribution. -* Neither the name of Rodrigo B. de Oliveira nor the names of its -contributors may be used to endorse or promote products derived from this -software without specific prior written permission. - -THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND -ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED -WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE -DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE -FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL -DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR -SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER -CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, -OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF -THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. -*************************************************************************** - - -DO NOT TRANSLATE OR LOCALIZE -*************************************************************************** -%%The following software may be included in this product: -Groovy Scripting Language -Use of any of this software is governed by the terms of the license below: -Apache License -Version 2.0, January 2004 -http://www.apache.org/licenses/ - -TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION - -1. Definitions. - -"License" shall mean the terms and conditions for use, reproduction, and -distribution as defined by Sections 1 through 9 of this document. - -"Licensor" shall mean the copyright owner or entity authorized by the copyright -owner that is granting the License. - -"Legal Entity" shall mean the union of the acting entity and all other entities -that control, are controlled by, or are under common control with that entity. -For the purposes of this definition, "control" means (i) the power, direct or -indirect, to cause the direction or management of such entity, whether by -contract or otherwise, or (ii) ownership of fifty percent (50%) or more of the -outstanding shares, or (iii) beneficial ownership of such entity. - -"You" (or "Your") shall mean an individual or Legal Entity exercising -permissions granted by this License. - -"Source" form shall mean the preferred form for making modifications, including -but not limited to software source code, documentation source, and configuration -files. - -"Object" form shall mean any form resulting from mechanical transformation or -translation of a Source form, including but not limited to compiled object code, -generated documentation, and conversions to other media types. - -"Work" shall mean the work of authorship, whether in Source or Object form, made -available under the License, as indicated by a copyright notice that is included -in or attached to the work (an example is provided in the Appendix below). - -"Derivative Works" shall mean any work, whether in Source or Object form, that -is based on (or derived from) the Work and for which the editorial revisions, -annotations, elaborations, or other modifications represent, as a whole, an -original work of authorship. For the purposes of this License, Derivative Works -shall not include works that remain separable from, or merely link (or bind by -name) to the interfaces of, the Work and Derivative Works thereof. - -"Contribution" shall mean any work of authorship, including the original version -of the Work and any modifications or additions to that Work or Derivative Works -thereof, that is intentionally submitted to Licensor for inclusion in the Work -by the copyright owner or by an individual or Legal Entity authorized to submit -on behalf of the copyright owner. For the purposes of this definition, -"submitted" means any form of electronic, verbal, or written communication sent -to the Licensor or its representatives, including but not limited to -communication on electronic mailing lists, source code control systems, and -issue tracking systems that are managed by, or on behalf of, the Licensor for -the purpose of discussing and improving the Work, but excluding communication -that is conspicuously marked or otherwise designated in writing by the copyright -owner as "Not a Contribution." - -"Contributor" shall mean Licensor and any individual or Legal Entity on behalf -of whom a Contribution has been received by Licensor and subsequently -incorporated within the Work. - -2. Grant of Copyright License. Subject to the terms and conditions of this -License, each Contributor hereby grants to You a perpetual, worldwide, -non-exclusive, no-charge, royalty-free, irrevocable copyright license to -reproduce, prepare Derivative Works of, publicly display, publicly perform, -sublicense, and distribute the Work and such Derivative Works in Source or -Object form. - -3. Grant of Patent License. Subject to the terms and conditions of this License, -each Contributor hereby grants to You a perpetual, worldwide, non-exclusive, -no-charge, royalty-free, irrevocable (except as stated in this section) patent -license to make, have made, use, offer to sell, sell, import, and otherwise -transfer the Work, where such license applies only to those patent claims -licensable by such Contributor that are necessarily infringed by their -Contribution(s) alone or by combination of their Contribution(s) with the Work -to which such Contribution(s) was submitted. If You institute patent litigation -against any entity (including a cross-claim or counterclaim in a lawsuit) -alleging that the Work or a Contribution incorporated within the Work -constitutes direct or contributory patent infringement, then any patent licenses -granted to You under this License for that Work shall terminate as of the date -such litigation is filed. - -4. Redistribution. You may reproduce and distribute copies of the Work or -Derivative Works thereof in any medium, with or without modifications, and in -Source or Object form, provided that You meet the following conditions: - -1. You must give any other recipients of the Work or Derivative Works a copy -of this License; and - -2. You must cause any modified files to carry prominent notices stating that -You changed the files; and - -3. You must retain, in the Source form of any Derivative Works that You -distribute, all copyright, patent, trademark, and attribution notices from the -Source form of the Work, excluding those notices that do not pertain to any part -of the Derivative Works; and - -4. If the Work includes a "NOTICE" text file as part of its distribution, -then any Derivative Works that You distribute must include a readable copy of -the attribution notices contained within such NOTICE file, excluding those -notices that do not pertain to any part of the Derivative Works, in at least one -of the following places: within a NOTICE text file distributed as part of the -Derivative Works; within the Source form or documentation, if provided along -with the Derivative Works; or, within a display generated by the Derivative -Works, if and wherever such third-party notices normally appear. The contents of -the NOTICE file are for informational purposes only and do not modify the -License. You may add Your own attribution notices within Derivative Works that -You distribute, alongside or as an addendum to the NOTICE text from the Work, -provided that such additional attribution notices cannot be construed as -modifying the License. - -You may add Your own copyright statement to Your modifications and may provide -additional or different license terms and conditions for use, reproduction, or -distribution of Your modifications, or for any such Derivative Works as a whole, -prov ided Your use, reproduction, and distribution of the Work otherwise complies -with the conditions stated in this License. - -5. Submission of Contributions. Unless You explicitly state otherwise, any -Contribution intentionally submitted for inclusion in the Work by You to the -Licensor shall be under the terms and conditions of this License, without any -additional terms or conditions. Notwithstanding the above, nothing herein shall -supersede or modify the terms of any separate license agreement you may have -executed with Licensor regarding such Contributions. - -6. Trademarks. This License does not grant permission to use the trade names, -trademarks, service marks, or product names of the Licensor, except as required -for reasonable and customary use in describing the origin of the Work and -reproducing the content of the NOTICE file. - -7. Disclaimer of Warranty. Unless required by applicable law or agreed to in -writing, Licensor provides the Work (and each Contributor provides its -Contributions) on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY -KIND, either express or implied, including, without limitation, any warranties -or conditions of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A -PARTICULAR PURPOSE. You are solely responsible for determining the -appropriateness of using or redistributing the Work and assume any risks -associated with Your exercise of permissions under this License. - -8. Limitation of Liability. In no event and under no legal theory, whether in -tort (including negligence), contract, or otherwise, unless required by -applicable law (such as deliberate and grossly negligent acts) or agreed to in -writing, shall any Contributor be liable to You for damages, including any -direct, indirect, special, incidental, or consequential damages of any character -arising as a result of this License or out of the use or inability to use the -Work (including but not limited to damages for loss of goodwill, work stoppage, -computer failure or malfunction, or any and all other commercial damages or -losses), even if such Contributor has been advised of the possibility of such -damages. - -9. Accepting Warranty or Additional Liability. While redistributing the Work or -Derivative Works thereof, You may choose to offer, and charge a fee for, -acceptance of support, warranty, indemnity, or other liability obligations -and/or rights consistent with this License. However, in accepting such -obligations, You may act only on Your own behalf and on Your sole -responsibility, not on behalf of any other Contributor, and only if You agree to -indemnify, defend, and hold each Contributor harmless for any liability incurred -by, or claims asserted against, such Contributor by reason of your accepting any -such warranty or additional liability. - -END OF TERMS AND CONDITIONS -APPENDIX: How to apply the Apache License to your work - -To apply the Apache License to your work, attach the following boilerplate -notice, with the fields enclosed by brackets "[]" replaced with your own -identifying information. (Don't include the brackets!) The text should be -enclosed in the appropriate comment syntax for the file format. We also -recommend that a file or class name and description of purpose be included on -the same "printed page" as the copyright notice for easier identification within -third-party archives. -Copyright [yyyy] [name of copyright owner] 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. -*************************************************************************** -%%The following software may be included in this product: -Apache Commons Pool -Use of any of this software is governed by the terms of the license below: -Apache License -Version 2.0, January 2004 -http://www.apache.org/licenses/ - -TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION - -1. Definitions. - -"License" shall mean the terms and conditions for use, reproduction, -and distribution as defined by Sections 1 through 9 of this document. - -"Licensor" shall mean the copyright owner or entity authorized by -the copyright owner that is granting the License. - -"Legal Entity" shall mean the union of the acting entity and all -other entities that control, are controlled by, or are under common -control with that entity. For the purposes of this definition, -"control" means (i) the power, direct or indirect, to cause the -direction or management of such entity, whether by contract or -otherwise, or (ii) ownership of fifty percent (50%) or more of the -outstanding shares, or (iii) beneficial ownership of such entity. - -"You" (or "Your") shall mean an individual or Legal Entity -exercising permissions granted by this License. - -"Source" form shall mean the preferred form for making modifications, -including but not limited to software source code, documentation -source, and configuration files. - -"Object" form shall mean any form resulting from mechanical -transformation or translation of a Source form, including but -not limited to compiled object code, generated documentation, -and conversions to other media types. - -"Work" shall mean the work of authorship, whether in Source or -Object form, made available under the License, as indicated by a -copyright notice that is included in or attached to the work -(an example is provided in the Appendix below). - -"Derivative Works" shall mean any work, whether in Source or Object -form, that is based on (or derived from) the Work and for which the -editorial revisions, annotations, elaborations, or other modifications -represent, as a whole, an original work of authorship. For the purposes -of this License, Derivative Works shall not include works that remain -separable from, or merely link (or bind by name) to the interfaces of, -the Work and Derivative Works thereof. - -"Contribution" shall mean any work of authorship, including -the original version of the Work and any modifications or additions -to that Work or Derivative Works thereof, that is intentionally -submitted to Licensor for inclusion in the Work by the copyright owner -or by an individual or Legal Entity authorized to submit on behalf of -the copyright owner. For the purposes of this definition, "submitted" -means any form of electronic, verbal, or written communication sent -to the Licensor or its representatives, including but not limited to -communication on electronic mailing lists, source code control systems, -and issue tracking systems that are managed by, or on behalf of, the -Licensor for the purpose of discussing and improving the Work, but -excluding communication that is conspicuously marked or otherwise -designated in writing by the copyright owner as "Not a Contribution." - -"Contributor" shall mean Licensor and any individual or Legal Entity -on behalf of whom a Contribution has been received by Licensor and -subsequently incorporated within the Work. - -2. Grant of Copyright License. Subject to the terms and conditions of -this License, each Contributor hereby grants to You a perpetual, -worldwide, non-exclusive, no-charge, royalty-free, irrevocable -copyright license to reproduce, prepare Derivative Works of, -publicly display, publicly perform, sublicense, and distribute the -Work and such Derivative Works in Source or Object form. - -3. Grant of Patent License. Subject to the terms and conditions of -this License, each Contributor hereby grants to You a perpetual, -worldwide, non-exclusive, no-charge, royalty-free, irrevocable -(except as stated in this section) patent license to make, have made, -use, offer to sell, sell, import, and otherwise transfer the Work, -where such license applies only to those patent claims licensable -by such Contributor that are necessarily infringed by their -Contribution(s) alone or by combination of their Contribution(s) -with the Work to which such Contribution(s) was submitted. If You -institute patent litigation against any entity (including a -cross-claim or counterclaim in a lawsuit) alleging that the Work -or a Contribution incorporated within the Work constitutes direct -or contributory patent infringement, then any patent licenses -granted to You under this License for that Work shall terminate -as of the date such litigation is filed. - -4. Redistribution. You may reproduce and distribute copies of the -Work or Derivative Works thereof in any medium, with or without -modifications, and in Source or Object form, provided that You -meet the following conditions: - -(a) You must give any other recipients of the Work or -Derivative Works a copy of this License; and - -(b) You must cause any modified files to carry prominent notices -stating that You changed the files; and - -(c) You must retain, in the Source form of any Derivative Works -that You distribute, all copyright, patent, trademark, and -attribution notices from the Source form of the Work, -excluding those notices that do not pertain to any part of -the Derivative Works; and - -(d) If the Work includes a "NOTICE" text file as part of its -distribution, then any Derivative Works that You distribute must -include a readable copy of the attribution notices contained -within such NOTICE file, excluding those notices that do not -pertain to any part of the Derivative Works, in at least one -of the following places: within a NOTICE text file distributed -as part of the Derivative Works; within the Source form or -documentation, if provided along with the Derivative Works; or, -within a display generated by the Derivative Works, if and -wherever such third-party notices normally appear. The contents -of the NOTICE file are for informational purposes only and -do not modify the License. You may add Your own attribution -notices within Derivative Works that You distribute, alongside -or as an addendum to the NOTICE text from the Work, provided -that such additional attribution notices cannot be construed -as modifying the License. - -You may add Your own copyright statement to Your modifications and -may provide additional or different license terms and conditions -for use, reproduction, or distribution of Your modifications, or -for any such Derivative Works as a whole, provided Your use, -reproduction, and distribution of the Work otherwise complies with -the conditions stated in this License. - -5. Submission of Contributions. Unless You explicitly state otherwise, -any Contribution intentionally submitted for inclusion in the Work -by You to the Licensor shall be under the terms and conditions of -this License, without any additional terms or conditions. -Notwithstanding the above, nothing herein shall supersede or modify -the terms of any separate license agreement you may have executed -with Licensor regarding such Contributions. - -6. Trademarks. This License does not grant permission to use the trade -names, trademarks, service marks, or product names of the Licensor, -except as required for reasonable and customary use in describing the -origin of the Work and reproducing the content of the NOTICE file. - -7. Disclaimer of Warranty. Unless required by applicable law or -agreed to in writing, Licensor provides the Work (and each -Contributor provides its Contributions) on an "AS IS" BASIS, -WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or -implied, including, without limitation, any warranties or conditions -of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A -PARTICULAR PURPOSE. You are solely responsible for determining the -appropriateness of using or redistributing the Work and assume any -risks associated with Your exercise of permissions under this License. - -8. Limitation of Liability. In no event and under no legal theory, -whether in tort (including negligence), contract, or otherwise, -unless required by applicable law (such as deliberate and grossly -negligent acts) or agreed to in writing, shall any Contributor be -liable to You for damages, including any direct, indirect, special, -incidental, or consequential damages of any character arising as a -result of this License or out of the use or inability to use the -Work (including but not limited to damages for loss of goodwill, -work stoppage, computer failure or malfunction, or any and all -other commercial damages or losses), even if such Contributor -has been advised of the possibility of such damages. - -9. Accepting Warranty or Additional Liability. While redistributing -the Work or Derivative Works thereof, You may choose to offer, -and charge a fee for, acceptance of support, warranty, indemnity, -or other liability obligations and/or rights consistent with this -License. However, in accepting such obligations, You may act only -on Your own behalf and on Your sole responsibility, not on behalf -of any other Contributor, and only if You agree to indemnify, -defend, and hold each Contributor harmless for any liability -incurred by, or claims asserted against, such Contributor by reason -of your accepting any such warranty or additional liability. - -END OF TERMS AND CONDITIONS - -APPENDIX: How to apply the Apache License to your work. - -To apply the Apache License to your work, attach the following -boilerplate notice, with the fields enclosed by brackets "[]" -replaced with your own identifying information. (Don't include -the brackets!) The text should be enclosed in the appropriate -comment syntax for the file format. We also recommend that a -file or class name and description of purpose be included on the -same "printed page" as the copyright notice for easier -identification within third-party archives. - -Copyright [yyyy] [name of copyright owner] - -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. -*************************************************************************** -%%The following software may be included in this product: -HSQL Database Engine -Use of any of this software is governed by the terms of the license below: -COPYRIGHTS AND LICENSES - -ORIGINAL LICENSE (a.k.a. "hypersonic_lic.txt") - -For content, code, and products originally developed by Thomas Mueller and the -Hypersonic SQL Group: - -Copyright (c) 1995-2000 by the Hypersonic SQL Group. -All rights reserved. - - -Redistribution and use in source and binary forms, with or without -modification, are permitted provided that the following conditions are met: - -Redistributions of source code must retain the above copyright notice, this -list of conditions and the following disclaimer. - -Redistributions in binary form must reproduce the above copyright notice, -this list of conditions and the following disclaimer in the documentation -and/or other materials provided with the distribution. - -Neither the name of the Hypersonic SQL Group nor the names of its -contributors may be used to endorse or promote products derived from this -software without specific prior written permission. - -THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" -AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE -IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE -ARE DISCLAIMED. IN NO EVENT SHALL THE HYPERSONIC SQL GROUP, -OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, -EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, -PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; -LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND -ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT -(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS -SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - -This software consists of voluntary contributions made by many individuals on -behalf of the -Hypersonic SQL Group. - - - -For work added by the HSQL Development Group (a.k.a. hsqldb_lic.txt): -Copyright (c) 2001-2005, The HSQL Development Group -All rights reserved. - -Redistribution and use in source and binary forms, with or without -modification, are permitted provided that the following conditions are met: - -Redistributions of source code must retain the above copyright notice, this -list of conditions and the following disclaimer. - -Redistributions in binary form must reproduce the above copyright notice, -this list of conditions and the following disclaimer in the documentation -and/or other materials provided with the distribution. - -Neither the name of the HSQL Development Group nor the names of its -contributors may be used to endorse or promote products derived from this -software without specific prior written permission. - -THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" -AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE -IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE -ARE DISCLAIMED. IN NO EVENT SHALL HSQL DEVELOPMENT GROUP, HSQLDB.ORG, -OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, -EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, -PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; -LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND -ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT -(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS -SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. -*************************************************************************** -%%The following software may be included in this product: -Derby -Use of any of this software is governed by the terms of the license below: -Apache License, Version 2.0 - -Apache License -Version 2.0, January 2004 -http://www.apache.org/licenses/ - -TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION - -1. Definitions. - -"License" shall mean the terms and conditions for use, reproduction, and -distribution as defined by Sections 1 through 9 of this document. - -"Licensor" shall mean the copyright owner or entity authorized by the copyright -owner that is granting the License. - -"Legal Entity" shall mean the union of the acting entity and all other entities -that control, are controlled by, or are under common control with that entity. -For the purposes of this definition, "control" means (i) the power, direct or -indirect, to cause the direction or management of such entity, whether by -contract or otherwise, or (ii) ownership of fifty percent (50%) or more of the -outstanding shares, or (iii) beneficial ownership of such entity. - -"You" (or "Your") shall mean an individual or Legal Entity exercising -permissions granted by this License. - -"Source" form shall mean the preferred form for making modifications, including -but not limited to software source code, documentation source, and configuration -files. - -"Object" form shall mean any form resulting from mechanical transformation or -translation of a Source form, including but not limited to compiled object code, -generated documentation, and conversions to other media types. - -"Work" shall mean the work of authorship, whether in Source or Object form, made -available under the License, as indicated by a copyright notice that is included -in or attached to the work (an example is provided in the Appendix below). - -"Derivative Works" shall mean any work, whether in Source or Object form, that -is based on (or derived from) the Work and for which the editorial revisions, -annotations, elaborations, or other modifications represent, as a whole, an -original work of authorship. For the purposes of this License, Derivative Works -shall not include works that remain separable from, or merely link (or bind by -name) to the interfaces of, the Work and Derivative Works thereof. - -"Contribution" shall mean any work of authorship, including the original version -of the Work and any modifications or additions to that Work or Derivative Works -thereof, that is intentionally submitted to Licensor for inclusion in the Work -by the copyright owner or by an individual or Legal Entity authorized to submit -on behalf of the copyright owner. For the purposes of this definition, -"submitted" means any form of electronic, verbal, or written communication sent -to the Licensor or its representatives, including but not limited to -communication on electronic mailing lists, source code control systems, and -issue tracking systems that are managed by, or on behalf of, the Licensor for -the purpose of discussing and improving the Work, but excluding communication -that is conspicuously marked or otherwise designated in writing by the copyright -owner as "Not a Contribution." - -"Contributor" shall mean Licensor and any individual or Legal Entity on behalf -of whom a Contribution has been received by Licensor and subsequently -incorporated within the Work. - -2. Grant of Copyright License. Subject to the terms and conditions of this -License, each Contributor hereby grants to You a perpetual, worldwide, -non-exclusive, no-charge, royalty-free, irrevocable copyright license to -reproduce, prepare Derivative Works of, publicly display, publicly perform, -sublicense, and distribute the Work and such Derivative Works in Source or -Object form. - -3. Grant of Patent License. Subject to the terms and conditions of this License, -each Contributor hereby grants to You a perpetual, worldwide, non-exclusive, -no-charge, royalty-free, irrevocable (except as stated in this section) patent -license to make, have made, use, offer to sell, sell, import, and otherwise -transfer the Work, where such license applies only to those patent claims -licensable by such Contributor that are necessarily infringed by their -Contribution(s) alone or by combination of their Contribution(s) with the Work -to which such Contribution(s) was submitted. If You institute patent litigation -against any entity (including a cross-claim or counterclaim in a lawsuit) -alleging that the Work or a Contribution incorporated within the Work -constitutes direct or contributory patent infringement, then any patent licenses -granted to You under this License for that Work shall terminate as of the date -such litigation is filed. - -4. Redistribution. You may reproduce and distribute copies of the Work or -Derivative Works thereof in any medium, with or without modifications, and in -Source or Object form, provided that You meet the following conditions: - -1. You must give any other recipients of the Work or Derivative Works a copy -of this License; and - -2. You must cause any modified files to carry prominent notices stating that -You changed the files; and - -3. You must retain, in the Source form of any Derivative Works that You -distribute, all copyright, patent, trademark, and attribution notices from the -Source form of the Work, excluding those notices that do not pertain to any part -of the Derivative Works; and - -4. If the Work includes a "NOTICE" text file as part of its distribution, -then any Derivative Works that You distribute must include a readable copy of -the attribution notices contained within such NOTICE file, excluding those -notices that do not pertain to any part of the Derivative Works, in at least one -of the following places: within a NOTICE text file distributed as part of the -Derivative Works; within the Source form or documentation, if provided along -with the Derivative Works; or, within a display generated by the Derivative -Works, if and wherever such third-party notices normally appear. The contents of -the NOTICE file are for informational purposes only and do not modify the -License. You may add Your own attribution notices within Derivative Works that -You distribute, alongside or as an addendum to the NOTICE text from the Work, -provided that such additional attribution notices cannot be construed as -modifying the License. - -You may add Your own copyright statement to Your modifications and may provide -additional or different license terms and conditions for use, reproduction, or -distribution of Your modifications, or for any such Derivative Works as a whole, -provided Your use, reproduction, and distribution of the Work otherwise complies -with the conditions stated in this License. - -5. Submission of Contributions. Unless You explicitly state otherwise, any -Contribution intentionally submitted for inclusion in the Work by You to the -Licensor shall be under the terms and conditions of this License, without any -additional terms or conditions. Notwithstanding the above, nothing herein shall -supersede or modify the terms of any separate license agreement you may have -executed with Licensor regarding such Contributions. - -6. Trademarks. This License does not grant permission to use the trade names, -trademarks, service marks, or product names of the Licensor, except as required -for reasonable and customary use in describing the origin of the Work and -reproducing the content of the NOTICE file. - -7. Disclaimer of Warranty. Unless required by applicable law or agreed to in -writing, Licensor provides the Work (and each Contributor provides its -Contributions) on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY -KIND, either express or implied, including, without limitation, any warranties -or conditions of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A -PARTICULAR PURPOSE. You are solely responsible for determining the -appropriateness of using or redistributing the Work and assume any risks -associated with Your exercise of permissions under this License. - -8. Limitation of Liability. In no event and under no legal theory, whether in -tort (including negligence), contract, or otherwise, unless required by -applicable law (such as deliberate and grossly negligent acts) or agreed to in -writing, shall any Contributor be liable to You for damages, including any -direct, indirect, special, incidental, or consequential damages of any character -arising as a result of this License or out of the use or inability to use the -Work (including but not limited to damages for loss of goodwill, work stoppage, -computer failure or malfunction, or any and all other commercial damages or -losses), even if such Contributor has been advised of the possibility of such -damages. - -9. Accepting Warranty or Additional Liability. While redistributing the Work or -Derivative Works thereof, You may choose to offer, and charge a fee for, -acceptance of support, warranty, indemnity, or other liability obligations -and/or rights consistent with this License. However, in accepting such -obligations, You may act only on Your own behalf and on Your sole -responsibility, not on behalf of any other Contributor, and only if You agree to -indemnify, defend, and hold each Contributor harmless for any liability incurred -by, or claims asserted against, such Contributor by reason of your accepting any -such warranty or additional liability. - -END OF TERMS AND CONDITIONS -APPENDIX: How to apply the Apache License to your work - -To apply the Apache License to your work, attach the following boilerplate -notice, with the fields enclosed by brackets "[]" replaced with your own -identifying information. (Don't include the brackets!) The text should be -enclosed in the appropriate comment syntax for the file format. We also -recommend that a file or class name and description of purpose be included on -the same "printed page" as the copyright notice for easier identification within -third-party archives. -Copyright [yyyy] [name of copyright owner] 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. -*************************************************************************** -%%The following software may be included in this product: -Apache Commons - commons-net-1.4.1.jar -Use of any of this software is governed by the terms of the license below: -Apache License -Version 2.0, January 2004 -http://www.apache.org/licenses/ - -TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION - -1. Definitions. - -"License" shall mean the terms and conditions for use, reproduction, -and distribution as defined by Sections 1 through 9 of this document. - -"Licensor" shall mean the copyright owner or entity authorized by -the copyright owner that is granting the License. - -"Legal Entity" shall mean the union of the acting entity and all -other entities that control, are controlled by, or are under common -control with that entity. For the purposes of this definition, -"control" means (i) the power, direct or indirect, to cause the -direction or management of such entity, whether by contract or -otherwise, or (ii) ownership of fifty percent (50%) or more of the -outstanding shares, or (iii) beneficial ownership of such entity. - -"You" (or "Your") shall mean an individual or Legal Entity -exercising permissions granted by this License. - -"Source" form shall mean the preferred form for making modifications, -including but not limited to software source code, documentation -source, and configuration files. - -"Object" form shall mean any form resulting from mechanical -transformation or translation of a Source form, including but -not limited to compiled object code, generated documentation, -and conversions to other media types. - -"Work" shall mean the work of authorship, whether in Source or -Object form, made available under the License, as indicated by a -copyright notice that is included in or attached to the work -(an example is provided in the Appendix below). - -"Derivative Works" shall mean any work, whether in Source or Object -form, that is based on (or derived from) the Work and for which the -editorial revisions, annotations, elaborations, or other modifications -represent, as a whole, an original work of authorship. For the purposes -of this License, Derivative Works shall not include works that remain -separable from, or merely link (or bind by name) to the interfaces of, -the Work and Derivative Works thereof. - -"Contribution" shall mean any work of authorship, including -the original version of the Work and any modifications or additions -to that Work or Derivative Works thereof, that is intentionally -submitted to Licensor for inclusion in the Work by the copyright owner -or by an individual or Legal Entity authorized to submit on behalf of -the copyright owner. For the purposes of this definition, "submitted" -means any form of electronic, verbal, or written communication sent -to the Licensor or its representatives, including but not limited to -communication on electronic mailing lists, source code control systems, -and issue tracking systems that are managed by, or on behalf of, the -Licensor for the purpose of discussing and improving the Work, but -excluding communication that is conspicuously marked or otherwise -designated in writing by the copyright owner as "Not a Contribution." - -"Contributor" shall mean Licensor and any individual or Legal Entity -on behalf of whom a Contribution has been received by Licensor and -subsequently incorporated within the Work. - -2. Grant of Copyright License. Subject to the terms and conditions of -this License, each Contributor hereby grants to You a perpetual, -worldwide, non-exclusive, no-charge, royalty-free, irrevocable -copyright license to reproduce, prepare Derivative Works of, -publicly display, publicly perform, sublicense, and distribute the -Work and such Derivative Works in Source or Object form. - -3. Grant of Patent License. Subject to the terms and conditions of -this License, each Contributor hereby grants to You a perpetual, -worldwide, non-exclusive, no-charge, royalty-free, irrevocable -(except as stated in this section) patent license to make, have made, -use, offer to sell, sell, import, and otherwise transfer the Work, -where such license applies only to those patent claims licensable -by such Contributor that are necessarily infringed by their -Contribution(s) alone or by combination of their Contribution(s) -with the Work to which such Contribution(s) was submitted. If You -institute patent litigation against any entity (including a -cross-claim or counterclaim in a lawsuit) alleging that the Work -or a Contribution incorporated within the Work constitutes direct -or contributory patent infringement, then any patent licenses -granted to You under this License for that Work shall terminate -as of the date such litigation is filed. - -4. Redistribution. You may reproduce and distribute copies of the -Work or Derivative Works thereof in any medium, with or without -modifications, and in Source or Object form, provided that You -meet the following conditions: - -(a) You must give any other recipients of the Work or -Derivative Works a copy of this License; and - -(b) You must cause any modified files to carry prominent notices -stating that You changed the files; and - -(c) You must retain, in the Source form of any Derivative Works -that You distribute, all copyright, patent, trademark, and -attribution notices from the Source form of the Work, -excluding those notices that do not pertain to any part of -the Derivative Works; and - -(d) If the Work includes a "NOTICE" text file as part of its -distribution, then any Derivative Works that You distribute must -include a readable copy of the attribution notices contained -within such NOTICE file, excluding those notices that do not -pertain to any part of the Derivative Works, in at least one -of the following places: within a NOTICE text file distributed -as part of the Derivative Works; within the Source form or -documentation, if provided along with the Derivative Works; or, -within a display generated by the Derivative Works, if and -wherever such third-party notices normally appear. The contents -of the NOTICE file are for informational purposes only and -do not modify the License. You may add Your own attribution -notices within Derivative Works that You distribute, alongside -or as an addendum to the NOTICE text from the Work, provided -that such additional attribution notices cannot be construed -as modifying the License. - -You may add Your own copyright statement to Your modifications and -may provide additional or different license terms and conditions -for use, reproduction, or distribution of Your modifications, or -for any such Derivative Works as a whole, provided Your use, -reproduction, and distribution of the Work otherwise complies with -the conditions stated in this License. - -5. Submission of Contributions. Unless You explicitly state otherwise, -any Contribution intentionally submitted for inclusion in the Work -by You to the Licensor shall be under the terms and conditions of -this License, without any additional terms or conditions. -Notwithstanding the above, nothing herein shall supersede or modify -the terms of any separate license agreement you may have executed -with Licensor regarding such Contributions. - -6. Trademarks. This License does not grant permission to use the trade -names, trademarks, service marks, or product names of the Licensor, -except as required for reasonable and customary use in describing the -origin of the Work and reproducing the content of the NOTICE file. - -7. Disclaimer of Warranty. Unless required by applicable law or -agreed to in writing, Licensor provides the Work (and each -Contributor provides its Contributions) on an "AS IS" BASIS, -WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or -implied, including, without limitation, any warranties or conditions -of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A -PARTICULAR PURPOSE. You are solely responsible for determining the -appropriateness of using or redistributing the Work and assume any -risks associated with Your exercise of permissions under this License. - -8. Limitation of Liability. In no event and under no legal theory, -whether in tort (including negligence), contract, or otherwise, -unless required by applicable law (such as deliberate and grossly -negligent acts) or agreed to in writing, shall any Contributor be -liable to You for damages, including any direct, indirect, special, -incidental, or consequential damages of any character arising as a -result of this License or out of the use or inability to use the -Work (including but not limited to damages for loss of goodwill, -work stoppage, computer failure or malfunction, or any and all -other commercial damages or losses), even if such Contributor -has been advised of the possibility of such damages. - -9. Accepting Warranty or Additional Liability. While redistributing -the Work or Derivative Works thereof, You may choose to offer, -and charge a fee for, acceptance of support, warranty, indemnity, -or other liability obligations and/or rights consistent with this -License. However, in accepting such obligations, You may act only -on Your own behalf and on Your sole responsibility, not on behalf -of any other Contributor, and only if You agree to indemnify, -defend, and hold each Contributor harmless for any liability -incurred by, or claims asserted against, such Contributor by reason -of your accepting any such warranty or additional liability. - -END OF TERMS AND CONDITIONS - -APPENDIX: How to apply the Apache License to your work. - -To apply the Apache License to your work, attach the following -boilerplate notice, with the fields enclosed by brackets "[]" -replaced with your own identifying information. (Don't include -the brackets!) The text should be enclosed in the appropriate -comment syntax for the file format. We also recommend that a -file or class name and description of purpose be included on the -same "printed page" as the copyright notice for easier -identification within third-party archives. - -Copyright [yyyy] [name of copyright owner] - -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. -*************************************************************************** -%%The following software may be included in this product: -Jakarta Oro -Use of any of this software is governed by the terms of the license below: -Apache License -Version 2.0, January 2004 -http://www.apache.org/licenses/ - -TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION - -1. Definitions. - -"License" shall mean the terms and conditions for use, reproduction, -and distribution as defined by Sections 1 through 9 of this document. - -"Licensor" shall mean the copyright owner or entity authorized by -the copyright owner that is granting the License. - -"Legal Entity" shall mean the union of the acting entity and all -other entities that control, are controlled by, or are under common -control with that entity. For the purposes of this definition, -"control" means (i) the power, direct or indirect, to cause the -direction or management of such entity, whether by contract or -otherwise, or (ii) ownership of fifty percent (50%) or more of the -outstanding shares, or (iii) beneficial ownership of such entity. - -"You" (or "Your") shall mean an individual or Legal Entity -exercising permissions granted by this License. - -"Source" form shall mean the preferred form for making modifications, -including but not limited to software source code, documentation -source, and configuration files. - -"Object" form shall mean any form resulting from mechanical -transformation or translation of a Source form, including but -not limited to compiled object code, generated documentation, -and conversions to other media types. - -"Work" shall mean the work of authorship, whether in Source or -Object form, made available under the License, as indicated by a -copyright notice that is included in or attached to the work -(an example is provided in the Appendix below). - -"Derivative Works" shall mean any work, whether in Source or Object -form, that is based on (or derived from) the Work and for which the -editorial revisions, annotations, elaborations, or other modifications -represent, as a whole, an original work of authorship. For the purposes -of this License, Derivative Works shall not include works that remain -separable from, or merely link (or bind by name) to the interfaces of, -the Work and Derivative Works thereof. - -"Contribution" shall mean any work of authorship, including -the original version of the Work and any modifications or additions -to that Work or Derivative Works thereof, that is intentionally -submitted to Licensor for inclusion in the Work by the copyright owner -or by an individual or Legal Entity authorized to submit on behalf of -the copyright owner. For the purposes of this definition, "submitted" -means any form of electronic, verbal, or written communication sent -to the Licensor or its representatives, including but not limited to -communication on electronic mailing lists, source code control systems, -and issue tracking systems that are managed by, or on behalf of, the -Licensor for the purpose of discussing and improving the Work, but -excluding communication that is conspicuously marked or otherwise -designated in writing by the copyright owner as "Not a Contribution." - -"Contributor" shall mean Licensor and any individual or Legal Entity -on behalf of whom a Contribution has been received by Licensor and -subsequently incorporated within the Work. - -2. Grant of Copyright License. Subject to the terms and conditions of -this License, each Contributor hereby grants to You a perpetual, -worldwide, non-exclusive, no-charge, royalty-free, irrevocable -copyright license to reproduce, prepare Derivative Works of, -publicly display, publicly perform, sublicense, and distribute the -Work and such Derivative Works in Source or Object form. - -3. Grant of Patent License. Subject to the terms and conditions of -this License, each Contributor hereby grants to You a perpetual, -worldwide, non-exclusive, no-charge, royalty-free, irrevocable -(except as stated in this section) patent license to make, have made, -use, offer to sell, sell, import, and otherwise transfer the Work, -where such license applies only to those patent claims licensable -by such Contributor that are necessarily infringed by their -Contribution(s) alone or by combination of their Contribution(s) -with the Work to which such Contribution(s) was submitted. If You -institute patent litigation against any entity (including a -cross-claim or counterclaim in a lawsuit) alleging that the Work -or a Contribution incorporated within the Work constitutes direct -or contributory patent infringement, then any patent licenses -granted to You under this License for that Work shall terminate -as of the date such litigation is filed. - -4. Redistribution. You may reproduce and distribute copies of the -Work or Derivative Works thereof in any medium, with or without -modifications, and in Source or Object form, provided that You -meet the following conditions: - -(a) You must give any other recipients of the Work or -Derivative Works a copy of this License; and - -(b) You must cause any modified files to carry prominent notices -stating that You changed the files; and - -(c) You must retain, in the Source form of any Derivative Works -that You distribute, all copyright, patent, trademark, and -attribution notices from the Source form of the Work, -excluding those notices that do not pertain to any part of -the Derivative Works; and - -(d) If the Work includes a "NOTICE" text file as part of its -distribution, then any Derivative Works that You distribute must -include a readable copy of the attribution notices contained -within such NOTICE file, excluding those notices that do not -pertain to any part of the Derivative Works, in at least one -of the following places: within a NOTICE text file distributed -as part of the Derivative Works; within the Source form or -documentation, if provided along with the Derivative Works; or, -within a display generated by the Derivative Works, if and -wherever such third-party notices normally appear. The contents -of the NOTICE file are for informational purposes only and -do not modify the License. You may add Your own attribution -notices within Derivative Works that You distribute, alongside -or as an addendum to the NOTICE text from the Work, provided -that such additional attribution notices cannot be construed -as modifying the License. - -You may add Your own copyright statement to Your modifications and -may provide additional or different license terms and conditions -for use, reproduction, or distribution of Your modifications, or -for any such Derivative Works as a whole, provided Your use, -reproduction, and distribution of the Work otherwise complies with -the conditions stated in this License. - -5. Submission of Contributions. Unless You explicitly state otherwise, -any Contribution intentionally submitted for inclusion in the Work -by You to the Licensor shall be under the terms and conditions of -this License, without any additional terms or conditions. -Notwithstanding the above, nothing herein shall supersede or modify -the terms of any separate license agreement you may have executed -with Licensor regarding such Contributions. - -6. Trademarks. This License does not grant permission to use the trade -names, trademarks, service marks, or product names of the Licensor, -except as required for reasonable and customary use in describing the -origin of the Work and reproducing the content of the NOTICE file. - -7. Disclaimer of Warranty. Unless required by applicable law or -agreed to in writing, Licensor provides the Work (and each -Contributor provides its Contributions) on an "AS IS" BASIS, -WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or -implied, including, without limitation, any warranties or conditions -of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A -PARTICULAR PURPOSE. You are solely responsible for determining the -appropriateness of using or redistributing the Work and assume any -risks associated with Your exercise of permissions under this License. - -8. Limitation of Liability. In no event and under no legal theory, -whether in tort (including ne gligence), contract, or otherwise, -unless required by applicable law (such as deliberate and grossly -negligent acts) or agreed to in writing, shall any Contributor be -liable to You for damages, including any direct, indirect, special, -incidental, or consequential damages of any character arising as a -result of this License or out of the use or inability to use the -Work (including but not limited to damages for loss of goodwill, -work stoppage, computer failure or malfunction, or any and all -other commercial damages or losses), even if such Contributor -has been advised of the possibility of such damages. - -9. Accepting Warranty or Additional Liability. While redistributing -the Work or Derivative Works thereof, You may choose to offer, -and charge a fee for, acceptance of support, warranty, indemnity, -or other liability obligations and/or rights consistent with this -License. However, in accepting such obligations, You may act only -on Your own behalf and on Your sole responsibility, not on behalf -of any other Contributor, and only if You agree to indemnify, -defend, and hold each Contributor harmless for any liability -incurred by, or claims asserted against, such Contributor by reason -of your accepting any such warranty or additional liability. - -END OF TERMS AND CONDITIONS - -APPENDIX: How to apply the Apache License to your work. - -To apply the Apache License to your work, attach the following -boilerplate notice, with the fields enclosed by brackets "[]" -replaced with your own identifying information. (Don't include -the brackets!) The text should be enclosed in the appropriate -comment syntax for the file format. We also recommend that a -file or class name and description of purpose be included on the -same "printed page" as the copyright notice for easier -identification within third-party archives. - -Copyright [yyyy] [name of copyright owner] - -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. -*************************************************************************** -%%The following software may be included in this product: -Jsch -Use of any of this software is governed by the terms of the license below: -JSch 0.0.* was released under the GNU LGPL license. Later, we have switched -over to a BSD-style license. - ------------------------------------------------------------------------------- -Copyright (c) 2002,2003,2004,2005,2006,2007,2008 Atsuhiko Yamanaka, JCraft,Inc. -All rights reserved. - -Redistribution and use in source and binary forms, with or without -modification, are permitted provided that the following conditions are met: - -1. Redistributions of source code must retain the above copyright notice, -this list of conditions and the following disclaimer. - -2. Redistributions in binary form must reproduce the above copyright -notice, this list of conditions and the following disclaimer in -the documentation and/or other materials provided with the distribution. - -3. The names of the authors may not be used to endorse or promote products -derived from this software without specific prior written permission. - -THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED OR IMPLIED WARRANTIES, -INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND -FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL JCRAFT, -INC. OR ANY CONTRIBUTORS TO THIS SOFTWARE BE LIABLE FOR ANY DIRECT, INDIRECT, -INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT -LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, -OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF -LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING -NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, -EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. -Additional License(s) -LICENSE.txt:JSch 0.0.* was released under the GNU LGPL license. -Later, we have switched -LICENSE.txt:over to a BSD-style license. -*************************************************************************** -%%The following software may be included in this product: -Expect4j -Use of any of this software is governed by the terms of the license below: -Apache License, Version 2.0 - -Apache License -Version 2.0, January 2004 -http://www.apache.org/licenses/ - -TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION - -1. Definitions. - -"License" shall mean the terms and conditions for use, reproduction, and -distribution as defined by Sections 1 through 9 of this document. - -"Licensor" shall mean the copyright owner or entity authorized by the copyright -owner that is granting the License. - -"Legal Entity" shall mean the union of the acting entity and all other entities -that control, are controlled by, or are under common control with that entity. -For the purposes of this definition, "control" means (i) the power, direct or -indirect, to cause the direction or management of such entity, whether by -contract or otherwise, or (ii) ownership of fifty percent (50%) or more of the -outstanding shares, or (iii) beneficial ownership of such entity. - -"You" (or "Your") shall mean an individual or Legal Entity exercising -permissions granted by this License. - -"Source" form shall mean the preferred form for making modifications, including -but not limited to software source code, documentation source, and configuration -files. - -"Object" form shall mean any form resulting from mechanical transformation or -translation of a Source form, including but not limited to compiled object code, -generated documentation, and conversions to other media types. - -"Work" shall mean the work of authorship, whether in Source or Object form, made -available under the License, as indicated by a copyright notice that is included -in or attached to the work (an example is provided in the Appendix below). - -"Derivative Works" shall mean any work, whether in Source or Object form, that -is based on (or derived from) the Work and for which the editorial revisions, -annotations, elaborations, or other modifications represent, as a whole, an -original work of authorship. For the purposes of this License, Derivative Works -shall not include works that remain separable from, or merely link (or bind by -name) to the interfaces of, the Work and Derivative Works thereof. - -"Contribution" shall mean any work of authorship, including the original version -of the Work and any modifications or additions to that Work or Derivative Works -thereof, that is intentionally submitted to Licensor for inclusion in the Work -by the copyright owner or by an individual or Legal Entity authorized to submit -on behalf of the copyright owner. For the purposes of this definition, -"submitted" means any form of electronic, verbal, or written communication sent -to the Licensor or its representatives, including but not limited to -communication on electronic mailing lists, source code control systems, and -issue tracking systems that are managed by, or on behalf of, the Licensor for -the purpose of discussing and improving the Work, but excluding communication -that is conspicuously marked or otherwise designated in writing by the copyright -owner as "Not a Contribution." - -"Contributor" shall mean Licensor and any individual or Legal Entity on behalf -of whom a Contribution has been received by Licensor and subsequently -incorporated within the Work. - -2. Grant of Copyright License. Subject to the terms and conditions of this -License, each Contributor hereby grants to You a perpetual, worldwide, -non-exclusive, no-charge, royalty-free, irrevocable copyright license to -reproduce, prepare Derivative Works of, publicly display, publicly perform, -sublicense, and distribute the Work and such Derivative Works in Source or -Object form. - -3. Grant of Patent License. Subject to the terms and conditions of this License, -each Contributor hereby grants to You a perpetual, worldwide, non-exclusive, -no-charge, royalty-free, irrevocable (except as stated in this section) patent -license to make, have made, use, offer to sell, sell, import, and otherwise -transfer the Work, where such license applies only to those patent claims -licensable by such Contributor that are necessarily infringed by their -Contribution(s) alone or by combination of their Contribution(s) with the Work -to which such Contribution(s) was submitted. If You institute patent litigation -against any entity (including a cross-claim or counterclaim in a lawsuit) -alleging that the Work or a Contribution incorporated within the Work -constitutes direct or contributory patent infringement, then any patent licenses -granted to You under this License for that Work shall terminate as of the date -such litigation is filed. - -4. Redistribution. You may reproduce and distribute copies of the Work or -Derivative Works thereof in any medium, with or without modifications, and in -Source or Object form, provided that You meet the following conditions: - -1. You must give any other recipients of the Work or Derivative Works a copy -of this License; and - -2. You must cause any modified files to carry prominent notices stating that -You changed the files; and - -3. You must retain, in the Source form of any Derivative Works that You -distribute, all copyright, patent, trademark, and attribution notices from the -Source form of the Work, excluding those notices that do not pertain to any part -of the Derivative Works; and - -4. If the Work includes a "NOTICE" text file as part of its distribution, -then any Derivative Works that You distribute must include a readable copy of -the attribution notices contained within such NOTICE file, excluding those -notices that do not pertain to any part of the Derivative Works, in at least one -of the following places: within a NOTICE text file distributed as part of the -Derivative Works; within the Source form or documentation, if provided along -with the Derivative Works; or, within a display generated by the Derivative -Works, if and wherever such third-party notices normally appear. The contents of -the NOTICE file are for informational purposes only and do not modify the -License. You may add Your own attribution notices within Derivative Works that -You distribute, alongside or as an addendum to the NOTICE text from the Work, -provided that such additional attribution notices cannot be construed as -modifying the License. - -You may add Your own copyright statement to Your modifications and may provide -additional or different license terms and conditions for use, reproduction, or -distribution of Your modifications, or for any such Derivative Works as a whole, -provided Your use, reproduction, and distribution of the Work otherwise complies -with the conditions stated in this License. - -5. Submission of Contributions. Unless You explicitly state otherwise, any -Contribution intentionally submitted for inclusion in the Work by You to the -Licensor shall be under the terms and conditions of this License, without any -additional terms or conditions. Notwithstanding the above, nothing herein shall -supersede or modify the terms of any separate license agreement you may have -executed with Licensor regarding such Contributions. - -6. Trademarks. This License does not grant permission to use the trade names, -trademarks, service marks, or product names of the Licensor, except as required -for reasonable and customary use in describing the origin of the Work and -reproducing the content of the NOTICE file. - -7. Disclaimer of Warranty. Unless required by applicable law or agreed to in -writing, Licensor provides the Work (and each Contributor provides its -Contributions) on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY -KIND, either express or implied, including, without limitation, any warranties -or conditions of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A -PARTICULAR PURPOSE. You are solely responsible for determining the -appropriateness of using or redistributing the Work and assume any risks -associated with Your exercise of permissions under this License. - -8. Limitation of Liability. In no event and under no legal theory, whether in -tort (including negligence), contract, or otherwise, unless required by -applicable law (such as deliberate and grossly negligent acts) or agreed to in -writing, shall any Contributor be liable to You for damages, including any -direct, indirect, special, incidental, or consequential damages of any character -arising as a result of this License or out of the use or inability to use the -Work (including but not limited to damages for loss of goodwill, work stoppage, -computer failure or malfunction, or any and all other commercial damages or -losses), even if such Contributor has been advised of the possibility of such -damages. - -9. Accepting Warranty or Additional Liability. While redistributing the Work or -Derivative Works thereof, You may choose to offer, and charge a fee for, -acceptance of support, warranty, indemnity, or other liability obligations -and/or rights consistent with this License. However, in accepting such -obligations, You may act only on Your own behalf and on Your sole -responsibility, not on behalf of any other Contributor, and only if You agree to -indemnify, defend, and hold each Contributor harmless for any liability incurred -by, or claims asserted against, such Contributor by reason of your accepting any -such warranty or additional liability. - -END OF TERMS AND CONDITIONS -APPENDIX: How to apply the Apache License to your work - -To apply the Apache License to your work, attach the following boilerplate -notice, with the fields enclosed by brackets "[]" replaced with your own -identifying information. (Don't include the brackets!) The text should be -enclosed in the appropriate comment syntax for the file format. We also -recommend that a file or class name and description of purpose be included on -the same "printed page" as the copyright notice for easier identification within -third-party archives. -Copyright [yyyy] [name of copyright owner] 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. -Apache Projects - -* HTTP Server -* ActiveMQ -* Ant -* APR -* Archiva -* Beehive -* Cayenne -* Cocoon -* Commons -* Continuum -* CXF -* DB -* Directory -* Excalibur -* Felix -* Forrest -* Geronimo -* Gump -* Hadoop -* Harmony -* HiveMind -* HttpComponents -* iBATIS -* Incubator -* Jackrabbit -* Jakarta -* James -* Labs -* Lenya -* Logging -* Lucene -* Maven -* Mina -* MyFaces -* ODE -* OFBiz -* OpenEJB -* OpenJPA -* Perl -* POI -* Portals -* Roller -* Santuario -* ServiceMix -* Shale -* SpamAssassin -* STDCXX -* Struts -* Synapse -* Tapestry -* TCL -* Tiles -* Tomcat -* Turbine -* Velocity -* Wicket -* Web Services -* Xalan -* Xerces -* XML -* XMLBeans -* XML Graphics - -Foundation - -* FAQ -* Licenses -* News -* Public Records -* Sponsorship -* Donations -* Thanks -* Contact - -Foundation Projects - -* Conferences -* Infrastructure -* JCP - -How it works - -* Introduction -* Meritocracy -* Structure -* Roles -* Collaboration -* Infrastructure -* Incubator -* Other entities -* Glossary -* Voting - -Get Involved - -* Mailing Lists -* Version Control -* Developer Info - -Download - -* from a mirror - -Related Sites - -* ApacheCon -* Bookstore -* Feathercast -* PlanetApache - -Copyright � 2008 The Apache Software Foundation, Licensed under the Apache -License, Version 2.0. -*************************************************************************** -%%The following software may be included in this product: -FreeHost3270 -Use of any of this software is governed by the terms of the license below: -GNU LESSER GENERAL PUBLIC LICENSE - -Version 2.1, February 1999 - -Copyright (C) 1991, 1999 Free Software Foundation, Inc. -51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA -Everyone is permitted to copy and distribute verbatim copies -of this license document, but changing it is not allowed. - -[This is the first released version of the Lesser GPL. It also counts -as the successor of the GNU Library Public License, version 2, hence -the version number 2.1.] -Preamble - -The licenses for most software are designed to take away your freedom to share and change it. By -contrast, the GNU General Public Licenses are intended to guarantee your freedom to share and change -free software--to make sure the software is free for all its users. - -This license, the Lesser General Public License, applies to some specially designated software -packages--typically libraries--of the Free Software Foundation and other authors who decide to use it. -You can use it too, but we suggest you first think carefully about whether this license or the ordinary -General Public License is the better strategy to use in any particular case, based on the explanations -below. - -When we speak of free software, we are referring to freedom of use, not price. Our General Public -Licenses are designed to make sure that you have the freedom to distribute copies of free software (and -charge for this service if you wish); that you receive source code or can get it if you want it; that you -can change the software and use pieces of it in new free programs; and that you are informed that you -can do these things. - -To protect your rights, we need to make restrictions that forbid distributors to deny you these rights or -to ask you to surrender these rights. These restrictions translate to certain responsibilities for you if -you distribute copies of the library or if you modify it. - -For example, if you distribute copies of the library, whether gratis or for a fee, you must give the -recipients all the rights that we gave you. You must make sure that they, too, receive or can get the -source code. If you link other code with the library, you must provide complete object files to the -recipients, so that they can relink them with the library after making changes to the library and -recompiling it. And you must show them these terms so they know their rights. - -We protect your rights with a two-step method: (1) we copyright the library, and (2) we offer you this -license, which gives you legal permission to copy, distribute and/or modify the library. - -To protect each distributor, we want to make it very clear that there is no warranty for the free library. -Also, if the library is modified by someone else and passed on, the recipients should know that what -they have is not the original version, so that the original author's reputation will not be affected by -problems that might be introduced by others. - -Finally, software patents pose a constant threat to the existence of any free program. We wish to make -sure that a company cannot effectively restrict the users of a free program by obtaining a restrictive -license from a patent holder. Therefore, we insist that any patent license obtained for a version of the -library must be consistent with the full freedom of use specified in this license. - -Most GNU software, including some libraries, is covered by the ordinary GNU General Public License. -This license, the GNU Lesser General Public License, applies to certain designated libraries, and is quite -different from the ordinary General Public License. We use this license for certain libraries in order to -permit linking those libraries into non-free programs. - -When a program is linked with a library, whether statically or using a shared library, the combination of -the two is legally speaking a combined work, a derivative of the original library. The ordinary General -Public License therefore permits such linking only if the entire combination fits its criteria of freedom. -The Lesser General Public License permits more lax criteria for linking other code with the library. - -We call this license the "Lesser" General Public License because it does Less to protect the user's -freedom than the ordinary General Public License. It also provides other free software developers Less -of an advantage over competing non-free programs. These disadvantages are the reason we use the -ordinary General Public License for many libraries. However, the Lesser license provides advantages in -certain special circumstances. - -For example, on rare occasions, there may be a special need to encourage the widest possible use of a -certain library, so that it becomes a de-facto standard. To achieve this, non-free programs must be -allowed to use the library. A more frequent case is that a free library does the same job as widely used -non-free libraries. In this case, there is little to gain by limiting the free library to free software only, so -we use the Lesser General Public License. - -In other cases, permission to use a particular library in non-free programs enables a greater number of -people to use a large body of free software. For example, permission to use the GNU C Library in non- -free programs enables many more people to use the whole GNU operating system, as well as its variant, -the GNU/Linux operating system. - -Although the Lesser General Public License is Less protective of the users' freedom, it does ensure that -the user of a program that is linked with the Library has the freedom and the wherewithal to run that -program using a modified version of the Library. - -The precise terms and conditions for copying, distribution and modification follow. Pay close attention -to the difference between a "work based on the library" and a "work that uses the library". The former -contains code derived from the library, whereas the latter must be combined with the library in order to -run. - -TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION - -0. This License Agreement applies to any software library or other program which contains a notice -placed by the copyright holder or other authorized party saying it may be distributed under the terms -of this Lesser General Public License (also called "this License"). Each licensee is addressed as "you". - -A "library" means a collection of software functions and/or data prepared so as to be conveniently -linked with application programs (which use some of those functions and data) to form executables. - -The "Library", below, refers to any such software library or work which has been distributed under these -terms. A "work based on the Library" means either the Library or any derivative work under copyright -law: that is to say, a work containing the Library or a portion of it, either verbatim or with modifications -and/or translated straightforwardly into another language. (Hereinafter, translation is included without -limitation in the term "modification".) - -"Source code" for a work means the preferred form of the work for making modifications to it. For a -library, complete source code means all the source code for all modules it contains, plus any associated -interface definition files, plus the scripts used to control compilation and installation of the library. - -Activities other than copying, distribution and modification are not covered by this License; they are -outside its scope. The act of running a program using the Library is not restricted, and output from -such a program is covered only if its contents constitute a work based on the Library (independent of -the use of the Library in a tool for writing it). Whether that is true depends on what the Library does -and what the program that uses the Library does. - -1. You may copy and distribute verbatim copies of the Library's complete source code as you receive it, -in any medium, provided that you conspicuously and appropriately publish on each copy an -appropriate copyright notice and disclaimer of warranty; keep intact all the notices that refer to this -License and to the absence of any warranty; and distribute a copy of this License along with the Library. - -You may charge a fee for the physical act of transferring a copy, and you may at your option offer -warranty protection in exchange for a fee. - -2. You may modify your copy or copies of the Library or any portion of it, thus forming a work based -on the Library, and copy and distribute such modifications or work under the terms of Section 1 above, -provided that you also meet all of these conditions: - -a) The modified work must itself be a software library. -b) You must cause the files modified to carry prominent notices stating that you changed the files and -the date of any change. -c) You must cause the whole of the work to be licensed at no charge to all third parties under the terms -of this License. -d) If a facility in the modified Library refers to a function or a table of data to be supplied by an -application program that uses the facility, other than as an argument passed when the facility is -invoked, then you must make a good faith effort to ensure that, in the event an application does not -supply such function or table, the facility still operates, and performs whatever part of its purpose -remains meaningful. -(For example, a function in a library to compute square roots has a purpose that is entirely well- -defined independent of the application. Therefore, Subsection 2d requires that any application- -supplied function or table used by this function must be optional: if the application does not supply it, -the square root function must still compute square roots.) - -These requirements apply to the modified work as a whole. If identifiable sections of that work are not -derived from the Library, and can be reasonably considered independent and separate works in -themselves, then this License, and its terms, do not apply to those sections when you distribute them as -separate works. But when you distribute the same sections as part of a whole which is a work based on -the Library, the distribution of the whole must be on the terms of this License, whose permissions for -other licensees extend to the entire whole, and thus to each and every part regardless of who wrote it. -Thus, it is not the intent of this section to claim rights or contest your rights to work written entirely by -you; rather, the intent is to exercise the right to control the distribution of derivative or collective works -based on the Library. - -In addition, mere aggregation of another work not based on the Library with the Library (or with a work -based on the Library) on a volume of a storage or distribution medium does not bring the other work -under the scope of this License. - -3. You may opt to apply the terms of the ordinary GNU General Public License instead of this License to -a given copy of the Library. To do this, you must alter all the notices that refer to this License, so that -they refer to the ordinary GNU General Public License, version 2, instead of to this License. (If a newer -version than version 2 of the ordinary GNU General Public License has appeared, then you can specify -that version instead if you wish.) Do not make any other change in these notices. - -Once this change is made in a given copy, it is irreversible for that copy, so the ordinary GNU General -Public License applies to all subsequent copies and derivative works made from that copy. - -This option is useful when you wish to copy part of the code of the Library into a program that is not a -library. - -4. You may copy and distribute the Library (or a portion or derivative of it, under Section 2) in object -code or executable form under the terms of Sections 1 and 2 above provided that you accompany it -with the complete corresponding machine-readable source code, which must be distributed under the -terms of Sections 1 and 2 above on a medium customarily used for software interchange. - -If distribution of object code is made by offering access to copy from a designated place, then offering -equivalent access to copy the source code from the same place satisfies the requirement to distribute -the source code, even though third parties are not compelled to copy the source along with the object -code. - -5. A program that contains no derivative of any portion of the Library, but is designed to work with the -Library by being compiled or linked with it, is called a "work that uses the Library". Such a work, in -isolation, is not a derivative work of the Library, and therefore falls outside the scope of this License. - -However, linking a "work that uses the Library" with the Library creates an executable that is a -derivative of the Library (because it contains portions of the Library), rather than a "work that uses the -library". The executable is therefore covered by this License. Section 6 states terms for distribution of -such executables. - -When a "work that uses the Library" uses material from a header file that is part of the Library, the -object code for the work may be a derivative work of the Library even though the source code is not. -Whether this is true is especially significant if the work can be linked without the Library, or if the work -is itself a library. The threshold for this to be true is not precisely defined by law. - -If such an object file uses only numerical parameters, data structure layouts and accessors, and small -macros and small inline functions (ten lines or less in length), then the use of the object file is -unrestricted, regardless of whether it is legally a derivative work. (Executables containing this object -code plus portions of the Library will still fall under Section 6.) - -Otherwise, if the work is a derivative of the Library, you may distribute the object code for the work -under the terms of Section 6. Any executables containing that work also fall under Section 6, whether -or not they are linked directly with the Library itself. - -6. As an exception to the Sections above, you may also combine or link a "work that uses the Library" -with the Library to produce a work containing portions of the Library, and distribute that work under -terms of your choice, provided that the terms permit modification of the work for the customer's own -use and reverse engineering for debugging such modifications. - -You must give prominent notice with each copy of the work that the Library is used in it and that the -Library and its use are covered by this License. You must supply a copy of this License. If the work -during execution displays copyright notices, you must include the copyright notice for the Library -among them, as well as a reference directing the user to the copy of this License. Also, you must do -one of these things: - -a) Accompany the work with the complete corresponding machine-readable source code for the Library -including whatever changes were used in the work (which must be distributed under Sections 1 and 2 -above); and, if the work is an executable linked with the Library, with the complete machine-readable -"work that uses the Library", as object code and/or source code, so that the user can modify the Library -and then relink to produce a modified executable containing the modified Library. (It is understood that -the user who changes the contents of definitions files in the Library will not necessarily be able to -recompile the application to use the modified definitions.) -b) Use a suitable shared library mechanism for linking with the Library. A suitable mechanism is one -that (1) uses at run time a copy of the library already present on the user's computer system, rather -than copying library functions into the executable, and (2) will operate properly with a modified version -of the library, if the user installs one, as long as the modified version is interface-compatible with the -version that the work was made with. -c) Accompany the work with a written offer, valid for at least three years, to give the same user the -materials specified in Subsection 6a, above, for a charge no more than the cost of performing this -distribution. -d) If distribution of the work is made by offering access to copy from a designated place, offer -equivalent access to copy the above specified materials from the same place. -e) Verify that the user has already received a copy of these materials or that you have already sent this -user a copy. -For an executable, the required form of the "work that uses the Library" must include any data and -utility programs needed for reproducing the executable from it. However, as a special exception, the -materials to be distributed need not include anything that is normally distributed (in either source or -binary form) with the major components (compiler, kernel, and so on) of the operating system on which -the executable runs, unless that component itself accompanies the executable. - -It may happen that this requirement contradicts the license restrictions of other proprietary libraries -that do not normally accompany the operating system. Such a contradiction means you cannot use both -them and the Library together in an executable that you distribute. - -7. You may place library facilities that are a work based on the Library side-by-side in a single library -together with other library facilities not covered by this License, and distribute such a combined library, -provided that the separate distribution of the work based on the Library and of the other library -facilities is otherwise permitted, and provided that you do these two things: - -a) Accompany the combined library with a copy of the same work based on the Library, uncombined -with any other library facilities. This must be distributed under the terms of the Sections above. -b) Give prominent notice with the combined library of the fact that part of it is a work based on the -Library, and explaining where to find the accompanying uncombined form of the same work. -8. You may not copy, modify, sublicense, link with, or distribute the Library except as expressly -provided under this License. Any attempt otherwise to copy, modify, sublicense, link with, or distribute -the Library is void, and will automatically terminate your rights under this License. However, parties -who have received copies, or rights, from you under this License will not have their licenses terminated -so long as such parties remain in full compliance. - -9. You are not required to accept this License, since you have not signed it. However, nothing else -grants you permission to modify or distribute the Library or its derivative works. These actions are -prohibited by law if you do not accept this License. Therefore, by modifying or distributing the Library -(or any work based on the Library), you indicate your acceptance of this License to do so, and all its -terms and conditions for copying, distributing or modifying the Library or works based on it. - -10. Each time you redistribute the Library (or any work based on the Library), the recipient -automatically receives a license from the original licensor to copy, distribute, link with or modify the -Library subject to these terms and conditions. You may not impose any further restrictions on the -recipients' exercise of the rights granted herein. You are not responsible for enforcing compliance by -third parties with this License. - -11. If, as a consequence of a court judgment or allegation of patent infringement or for any other -reason (not limited to patent issues), conditions are imposed on you (whether by court order, -agreement or otherwise) that contradict the conditions of this License, they do not excuse you from the -conditions of this License. If you cannot distribute so as to satisfy simultaneously your obligations -under this License and any other pertinent obligations, then as a consequence you may not distribute -the Library at all. For example, if a patent license would not permit royalty-free redistribution of the -Library by all those who receive copies directly or indirectly through you, then the only way you could -satisfy both it and this License would be to refrain entirely from distribution of the Library. - -If any portion of this section is held invalid or unenforceable under any particular circumstance, the -balance of the section is intended to apply, and the section as a whole is intended to apply in other -circumstances. - -It is not the purpose of this section to induce you to infringe any patents or other property right claims -or to contest validity of any such claims; this section has the sole purpose of protecting the integrity of -the free software distribution system which is implemented by public license practices. Many people -have made generous contributions to the wide range of software distributed through that system in -reliance on consistent application of that system; it is up to the author/donor to decide if he or she is -willing to distribute software through any other system and a licensee cannot impose that choice. - -This section is intended to make thoroughly clear what is believed to be a consequence of the rest of -this License. - -12. If the distribution and/or use of the Library is restricted in certain countries either by patents or by -copyrighted interfaces, the original copyright holder who places the Library under this License may add -an explicit geographical distribution limitation excluding those countries, so that distribution is -permitted only in or among countries not thus excluded. In such case, this License incorporates the -limitation as if written in the body of this License. - -13. The Free Software Foundation may publish revised and/or new versions of the Lesser General Public -License from time to time. Such new versions will be similar in spirit to the present version, but may -differ in detail to address new problems or concerns. - -Each version is given a distinguishing version number. If the Library specifies a version number of this -License which applies to it and "any later version", you have the option of following the terms and -conditions either of that version or of any later version published by the Free Software Foundation. If -the Library does not specify a license version number, you may choose any version ever published by -the Free Software Foundation. - -14. If you wish to incorporate parts of the Library into other free programs whose distribution -conditions are incompatible with these, write to the author to ask for permission. For software which is -copyrighted by the Free Software Foundation, write to the Free Software Foundation; we sometimes -make exceptions for this. Our decision will be guided by the two goals of preserving the free status of -all derivatives of our free software and of promoting the sharing and reuse of software generally. - -NO WARRANTY - -15. BECAUSE THE LIBRARY IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY FOR THE LIBRARY, -TO THE EXTENT PERMITTED BY APPLICABLE LAW. EXCEPT WHEN OTHERWISE STATED IN WRITING THE -COPYRIGHT HOLDERS AND/OR OTHER PARTIES PROVIDE THE LIBRARY "AS IS" WITHOUT WARRANTY OF -ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES -OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE ENTIRE RISK AS TO THE QUALITY -AND PERFORMANCE OF THE LIBRARY IS WITH YOU. SHOULD THE LIBRARY PROVE DEFECTIVE, YOU -ASSUME THE COST OF ALL NECESSARY SERVICING, REPAIR OR CORRECTION. - -16. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING WILL ANY -COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY AND/OR REDISTRIBUTE THE LIBRARY AS -PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL -OR CONSEQUENTIAL DAMAGES ARISING OUT OF THE USE OR INABILITY TO USE THE LIBRARY -(INCLUDING BUT NOT LIMITED TO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR LOSSES -SUSTAINED BY YOU OR THIRD PARTIES OR A FAILURE OF THE LIBRARY TO OPERATE WITH ANY OTHER -SOFTWARE), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH -DAMAGES. - -END OF TERMS AND CONDITIONS - -How to Apply These Terms to Your New Libraries - -If you develop a new library, and you want it to be of the greatest possible use to the public, we -recommend making it free software that everyone can redistribute and change. You can do so by -permitting redistribution under these terms (or, alternatively, under the terms of the ordinary General -Public License). - -To apply these terms, attach the following notices to the library. It is safest to attach them to the start -of each source file to most effectively convey the exclusion of warranty; and each file should have at -least the "copyright" line and a pointer to where the full notice is found. - -one line to give the library's name and an idea of what it does. -Copyright (C) year name of author - -This library is free software; you can redistribute it and/or -modify it under the terms of the GNU Lesser General Public -License as published by the Free Software Foundation; either -version 2.1 of the License, or (at your option) any later version. - -This library is distributed in the hope that it will be useful, -but WITHOUT ANY WARRANTY; without even the implied warranty of -MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU -Lesser General Public License for more details. - -You should have received a copy of the GNU Lesser General Public -License along with this library; if not, write to the Free Software -Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA -Also add information on how to contact you by electronic and paper mail. - -You should also get your employer (if you work as a programmer) or your school, if any, to sign a -"copyright disclaimer" for the library, if necessary. Here is a sample; alter the names: - -Yoyodyne, Inc., hereby disclaims all copyright interest in -the library `Frob' (a library for tweaking knobs) written -by James Random Hacker. - -signature of Ty Coon, 1 April 1990 -Ty Coon, President of Vice -That's all there is to it! -*************************************************************************** -%%The following software may be included in this product: -Boo -Use of any of this software is governed by the terms of the license below: -Copyright (c) 2003, 2004, Rodrigo B. de Oliveira (rbo@acm.org) -All rights reserved. - -Redistribution and use in source and binary forms, with or without modification, -are permitted provided that the following conditions are met: - -* Redistributions of source code must retain the above copyright notice, -this list of conditions and the following disclaimer. -* Redistributions in binary form must reproduce the above copyright notice, -this list of conditions and the following disclaimer in the documentation -and/or other materials provided with the distribution. -* Neither the name of Rodrigo B. de Oliveira nor the names of its -contributors may be used to endorse or promote products derived from this -software without specific prior written permission. - -THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND -ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED -WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE -DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE -FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL -DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR -SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER -CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, -OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF -THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. -*************************************************************************** - From 9ac9a428956bd0ac81456b1a3f65d10024788338 Mon Sep 17 00:00:00 2001 From: kyarbro Date: Thu, 12 Feb 2009 18:27:52 +0000 Subject: [PATCH 190/342] License notices --- DotNetCommonBuild.Targets | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/DotNetCommonBuild.Targets b/DotNetCommonBuild.Targets index 9f609826..e90da7e4 100644 --- a/DotNetCommonBuild.Targets +++ b/DotNetCommonBuild.Targets @@ -53,11 +53,11 @@ From c7adfa4f238c19910a9174122e6744941a2e9f1b Mon Sep 17 00:00:00 2001 From: kyarbro Date: Thu, 12 Feb 2009 20:34:09 +0000 Subject: [PATCH 191/342] License files --- DotNetCommonBuild.Targets | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/DotNetCommonBuild.Targets b/DotNetCommonBuild.Targets index e90da7e4..ae09b816 100644 --- a/DotNetCommonBuild.Targets +++ b/DotNetCommonBuild.Targets @@ -53,11 +53,11 @@ From 31e4f750c04503576184e92ca408d9c286fc1244 Mon Sep 17 00:00:00 2001 From: kyarbro Date: Thu, 12 Feb 2009 21:07:37 +0000 Subject: [PATCH 192/342] License files --- DotNetCommonBuild.Targets | 4 +- THIRDPARTYREADME.txt | 3591 +++++++++++++++++++++++++++++++++++++ license.txt | 383 ++++ 3 files changed, 3976 insertions(+), 2 deletions(-) create mode 100644 THIRDPARTYREADME.txt create mode 100644 license.txt diff --git a/DotNetCommonBuild.Targets b/DotNetCommonBuild.Targets index ae09b816..a55ef0e4 100644 --- a/DotNetCommonBuild.Targets +++ b/DotNetCommonBuild.Targets @@ -53,11 +53,11 @@ diff --git a/THIRDPARTYREADME.txt b/THIRDPARTYREADME.txt new file mode 100644 index 00000000..797aa123 --- /dev/null +++ b/THIRDPARTYREADME.txt @@ -0,0 +1,3591 @@ +DO NOT TRANSLATE OR LOCALIZE +*************************************************************************** +%%The following software may be included in this product: +Groovy Scripting Language +Use of any of this software is governed by the terms of the license below: +Apache License +Version 2.0, January 2004 +http://www.apache.org/licenses/ + +TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION + +1. Definitions. + +"License" shall mean the terms and conditions for use, reproduction, and +distribution as defined by Sections 1 through 9 of this document. + +"Licensor" shall mean the copyright owner or entity authorized by the copyright +owner that is granting the License. + +"Legal Entity" shall mean the union of the acting entity and all other entities +that control, are controlled by, or are under common control with that entity. +For the purposes of this definition, "control" means (i) the power, direct or +indirect, to cause the direction or management of such entity, whether by +contract or otherwise, or (ii) ownership of fifty percent (50%) or more of the +outstanding shares, or (iii) beneficial ownership of such entity. + +"You" (or "Your") shall mean an individual or Legal Entity exercising +permissions granted by this License. + +"Source" form shall mean the preferred form for making modifications, including +but not limited to software source code, documentation source, and configuration +files. + +"Object" form shall mean any form resulting from mechanical transformation or +translation of a Source form, including but not limited to compiled object code, +generated documentation, and conversions to other media types. + +"Work" shall mean the work of authorship, whether in Source or Object form, made +available under the License, as indicated by a copyright notice that is included +in or attached to the work (an example is provided in the Appendix below). + +"Derivative Works" shall mean any work, whether in Source or Object form, that +is based on (or derived from) the Work and for which the editorial revisions, +annotations, elaborations, or other modifications represent, as a whole, an +original work of authorship. For the purposes of this License, Derivative Works +shall not include works that remain separable from, or merely link (or bind by +name) to the interfaces of, the Work and Derivative Works thereof. + +"Contribution" shall mean any work of authorship, including the original version +of the Work and any modifications or additions to that Work or Derivative Works +thereof, that is intentionally submitted to Licensor for inclusion in the Work +by the copyright owner or by an individual or Legal Entity authorized to submit +on behalf of the copyright owner. For the purposes of this definition, +"submitted" means any form of electronic, verbal, or written communication sent +to the Licensor or its representatives, including but not limited to +communication on electronic mailing lists, source code control systems, and +issue tracking systems that are managed by, or on behalf of, the Licensor for +the purpose of discussing and improving the Work, but excluding communication +that is conspicuously marked or otherwise designated in writing by the copyright +owner as "Not a Contribution." + +"Contributor" shall mean Licensor and any individual or Legal Entity on behalf +of whom a Contribution has been received by Licensor and subsequently +incorporated within the Work. + +2. Grant of Copyright License. Subject to the terms and conditions of this +License, each Contributor hereby grants to You a perpetual, worldwide, +non-exclusive, no-charge, royalty-free, irrevocable copyright license to +reproduce, prepare Derivative Works of, publicly display, publicly perform, +sublicense, and distribute the Work and such Derivative Works in Source or +Object form. + +3. Grant of Patent License. Subject to the terms and conditions of this License, +each Contributor hereby grants to You a perpetual, worldwide, non-exclusive, +no-charge, royalty-free, irrevocable (except as stated in this section) patent +license to make, have made, use, offer to sell, sell, import, and otherwise +transfer the Work, where such license applies only to those patent claims +licensable by such Contributor that are necessarily infringed by their +Contribution(s) alone or by combination of their Contribution(s) with the Work +to which such Contribution(s) was submitted. If You institute patent litigation +against any entity (including a cross-claim or counterclaim in a lawsuit) +alleging that the Work or a Contribution incorporated within the Work +constitutes direct or contributory patent infringement, then any patent licenses +granted to You under this License for that Work shall terminate as of the date +such litigation is filed. + +4. Redistribution. You may reproduce and distribute copies of the Work or +Derivative Works thereof in any medium, with or without modifications, and in +Source or Object form, provided that You meet the following conditions: + +1. You must give any other recipients of the Work or Derivative Works a copy +of this License; and + +2. You must cause any modified files to carry prominent notices stating that +You changed the files; and + +3. You must retain, in the Source form of any Derivative Works that You +distribute, all copyright, patent, trademark, and attribution notices from the +Source form of the Work, excluding those notices that do not pertain to any part +of the Derivative Works; and + +4. If the Work includes a "NOTICE" text file as part of its distribution, +then any Derivative Works that You distribute must include a readable copy of +the attribution notices contained within such NOTICE file, excluding those +notices that do not pertain to any part of the Derivative Works, in at least one +of the following places: within a NOTICE text file distributed as part of the +Derivative Works; within the Source form or documentation, if provided along +with the Derivative Works; or, within a display generated by the Derivative +Works, if and wherever such third-party notices normally appear. The contents of +the NOTICE file are for informational purposes only and do not modify the +License. You may add Your own attribution notices within Derivative Works that +You distribute, alongside or as an addendum to the NOTICE text from the Work, +provided that such additional attribution notices cannot be construed as +modifying the License. + +You may add Your own copyright statement to Your modifications and may provide +additional or different license terms and conditions for use, reproduction, or +distribution of Your modifications, or for any such Derivative Works as a whole, +provided Your use, reproduction, and distribution of the Work otherwise complies +with the conditions stated in this License. + +5. Submission of Contributions. Unless You explicitly state otherwise, any +Contribution intentionally submitted for inclusion in the Work by You to the +Licensor shall be under the terms and conditions of this License, without any +additional terms or conditions. Notwithstanding the above, nothing herein shall +supersede or modify the terms of any separate license agreement you may have +executed with Licensor regarding such Contributions. + +6. Trademarks. This License does not grant permission to use the trade names, +trademarks, service marks, or product names of the Licensor, except as required +for reasonable and customary use in describing the origin of the Work and +reproducing the content of the NOTICE file. + +7. Disclaimer of Warranty. Unless required by applicable law or agreed to in +writing, Licensor provides the Work (and each Contributor provides its +Contributions) on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +KIND, either express or implied, including, without limitation, any warranties +or conditions of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A +PARTICULAR PURPOSE. You are solely responsible for determining the +appropriateness of using or redistributing the Work and assume any risks +associated with Your exercise of permissions under this License. + +8. Limitation of Liability. In no event and under no legal theory, whether in +tort (including negligence), contract, or otherwise, unless required by +applicable law (such as deliberate and grossly negligent acts) or agreed to in +writing, shall any Contributor be liable to You for damages, including any +direct, indirect, special, incidental, or consequential damages of any character +arising as a result of this License or out of the use or inability to use the +Work (including but not limited to damages for loss of goodwill, work stoppage, +computer failure or malfunction, or any and all other commercial damages or +losses), even if such Contributor has been advised of the possibility of such +damages. + +9. Accepting Warranty or Additional Liability. While redistributing the Work or +Derivative Works thereof, You may choose to offer, and charge a fee for, +acceptance of support, warranty, indemnity, or other liability obligations +and/or rights consistent with this License. However, in accepting such +obligations, You may act only on Your own behalf and on Your sole +responsibility, not on behalf of any other Contributor, and only if You agree to +indemnify, defend, and hold each Contributor harmless for any liability incurred +by, or claims asserted against, such Contributor by reason of your accepting any +such warranty or additional liability. + +END OF TERMS AND CONDITIONS +APPENDIX: How to apply the Apache License to your work + +To apply the Apache License to your work, attach the following boilerplate +notice, with the fields enclosed by brackets "[]" replaced with your own +identifying information. (Don't include the brackets!) The text should be +enclosed in the appropriate comment syntax for the file format. We also +recommend that a file or class name and description of purpose be included on +the same "printed page" as the copyright notice for easier identification within +third-party archives. +Copyright [yyyy] [name of copyright owner] 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. +*************************************************************************** +%%The following software may be included in this product: +Apache Commons Pool +Use of any of this software is governed by the terms of the license below: +Apache License +Version 2.0, January 2004 +http://www.apache.org/licenses/ + +TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION + +1. Definitions. + +"License" shall mean the terms and conditions for use, reproduction, +and distribution as defined by Sections 1 through 9 of this document. + +"Licensor" shall mean the copyright owner or entity authorized by +the copyright owner that is granting the License. + +"Legal Entity" shall mean the union of the acting entity and all +other entities that control, are controlled by, or are under common +control with that entity. For the purposes of this definition, +"control" means (i) the power, direct or indirect, to cause the +direction or management of such entity, whether by contract or +otherwise, or (ii) ownership of fifty percent (50%) or more of the +outstanding shares, or (iii) beneficial ownership of such entity. + +"You" (or "Your") shall mean an individual or Legal Entity +exercising permissions granted by this License. + +"Source" form shall mean the preferred form for making modifications, +including but not limited to software source code, documentation +source, and configuration files. + +"Object" form shall mean any form resulting from mechanical +transformation or translation of a Source form, including but +not limited to compiled object code, generated documentation, +and conversions to other media types. + +"Work" shall mean the work of authorship, whether in Source or +Object form, made available under the License, as indicated by a +copyright notice that is included in or attached to the work +(an example is provided in the Appendix below). + +"Derivative Works" shall mean any work, whether in Source or Object +form, that is based on (or derived from) the Work and for which the +editorial revisions, annotations, elaborations, or other modifications +represent, as a whole, an original work of authorship. For the purposes +of this License, Derivative Works shall not include works that remain +separable from, or merely link (or bind by name) to the interfaces of, +the Work and Derivative Works thereof. + +"Contribution" shall mean any work of authorship, including +the original version of the Work and any modifications or additions +to that Work or Derivative Works thereof, that is intentionally +submitted to Licensor for inclusion in the Work by the copyright owner +or by an individual or Legal Entity authorized to submit on behalf of +the copyright owner. For the purposes of this definition, "submitted" +means any form of electronic, verbal, or written communication sent +to the Licensor or its representatives, including but not limited to +communication on electronic mailing lists, source code control systems, +and issue tracking systems that are managed by, or on behalf of, the +Licensor for the purpose of discussing and improving the Work, but +excluding communication that is conspicuously marked or otherwise +designated in writing by the copyright owner as "Not a Contribution." + +"Contributor" shall mean Licensor and any individual or Legal Entity +on behalf of whom a Contribution has been received by Licensor and +subsequently incorporated within the Work. + +2. Grant of Copyright License. Subject to the terms and conditions of +this License, each Contributor hereby grants to You a perpetual, +worldwide, non-exclusive, no-charge, royalty-free, irrevocable +copyright license to reproduce, prepare Derivative Works of, +publicly display, publicly perform, sublicense, and distribute the +Work and such Derivative Works in Source or Object form. + +3. Grant of Patent License. Subject to the terms and conditions of +this License, each Contributor hereby grants to You a perpetual, +worldwide, non-exclusive, no-charge, royalty-free, irrevocable +(except as stated in this section) patent license to make, have made, +use, offer to sell, sell, import, and otherwise transfer the Work, +where such license applies only to those patent claims licensable +by such Contributor that are necessarily infringed by their +Contribution(s) alone or by combination of their Contribution(s) +with the Work to which such Contribution(s) was submitted. If You +institute patent litigation against any entity (including a +cross-claim or counterclaim in a lawsuit) alleging that the Work +or a Contribution incorporated within the Work constitutes direct +or contributory patent infringement, then any patent licenses +granted to You under this License for that Work shall terminate +as of the date such litigation is filed. + +4. Redistribution. You may reproduce and distribute copies of the +Work or Derivative Works thereof in any medium, with or without +modifications, and in Source or Object form, provided that You +meet the following conditions: + +(a) You must give any other recipients of the Work or +Derivative Works a copy of this License; and + +(b) You must cause any modified files to carry prominent notices +stating that You changed the files; and + +(c) You must retain, in the Source form of any Derivative Works +that You distribute, all copyright, patent, trademark, and +attribution notices from the Source form of the Work, +excluding those notices that do not pertain to any part of +the Derivative Works; and + +(d) If the Work includes a "NOTICE" text file as part of its +distribution, then any Derivative Works that You distribute must +include a readable copy of the attribution notices contained +within such NOTICE file, excluding those notices that do not +pertain to any part of the Derivative Works, in at least one +of the following places: within a NOTICE text file distributed +as part of the Derivative Works; within the Source form or +documentation, if provided along with the Derivative Works; or, +within a display generated by the Derivative Works, if and +wherever such third-party notices normally appear. The contents +of the NOTICE file are for informational purposes only and +do not modify the License. You may add Your own attribution +notices within Derivative Works that You distribute, alongside +or as an addendum to the NOTICE text from the Work, provided +that such additional attribution notices cannot be construed +as modifying the License. + +You may add Your own copyright statement to Your modifications and +may provide additional or different license terms and conditions +for use, reproduction, or distribution of Your modifications, or +for any such Derivative Works as a whole, provided Your use, +reproduction, and distribution of the Work otherwise complies with +the conditions stated in this License. + +5. Submission of Contributions. Unless You explicitly state otherwise, +any Contribution intentionally submitted for inclusion in the Work +by You to the Licensor shall be under the terms and conditions of +this License, without any additional terms or conditions. +Notwithstanding the above, nothing herein shall supersede or modify +the terms of any separate license agreement you may have executed +with Licensor regarding such Contributions. + +6. Trademarks. This License does not grant permission to use the trade +names, trademarks, service marks, or product names of the Licensor, +except as required for reasonable and customary use in describing the +origin of the Work and reproducing the content of the NOTICE file. + +7. Disclaimer of Warranty. Unless required by applicable law or +agreed to in writing, Licensor provides the Work (and each +Contributor provides its Contributions) on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or +implied, including, without limitation, any warranties or conditions +of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A +PARTICULAR PURPOSE. You are solely responsible for determining the +appropriateness of using or redistributing the Work and assume any +risks associated with Your exercise of permissions under this License. + +8. Limitation of Liability. In no event and under no legal theory, +whether in tort (including negligence), contract, or otherwise, +unless required by applicable law (such as deliberate and grossly +negligent acts) or agreed to in writing, shall any Contributor be +liable to You for damages, including any direct, indirect, special, +incidental, or consequential damages of any character arising as a +result of this License or out of the use or inability to use the +Work (including but not limited to damages for loss of goodwill, +work stoppage, computer failure or malfunction, or any and all +other commercial damages or losses), even if such Contributor +has been advised of the possibility of such damages. + +9. Accepting Warranty or Additional Liability. While redistributing +the Work or Derivative Works thereof, You may choose to offer, +and charge a fee for, acceptance of support, warranty, indemnity, +or other liability obligations and/or rights consistent with this +License. However, in accepting such obligations, You may act only +on Your own behalf and on Your sole responsibility, not on behalf +of any other Contributor, and only if You agree to indemnify, +defend, and hold each Contributor harmless for any liability +incurred by, or claims asserted against, such Contributor by reason +of your accepting any such warranty or additional liability. + +END OF TERMS AND CONDITIONS + +APPENDIX: How to apply the Apache License to your work. + +To apply the Apache License to your work, attach the following +boilerplate notice, with the fields enclosed by brackets "[]" +replaced with your own identifying information. (Don't include +the brackets!) The text should be enclosed in the appropriate +comment syntax for the file format. We also recommend that a +file or class name and description of purpose be included on the +same "printed page" as the copyright notice for easier +identification within third-party archives. + +Copyright [yyyy] [name of copyright owner] + +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. +*************************************************************************** +%%The following software may be included in this product: +HSQL Database Engine +Use of any of this software is governed by the terms of the license below: +COPYRIGHTS AND LICENSES + +ORIGINAL LICENSE (a.k.a. "hypersonic_lic.txt") + +For content, code, and products originally developed by Thomas Mueller and the +Hypersonic SQL Group: + +Copyright (c) 1995-2000 by the Hypersonic SQL Group. +All rights reserved. + + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are met: + +Redistributions of source code must retain the above copyright notice, this +list of conditions and the following disclaimer. + +Redistributions in binary form must reproduce the above copyright notice, +this list of conditions and the following disclaimer in the documentation +and/or other materials provided with the distribution. + +Neither the name of the Hypersonic SQL Group nor the names of its +contributors may be used to endorse or promote products derived from this +software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" +AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE +ARE DISCLAIMED. IN NO EVENT SHALL THE HYPERSONIC SQL GROUP, +OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; +LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND +ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS +SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +This software consists of voluntary contributions made by many individuals on +behalf of the +Hypersonic SQL Group. + + + +For work added by the HSQL Development Group (a.k.a. hsqldb_lic.txt): +Copyright (c) 2001-2005, The HSQL Development Group +All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are met: + +Redistributions of source code must retain the above copyright notice, this +list of conditions and the following disclaimer. + +Redistributions in binary form must reproduce the above copyright notice, +this list of conditions and the following disclaimer in the documentation +and/or other materials provided with the distribution. + +Neither the name of the HSQL Development Group nor the names of its +contributors may be used to endorse or promote products derived from this +software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" +AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE +ARE DISCLAIMED. IN NO EVENT SHALL HSQL DEVELOPMENT GROUP, HSQLDB.ORG, +OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; +LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND +ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS +SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +*************************************************************************** +%%The following software may be included in this product: +Derby +Use of any of this software is governed by the terms of the license below: +Apache License, Version 2.0 + +Apache License +Version 2.0, January 2004 +http://www.apache.org/licenses/ + +TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION + +1. Definitions. + +"License" shall mean the terms and conditions for use, reproduction, and +distribution as defined by Sections 1 through 9 of this document. + +"Licensor" shall mean the copyright owner or entity authorized by the copyright +owner that is granting the License. + +"Legal Entity" shall mean the union of the acting entity and all other entities +that control, are controlled by, or are under common control with that entity. +For the purposes of this definition, "control" means (i) the power, direct or +indirect, to cause the direction or management of such entity, whether by +contract or otherwise, or (ii) ownership of fifty percent (50%) or more of the +outstanding shares, or (iii) beneficial ownership of such entity. + +"You" (or "Your") shall mean an individual or Legal Entity exercising +permissions granted by this License. + +"Source" form shall mean the preferred form for making modifications, including +but not limited to software source code, documentation source, and configuration +files. + +"Object" form shall mean any form resulting from mechanical transformation or +translation of a Source form, including but not limited to compiled object code, +generated documentation, and conversions to other media types. + +"Work" shall mean the work of authorship, whether in Source or Object form, made +available under the License, as indicated by a copyright notice that is included +in or attached to the work (an example is provided in the Appendix below). + +"Derivative Works" shall mean any work, whether in Source or Object form, that +is based on (or derived from) the Work and for which the editorial revisions, +annotations, elaborations, or other modifications represent, as a whole, an +original work of authorship. For the purposes of this License, Derivative Works +shall not include works that remain separable from, or merely link (or bind by +name) to the interfaces of, the Work and Derivative Works thereof. + +"Contribution" shall mean any work of authorship, including the original version +of the Work and any modifications or additions to that Work or Derivative Works +thereof, that is intentionally submitted to Licensor for inclusion in the Work +by the copyright owner or by an individual or Legal Entity authorized to submit +on behalf of the copyright owner. For the purposes of this definition, +"submitted" means any form of electronic, verbal, or written communication sent +to the Licensor or its representatives, including but not limited to +communication on electronic mailing lists, source code control systems, and +issue tracking systems that are managed by, or on behalf of, the Licensor for +the purpose of discussing and improving the Work, but excluding communication +that is conspicuously marked or otherwise designated in writing by the copyright +owner as "Not a Contribution." + +"Contributor" shall mean Licensor and any individual or Legal Entity on behalf +of whom a Contribution has been received by Licensor and subsequently +incorporated within the Work. + +2. Grant of Copyright License. Subject to the terms and conditions of this +License, each Contributor hereby grants to You a perpetual, worldwide, +non-exclusive, no-charge, royalty-free, irrevocable copyright license to +reproduce, prepare Derivative Works of, publicly display, publicly perform, +sublicense, and distribute the Work and such Derivative Works in Source or +Object form. + +3. Grant of Patent License. Subject to the terms and conditions of this License, +each Contributor hereby grants to You a perpetual, worldwide, non-exclusive, +no-charge, royalty-free, irrevocable (except as stated in this section) patent +license to make, have made, use, offer to sell, sell, import, and otherwise +transfer the Work, where such license applies only to those patent claims +licensable by such Contributor that are necessarily infringed by their +Contribution(s) alone or by combination of their Contribution(s) with the Work +to which such Contribution(s) was submitted. If You institute patent litigation +against any entity (including a cross-claim or counterclaim in a lawsuit) +alleging that the Work or a Contribution incorporated within the Work +constitutes direct or contributory patent infringement, then any patent licenses +granted to You under this License for that Work shall terminate as of the date +such litigation is filed. + +4. Redistribution. You may reproduce and distribute copies of the Work or +Derivative Works thereof in any medium, with or without modifications, and in +Source or Object form, provided that You meet the following conditions: + +1. You must give any other recipients of the Work or Derivative Works a copy +of this License; and + +2. You must cause any modified files to carry prominent notices stating that +You changed the files; and + +3. You must retain, in the Source form of any Derivative Works that You +distribute, all copyright, patent, trademark, and attribution notices from the +Source form of the Work, excluding those notices that do not pertain to any part +of the Derivative Works; and + +4. If the Work includes a "NOTICE" text file as part of its distribution, +then any Derivative Works that You distribute must include a readable copy of +the attribution notices contained within such NOTICE file, excluding those +notices that do not pertain to any part of the Derivative Works, in at least one +of the following places: within a NOTICE text file distributed as part of the +Derivative Works; within the Source form or documentation, if provided along +with the Derivative Works; or, within a display generated by the Derivative +Works, if and wherever such third-party notices normally appear. The contents of +the NOTICE file are for informational purposes only and do not modify the +License. You may add Your own attribution notices within Derivative Works that +You distribute, alongside or as an addendum to the NOTICE text from the Work, +provided that such additional attribution notices cannot be construed as +modifying the License. + +You may add Your own copyright statement to Your modifications and may provide +additional or different license terms and conditions for use, reproduction, or +distribution of Your modifications, or for any such Derivative Works as a whole, +provided Your use, reproduction, and distribution of the Work otherwise complies +with the conditions stated in this License. + +5. Submission of Contributions. Unless You explicitly state otherwise, any +Contribution intentionally submitted for inclusion in the Work by You to the +Licensor shall be under the terms and conditions of this License, without any +additional terms or conditions. Notwithstanding the above, nothing herein shall +supersede or modify the terms of any separate license agreement you may have +executed with Licensor regarding such Contributions. + +6. Trademarks. This License does not grant permission to use the trade names, +trademarks, service marks, or product names of the Licensor, except as required +for reasonable and customary use in describing the origin of the Work and +reproducing the content of the NOTICE file. + +7. Disclaimer of Warranty. Unless required by applicable law or agreed to in +writing, Licensor provides the Work (and each Contributor provides its +Contributions) on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +KIND, either express or implied, including, without limitation, any warranties +or conditions of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A +PARTICULAR PURPOSE. You are solely responsible for determining the +appropriateness of using or redistributing the Work and assume any risks +associated with Your exercise of permissions under this License. + +8. Limitation of Liability. In no event and under no legal theory, whether in +tort (including negligence), contract, or otherwise, unless required by +applicable law (such as deliberate and grossly negligent acts) or agreed to in +writing, shall any Contributor be liable to You for damages, including any +direct, indirect, special, incidental, or consequential damages of any character +arising as a result of this License or out of the use or inability to use the +Work (including but not limited to damages for loss of goodwill, work stoppage, +computer failure or malfunction, or any and all other commercial damages or +losses), even if such Contributor has been advised of the possibility of such +damages. + +9. Accepting Warranty or Additional Liability. While redistributing the Work or +Derivative Works thereof, You may choose to offer, and charge a fee for, +acceptance of support, warranty, indemnity, or other liability obligations +and/or rights consistent with this License. However, in accepting such +obligations, You may act only on Your own behalf and on Your sole +responsibility, not on behalf of any other Contributor, and only if You agree to +indemnify, defend, and hold each Contributor harmless for any liability incurred +by, or claims asserted against, such Contributor by reason of your accepting any +such warranty or additional liability. + +END OF TERMS AND CONDITIONS +APPENDIX: How to apply the Apache License to your work + +To apply the Apache License to your work, attach the following boilerplate +notice, with the fields enclosed by brackets "[]" replaced with your own +identifying information. (Don't include the brackets!) The text should be +enclosed in the appropriate comment syntax for the file format. We also +recommend that a file or class name and description of purpose be included on +the same "printed page" as the copyright notice for easier identification within +third-party archives. +Copyright [yyyy] [name of copyright owner] 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. +*************************************************************************** +%%The following software may be included in this product: +Apache Commons - commons-net-1.4.1.jar +Use of any of this software is governed by the terms of the license below: +Apache License +Version 2.0, January 2004 +http://www.apache.org/licenses/ + +TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION + +1. Definitions. + +"License" shall mean the terms and conditions for use, reproduction, +and distribution as defined by Sections 1 through 9 of this document. + +"Licensor" shall mean the copyright owner or entity authorized by +the copyright owner that is granting the License. + +"Legal Entity" shall mean the union of the acting entity and all +other entities that control, are controlled by, or are under common +control with that entity. For the purposes of this definition, +"control" means (i) the power, direct or indirect, to cause the +direction or management of such entity, whether by contract or +otherwise, or (ii) ownership of fifty percent (50%) or more of the +outstanding shares, or (iii) beneficial ownership of such entity. + +"You" (or "Your") shall mean an individual or Legal Entity +exercising permissions granted by this License. + +"Source" form shall mean the preferred form for making modifications, +including but not limited to software source code, documentation +source, and configuration files. + +"Object" form shall mean any form resulting from mechanical +transformation or translation of a Source form, including but +not limited to compiled object code, generated documentation, +and conversions to other media types. + +"Work" shall mean the work of authorship, whether in Source or +Object form, made available under the License, as indicated by a +copyright notice that is included in or attached to the work +(an example is provided in the Appendix below). + +"Derivative Works" shall mean any work, whether in Source or Object +form, that is based on (or derived from) the Work and for which the +editorial revisions, annotations, elaborations, or other modifications +represent, as a whole, an original work of authorship. For the purposes +of this License, Derivative Works shall not include works that remain +separable from, or merely link (or bind by name) to the interfaces of, +the Work and Derivative Works thereof. + +"Contribution" shall mean any work of authorship, including +the original version of the Work and any modifications or additions +to that Work or Derivative Works thereof, that is intentionally +submitted to Licensor for inclusion in the Work by the copyright owner +or by an individual or Legal Entity authorized to submit on behalf of +the copyright owner. For the purposes of this definition, "submitted" +means any form of electronic, verbal, or written communication sent +to the Licensor or its representatives, including but not limited to +communication on electronic mailing lists, source code control systems, +and issue tracking systems that are managed by, or on behalf of, the +Licensor for the purpose of discussing and improving the Work, but +excluding communication that is conspicuously marked or otherwise +designated in writing by the copyright owner as "Not a Contribution." + +"Contributor" shall mean Licensor and any individual or Legal Entity +on behalf of whom a Contribution has been received by Licensor and +subsequently incorporated within the Work. + +2. Grant of Copyright License. Subject to the terms and conditions of +this License, each Contributor hereby grants to You a perpetual, +worldwide, non-exclusive, no-charge, royalty-free, irrevocable +copyright license to reproduce, prepare Derivative Works of, +publicly display, publicly perform, sublicense, and distribute the +Work and such Derivative Works in Source or Object form. + +3. Grant of Patent License. Subject to the terms and conditions of +this License, each Contributor hereby grants to You a perpetual, +worldwide, non-exclusive, no-charge, royalty-free, irrevocable +(except as stated in this section) patent license to make, have made, +use, offer to sell, sell, import, and otherwise transfer the Work, +where such license applies only to those patent claims licensable +by such Contributor that are necessarily infringed by their +Contribution(s) alone or by combination of their Contribution(s) +with the Work to which such Contribution(s) was submitted. If You +institute patent litigation against any entity (including a +cross-claim or counterclaim in a lawsuit) alleging that the Work +or a Contribution incorporated within the Work constitutes direct +or contributory patent infringement, then any patent licenses +granted to You under this License for that Work shall terminate +as of the date such litigation is filed. + +4. Redistribution. You may reproduce and distribute copies of the +Work or Derivative Works thereof in any medium, with or without +modifications, and in Source or Object form, provided that You +meet the following conditions: + +(a) You must give any other recipients of the Work or +Derivative Works a copy of this License; and + +(b) You must cause any modified files to carry prominent notices +stating that You changed the files; and + +(c) You must retain, in the Source form of any Derivative Works +that You distribute, all copyright, patent, trademark, and +attribution notices from the Source form of the Work, +excluding those notices that do not pertain to any part of +the Derivative Works; and + +(d) If the Work includes a "NOTICE" text file as part of its +distribution, then any Derivative Works that You distribute must +include a readable copy of the attribution notices contained +within such NOTICE file, excluding those notices that do not +pertain to any part of the Derivative Works, in at least one +of the following places: within a NOTICE text file distributed +as part of the Derivative Works; within the Source form or +documentation, if provided along with the Derivative Works; or, +within a display generated by the Derivative Works, if and +wherever such third-party notices normally appear. The contents +of the NOTICE file are for informational purposes only and +do not modify the License. You may add Your own attribution +notices within Derivative Works that You distribute, alongside +or as an addendum to the NOTICE text from the Work, provided +that such additional attribution notices cannot be construed +as modifying the License. + +You may add Your own copyright statement to Your modifications and +may provide additional or different license terms and conditions +for use, reproduction, or distribution of Your modifications, or +for any such Derivative Works as a whole, provided Your use, +reproduction, and distribution of the Work otherwise complies with +the conditions stated in this License. + +5. Submission of Contributions. Unless You explicitly state otherwise, +any Contribution intentionally submitted for inclusion in the Work +by You to the Licensor shall be under the terms and conditions of +this License, without any additional terms or conditions. +Notwithstanding the above, nothing herein shall supersede or modify +the terms of any separate license agreement you may have executed +with Licensor regarding such Contributions. + +6. Trademarks. This License does not grant permission to use the trade +names, trademarks, service marks, or product names of the Licensor, +except as required for reasonable and customary use in describing the +origin of the Work and reproducing the content of the NOTICE file. + +7. Disclaimer of Warranty. Unless required by applicable law or +agreed to in writing, Licensor provides the Work (and each +Contributor provides its Contributions) on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or +implied, including, without limitation, any warranties or conditions +of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A +PARTICULAR PURPOSE. You are solely responsible for determining the +appropriateness of using or redistributing the Work and assume any +risks associated with Your exercise of permissions under this License. + +8. Limitation of Liability. In no event and under no legal theory, +whether in tort (including negligence), contract, or otherwise, +unless required by applicable law (such as deliberate and grossly +negligent acts) or agreed to in writing, shall any Contributor be +liable to You for damages, including any direct, indirect, special, +incidental, or consequential damages of any character arising as a +result of this License or out of the use or inability to use the +Work (including but not limited to damages for loss of goodwill, +work stoppage, computer failure or malfunction, or any and all +other commercial damages or losses), even if such Contributor +has been advised of the possibility of such damages. + +9. Accepting Warranty or Additional Liability. While redistributing +the Work or Derivative Works thereof, You may choose to offer, +and charge a fee for, acceptance of support, warranty, indemnity, +or other liability obligations and/or rights consistent with this +License. However, in accepting such obligations, You may act only +on Your own behalf and on Your sole responsibility, not on behalf +of any other Contributor, and only if You agree to indemnify, +defend, and hold each Contributor harmless for any liability +incurred by, or claims asserted against, such Contributor by reason +of your accepting any such warranty or additional liability. + +END OF TERMS AND CONDITIONS + +APPENDIX: How to apply the Apache License to your work. + +To apply the Apache License to your work, attach the following +boilerplate notice, with the fields enclosed by brackets "[]" +replaced with your own identifying information. (Don't include +the brackets!) The text should be enclosed in the appropriate +comment syntax for the file format. We also recommend that a +file or class name and description of purpose be included on the +same "printed page" as the copyright notice for easier +identification within third-party archives. + +Copyright [yyyy] [name of copyright owner] + +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. +*************************************************************************** +%%The following software may be included in this product: +Jakarta Oro +Use of any of this software is governed by the terms of the license below: +Apache License +Version 2.0, January 2004 +http://www.apache.org/licenses/ + +TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION + +1. Definitions. + +"License" shall mean the terms and conditions for use, reproduction, +and distribution as defined by Sections 1 through 9 of this document. + +"Licensor" shall mean the copyright owner or entity authorized by +the copyright owner that is granting the License. + +"Legal Entity" shall mean the union of the acting entity and all +other entities that control, are controlled by, or are under common +control with that entity. For the purposes of this definition, +"control" means (i) the power, direct or indirect, to cause the +direction or management of such entity, whether by contract or +otherwise, or (ii) ownership of fifty percent (50%) or more of the +outstanding shares, or (iii) beneficial ownership of such entity. + +"You" (or "Your") shall mean an individual or Legal Entity +exercising permissions granted by this License. + +"Source" form shall mean the preferred form for making modifications, +including but not limited to software source code, documentation +source, and configuration files. + +"Object" form shall mean any form resulting from mechanical +transformation or translation of a Source form, including but +not limited to compiled object code, generated documentation, +and conversions to other media types. + +"Work" shall mean the work of authorship, whether in Source or +Object form, made available under the License, as indicated by a +copyright notice that is included in or attached to the work +(an example is provided in the Appendix below). + +"Derivative Works" shall mean any work, whether in Source or Object +form, that is based on (or derived from) the Work and for which the +editorial revisions, annotations, elaborations, or other modifications +represent, as a whole, an original work of authorship. For the purposes +of this License, Derivative Works shall not include works that remain +separable from, or merely link (or bind by name) to the interfaces of, +the Work and Derivative Works thereof. + +"Contribution" shall mean any work of authorship, including +the original version of the Work and any modifications or additions +to that Work or Derivative Works thereof, that is intentionally +submitted to Licensor for inclusion in the Work by the copyright owner +or by an individual or Legal Entity authorized to submit on behalf of +the copyright owner. For the purposes of this definition, "submitted" +means any form of electronic, verbal, or written communication sent +to the Licensor or its representatives, including but not limited to +communication on electronic mailing lists, source code control systems, +and issue tracking systems that are managed by, or on behalf of, the +Licensor for the purpose of discussing and improving the Work, but +excluding communication that is conspicuously marked or otherwise +designated in writing by the copyright owner as "Not a Contribution." + +"Contributor" shall mean Licensor and any individual or Legal Entity +on behalf of whom a Contribution has been received by Licensor and +subsequently incorporated within the Work. + +2. Grant of Copyright License. Subject to the terms and conditions of +this License, each Contributor hereby grants to You a perpetual, +worldwide, non-exclusive, no-charge, royalty-free, irrevocable +copyright license to reproduce, prepare Derivative Works of, +publicly display, publicly perform, sublicense, and distribute the +Work and such Derivative Works in Source or Object form. + +3. Grant of Patent License. Subject to the terms and conditions of +this License, each Contributor hereby grants to You a perpetual, +worldwide, non-exclusive, no-charge, royalty-free, irrevocable +(except as stated in this section) patent license to make, have made, +use, offer to sell, sell, import, and otherwise transfer the Work, +where such license applies only to those patent claims licensable +by such Contributor that are necessarily infringed by their +Contribution(s) alone or by combination of their Contribution(s) +with the Work to which such Contribution(s) was submitted. If You +institute patent litigation against any entity (including a +cross-claim or counterclaim in a lawsuit) alleging that the Work +or a Contribution incorporated within the Work constitutes direct +or contributory patent infringement, then any patent licenses +granted to You under this License for that Work shall terminate +as of the date such litigation is filed. + +4. Redistribution. You may reproduce and distribute copies of the +Work or Derivative Works thereof in any medium, with or without +modifications, and in Source or Object form, provided that You +meet the following conditions: + +(a) You must give any other recipients of the Work or +Derivative Works a copy of this License; and + +(b) You must cause any modified files to carry prominent notices +stating that You changed the files; and + +(c) You must retain, in the Source form of any Derivative Works +that You distribute, all copyright, patent, trademark, and +attribution notices from the Source form of the Work, +excluding those notices that do not pertain to any part of +the Derivative Works; and + +(d) If the Work includes a "NOTICE" text file as part of its +distribution, then any Derivative Works that You distribute must +include a readable copy of the attribution notices contained +within such NOTICE file, excluding those notices that do not +pertain to any part of the Derivative Works, in at least one +of the following places: within a NOTICE text file distributed +as part of the Derivative Works; within the Source form or +documentation, if provided along with the Derivative Works; or, +within a display generated by the Derivative Works, if and +wherever such third-party notices normally appear. The contents +of the NOTICE file are for informational purposes only and +do not modify the License. You may add Your own attribution +notices within Derivative Works that You distribute, alongside +or as an addendum to the NOTICE text from the Work, provided +that such additional attribution notices cannot be construed +as modifying the License. + +You may add Your own copyright statement to Your modifications and +may provide additional or different license terms and conditions +for use, reproduction, or distribution of Your modifications, or +for any such Derivative Works as a whole, provided Your use, +reproduction, and distribution of the Work otherwise complies with +the conditions stated in this License. + +5. Submission of Contributions. Unless You explicitly state otherwise, +any Contribution intentionally submitted for inclusion in the Work +by You to the Licensor shall be under the terms and conditions of +this License, without any additional terms or conditions. +Notwithstanding the above, nothing herein shall supersede or modify +the terms of any separate license agreement you may have executed +with Licensor regarding such Contributions. + +6. Trademarks. This License does not grant permission to use the trade +names, trademarks, service marks, or product names of the Licensor, +except as required for reasonable and customary use in describing the +origin of the Work and reproducing the content of the NOTICE file. + +7. Disclaimer of Warranty. Unless required by applicable law or +agreed to in writing, Licensor provides the Work (and each +Contributor provides its Contributions) on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or +implied, including, without limitation, any warranties or conditions +of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A +PARTICULAR PURPOSE. You are solely responsible for determining the +appropriateness of using or redistributing the Work and assume any +risks associated with Your exercise of permissions under this License. + +8. Limitation of Liability. In no event and under no legal theory, +whether in tort (including negligence), contract, or otherwise, +unless required by applicable law (such as deliberate and grossly +negligent acts) or agreed to in writing, shall any Contributor be +liable to You for damages, including any direct, indirect, special, +incidental, or consequential damages of any character arising as a +result of this License or out of the use or inability to use the +Work (including but not limited to damages for loss of goodwill, +work stoppage, computer failure or malfunction, or any and all +other commercial damages or losses), even if such Contributor +has been advised of the possibility of such damages. + +9. Accepting Warranty or Additional Liability. While redistributing +the Work or Derivative Works thereof, You may choose to offer, +and charge a fee for, acceptance of support, warranty, indemnity, +or other liability obligations and/or rights consistent with this +License. However, in accepting such obligations, You may act only +on Your own behalf and on Your sole responsibility, not on behalf +of any other Contributor, and only if You agree to indemnify, +defend, and hold each Contributor harmless for any liability +incurred by, or claims asserted against, such Contributor by reason +of your accepting any such warranty or additional liability. + +END OF TERMS AND CONDITIONS + +APPENDIX: How to apply the Apache License to your work. + +To apply the Apache License to your work, attach the following +boilerplate notice, with the fields enclosed by brackets "[]" +replaced with your own identifying information. (Don't include +the brackets!) The text should be enclosed in the appropriate +comment syntax for the file format. We also recommend that a +file or class name and description of purpose be included on the +same "printed page" as the copyright notice for easier +identification within third-party archives. + +Copyright [yyyy] [name of copyright owner] + +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. +*************************************************************************** +%%The following software may be included in this product: +Jsch +Use of any of this software is governed by the terms of the license below: +JSch 0.0.* was released under the GNU LGPL license. Later, we have switched +over to a BSD-style license. + +------------------------------------------------------------------------------ +Copyright (c) 2002,2003,2004,2005,2006,2007,2008 Atsuhiko Yamanaka, JCraft,Inc. +All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are met: + +1. Redistributions of source code must retain the above copyright notice, +this list of conditions and the following disclaimer. + +2. Redistributions in binary form must reproduce the above copyright +notice, this list of conditions and the following disclaimer in +the documentation and/or other materials provided with the distribution. + +3. The names of the authors may not be used to endorse or promote products +derived from this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED OR IMPLIED WARRANTIES, +INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND +FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL JCRAFT, +INC. OR ANY CONTRIBUTORS TO THIS SOFTWARE BE LIABLE FOR ANY DIRECT, INDIRECT, +INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, +OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF +LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING +NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, +EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +Additional License(s) +LICENSE.txt:JSch 0.0.* was released under the GNU LGPL license. +Later, we have switched +LICENSE.txt:over to a BSD-style license. +*************************************************************************** +%%The following software may be included in this product: +Expect4j +Use of any of this software is governed by the terms of the license below: +Apache License, Version 2.0 + +Apache License +Version 2.0, January 2004 +http://www.apache.org/licenses/ + +TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION + +1. Definitions. + +"License" shall mean the terms and conditions for use, reproduction, and +distribution as defined by Sections 1 through 9 of this document. + +"Licensor" shall mean the copyright owner or entity authorized by the copyright +owner that is granting the License. + +"Legal Entity" shall mean the union of the acting entity and all other entities +that control, are controlled by, or are under common control with that entity. +For the purposes of this definition, "control" means (i) the power, direct or +indirect, to cause the direction or management of such entity, whether by +contract or otherwise, or (ii) ownership of fifty percent (50%) or more of the +outstanding shares, or (iii) beneficial ownership of such entity. + +"You" (or "Your") shall mean an individual or Legal Entity exercising +permissions granted by this License. + +"Source" form shall mean the preferred form for making modifications, including +but not limited to software source code, documentation source, and configuration +files. + +"Object" form shall mean any form resulting from mechanical transformation or +translation of a Source form, including but not limited to compiled object code, +generated documentation, and conversions to other media types. + +"Work" shall mean the work of authorship, whether in Source or Object form, made +available under the License, as indicated by a copyright notice that is included +in or attached to the work (an example is provided in the Appendix below). + +"Derivative Works" shall mean any work, whether in Source or Object form, that +is based on (or derived from) the Work and for which the editorial revisions, +annotations, elaborations, or other modifications represent, as a whole, an +original work of authorship. For the purposes of this License, Derivative Works +shall not include works that remain separable from, or merely link (or bind by +name) to the interfaces of, the Work and Derivative Works thereof. + +"Contribution" shall mean any work of authorship, including the original version +of the Work and any modifications or additions to that Work or Derivative Works +thereof, that is intentionally submitted to Licensor for inclusion in the Work +by the copyright owner or by an individual or Legal Entity authorized to submit +on behalf of the copyright owner. For the purposes of this definition, +"submitted" means any form of electronic, verbal, or written communication sent +to the Licensor or its representatives, including but not limited to +communication on electronic mailing lists, source code control systems, and +issue tracking systems that are managed by, or on behalf of, the Licensor for +the purpose of discussing and improving the Work, but excluding communication +that is conspicuously marked or otherwise designated in writing by the copyright +owner as "Not a Contribution." + +"Contributor" shall mean Licensor and any individual or Legal Entity on behalf +of whom a Contribution has been received by Licensor and subsequently +incorporated within the Work. + +2. Grant of Copyright License. Subject to the terms and conditions of this +License, each Contributor hereby grants to You a perpetual, worldwide, +non-exclusive, no-charge, royalty-free, irrevocable copyright license to +reproduce, prepare Derivative Works of, publicly display, publicly perform, +sublicense, and distribute the Work and such Derivative Works in Source or +Object form. + +3. Grant of Patent License. Subject to the terms and conditions of this License, +each Contributor hereby grants to You a perpetual, worldwide, non-exclusive, +no-charge, royalty-free, irrevocable (except as stated in this section) patent +license to make, have made, use, offer to sell, sell, import, and otherwise +transfer the Work, where such license applies only to those patent claims +licensable by such Contributor that are necessarily infringed by their +Contribution(s) alone or by combination of their Contribution(s) with the Work +to which such Contribution(s) was submitted. If You institute patent litigation +against any entity (including a cross-claim or counterclaim in a lawsuit) +alleging that the Work or a Contribution incorporated within the Work +constitutes direct or contributory patent infringement, then any patent licenses +granted to You under this License for that Work shall terminate as of the date +such litigation is filed. + +4. Redistribution. You may reproduce and distribute copies of the Work or +Derivative Works thereof in any medium, with or without modifications, and in +Source or Object form, provided that You meet the following conditions: + +1. You must give any other recipients of the Work or Derivative Works a copy +of this License; and + +2. You must cause any modified files to carry prominent notices stating that +You changed the files; and + +3. You must retain, in the Source form of any Derivative Works that You +distribute, all copyright, patent, trademark, and attribution notices from the +Source form of the Work, excluding those notices that do not pertain to any part +of the Derivative Works; and + +4. If the Work includes a "NOTICE" text file as part of its distribution, +then any Derivative Works that You distribute must include a readable copy of +the attribution notices contained within such NOTICE file, excluding those +notices that do not pertain to any part of the Derivative Works, in at least one +of the following places: within a NOTICE text file distributed as part of the +Derivative Works; within the Source form or documentation, if provided along +with the Derivative Works; or, within a display generated by the Derivative +Works, if and wherever such third-party notices normally appear. The contents of +the NOTICE file are for informational purposes only and do not modify the +License. You may add Your own attribution notices within Derivative Works that +You distribute, alongside or as an addendum to the NOTICE text from the Work, +provided that such additional attribution notices cannot be construed as +modifying the License. + +You may add Your own copyright statement to Your modifications and may provide +additional or different license terms and conditions for use, reproduction, or +distribution of Your modifications, or for any such Derivative Works as a whole, +provided Your use, reproduction, and distribution of the Work otherwise complies +with the conditions stated in this License. + +5. Submission of Contributions. Unless You explicitly state otherwise, any +Contribution intentionally submitted for inclusion in the Work by You to the +Licensor shall be under the terms and conditions of this License, without any +additional terms or conditions. Notwithstanding the above, nothing herein shall +supersede or modify the terms of any separate license agreement you may have +executed with Licensor regarding such Contributions. + +6. Trademarks. This License does not grant permission to use the trade names, +trademarks, service marks, or product names of the Licensor, except as required +for reasonable and customary use in describing the origin of the Work and +reproducing the content of the NOTICE file. + +7. Disclaimer of Warranty. Unless required by applicable law or agreed to in +writing, Licensor provides the Work (and each Contributor provides its +Contributions) on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +KIND, either express or implied, including, without limitation, any warranties +or conditions of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A +PARTICULAR PURPOSE. You are solely responsible for determining the +appropriateness of using or redistributing the Work and assume any risks +associated with Your exercise of permissions under this License. + +8. Limitation of Liability. In no event and under no legal theory, whether in +tort (including negligence), contract, or otherwise, unless required by +applicable law (such as deliberate and grossly negligent acts) or agreed to in +writing, shall any Contributor be liable to You for damages, including any +direct, indirect, special, incidental, or consequential damages of any character +arising as a result of this License or out of the use or inability to use the +Work (including but not limited to damages for loss of goodwill, work stoppage, +computer failure or malfunction, or any and all other commercial damages or +losses), even if such Contributor has been advised of the possibility of such +damages. + +9. Accepting Warranty or Additional Liability. While redistributing the Work or +Derivative Works thereof, You may choose to offer, and charge a fee for, +acceptance of support, warranty, indemnity, or other liability obligations +and/or rights consistent with this License. However, in accepting such +obligations, You may act only on Your own behalf and on Your sole +responsibility, not on behalf of any other Contributor, and only if You agree to +indemnify, defend, and hold each Contributor harmless for any liability incurred +by, or claims asserted against, such Contributor by reason of your accepting any +such warranty or additional liability. + +END OF TERMS AND CONDITIONS +APPENDIX: How to apply the Apache License to your work + +To apply the Apache License to your work, attach the following boilerplate +notice, with the fields enclosed by brackets "[]" replaced with your own +identifying information. (Don't include the brackets!) The text should be +enclosed in the appropriate comment syntax for the file format. We also +recommend that a file or class name and description of purpose be included on +the same "printed page" as the copyright notice for easier identification within +third-party archives. +Copyright [yyyy] [name of copyright owner] 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. +Apache Projects + +* HTTP Server +* ActiveMQ +* Ant +* APR +* Archiva +* Beehive +* Cayenne +* Cocoon +* Commons +* Continuum +* CXF +* DB +* Directory +* Excalibur +* Felix +* Forrest +* Geronimo +* Gump +* Hadoop +* Harmony +* HiveMind +* HttpComponents +* iBATIS +* Incubator +* Jackrabbit +* Jakarta +* James +* Labs +* Lenya +* Logging +* Lucene +* Maven +* Mina +* MyFaces +* ODE +* OFBiz +* OpenEJB +* OpenJPA +* Perl +* POI +* Portals +* Roller +* Santuario +* ServiceMix +* Shale +* SpamAssassin +* STDCXX +* Struts +* Synapse +* Tapestry +* TCL +* Tiles +* Tomcat +* Turbine +* Velocity +* Wicket +* Web Services +* Xalan +* Xerces +* XML +* XMLBeans +* XML Graphics + +Foundation + +* FAQ +* Licenses +* News +* Public Records +* Sponsorship +* Donations +* Thanks +* Contact + +Foundation Projects + +* Conferences +* Infrastructure +* JCP + +How it works + +* Introduction +* Meritocracy +* Structure +* Roles +* Collaboration +* Infrastructure +* Incubator +* Other entities +* Glossary +* Voting + +Get Involved + +* Mailing Lists +* Version Control +* Developer Info + +Download + +* from a mirror + +Related Sites + +* ApacheCon +* Bookstore +* Feathercast +* PlanetApache + +Copyright © 2008 The Apache Software Foundation, Licensed under the Apache +License, Version 2.0. +*************************************************************************** +%%The following software may be included in this product: +FreeHost3270 +Use of any of this software is governed by the terms of the license below: +GNU LESSER GENERAL PUBLIC LICENSE + +Version 2.1, February 1999 + +Copyright (C) 1991, 1999 Free Software Foundation, Inc. +51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA +Everyone is permitted to copy and distribute verbatim copies +of this license document, but changing it is not allowed. + +[This is the first released version of the Lesser GPL. It also counts +as the successor of the GNU Library Public License, version 2, hence +the version number 2.1.] +Preamble + +The licenses for most software are designed to take away your freedom to share and change it. By +contrast, the GNU General Public Licenses are intended to guarantee your freedom to share and change +free software--to make sure the software is free for all its users. + +This license, the Lesser General Public License, applies to some specially designated software +packages--typically libraries--of the Free Software Foundation and other authors who decide to use it. +You can use it too, but we suggest you first think carefully about whether this license or the ordinary +General Public License is the better strategy to use in any particular case, based on the explanations +below. + +When we speak of free software, we are referring to freedom of use, not price. Our General Public +Licenses are designed to make sure that you have the freedom to distribute copies of free software (and +charge for this service if you wish); that you receive source code or can get it if you want it; that you +can change the software and use pieces of it in new free programs; and that you are informed that you +can do these things. + +To protect your rights, we need to make restrictions that forbid distributors to deny you these rights or +to ask you to surrender these rights. These restrictions translate to certain responsibilities for you if +you distribute copies of the library or if you modify it. + +For example, if you distribute copies of the library, whether gratis or for a fee, you must give the +recipients all the rights that we gave you. You must make sure that they, too, receive or can get the +source code. If you link other code with the library, you must provide complete object files to the +recipients, so that they can relink them with the library after making changes to the library and +recompiling it. And you must show them these terms so they know their rights. + +We protect your rights with a two-step method: (1) we copyright the library, and (2) we offer you this +license, which gives you legal permission to copy, distribute and/or modify the library. + +To protect each distributor, we want to make it very clear that there is no warranty for the free library. +Also, if the library is modified by someone else and passed on, the recipients should know that what +they have is not the original version, so that the original author's reputation will not be affected by +problems that might be introduced by others. + +Finally, software patents pose a constant threat to the existence of any free program. We wish to make +sure that a company cannot effectively restrict the users of a free program by obtaining a restrictive +license from a patent holder. Therefore, we insist that any patent license obtained for a version of the +library must be consistent with the full freedom of use specified in this license. + +Most GNU software, including some libraries, is covered by the ordinary GNU General Public License. +This license, the GNU Lesser General Public License, applies to certain designated libraries, and is quite +different from the ordinary General Public License. We use this license for certain libraries in order to +permit linking those libraries into non-free programs. + +When a program is linked with a library, whether statically or using a shared library, the combination of +the two is legally speaking a combined work, a derivative of the original library. The ordinary General +Public License therefore permits such linking only if the entire combination fits its criteria of freedom. +The Lesser General Public License permits more lax criteria for linking other code with the library. + +We call this license the "Lesser" General Public License because it does Less to protect the user's +freedom than the ordinary General Public License. It also provides other free software developers Less +of an advantage over competing non-free programs. These disadvantages are the reason we use the +ordinary General Public License for many libraries. However, the Lesser license provides advantages in +certain special circumstances. + +For example, on rare occasions, there may be a special need to encourage the widest possible use of a +certain library, so that it becomes a de-facto standard. To achieve this, non-free programs must be +allowed to use the library. A more frequent case is that a free library does the same job as widely used +non-free libraries. In this case, there is little to gain by limiting the free library to free software only, so +we use the Lesser General Public License. + +In other cases, permission to use a particular library in non-free programs enables a greater number of +people to use a large body of free software. For example, permission to use the GNU C Library in non- +free programs enables many more people to use the whole GNU operating system, as well as its variant, +the GNU/Linux operating system. + +Although the Lesser General Public License is Less protective of the users' freedom, it does ensure that +the user of a program that is linked with the Library has the freedom and the wherewithal to run that +program using a modified version of the Library. + +The precise terms and conditions for copying, distribution and modification follow. Pay close attention +to the difference between a "work based on the library" and a "work that uses the library". The former +contains code derived from the library, whereas the latter must be combined with the library in order to +run. + +TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION + +0. This License Agreement applies to any software library or other program which contains a notice +placed by the copyright holder or other authorized party saying it may be distributed under the terms +of this Lesser General Public License (also called "this License"). Each licensee is addressed as "you". + +A "library" means a collection of software functions and/or data prepared so as to be conveniently +linked with application programs (which use some of those functions and data) to form executables. + +The "Library", below, refers to any such software library or work which has been distributed under these +terms. A "work based on the Library" means either the Library or any derivative work under copyright +law: that is to say, a work containing the Library or a portion of it, either verbatim or with modifications +and/or translated straightforwardly into another language. (Hereinafter, translation is included without +limitation in the term "modification".) + +"Source code" for a work means the preferred form of the work for making modifications to it. For a +library, complete source code means all the source code for all modules it contains, plus any associated +interface definition files, plus the scripts used to control compilation and installation of the library. + +Activities other than copying, distribution and modification are not covered by this License; they are +outside its scope. The act of running a program using the Library is not restricted, and output from +such a program is covered only if its contents constitute a work based on the Library (independent of +the use of the Library in a tool for writing it). Whether that is true depends on what the Library does +and what the program that uses the Library does. + +1. You may copy and distribute verbatim copies of the Library's complete source code as you receive it, +in any medium, provided that you conspicuously and appropriately publish on each copy an +appropriate copyright notice and disclaimer of warranty; keep intact all the notices that refer to this +License and to the absence of any warranty; and distribute a copy of this License along with the Library. + +You may charge a fee for the physical act of transferring a copy, and you may at your option offer +warranty protection in exchange for a fee. + +2. You may modify your copy or copies of the Library or any portion of it, thus forming a work based +on the Library, and copy and distribute such modifications or work under the terms of Section 1 above, +provided that you also meet all of these conditions: + +a) The modified work must itself be a software library. +b) You must cause the files modified to carry prominent notices stating that you changed the files and +the date of any change. +c) You must cause the whole of the work to be licensed at no charge to all third parties under the terms +of this License. +d) If a facility in the modified Library refers to a function or a table of data to be supplied by an +application program that uses the facility, other than as an argument passed when the facility is +invoked, then you must make a good faith effort to ensure that, in the event an application does not +supply such function or table, the facility still operates, and performs whatever part of its purpose +remains meaningful. +(For example, a function in a library to compute square roots has a purpose that is entirely well- +defined independent of the application. Therefore, Subsection 2d requires that any application- +supplied function or table used by this function must be optional: if the application does not supply it, +the square root function must still compute square roots.) + +These requirements apply to the modified work as a whole. If identifiable sections of that work are not +derived from the Library, and can be reasonably considered independent and separate works in +themselves, then this License, and its terms, do not apply to those sections when you distribute them as +separate works. But when you distribute the same sections as part of a whole which is a work based on +the Library, the distribution of the whole must be on the terms of this License, whose permissions for +other licensees extend to the entire whole, and thus to each and every part regardless of who wrote it. +Thus, it is not the intent of this section to claim rights or contest your rights to work written entirely by +you; rather, the intent is to exercise the right to control the distribution of derivative or collective works +based on the Library. + +In addition, mere aggregation of another work not based on the Library with the Library (or with a work +based on the Library) on a volume of a storage or distribution medium does not bring the other work +under the scope of this License. + +3. You may opt to apply the terms of the ordinary GNU General Public License instead of this License to +a given copy of the Library. To do this, you must alter all the notices that refer to this License, so that +they refer to the ordinary GNU General Public License, version 2, instead of to this License. (If a newer +version than version 2 of the ordinary GNU General Public License has appeared, then you can specify +that version instead if you wish.) Do not make any other change in these notices. + +Once this change is made in a given copy, it is irreversible for that copy, so the ordinary GNU General +Public License applies to all subsequent copies and derivative works made from that copy. + +This option is useful when you wish to copy part of the code of the Library into a program that is not a +library. + +4. You may copy and distribute the Library (or a portion or derivative of it, under Section 2) in object +code or executable form under the terms of Sections 1 and 2 above provided that you accompany it +with the complete corresponding machine-readable source code, which must be distributed under the +terms of Sections 1 and 2 above on a medium customarily used for software interchange. + +If distribution of object code is made by offering access to copy from a designated place, then offering +equivalent access to copy the source code from the same place satisfies the requirement to distribute +the source code, even though third parties are not compelled to copy the source along with the object +code. + +5. A program that contains no derivative of any portion of the Library, but is designed to work with the +Library by being compiled or linked with it, is called a "work that uses the Library". Such a work, in +isolation, is not a derivative work of the Library, and therefore falls outside the scope of this License. + +However, linking a "work that uses the Library" with the Library creates an executable that is a +derivative of the Library (because it contains portions of the Library), rather than a "work that uses the +library". The executable is therefore covered by this License. Section 6 states terms for distribution of +such executables. + +When a "work that uses the Library" uses material from a header file that is part of the Library, the +object code for the work may be a derivative work of the Library even though the source code is not. +Whether this is true is especially significant if the work can be linked without the Library, or if the work +is itself a library. The threshold for this to be true is not precisely defined by law. + +If such an object file uses only numerical parameters, data structure layouts and accessors, and small +macros and small inline functions (ten lines or less in length), then the use of the object file is +unrestricted, regardless of whether it is legally a derivative work. (Executables containing this object +code plus portions of the Library will still fall under Section 6.) + +Otherwise, if the work is a derivative of the Library, you may distribute the object code for the work +under the terms of Section 6. Any executables containing that work also fall under Section 6, whether +or not they are linked directly with the Library itself. + +6. As an exception to the Sections above, you may also combine or link a "work that uses the Library" +with the Library to produce a work containing portions of the Library, and distribute that work under +terms of your choice, provided that the terms permit modification of the work for the customer's own +use and reverse engineering for debugging such modifications. + +You must give prominent notice with each copy of the work that the Library is used in it and that the +Library and its use are covered by this License. You must supply a copy of this License. If the work +during execution displays copyright notices, you must include the copyright notice for the Library +among them, as well as a reference directing the user to the copy of this License. Also, you must do +one of these things: + +a) Accompany the work with the complete corresponding machine-readable source code for the Library +including whatever changes were used in the work (which must be distributed under Sections 1 and 2 +above); and, if the work is an executable linked with the Library, with the complete machine-readable +"work that uses the Library", as object code and/or source code, so that the user can modify the Library +and then relink to produce a modified executable containing the modified Library. (It is understood that +the user who changes the contents of definitions files in the Library will not necessarily be able to +recompile the application to use the modified definitions.) +b) Use a suitable shared library mechanism for linking with the Library. A suitable mechanism is one +that (1) uses at run time a copy of the library already present on the user's computer system, rather +than copying library functions into the executable, and (2) will operate properly with a modified version +of the library, if the user installs one, as long as the modified version is interface-compatible with the +version that the work was made with. +c) Accompany the work with a written offer, valid for at least three years, to give the same user the +materials specified in Subsection 6a, above, for a charge no more than the cost of performing this +distribution. +d) If distribution of the work is made by offering access to copy from a designated place, offer +equivalent access to copy the above specified materials from the same place. +e) Verify that the user has already received a copy of these materials or that you have already sent this +user a copy. +For an executable, the required form of the "work that uses the Library" must include any data and +utility programs needed for reproducing the executable from it. However, as a special exception, the +materials to be distributed need not include anything that is normally distributed (in either source or +binary form) with the major components (compiler, kernel, and so on) of the operating system on which +the executable runs, unless that component itself accompanies the executable. + +It may happen that this requirement contradicts the license restrictions of other proprietary libraries +that do not normally accompany the operating system. Such a contradiction means you cannot use both +them and the Library together in an executable that you distribute. + +7. You may place library facilities that are a work based on the Library side-by-side in a single library +together with other library facilities not covered by this License, and distribute such a combined library, +provided that the separate distribution of the work based on the Library and of the other library +facilities is otherwise permitted, and provided that you do these two things: + +a) Accompany the combined library with a copy of the same work based on the Library, uncombined +with any other library facilities. This must be distributed under the terms of the Sections above. +b) Give prominent notice with the combined library of the fact that part of it is a work based on the +Library, and explaining where to find the accompanying uncombined form of the same work. +8. You may not copy, modify, sublicense, link with, or distribute the Library except as expressly +provided under this License. Any attempt otherwise to copy, modify, sublicense, link with, or distribute +the Library is void, and will automatically terminate your rights under this License. However, parties +who have received copies, or rights, from you under this License will not have their licenses terminated +so long as such parties remain in full compliance. + +9. You are not required to accept this License, since you have not signed it. However, nothing else +grants you permission to modify or distribute the Library or its derivative works. These actions are +prohibited by law if you do not accept this License. Therefore, by modifying or distributing the Library +(or any work based on the Library), you indicate your acceptance of this License to do so, and all its +terms and conditions for copying, distributing or modifying the Library or works based on it. + +10. Each time you redistribute the Library (or any work based on the Library), the recipient +automatically receives a license from the original licensor to copy, distribute, link with or modify the +Library subject to these terms and conditions. You may not impose any further restrictions on the +recipients' exercise of the rights granted herein. You are not responsible for enforcing compliance by +third parties with this License. + +11. If, as a consequence of a court judgment or allegation of patent infringement or for any other +reason (not limited to patent issues), conditions are imposed on you (whether by court order, +agreement or otherwise) that contradict the conditions of this License, they do not excuse you from the +conditions of this License. If you cannot distribute so as to satisfy simultaneously your obligations +under this License and any other pertinent obligations, then as a consequence you may not distribute +the Library at all. For example, if a patent license would not permit royalty-free redistribution of the +Library by all those who receive copies directly or indirectly through you, then the only way you could +satisfy both it and this License would be to refrain entirely from distribution of the Library. + +If any portion of this section is held invalid or unenforceable under any particular circumstance, the +balance of the section is intended to apply, and the section as a whole is intended to apply in other +circumstances. + +It is not the purpose of this section to induce you to infringe any patents or other property right claims +or to contest validity of any such claims; this section has the sole purpose of protecting the integrity of +the free software distribution system which is implemented by public license practices. Many people +have made generous contributions to the wide range of software distributed through that system in +reliance on consistent application of that system; it is up to the author/donor to decide if he or she is +willing to distribute software through any other system and a licensee cannot impose that choice. + +This section is intended to make thoroughly clear what is believed to be a consequence of the rest of +this License. + +12. If the distribution and/or use of the Library is restricted in certain countries either by patents or by +copyrighted interfaces, the original copyright holder who places the Library under this License may add +an explicit geographical distribution limitation excluding those countries, so that distribution is +permitted only in or among countries not thus excluded. In such case, this License incorporates the +limitation as if written in the body of this License. + +13. The Free Software Foundation may publish revised and/or new versions of the Lesser General Public +License from time to time. Such new versions will be similar in spirit to the present version, but may +differ in detail to address new problems or concerns. + +Each version is given a distinguishing version number. If the Library specifies a version number of this +License which applies to it and "any later version", you have the option of following the terms and +conditions either of that version or of any later version published by the Free Software Foundation. If +the Library does not specify a license version number, you may choose any version ever published by +the Free Software Foundation. + +14. If you wish to incorporate parts of the Library into other free programs whose distribution +conditions are incompatible with these, write to the author to ask for permission. For software which is +copyrighted by the Free Software Foundation, write to the Free Software Foundation; we sometimes +make exceptions for this. Our decision will be guided by the two goals of preserving the free status of +all derivatives of our free software and of promoting the sharing and reuse of software generally. + +NO WARRANTY + +15. BECAUSE THE LIBRARY IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY FOR THE LIBRARY, +TO THE EXTENT PERMITTED BY APPLICABLE LAW. EXCEPT WHEN OTHERWISE STATED IN WRITING THE +COPYRIGHT HOLDERS AND/OR OTHER PARTIES PROVIDE THE LIBRARY "AS IS" WITHOUT WARRANTY OF +ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES +OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE ENTIRE RISK AS TO THE QUALITY +AND PERFORMANCE OF THE LIBRARY IS WITH YOU. SHOULD THE LIBRARY PROVE DEFECTIVE, YOU +ASSUME THE COST OF ALL NECESSARY SERVICING, REPAIR OR CORRECTION. + +16. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING WILL ANY +COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY AND/OR REDISTRIBUTE THE LIBRARY AS +PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL +OR CONSEQUENTIAL DAMAGES ARISING OUT OF THE USE OR INABILITY TO USE THE LIBRARY +(INCLUDING BUT NOT LIMITED TO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR LOSSES +SUSTAINED BY YOU OR THIRD PARTIES OR A FAILURE OF THE LIBRARY TO OPERATE WITH ANY OTHER +SOFTWARE), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH +DAMAGES. + +END OF TERMS AND CONDITIONS + +How to Apply These Terms to Your New Libraries + +If you develop a new library, and you want it to be of the greatest possible use to the public, we +recommend making it free software that everyone can redistribute and change. You can do so by +permitting redistribution under these terms (or, alternatively, under the terms of the ordinary General +Public License). + +To apply these terms, attach the following notices to the library. It is safest to attach them to the start +of each source file to most effectively convey the exclusion of warranty; and each file should have at +least the "copyright" line and a pointer to where the full notice is found. + +one line to give the library's name and an idea of what it does. +Copyright (C) year name of author + +This library is free software; you can redistribute it and/or +modify it under the terms of the GNU Lesser General Public +License as published by the Free Software Foundation; either +version 2.1 of the License, or (at your option) any later version. + +This library is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +Lesser General Public License for more details. + +You should have received a copy of the GNU Lesser General Public +License along with this library; if not, write to the Free Software +Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA +Also add information on how to contact you by electronic and paper mail. + +You should also get your employer (if you work as a programmer) or your school, if any, to sign a +"copyright disclaimer" for the library, if necessary. Here is a sample; alter the names: + +Yoyodyne, Inc., hereby disclaims all copyright interest in +the library `Frob' (a library for tweaking knobs) written +by James Random Hacker. + +signature of Ty Coon, 1 April 1990 +Ty Coon, President of Vice +That's all there is to it! +*************************************************************************** +%%The following software may be included in this product: +Boo +Use of any of this software is governed by the terms of the license below: +Copyright (c) 2003, 2004, Rodrigo B. de Oliveira (rbo@acm.org) +All rights reserved. + +Redistribution and use in source and binary forms, with or without modification, +are permitted provided that the following conditions are met: + +* Redistributions of source code must retain the above copyright notice, +this list of conditions and the following disclaimer. +* Redistributions in binary form must reproduce the above copyright notice, +this list of conditions and the following disclaimer in the documentation +and/or other materials provided with the distribution. +* Neither the name of Rodrigo B. de Oliveira nor the names of its +contributors may be used to endorse or promote products derived from this +software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND +ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED +WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE +FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL +DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR +SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER +CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, +OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF +THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +*************************************************************************** + + +DO NOT TRANSLATE OR LOCALIZE +*************************************************************************** +%%The following software may be included in this product: +Groovy Scripting Language +Use of any of this software is governed by the terms of the license below: +Apache License +Version 2.0, January 2004 +http://www.apache.org/licenses/ + +TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION + +1. Definitions. + +"License" shall mean the terms and conditions for use, reproduction, and +distribution as defined by Sections 1 through 9 of this document. + +"Licensor" shall mean the copyright owner or entity authorized by the copyright +owner that is granting the License. + +"Legal Entity" shall mean the union of the acting entity and all other entities +that control, are controlled by, or are under common control with that entity. +For the purposes of this definition, "control" means (i) the power, direct or +indirect, to cause the direction or management of such entity, whether by +contract or otherwise, or (ii) ownership of fifty percent (50%) or more of the +outstanding shares, or (iii) beneficial ownership of such entity. + +"You" (or "Your") shall mean an individual or Legal Entity exercising +permissions granted by this License. + +"Source" form shall mean the preferred form for making modifications, including +but not limited to software source code, documentation source, and configuration +files. + +"Object" form shall mean any form resulting from mechanical transformation or +translation of a Source form, including but not limited to compiled object code, +generated documentation, and conversions to other media types. + +"Work" shall mean the work of authorship, whether in Source or Object form, made +available under the License, as indicated by a copyright notice that is included +in or attached to the work (an example is provided in the Appendix below). + +"Derivative Works" shall mean any work, whether in Source or Object form, that +is based on (or derived from) the Work and for which the editorial revisions, +annotations, elaborations, or other modifications represent, as a whole, an +original work of authorship. For the purposes of this License, Derivative Works +shall not include works that remain separable from, or merely link (or bind by +name) to the interfaces of, the Work and Derivative Works thereof. + +"Contribution" shall mean any work of authorship, including the original version +of the Work and any modifications or additions to that Work or Derivative Works +thereof, that is intentionally submitted to Licensor for inclusion in the Work +by the copyright owner or by an individual or Legal Entity authorized to submit +on behalf of the copyright owner. For the purposes of this definition, +"submitted" means any form of electronic, verbal, or written communication sent +to the Licensor or its representatives, including but not limited to +communication on electronic mailing lists, source code control systems, and +issue tracking systems that are managed by, or on behalf of, the Licensor for +the purpose of discussing and improving the Work, but excluding communication +that is conspicuously marked or otherwise designated in writing by the copyright +owner as "Not a Contribution." + +"Contributor" shall mean Licensor and any individual or Legal Entity on behalf +of whom a Contribution has been received by Licensor and subsequently +incorporated within the Work. + +2. Grant of Copyright License. Subject to the terms and conditions of this +License, each Contributor hereby grants to You a perpetual, worldwide, +non-exclusive, no-charge, royalty-free, irrevocable copyright license to +reproduce, prepare Derivative Works of, publicly display, publicly perform, +sublicense, and distribute the Work and such Derivative Works in Source or +Object form. + +3. Grant of Patent License. Subject to the terms and conditions of this License, +each Contributor hereby grants to You a perpetual, worldwide, non-exclusive, +no-charge, royalty-free, irrevocable (except as stated in this section) patent +license to make, have made, use, offer to sell, sell, import, and otherwise +transfer the Work, where such license applies only to those patent claims +licensable by such Contributor that are necessarily infringed by their +Contribution(s) alone or by combination of their Contribution(s) with the Work +to which such Contribution(s) was submitted. If You institute patent litigation +against any entity (including a cross-claim or counterclaim in a lawsuit) +alleging that the Work or a Contribution incorporated within the Work +constitutes direct or contributory patent infringement, then any patent licenses +granted to You under this License for that Work shall terminate as of the date +such litigation is filed. + +4. Redistribution. You may reproduce and distribute copies of the Work or +Derivative Works thereof in any medium, with or without modifications, and in +Source or Object form, provided that You meet the following conditions: + +1. You must give any other recipients of the Work or Derivative Works a copy +of this License; and + +2. You must cause any modified files to carry prominent notices stating that +You changed the files; and + +3. You must retain, in the Source form of any Derivative Works that You +distribute, all copyright, patent, trademark, and attribution notices from the +Source form of the Work, excluding those notices that do not pertain to any part +of the Derivative Works; and + +4. If the Work includes a "NOTICE" text file as part of its distribution, +then any Derivative Works that You distribute must include a readable copy of +the attribution notices contained within such NOTICE file, excluding those +notices that do not pertain to any part of the Derivative Works, in at least one +of the following places: within a NOTICE text file distributed as part of the +Derivative Works; within the Source form or documentation, if provided along +with the Derivative Works; or, within a display generated by the Derivative +Works, if and wherever such third-party notices normally appear. The contents of +the NOTICE file are for informational purposes only and do not modify the +License. You may add Your own attribution notices within Derivative Works that +You distribute, alongside or as an addendum to the NOTICE text from the Work, +provided that such additional attribution notices cannot be construed as +modifying the License. + +You may add Your own copyright statement to Your modifications and may provide +additional or different license terms and conditions for use, reproduction, or +distribution of Your modifications, or for any such Derivative Works as a whole, +prov ided Your use, reproduction, and distribution of the Work otherwise complies +with the conditions stated in this License. + +5. Submission of Contributions. Unless You explicitly state otherwise, any +Contribution intentionally submitted for inclusion in the Work by You to the +Licensor shall be under the terms and conditions of this License, without any +additional terms or conditions. Notwithstanding the above, nothing herein shall +supersede or modify the terms of any separate license agreement you may have +executed with Licensor regarding such Contributions. + +6. Trademarks. This License does not grant permission to use the trade names, +trademarks, service marks, or product names of the Licensor, except as required +for reasonable and customary use in describing the origin of the Work and +reproducing the content of the NOTICE file. + +7. Disclaimer of Warranty. Unless required by applicable law or agreed to in +writing, Licensor provides the Work (and each Contributor provides its +Contributions) on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +KIND, either express or implied, including, without limitation, any warranties +or conditions of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A +PARTICULAR PURPOSE. You are solely responsible for determining the +appropriateness of using or redistributing the Work and assume any risks +associated with Your exercise of permissions under this License. + +8. Limitation of Liability. In no event and under no legal theory, whether in +tort (including negligence), contract, or otherwise, unless required by +applicable law (such as deliberate and grossly negligent acts) or agreed to in +writing, shall any Contributor be liable to You for damages, including any +direct, indirect, special, incidental, or consequential damages of any character +arising as a result of this License or out of the use or inability to use the +Work (including but not limited to damages for loss of goodwill, work stoppage, +computer failure or malfunction, or any and all other commercial damages or +losses), even if such Contributor has been advised of the possibility of such +damages. + +9. Accepting Warranty or Additional Liability. While redistributing the Work or +Derivative Works thereof, You may choose to offer, and charge a fee for, +acceptance of support, warranty, indemnity, or other liability obligations +and/or rights consistent with this License. However, in accepting such +obligations, You may act only on Your own behalf and on Your sole +responsibility, not on behalf of any other Contributor, and only if You agree to +indemnify, defend, and hold each Contributor harmless for any liability incurred +by, or claims asserted against, such Contributor by reason of your accepting any +such warranty or additional liability. + +END OF TERMS AND CONDITIONS +APPENDIX: How to apply the Apache License to your work + +To apply the Apache License to your work, attach the following boilerplate +notice, with the fields enclosed by brackets "[]" replaced with your own +identifying information. (Don't include the brackets!) The text should be +enclosed in the appropriate comment syntax for the file format. We also +recommend that a file or class name and description of purpose be included on +the same "printed page" as the copyright notice for easier identification within +third-party archives. +Copyright [yyyy] [name of copyright owner] 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. +*************************************************************************** +%%The following software may be included in this product: +Apache Commons Pool +Use of any of this software is governed by the terms of the license below: +Apache License +Version 2.0, January 2004 +http://www.apache.org/licenses/ + +TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION + +1. Definitions. + +"License" shall mean the terms and conditions for use, reproduction, +and distribution as defined by Sections 1 through 9 of this document. + +"Licensor" shall mean the copyright owner or entity authorized by +the copyright owner that is granting the License. + +"Legal Entity" shall mean the union of the acting entity and all +other entities that control, are controlled by, or are under common +control with that entity. For the purposes of this definition, +"control" means (i) the power, direct or indirect, to cause the +direction or management of such entity, whether by contract or +otherwise, or (ii) ownership of fifty percent (50%) or more of the +outstanding shares, or (iii) beneficial ownership of such entity. + +"You" (or "Your") shall mean an individual or Legal Entity +exercising permissions granted by this License. + +"Source" form shall mean the preferred form for making modifications, +including but not limited to software source code, documentation +source, and configuration files. + +"Object" form shall mean any form resulting from mechanical +transformation or translation of a Source form, including but +not limited to compiled object code, generated documentation, +and conversions to other media types. + +"Work" shall mean the work of authorship, whether in Source or +Object form, made available under the License, as indicated by a +copyright notice that is included in or attached to the work +(an example is provided in the Appendix below). + +"Derivative Works" shall mean any work, whether in Source or Object +form, that is based on (or derived from) the Work and for which the +editorial revisions, annotations, elaborations, or other modifications +represent, as a whole, an original work of authorship. For the purposes +of this License, Derivative Works shall not include works that remain +separable from, or merely link (or bind by name) to the interfaces of, +the Work and Derivative Works thereof. + +"Contribution" shall mean any work of authorship, including +the original version of the Work and any modifications or additions +to that Work or Derivative Works thereof, that is intentionally +submitted to Licensor for inclusion in the Work by the copyright owner +or by an individual or Legal Entity authorized to submit on behalf of +the copyright owner. For the purposes of this definition, "submitted" +means any form of electronic, verbal, or written communication sent +to the Licensor or its representatives, including but not limited to +communication on electronic mailing lists, source code control systems, +and issue tracking systems that are managed by, or on behalf of, the +Licensor for the purpose of discussing and improving the Work, but +excluding communication that is conspicuously marked or otherwise +designated in writing by the copyright owner as "Not a Contribution." + +"Contributor" shall mean Licensor and any individual or Legal Entity +on behalf of whom a Contribution has been received by Licensor and +subsequently incorporated within the Work. + +2. Grant of Copyright License. Subject to the terms and conditions of +this License, each Contributor hereby grants to You a perpetual, +worldwide, non-exclusive, no-charge, royalty-free, irrevocable +copyright license to reproduce, prepare Derivative Works of, +publicly display, publicly perform, sublicense, and distribute the +Work and such Derivative Works in Source or Object form. + +3. Grant of Patent License. Subject to the terms and conditions of +this License, each Contributor hereby grants to You a perpetual, +worldwide, non-exclusive, no-charge, royalty-free, irrevocable +(except as stated in this section) patent license to make, have made, +use, offer to sell, sell, import, and otherwise transfer the Work, +where such license applies only to those patent claims licensable +by such Contributor that are necessarily infringed by their +Contribution(s) alone or by combination of their Contribution(s) +with the Work to which such Contribution(s) was submitted. If You +institute patent litigation against any entity (including a +cross-claim or counterclaim in a lawsuit) alleging that the Work +or a Contribution incorporated within the Work constitutes direct +or contributory patent infringement, then any patent licenses +granted to You under this License for that Work shall terminate +as of the date such litigation is filed. + +4. Redistribution. You may reproduce and distribute copies of the +Work or Derivative Works thereof in any medium, with or without +modifications, and in Source or Object form, provided that You +meet the following conditions: + +(a) You must give any other recipients of the Work or +Derivative Works a copy of this License; and + +(b) You must cause any modified files to carry prominent notices +stating that You changed the files; and + +(c) You must retain, in the Source form of any Derivative Works +that You distribute, all copyright, patent, trademark, and +attribution notices from the Source form of the Work, +excluding those notices that do not pertain to any part of +the Derivative Works; and + +(d) If the Work includes a "NOTICE" text file as part of its +distribution, then any Derivative Works that You distribute must +include a readable copy of the attribution notices contained +within such NOTICE file, excluding those notices that do not +pertain to any part of the Derivative Works, in at least one +of the following places: within a NOTICE text file distributed +as part of the Derivative Works; within the Source form or +documentation, if provided along with the Derivative Works; or, +within a display generated by the Derivative Works, if and +wherever such third-party notices normally appear. The contents +of the NOTICE file are for informational purposes only and +do not modify the License. You may add Your own attribution +notices within Derivative Works that You distribute, alongside +or as an addendum to the NOTICE text from the Work, provided +that such additional attribution notices cannot be construed +as modifying the License. + +You may add Your own copyright statement to Your modifications and +may provide additional or different license terms and conditions +for use, reproduction, or distribution of Your modifications, or +for any such Derivative Works as a whole, provided Your use, +reproduction, and distribution of the Work otherwise complies with +the conditions stated in this License. + +5. Submission of Contributions. Unless You explicitly state otherwise, +any Contribution intentionally submitted for inclusion in the Work +by You to the Licensor shall be under the terms and conditions of +this License, without any additional terms or conditions. +Notwithstanding the above, nothing herein shall supersede or modify +the terms of any separate license agreement you may have executed +with Licensor regarding such Contributions. + +6. Trademarks. This License does not grant permission to use the trade +names, trademarks, service marks, or product names of the Licensor, +except as required for reasonable and customary use in describing the +origin of the Work and reproducing the content of the NOTICE file. + +7. Disclaimer of Warranty. Unless required by applicable law or +agreed to in writing, Licensor provides the Work (and each +Contributor provides its Contributions) on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or +implied, including, without limitation, any warranties or conditions +of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A +PARTICULAR PURPOSE. You are solely responsible for determining the +appropriateness of using or redistributing the Work and assume any +risks associated with Your exercise of permissions under this License. + +8. Limitation of Liability. In no event and under no legal theory, +whether in tort (including negligence), contract, or otherwise, +unless required by applicable law (such as deliberate and grossly +negligent acts) or agreed to in writing, shall any Contributor be +liable to You for damages, including any direct, indirect, special, +incidental, or consequential damages of any character arising as a +result of this License or out of the use or inability to use the +Work (including but not limited to damages for loss of goodwill, +work stoppage, computer failure or malfunction, or any and all +other commercial damages or losses), even if such Contributor +has been advised of the possibility of such damages. + +9. Accepting Warranty or Additional Liability. While redistributing +the Work or Derivative Works thereof, You may choose to offer, +and charge a fee for, acceptance of support, warranty, indemnity, +or other liability obligations and/or rights consistent with this +License. However, in accepting such obligations, You may act only +on Your own behalf and on Your sole responsibility, not on behalf +of any other Contributor, and only if You agree to indemnify, +defend, and hold each Contributor harmless for any liability +incurred by, or claims asserted against, such Contributor by reason +of your accepting any such warranty or additional liability. + +END OF TERMS AND CONDITIONS + +APPENDIX: How to apply the Apache License to your work. + +To apply the Apache License to your work, attach the following +boilerplate notice, with the fields enclosed by brackets "[]" +replaced with your own identifying information. (Don't include +the brackets!) The text should be enclosed in the appropriate +comment syntax for the file format. We also recommend that a +file or class name and description of purpose be included on the +same "printed page" as the copyright notice for easier +identification within third-party archives. + +Copyright [yyyy] [name of copyright owner] + +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. +*************************************************************************** +%%The following software may be included in this product: +HSQL Database Engine +Use of any of this software is governed by the terms of the license below: +COPYRIGHTS AND LICENSES + +ORIGINAL LICENSE (a.k.a. "hypersonic_lic.txt") + +For content, code, and products originally developed by Thomas Mueller and the +Hypersonic SQL Group: + +Copyright (c) 1995-2000 by the Hypersonic SQL Group. +All rights reserved. + + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are met: + +Redistributions of source code must retain the above copyright notice, this +list of conditions and the following disclaimer. + +Redistributions in binary form must reproduce the above copyright notice, +this list of conditions and the following disclaimer in the documentation +and/or other materials provided with the distribution. + +Neither the name of the Hypersonic SQL Group nor the names of its +contributors may be used to endorse or promote products derived from this +software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" +AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE +ARE DISCLAIMED. IN NO EVENT SHALL THE HYPERSONIC SQL GROUP, +OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; +LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND +ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS +SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +This software consists of voluntary contributions made by many individuals on +behalf of the +Hypersonic SQL Group. + + + +For work added by the HSQL Development Group (a.k.a. hsqldb_lic.txt): +Copyright (c) 2001-2005, The HSQL Development Group +All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are met: + +Redistributions of source code must retain the above copyright notice, this +list of conditions and the following disclaimer. + +Redistributions in binary form must reproduce the above copyright notice, +this list of conditions and the following disclaimer in the documentation +and/or other materials provided with the distribution. + +Neither the name of the HSQL Development Group nor the names of its +contributors may be used to endorse or promote products derived from this +software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" +AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE +ARE DISCLAIMED. IN NO EVENT SHALL HSQL DEVELOPMENT GROUP, HSQLDB.ORG, +OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; +LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND +ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS +SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +*************************************************************************** +%%The following software may be included in this product: +Derby +Use of any of this software is governed by the terms of the license below: +Apache License, Version 2.0 + +Apache License +Version 2.0, January 2004 +http://www.apache.org/licenses/ + +TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION + +1. Definitions. + +"License" shall mean the terms and conditions for use, reproduction, and +distribution as defined by Sections 1 through 9 of this document. + +"Licensor" shall mean the copyright owner or entity authorized by the copyright +owner that is granting the License. + +"Legal Entity" shall mean the union of the acting entity and all other entities +that control, are controlled by, or are under common control with that entity. +For the purposes of this definition, "control" means (i) the power, direct or +indirect, to cause the direction or management of such entity, whether by +contract or otherwise, or (ii) ownership of fifty percent (50%) or more of the +outstanding shares, or (iii) beneficial ownership of such entity. + +"You" (or "Your") shall mean an individual or Legal Entity exercising +permissions granted by this License. + +"Source" form shall mean the preferred form for making modifications, including +but not limited to software source code, documentation source, and configuration +files. + +"Object" form shall mean any form resulting from mechanical transformation or +translation of a Source form, including but not limited to compiled object code, +generated documentation, and conversions to other media types. + +"Work" shall mean the work of authorship, whether in Source or Object form, made +available under the License, as indicated by a copyright notice that is included +in or attached to the work (an example is provided in the Appendix below). + +"Derivative Works" shall mean any work, whether in Source or Object form, that +is based on (or derived from) the Work and for which the editorial revisions, +annotations, elaborations, or other modifications represent, as a whole, an +original work of authorship. For the purposes of this License, Derivative Works +shall not include works that remain separable from, or merely link (or bind by +name) to the interfaces of, the Work and Derivative Works thereof. + +"Contribution" shall mean any work of authorship, including the original version +of the Work and any modifications or additions to that Work or Derivative Works +thereof, that is intentionally submitted to Licensor for inclusion in the Work +by the copyright owner or by an individual or Legal Entity authorized to submit +on behalf of the copyright owner. For the purposes of this definition, +"submitted" means any form of electronic, verbal, or written communication sent +to the Licensor or its representatives, including but not limited to +communication on electronic mailing lists, source code control systems, and +issue tracking systems that are managed by, or on behalf of, the Licensor for +the purpose of discussing and improving the Work, but excluding communication +that is conspicuously marked or otherwise designated in writing by the copyright +owner as "Not a Contribution." + +"Contributor" shall mean Licensor and any individual or Legal Entity on behalf +of whom a Contribution has been received by Licensor and subsequently +incorporated within the Work. + +2. Grant of Copyright License. Subject to the terms and conditions of this +License, each Contributor hereby grants to You a perpetual, worldwide, +non-exclusive, no-charge, royalty-free, irrevocable copyright license to +reproduce, prepare Derivative Works of, publicly display, publicly perform, +sublicense, and distribute the Work and such Derivative Works in Source or +Object form. + +3. Grant of Patent License. Subject to the terms and conditions of this License, +each Contributor hereby grants to You a perpetual, worldwide, non-exclusive, +no-charge, royalty-free, irrevocable (except as stated in this section) patent +license to make, have made, use, offer to sell, sell, import, and otherwise +transfer the Work, where such license applies only to those patent claims +licensable by such Contributor that are necessarily infringed by their +Contribution(s) alone or by combination of their Contribution(s) with the Work +to which such Contribution(s) was submitted. If You institute patent litigation +against any entity (including a cross-claim or counterclaim in a lawsuit) +alleging that the Work or a Contribution incorporated within the Work +constitutes direct or contributory patent infringement, then any patent licenses +granted to You under this License for that Work shall terminate as of the date +such litigation is filed. + +4. Redistribution. You may reproduce and distribute copies of the Work or +Derivative Works thereof in any medium, with or without modifications, and in +Source or Object form, provided that You meet the following conditions: + +1. You must give any other recipients of the Work or Derivative Works a copy +of this License; and + +2. You must cause any modified files to carry prominent notices stating that +You changed the files; and + +3. You must retain, in the Source form of any Derivative Works that You +distribute, all copyright, patent, trademark, and attribution notices from the +Source form of the Work, excluding those notices that do not pertain to any part +of the Derivative Works; and + +4. If the Work includes a "NOTICE" text file as part of its distribution, +then any Derivative Works that You distribute must include a readable copy of +the attribution notices contained within such NOTICE file, excluding those +notices that do not pertain to any part of the Derivative Works, in at least one +of the following places: within a NOTICE text file distributed as part of the +Derivative Works; within the Source form or documentation, if provided along +with the Derivative Works; or, within a display generated by the Derivative +Works, if and wherever such third-party notices normally appear. The contents of +the NOTICE file are for informational purposes only and do not modify the +License. You may add Your own attribution notices within Derivative Works that +You distribute, alongside or as an addendum to the NOTICE text from the Work, +provided that such additional attribution notices cannot be construed as +modifying the License. + +You may add Your own copyright statement to Your modifications and may provide +additional or different license terms and conditions for use, reproduction, or +distribution of Your modifications, or for any such Derivative Works as a whole, +provided Your use, reproduction, and distribution of the Work otherwise complies +with the conditions stated in this License. + +5. Submission of Contributions. Unless You explicitly state otherwise, any +Contribution intentionally submitted for inclusion in the Work by You to the +Licensor shall be under the terms and conditions of this License, without any +additional terms or conditions. Notwithstanding the above, nothing herein shall +supersede or modify the terms of any separate license agreement you may have +executed with Licensor regarding such Contributions. + +6. Trademarks. This License does not grant permission to use the trade names, +trademarks, service marks, or product names of the Licensor, except as required +for reasonable and customary use in describing the origin of the Work and +reproducing the content of the NOTICE file. + +7. Disclaimer of Warranty. Unless required by applicable law or agreed to in +writing, Licensor provides the Work (and each Contributor provides its +Contributions) on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +KIND, either express or implied, including, without limitation, any warranties +or conditions of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A +PARTICULAR PURPOSE. You are solely responsible for determining the +appropriateness of using or redistributing the Work and assume any risks +associated with Your exercise of permissions under this License. + +8. Limitation of Liability. In no event and under no legal theory, whether in +tort (including negligence), contract, or otherwise, unless required by +applicable law (such as deliberate and grossly negligent acts) or agreed to in +writing, shall any Contributor be liable to You for damages, including any +direct, indirect, special, incidental, or consequential damages of any character +arising as a result of this License or out of the use or inability to use the +Work (including but not limited to damages for loss of goodwill, work stoppage, +computer failure or malfunction, or any and all other commercial damages or +losses), even if such Contributor has been advised of the possibility of such +damages. + +9. Accepting Warranty or Additional Liability. While redistributing the Work or +Derivative Works thereof, You may choose to offer, and charge a fee for, +acceptance of support, warranty, indemnity, or other liability obligations +and/or rights consistent with this License. However, in accepting such +obligations, You may act only on Your own behalf and on Your sole +responsibility, not on behalf of any other Contributor, and only if You agree to +indemnify, defend, and hold each Contributor harmless for any liability incurred +by, or claims asserted against, such Contributor by reason of your accepting any +such warranty or additional liability. + +END OF TERMS AND CONDITIONS +APPENDIX: How to apply the Apache License to your work + +To apply the Apache License to your work, attach the following boilerplate +notice, with the fields enclosed by brackets "[]" replaced with your own +identifying information. (Don't include the brackets!) The text should be +enclosed in the appropriate comment syntax for the file format. We also +recommend that a file or class name and description of purpose be included on +the same "printed page" as the copyright notice for easier identification within +third-party archives. +Copyright [yyyy] [name of copyright owner] 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. +*************************************************************************** +%%The following software may be included in this product: +Apache Commons - commons-net-1.4.1.jar +Use of any of this software is governed by the terms of the license below: +Apache License +Version 2.0, January 2004 +http://www.apache.org/licenses/ + +TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION + +1. Definitions. + +"License" shall mean the terms and conditions for use, reproduction, +and distribution as defined by Sections 1 through 9 of this document. + +"Licensor" shall mean the copyright owner or entity authorized by +the copyright owner that is granting the License. + +"Legal Entity" shall mean the union of the acting entity and all +other entities that control, are controlled by, or are under common +control with that entity. For the purposes of this definition, +"control" means (i) the power, direct or indirect, to cause the +direction or management of such entity, whether by contract or +otherwise, or (ii) ownership of fifty percent (50%) or more of the +outstanding shares, or (iii) beneficial ownership of such entity. + +"You" (or "Your") shall mean an individual or Legal Entity +exercising permissions granted by this License. + +"Source" form shall mean the preferred form for making modifications, +including but not limited to software source code, documentation +source, and configuration files. + +"Object" form shall mean any form resulting from mechanical +transformation or translation of a Source form, including but +not limited to compiled object code, generated documentation, +and conversions to other media types. + +"Work" shall mean the work of authorship, whether in Source or +Object form, made available under the License, as indicated by a +copyright notice that is included in or attached to the work +(an example is provided in the Appendix below). + +"Derivative Works" shall mean any work, whether in Source or Object +form, that is based on (or derived from) the Work and for which the +editorial revisions, annotations, elaborations, or other modifications +represent, as a whole, an original work of authorship. For the purposes +of this License, Derivative Works shall not include works that remain +separable from, or merely link (or bind by name) to the interfaces of, +the Work and Derivative Works thereof. + +"Contribution" shall mean any work of authorship, including +the original version of the Work and any modifications or additions +to that Work or Derivative Works thereof, that is intentionally +submitted to Licensor for inclusion in the Work by the copyright owner +or by an individual or Legal Entity authorized to submit on behalf of +the copyright owner. For the purposes of this definition, "submitted" +means any form of electronic, verbal, or written communication sent +to the Licensor or its representatives, including but not limited to +communication on electronic mailing lists, source code control systems, +and issue tracking systems that are managed by, or on behalf of, the +Licensor for the purpose of discussing and improving the Work, but +excluding communication that is conspicuously marked or otherwise +designated in writing by the copyright owner as "Not a Contribution." + +"Contributor" shall mean Licensor and any individual or Legal Entity +on behalf of whom a Contribution has been received by Licensor and +subsequently incorporated within the Work. + +2. Grant of Copyright License. Subject to the terms and conditions of +this License, each Contributor hereby grants to You a perpetual, +worldwide, non-exclusive, no-charge, royalty-free, irrevocable +copyright license to reproduce, prepare Derivative Works of, +publicly display, publicly perform, sublicense, and distribute the +Work and such Derivative Works in Source or Object form. + +3. Grant of Patent License. Subject to the terms and conditions of +this License, each Contributor hereby grants to You a perpetual, +worldwide, non-exclusive, no-charge, royalty-free, irrevocable +(except as stated in this section) patent license to make, have made, +use, offer to sell, sell, import, and otherwise transfer the Work, +where such license applies only to those patent claims licensable +by such Contributor that are necessarily infringed by their +Contribution(s) alone or by combination of their Contribution(s) +with the Work to which such Contribution(s) was submitted. If You +institute patent litigation against any entity (including a +cross-claim or counterclaim in a lawsuit) alleging that the Work +or a Contribution incorporated within the Work constitutes direct +or contributory patent infringement, then any patent licenses +granted to You under this License for that Work shall terminate +as of the date such litigation is filed. + +4. Redistribution. You may reproduce and distribute copies of the +Work or Derivative Works thereof in any medium, with or without +modifications, and in Source or Object form, provided that You +meet the following conditions: + +(a) You must give any other recipients of the Work or +Derivative Works a copy of this License; and + +(b) You must cause any modified files to carry prominent notices +stating that You changed the files; and + +(c) You must retain, in the Source form of any Derivative Works +that You distribute, all copyright, patent, trademark, and +attribution notices from the Source form of the Work, +excluding those notices that do not pertain to any part of +the Derivative Works; and + +(d) If the Work includes a "NOTICE" text file as part of its +distribution, then any Derivative Works that You distribute must +include a readable copy of the attribution notices contained +within such NOTICE file, excluding those notices that do not +pertain to any part of the Derivative Works, in at least one +of the following places: within a NOTICE text file distributed +as part of the Derivative Works; within the Source form or +documentation, if provided along with the Derivative Works; or, +within a display generated by the Derivative Works, if and +wherever such third-party notices normally appear. The contents +of the NOTICE file are for informational purposes only and +do not modify the License. You may add Your own attribution +notices within Derivative Works that You distribute, alongside +or as an addendum to the NOTICE text from the Work, provided +that such additional attribution notices cannot be construed +as modifying the License. + +You may add Your own copyright statement to Your modifications and +may provide additional or different license terms and conditions +for use, reproduction, or distribution of Your modifications, or +for any such Derivative Works as a whole, provided Your use, +reproduction, and distribution of the Work otherwise complies with +the conditions stated in this License. + +5. Submission of Contributions. Unless You explicitly state otherwise, +any Contribution intentionally submitted for inclusion in the Work +by You to the Licensor shall be under the terms and conditions of +this License, without any additional terms or conditions. +Notwithstanding the above, nothing herein shall supersede or modify +the terms of any separate license agreement you may have executed +with Licensor regarding such Contributions. + +6. Trademarks. This License does not grant permission to use the trade +names, trademarks, service marks, or product names of the Licensor, +except as required for reasonable and customary use in describing the +origin of the Work and reproducing the content of the NOTICE file. + +7. Disclaimer of Warranty. Unless required by applicable law or +agreed to in writing, Licensor provides the Work (and each +Contributor provides its Contributions) on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or +implied, including, without limitation, any warranties or conditions +of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A +PARTICULAR PURPOSE. You are solely responsible for determining the +appropriateness of using or redistributing the Work and assume any +risks associated with Your exercise of permissions under this License. + +8. Limitation of Liability. In no event and under no legal theory, +whether in tort (including negligence), contract, or otherwise, +unless required by applicable law (such as deliberate and grossly +negligent acts) or agreed to in writing, shall any Contributor be +liable to You for damages, including any direct, indirect, special, +incidental, or consequential damages of any character arising as a +result of this License or out of the use or inability to use the +Work (including but not limited to damages for loss of goodwill, +work stoppage, computer failure or malfunction, or any and all +other commercial damages or losses), even if such Contributor +has been advised of the possibility of such damages. + +9. Accepting Warranty or Additional Liability. While redistributing +the Work or Derivative Works thereof, You may choose to offer, +and charge a fee for, acceptance of support, warranty, indemnity, +or other liability obligations and/or rights consistent with this +License. However, in accepting such obligations, You may act only +on Your own behalf and on Your sole responsibility, not on behalf +of any other Contributor, and only if You agree to indemnify, +defend, and hold each Contributor harmless for any liability +incurred by, or claims asserted against, such Contributor by reason +of your accepting any such warranty or additional liability. + +END OF TERMS AND CONDITIONS + +APPENDIX: How to apply the Apache License to your work. + +To apply the Apache License to your work, attach the following +boilerplate notice, with the fields enclosed by brackets "[]" +replaced with your own identifying information. (Don't include +the brackets!) The text should be enclosed in the appropriate +comment syntax for the file format. We also recommend that a +file or class name and description of purpose be included on the +same "printed page" as the copyright notice for easier +identification within third-party archives. + +Copyright [yyyy] [name of copyright owner] + +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. +*************************************************************************** +%%The following software may be included in this product: +Jakarta Oro +Use of any of this software is governed by the terms of the license below: +Apache License +Version 2.0, January 2004 +http://www.apache.org/licenses/ + +TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION + +1. Definitions. + +"License" shall mean the terms and conditions for use, reproduction, +and distribution as defined by Sections 1 through 9 of this document. + +"Licensor" shall mean the copyright owner or entity authorized by +the copyright owner that is granting the License. + +"Legal Entity" shall mean the union of the acting entity and all +other entities that control, are controlled by, or are under common +control with that entity. For the purposes of this definition, +"control" means (i) the power, direct or indirect, to cause the +direction or management of such entity, whether by contract or +otherwise, or (ii) ownership of fifty percent (50%) or more of the +outstanding shares, or (iii) beneficial ownership of such entity. + +"You" (or "Your") shall mean an individual or Legal Entity +exercising permissions granted by this License. + +"Source" form shall mean the preferred form for making modifications, +including but not limited to software source code, documentation +source, and configuration files. + +"Object" form shall mean any form resulting from mechanical +transformation or translation of a Source form, including but +not limited to compiled object code, generated documentation, +and conversions to other media types. + +"Work" shall mean the work of authorship, whether in Source or +Object form, made available under the License, as indicated by a +copyright notice that is included in or attached to the work +(an example is provided in the Appendix below). + +"Derivative Works" shall mean any work, whether in Source or Object +form, that is based on (or derived from) the Work and for which the +editorial revisions, annotations, elaborations, or other modifications +represent, as a whole, an original work of authorship. For the purposes +of this License, Derivative Works shall not include works that remain +separable from, or merely link (or bind by name) to the interfaces of, +the Work and Derivative Works thereof. + +"Contribution" shall mean any work of authorship, including +the original version of the Work and any modifications or additions +to that Work or Derivative Works thereof, that is intentionally +submitted to Licensor for inclusion in the Work by the copyright owner +or by an individual or Legal Entity authorized to submit on behalf of +the copyright owner. For the purposes of this definition, "submitted" +means any form of electronic, verbal, or written communication sent +to the Licensor or its representatives, including but not limited to +communication on electronic mailing lists, source code control systems, +and issue tracking systems that are managed by, or on behalf of, the +Licensor for the purpose of discussing and improving the Work, but +excluding communication that is conspicuously marked or otherwise +designated in writing by the copyright owner as "Not a Contribution." + +"Contributor" shall mean Licensor and any individual or Legal Entity +on behalf of whom a Contribution has been received by Licensor and +subsequently incorporated within the Work. + +2. Grant of Copyright License. Subject to the terms and conditions of +this License, each Contributor hereby grants to You a perpetual, +worldwide, non-exclusive, no-charge, royalty-free, irrevocable +copyright license to reproduce, prepare Derivative Works of, +publicly display, publicly perform, sublicense, and distribute the +Work and such Derivative Works in Source or Object form. + +3. Grant of Patent License. Subject to the terms and conditions of +this License, each Contributor hereby grants to You a perpetual, +worldwide, non-exclusive, no-charge, royalty-free, irrevocable +(except as stated in this section) patent license to make, have made, +use, offer to sell, sell, import, and otherwise transfer the Work, +where such license applies only to those patent claims licensable +by such Contributor that are necessarily infringed by their +Contribution(s) alone or by combination of their Contribution(s) +with the Work to which such Contribution(s) was submitted. If You +institute patent litigation against any entity (including a +cross-claim or counterclaim in a lawsuit) alleging that the Work +or a Contribution incorporated within the Work constitutes direct +or contributory patent infringement, then any patent licenses +granted to You under this License for that Work shall terminate +as of the date such litigation is filed. + +4. Redistribution. You may reproduce and distribute copies of the +Work or Derivative Works thereof in any medium, with or without +modifications, and in Source or Object form, provided that You +meet the following conditions: + +(a) You must give any other recipients of the Work or +Derivative Works a copy of this License; and + +(b) You must cause any modified files to carry prominent notices +stating that You changed the files; and + +(c) You must retain, in the Source form of any Derivative Works +that You distribute, all copyright, patent, trademark, and +attribution notices from the Source form of the Work, +excluding those notices that do not pertain to any part of +the Derivative Works; and + +(d) If the Work includes a "NOTICE" text file as part of its +distribution, then any Derivative Works that You distribute must +include a readable copy of the attribution notices contained +within such NOTICE file, excluding those notices that do not +pertain to any part of the Derivative Works, in at least one +of the following places: within a NOTICE text file distributed +as part of the Derivative Works; within the Source form or +documentation, if provided along with the Derivative Works; or, +within a display generated by the Derivative Works, if and +wherever such third-party notices normally appear. The contents +of the NOTICE file are for informational purposes only and +do not modify the License. You may add Your own attribution +notices within Derivative Works that You distribute, alongside +or as an addendum to the NOTICE text from the Work, provided +that such additional attribution notices cannot be construed +as modifying the License. + +You may add Your own copyright statement to Your modifications and +may provide additional or different license terms and conditions +for use, reproduction, or distribution of Your modifications, or +for any such Derivative Works as a whole, provided Your use, +reproduction, and distribution of the Work otherwise complies with +the conditions stated in this License. + +5. Submission of Contributions. Unless You explicitly state otherwise, +any Contribution intentionally submitted for inclusion in the Work +by You to the Licensor shall be under the terms and conditions of +this License, without any additional terms or conditions. +Notwithstanding the above, nothing herein shall supersede or modify +the terms of any separate license agreement you may have executed +with Licensor regarding such Contributions. + +6. Trademarks. This License does not grant permission to use the trade +names, trademarks, service marks, or product names of the Licensor, +except as required for reasonable and customary use in describing the +origin of the Work and reproducing the content of the NOTICE file. + +7. Disclaimer of Warranty. Unless required by applicable law or +agreed to in writing, Licensor provides the Work (and each +Contributor provides its Contributions) on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or +implied, including, without limitation, any warranties or conditions +of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A +PARTICULAR PURPOSE. You are solely responsible for determining the +appropriateness of using or redistributing the Work and assume any +risks associated with Your exercise of permissions under this License. + +8. Limitation of Liability. In no event and under no legal theory, +whether in tort (including ne gligence), contract, or otherwise, +unless required by applicable law (such as deliberate and grossly +negligent acts) or agreed to in writing, shall any Contributor be +liable to You for damages, including any direct, indirect, special, +incidental, or consequential damages of any character arising as a +result of this License or out of the use or inability to use the +Work (including but not limited to damages for loss of goodwill, +work stoppage, computer failure or malfunction, or any and all +other commercial damages or losses), even if such Contributor +has been advised of the possibility of such damages. + +9. Accepting Warranty or Additional Liability. While redistributing +the Work or Derivative Works thereof, You may choose to offer, +and charge a fee for, acceptance of support, warranty, indemnity, +or other liability obligations and/or rights consistent with this +License. However, in accepting such obligations, You may act only +on Your own behalf and on Your sole responsibility, not on behalf +of any other Contributor, and only if You agree to indemnify, +defend, and hold each Contributor harmless for any liability +incurred by, or claims asserted against, such Contributor by reason +of your accepting any such warranty or additional liability. + +END OF TERMS AND CONDITIONS + +APPENDIX: How to apply the Apache License to your work. + +To apply the Apache License to your work, attach the following +boilerplate notice, with the fields enclosed by brackets "[]" +replaced with your own identifying information. (Don't include +the brackets!) The text should be enclosed in the appropriate +comment syntax for the file format. We also recommend that a +file or class name and description of purpose be included on the +same "printed page" as the copyright notice for easier +identification within third-party archives. + +Copyright [yyyy] [name of copyright owner] + +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. +*************************************************************************** +%%The following software may be included in this product: +Jsch +Use of any of this software is governed by the terms of the license below: +JSch 0.0.* was released under the GNU LGPL license. Later, we have switched +over to a BSD-style license. + +------------------------------------------------------------------------------ +Copyright (c) 2002,2003,2004,2005,2006,2007,2008 Atsuhiko Yamanaka, JCraft,Inc. +All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are met: + +1. Redistributions of source code must retain the above copyright notice, +this list of conditions and the following disclaimer. + +2. Redistributions in binary form must reproduce the above copyright +notice, this list of conditions and the following disclaimer in +the documentation and/or other materials provided with the distribution. + +3. The names of the authors may not be used to endorse or promote products +derived from this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED OR IMPLIED WARRANTIES, +INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND +FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL JCRAFT, +INC. OR ANY CONTRIBUTORS TO THIS SOFTWARE BE LIABLE FOR ANY DIRECT, INDIRECT, +INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, +OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF +LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING +NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, +EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +Additional License(s) +LICENSE.txt:JSch 0.0.* was released under the GNU LGPL license. +Later, we have switched +LICENSE.txt:over to a BSD-style license. +*************************************************************************** +%%The following software may be included in this product: +Expect4j +Use of any of this software is governed by the terms of the license below: +Apache License, Version 2.0 + +Apache License +Version 2.0, January 2004 +http://www.apache.org/licenses/ + +TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION + +1. Definitions. + +"License" shall mean the terms and conditions for use, reproduction, and +distribution as defined by Sections 1 through 9 of this document. + +"Licensor" shall mean the copyright owner or entity authorized by the copyright +owner that is granting the License. + +"Legal Entity" shall mean the union of the acting entity and all other entities +that control, are controlled by, or are under common control with that entity. +For the purposes of this definition, "control" means (i) the power, direct or +indirect, to cause the direction or management of such entity, whether by +contract or otherwise, or (ii) ownership of fifty percent (50%) or more of the +outstanding shares, or (iii) beneficial ownership of such entity. + +"You" (or "Your") shall mean an individual or Legal Entity exercising +permissions granted by this License. + +"Source" form shall mean the preferred form for making modifications, including +but not limited to software source code, documentation source, and configuration +files. + +"Object" form shall mean any form resulting from mechanical transformation or +translation of a Source form, including but not limited to compiled object code, +generated documentation, and conversions to other media types. + +"Work" shall mean the work of authorship, whether in Source or Object form, made +available under the License, as indicated by a copyright notice that is included +in or attached to the work (an example is provided in the Appendix below). + +"Derivative Works" shall mean any work, whether in Source or Object form, that +is based on (or derived from) the Work and for which the editorial revisions, +annotations, elaborations, or other modifications represent, as a whole, an +original work of authorship. For the purposes of this License, Derivative Works +shall not include works that remain separable from, or merely link (or bind by +name) to the interfaces of, the Work and Derivative Works thereof. + +"Contribution" shall mean any work of authorship, including the original version +of the Work and any modifications or additions to that Work or Derivative Works +thereof, that is intentionally submitted to Licensor for inclusion in the Work +by the copyright owner or by an individual or Legal Entity authorized to submit +on behalf of the copyright owner. For the purposes of this definition, +"submitted" means any form of electronic, verbal, or written communication sent +to the Licensor or its representatives, including but not limited to +communication on electronic mailing lists, source code control systems, and +issue tracking systems that are managed by, or on behalf of, the Licensor for +the purpose of discussing and improving the Work, but excluding communication +that is conspicuously marked or otherwise designated in writing by the copyright +owner as "Not a Contribution." + +"Contributor" shall mean Licensor and any individual or Legal Entity on behalf +of whom a Contribution has been received by Licensor and subsequently +incorporated within the Work. + +2. Grant of Copyright License. Subject to the terms and conditions of this +License, each Contributor hereby grants to You a perpetual, worldwide, +non-exclusive, no-charge, royalty-free, irrevocable copyright license to +reproduce, prepare Derivative Works of, publicly display, publicly perform, +sublicense, and distribute the Work and such Derivative Works in Source or +Object form. + +3. Grant of Patent License. Subject to the terms and conditions of this License, +each Contributor hereby grants to You a perpetual, worldwide, non-exclusive, +no-charge, royalty-free, irrevocable (except as stated in this section) patent +license to make, have made, use, offer to sell, sell, import, and otherwise +transfer the Work, where such license applies only to those patent claims +licensable by such Contributor that are necessarily infringed by their +Contribution(s) alone or by combination of their Contribution(s) with the Work +to which such Contribution(s) was submitted. If You institute patent litigation +against any entity (including a cross-claim or counterclaim in a lawsuit) +alleging that the Work or a Contribution incorporated within the Work +constitutes direct or contributory patent infringement, then any patent licenses +granted to You under this License for that Work shall terminate as of the date +such litigation is filed. + +4. Redistribution. You may reproduce and distribute copies of the Work or +Derivative Works thereof in any medium, with or without modifications, and in +Source or Object form, provided that You meet the following conditions: + +1. You must give any other recipients of the Work or Derivative Works a copy +of this License; and + +2. You must cause any modified files to carry prominent notices stating that +You changed the files; and + +3. You must retain, in the Source form of any Derivative Works that You +distribute, all copyright, patent, trademark, and attribution notices from the +Source form of the Work, excluding those notices that do not pertain to any part +of the Derivative Works; and + +4. If the Work includes a "NOTICE" text file as part of its distribution, +then any Derivative Works that You distribute must include a readable copy of +the attribution notices contained within such NOTICE file, excluding those +notices that do not pertain to any part of the Derivative Works, in at least one +of the following places: within a NOTICE text file distributed as part of the +Derivative Works; within the Source form or documentation, if provided along +with the Derivative Works; or, within a display generated by the Derivative +Works, if and wherever such third-party notices normally appear. The contents of +the NOTICE file are for informational purposes only and do not modify the +License. You may add Your own attribution notices within Derivative Works that +You distribute, alongside or as an addendum to the NOTICE text from the Work, +provided that such additional attribution notices cannot be construed as +modifying the License. + +You may add Your own copyright statement to Your modifications and may provide +additional or different license terms and conditions for use, reproduction, or +distribution of Your modifications, or for any such Derivative Works as a whole, +provided Your use, reproduction, and distribution of the Work otherwise complies +with the conditions stated in this License. + +5. Submission of Contributions. Unless You explicitly state otherwise, any +Contribution intentionally submitted for inclusion in the Work by You to the +Licensor shall be under the terms and conditions of this License, without any +additional terms or conditions. Notwithstanding the above, nothing herein shall +supersede or modify the terms of any separate license agreement you may have +executed with Licensor regarding such Contributions. + +6. Trademarks. This License does not grant permission to use the trade names, +trademarks, service marks, or product names of the Licensor, except as required +for reasonable and customary use in describing the origin of the Work and +reproducing the content of the NOTICE file. + +7. Disclaimer of Warranty. Unless required by applicable law or agreed to in +writing, Licensor provides the Work (and each Contributor provides its +Contributions) on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +KIND, either express or implied, including, without limitation, any warranties +or conditions of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A +PARTICULAR PURPOSE. You are solely responsible for determining the +appropriateness of using or redistributing the Work and assume any risks +associated with Your exercise of permissions under this License. + +8. Limitation of Liability. In no event and under no legal theory, whether in +tort (including negligence), contract, or otherwise, unless required by +applicable law (such as deliberate and grossly negligent acts) or agreed to in +writing, shall any Contributor be liable to You for damages, including any +direct, indirect, special, incidental, or consequential damages of any character +arising as a result of this License or out of the use or inability to use the +Work (including but not limited to damages for loss of goodwill, work stoppage, +computer failure or malfunction, or any and all other commercial damages or +losses), even if such Contributor has been advised of the possibility of such +damages. + +9. Accepting Warranty or Additional Liability. While redistributing the Work or +Derivative Works thereof, You may choose to offer, and charge a fee for, +acceptance of support, warranty, indemnity, or other liability obligations +and/or rights consistent with this License. However, in accepting such +obligations, You may act only on Your own behalf and on Your sole +responsibility, not on behalf of any other Contributor, and only if You agree to +indemnify, defend, and hold each Contributor harmless for any liability incurred +by, or claims asserted against, such Contributor by reason of your accepting any +such warranty or additional liability. + +END OF TERMS AND CONDITIONS +APPENDIX: How to apply the Apache License to your work + +To apply the Apache License to your work, attach the following boilerplate +notice, with the fields enclosed by brackets "[]" replaced with your own +identifying information. (Don't include the brackets!) The text should be +enclosed in the appropriate comment syntax for the file format. We also +recommend that a file or class name and description of purpose be included on +the same "printed page" as the copyright notice for easier identification within +third-party archives. +Copyright [yyyy] [name of copyright owner] 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. +Apache Projects + +* HTTP Server +* ActiveMQ +* Ant +* APR +* Archiva +* Beehive +* Cayenne +* Cocoon +* Commons +* Continuum +* CXF +* DB +* Directory +* Excalibur +* Felix +* Forrest +* Geronimo +* Gump +* Hadoop +* Harmony +* HiveMind +* HttpComponents +* iBATIS +* Incubator +* Jackrabbit +* Jakarta +* James +* Labs +* Lenya +* Logging +* Lucene +* Maven +* Mina +* MyFaces +* ODE +* OFBiz +* OpenEJB +* OpenJPA +* Perl +* POI +* Portals +* Roller +* Santuario +* ServiceMix +* Shale +* SpamAssassin +* STDCXX +* Struts +* Synapse +* Tapestry +* TCL +* Tiles +* Tomcat +* Turbine +* Velocity +* Wicket +* Web Services +* Xalan +* Xerces +* XML +* XMLBeans +* XML Graphics + +Foundation + +* FAQ +* Licenses +* News +* Public Records +* Sponsorship +* Donations +* Thanks +* Contact + +Foundation Projects + +* Conferences +* Infrastructure +* JCP + +How it works + +* Introduction +* Meritocracy +* Structure +* Roles +* Collaboration +* Infrastructure +* Incubator +* Other entities +* Glossary +* Voting + +Get Involved + +* Mailing Lists +* Version Control +* Developer Info + +Download + +* from a mirror + +Related Sites + +* ApacheCon +* Bookstore +* Feathercast +* PlanetApache + +Copyright � 2008 The Apache Software Foundation, Licensed under the Apache +License, Version 2.0. +*************************************************************************** +%%The following software may be included in this product: +FreeHost3270 +Use of any of this software is governed by the terms of the license below: +GNU LESSER GENERAL PUBLIC LICENSE + +Version 2.1, February 1999 + +Copyright (C) 1991, 1999 Free Software Foundation, Inc. +51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA +Everyone is permitted to copy and distribute verbatim copies +of this license document, but changing it is not allowed. + +[This is the first released version of the Lesser GPL. It also counts +as the successor of the GNU Library Public License, version 2, hence +the version number 2.1.] +Preamble + +The licenses for most software are designed to take away your freedom to share and change it. By +contrast, the GNU General Public Licenses are intended to guarantee your freedom to share and change +free software--to make sure the software is free for all its users. + +This license, the Lesser General Public License, applies to some specially designated software +packages--typically libraries--of the Free Software Foundation and other authors who decide to use it. +You can use it too, but we suggest you first think carefully about whether this license or the ordinary +General Public License is the better strategy to use in any particular case, based on the explanations +below. + +When we speak of free software, we are referring to freedom of use, not price. Our General Public +Licenses are designed to make sure that you have the freedom to distribute copies of free software (and +charge for this service if you wish); that you receive source code or can get it if you want it; that you +can change the software and use pieces of it in new free programs; and that you are informed that you +can do these things. + +To protect your rights, we need to make restrictions that forbid distributors to deny you these rights or +to ask you to surrender these rights. These restrictions translate to certain responsibilities for you if +you distribute copies of the library or if you modify it. + +For example, if you distribute copies of the library, whether gratis or for a fee, you must give the +recipients all the rights that we gave you. You must make sure that they, too, receive or can get the +source code. If you link other code with the library, you must provide complete object files to the +recipients, so that they can relink them with the library after making changes to the library and +recompiling it. And you must show them these terms so they know their rights. + +We protect your rights with a two-step method: (1) we copyright the library, and (2) we offer you this +license, which gives you legal permission to copy, distribute and/or modify the library. + +To protect each distributor, we want to make it very clear that there is no warranty for the free library. +Also, if the library is modified by someone else and passed on, the recipients should know that what +they have is not the original version, so that the original author's reputation will not be affected by +problems that might be introduced by others. + +Finally, software patents pose a constant threat to the existence of any free program. We wish to make +sure that a company cannot effectively restrict the users of a free program by obtaining a restrictive +license from a patent holder. Therefore, we insist that any patent license obtained for a version of the +library must be consistent with the full freedom of use specified in this license. + +Most GNU software, including some libraries, is covered by the ordinary GNU General Public License. +This license, the GNU Lesser General Public License, applies to certain designated libraries, and is quite +different from the ordinary General Public License. We use this license for certain libraries in order to +permit linking those libraries into non-free programs. + +When a program is linked with a library, whether statically or using a shared library, the combination of +the two is legally speaking a combined work, a derivative of the original library. The ordinary General +Public License therefore permits such linking only if the entire combination fits its criteria of freedom. +The Lesser General Public License permits more lax criteria for linking other code with the library. + +We call this license the "Lesser" General Public License because it does Less to protect the user's +freedom than the ordinary General Public License. It also provides other free software developers Less +of an advantage over competing non-free programs. These disadvantages are the reason we use the +ordinary General Public License for many libraries. However, the Lesser license provides advantages in +certain special circumstances. + +For example, on rare occasions, there may be a special need to encourage the widest possible use of a +certain library, so that it becomes a de-facto standard. To achieve this, non-free programs must be +allowed to use the library. A more frequent case is that a free library does the same job as widely used +non-free libraries. In this case, there is little to gain by limiting the free library to free software only, so +we use the Lesser General Public License. + +In other cases, permission to use a particular library in non-free programs enables a greater number of +people to use a large body of free software. For example, permission to use the GNU C Library in non- +free programs enables many more people to use the whole GNU operating system, as well as its variant, +the GNU/Linux operating system. + +Although the Lesser General Public License is Less protective of the users' freedom, it does ensure that +the user of a program that is linked with the Library has the freedom and the wherewithal to run that +program using a modified version of the Library. + +The precise terms and conditions for copying, distribution and modification follow. Pay close attention +to the difference between a "work based on the library" and a "work that uses the library". The former +contains code derived from the library, whereas the latter must be combined with the library in order to +run. + +TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION + +0. This License Agreement applies to any software library or other program which contains a notice +placed by the copyright holder or other authorized party saying it may be distributed under the terms +of this Lesser General Public License (also called "this License"). Each licensee is addressed as "you". + +A "library" means a collection of software functions and/or data prepared so as to be conveniently +linked with application programs (which use some of those functions and data) to form executables. + +The "Library", below, refers to any such software library or work which has been distributed under these +terms. A "work based on the Library" means either the Library or any derivative work under copyright +law: that is to say, a work containing the Library or a portion of it, either verbatim or with modifications +and/or translated straightforwardly into another language. (Hereinafter, translation is included without +limitation in the term "modification".) + +"Source code" for a work means the preferred form of the work for making modifications to it. For a +library, complete source code means all the source code for all modules it contains, plus any associated +interface definition files, plus the scripts used to control compilation and installation of the library. + +Activities other than copying, distribution and modification are not covered by this License; they are +outside its scope. The act of running a program using the Library is not restricted, and output from +such a program is covered only if its contents constitute a work based on the Library (independent of +the use of the Library in a tool for writing it). Whether that is true depends on what the Library does +and what the program that uses the Library does. + +1. You may copy and distribute verbatim copies of the Library's complete source code as you receive it, +in any medium, provided that you conspicuously and appropriately publish on each copy an +appropriate copyright notice and disclaimer of warranty; keep intact all the notices that refer to this +License and to the absence of any warranty; and distribute a copy of this License along with the Library. + +You may charge a fee for the physical act of transferring a copy, and you may at your option offer +warranty protection in exchange for a fee. + +2. You may modify your copy or copies of the Library or any portion of it, thus forming a work based +on the Library, and copy and distribute such modifications or work under the terms of Section 1 above, +provided that you also meet all of these conditions: + +a) The modified work must itself be a software library. +b) You must cause the files modified to carry prominent notices stating that you changed the files and +the date of any change. +c) You must cause the whole of the work to be licensed at no charge to all third parties under the terms +of this License. +d) If a facility in the modified Library refers to a function or a table of data to be supplied by an +application program that uses the facility, other than as an argument passed when the facility is +invoked, then you must make a good faith effort to ensure that, in the event an application does not +supply such function or table, the facility still operates, and performs whatever part of its purpose +remains meaningful. +(For example, a function in a library to compute square roots has a purpose that is entirely well- +defined independent of the application. Therefore, Subsection 2d requires that any application- +supplied function or table used by this function must be optional: if the application does not supply it, +the square root function must still compute square roots.) + +These requirements apply to the modified work as a whole. If identifiable sections of that work are not +derived from the Library, and can be reasonably considered independent and separate works in +themselves, then this License, and its terms, do not apply to those sections when you distribute them as +separate works. But when you distribute the same sections as part of a whole which is a work based on +the Library, the distribution of the whole must be on the terms of this License, whose permissions for +other licensees extend to the entire whole, and thus to each and every part regardless of who wrote it. +Thus, it is not the intent of this section to claim rights or contest your rights to work written entirely by +you; rather, the intent is to exercise the right to control the distribution of derivative or collective works +based on the Library. + +In addition, mere aggregation of another work not based on the Library with the Library (or with a work +based on the Library) on a volume of a storage or distribution medium does not bring the other work +under the scope of this License. + +3. You may opt to apply the terms of the ordinary GNU General Public License instead of this License to +a given copy of the Library. To do this, you must alter all the notices that refer to this License, so that +they refer to the ordinary GNU General Public License, version 2, instead of to this License. (If a newer +version than version 2 of the ordinary GNU General Public License has appeared, then you can specify +that version instead if you wish.) Do not make any other change in these notices. + +Once this change is made in a given copy, it is irreversible for that copy, so the ordinary GNU General +Public License applies to all subsequent copies and derivative works made from that copy. + +This option is useful when you wish to copy part of the code of the Library into a program that is not a +library. + +4. You may copy and distribute the Library (or a portion or derivative of it, under Section 2) in object +code or executable form under the terms of Sections 1 and 2 above provided that you accompany it +with the complete corresponding machine-readable source code, which must be distributed under the +terms of Sections 1 and 2 above on a medium customarily used for software interchange. + +If distribution of object code is made by offering access to copy from a designated place, then offering +equivalent access to copy the source code from the same place satisfies the requirement to distribute +the source code, even though third parties are not compelled to copy the source along with the object +code. + +5. A program that contains no derivative of any portion of the Library, but is designed to work with the +Library by being compiled or linked with it, is called a "work that uses the Library". Such a work, in +isolation, is not a derivative work of the Library, and therefore falls outside the scope of this License. + +However, linking a "work that uses the Library" with the Library creates an executable that is a +derivative of the Library (because it contains portions of the Library), rather than a "work that uses the +library". The executable is therefore covered by this License. Section 6 states terms for distribution of +such executables. + +When a "work that uses the Library" uses material from a header file that is part of the Library, the +object code for the work may be a derivative work of the Library even though the source code is not. +Whether this is true is especially significant if the work can be linked without the Library, or if the work +is itself a library. The threshold for this to be true is not precisely defined by law. + +If such an object file uses only numerical parameters, data structure layouts and accessors, and small +macros and small inline functions (ten lines or less in length), then the use of the object file is +unrestricted, regardless of whether it is legally a derivative work. (Executables containing this object +code plus portions of the Library will still fall under Section 6.) + +Otherwise, if the work is a derivative of the Library, you may distribute the object code for the work +under the terms of Section 6. Any executables containing that work also fall under Section 6, whether +or not they are linked directly with the Library itself. + +6. As an exception to the Sections above, you may also combine or link a "work that uses the Library" +with the Library to produce a work containing portions of the Library, and distribute that work under +terms of your choice, provided that the terms permit modification of the work for the customer's own +use and reverse engineering for debugging such modifications. + +You must give prominent notice with each copy of the work that the Library is used in it and that the +Library and its use are covered by this License. You must supply a copy of this License. If the work +during execution displays copyright notices, you must include the copyright notice for the Library +among them, as well as a reference directing the user to the copy of this License. Also, you must do +one of these things: + +a) Accompany the work with the complete corresponding machine-readable source code for the Library +including whatever changes were used in the work (which must be distributed under Sections 1 and 2 +above); and, if the work is an executable linked with the Library, with the complete machine-readable +"work that uses the Library", as object code and/or source code, so that the user can modify the Library +and then relink to produce a modified executable containing the modified Library. (It is understood that +the user who changes the contents of definitions files in the Library will not necessarily be able to +recompile the application to use the modified definitions.) +b) Use a suitable shared library mechanism for linking with the Library. A suitable mechanism is one +that (1) uses at run time a copy of the library already present on the user's computer system, rather +than copying library functions into the executable, and (2) will operate properly with a modified version +of the library, if the user installs one, as long as the modified version is interface-compatible with the +version that the work was made with. +c) Accompany the work with a written offer, valid for at least three years, to give the same user the +materials specified in Subsection 6a, above, for a charge no more than the cost of performing this +distribution. +d) If distribution of the work is made by offering access to copy from a designated place, offer +equivalent access to copy the above specified materials from the same place. +e) Verify that the user has already received a copy of these materials or that you have already sent this +user a copy. +For an executable, the required form of the "work that uses the Library" must include any data and +utility programs needed for reproducing the executable from it. However, as a special exception, the +materials to be distributed need not include anything that is normally distributed (in either source or +binary form) with the major components (compiler, kernel, and so on) of the operating system on which +the executable runs, unless that component itself accompanies the executable. + +It may happen that this requirement contradicts the license restrictions of other proprietary libraries +that do not normally accompany the operating system. Such a contradiction means you cannot use both +them and the Library together in an executable that you distribute. + +7. You may place library facilities that are a work based on the Library side-by-side in a single library +together with other library facilities not covered by this License, and distribute such a combined library, +provided that the separate distribution of the work based on the Library and of the other library +facilities is otherwise permitted, and provided that you do these two things: + +a) Accompany the combined library with a copy of the same work based on the Library, uncombined +with any other library facilities. This must be distributed under the terms of the Sections above. +b) Give prominent notice with the combined library of the fact that part of it is a work based on the +Library, and explaining where to find the accompanying uncombined form of the same work. +8. You may not copy, modify, sublicense, link with, or distribute the Library except as expressly +provided under this License. Any attempt otherwise to copy, modify, sublicense, link with, or distribute +the Library is void, and will automatically terminate your rights under this License. However, parties +who have received copies, or rights, from you under this License will not have their licenses terminated +so long as such parties remain in full compliance. + +9. You are not required to accept this License, since you have not signed it. However, nothing else +grants you permission to modify or distribute the Library or its derivative works. These actions are +prohibited by law if you do not accept this License. Therefore, by modifying or distributing the Library +(or any work based on the Library), you indicate your acceptance of this License to do so, and all its +terms and conditions for copying, distributing or modifying the Library or works based on it. + +10. Each time you redistribute the Library (or any work based on the Library), the recipient +automatically receives a license from the original licensor to copy, distribute, link with or modify the +Library subject to these terms and conditions. You may not impose any further restrictions on the +recipients' exercise of the rights granted herein. You are not responsible for enforcing compliance by +third parties with this License. + +11. If, as a consequence of a court judgment or allegation of patent infringement or for any other +reason (not limited to patent issues), conditions are imposed on you (whether by court order, +agreement or otherwise) that contradict the conditions of this License, they do not excuse you from the +conditions of this License. If you cannot distribute so as to satisfy simultaneously your obligations +under this License and any other pertinent obligations, then as a consequence you may not distribute +the Library at all. For example, if a patent license would not permit royalty-free redistribution of the +Library by all those who receive copies directly or indirectly through you, then the only way you could +satisfy both it and this License would be to refrain entirely from distribution of the Library. + +If any portion of this section is held invalid or unenforceable under any particular circumstance, the +balance of the section is intended to apply, and the section as a whole is intended to apply in other +circumstances. + +It is not the purpose of this section to induce you to infringe any patents or other property right claims +or to contest validity of any such claims; this section has the sole purpose of protecting the integrity of +the free software distribution system which is implemented by public license practices. Many people +have made generous contributions to the wide range of software distributed through that system in +reliance on consistent application of that system; it is up to the author/donor to decide if he or she is +willing to distribute software through any other system and a licensee cannot impose that choice. + +This section is intended to make thoroughly clear what is believed to be a consequence of the rest of +this License. + +12. If the distribution and/or use of the Library is restricted in certain countries either by patents or by +copyrighted interfaces, the original copyright holder who places the Library under this License may add +an explicit geographical distribution limitation excluding those countries, so that distribution is +permitted only in or among countries not thus excluded. In such case, this License incorporates the +limitation as if written in the body of this License. + +13. The Free Software Foundation may publish revised and/or new versions of the Lesser General Public +License from time to time. Such new versions will be similar in spirit to the present version, but may +differ in detail to address new problems or concerns. + +Each version is given a distinguishing version number. If the Library specifies a version number of this +License which applies to it and "any later version", you have the option of following the terms and +conditions either of that version or of any later version published by the Free Software Foundation. If +the Library does not specify a license version number, you may choose any version ever published by +the Free Software Foundation. + +14. If you wish to incorporate parts of the Library into other free programs whose distribution +conditions are incompatible with these, write to the author to ask for permission. For software which is +copyrighted by the Free Software Foundation, write to the Free Software Foundation; we sometimes +make exceptions for this. Our decision will be guided by the two goals of preserving the free status of +all derivatives of our free software and of promoting the sharing and reuse of software generally. + +NO WARRANTY + +15. BECAUSE THE LIBRARY IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY FOR THE LIBRARY, +TO THE EXTENT PERMITTED BY APPLICABLE LAW. EXCEPT WHEN OTHERWISE STATED IN WRITING THE +COPYRIGHT HOLDERS AND/OR OTHER PARTIES PROVIDE THE LIBRARY "AS IS" WITHOUT WARRANTY OF +ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES +OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE ENTIRE RISK AS TO THE QUALITY +AND PERFORMANCE OF THE LIBRARY IS WITH YOU. SHOULD THE LIBRARY PROVE DEFECTIVE, YOU +ASSUME THE COST OF ALL NECESSARY SERVICING, REPAIR OR CORRECTION. + +16. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING WILL ANY +COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY AND/OR REDISTRIBUTE THE LIBRARY AS +PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL +OR CONSEQUENTIAL DAMAGES ARISING OUT OF THE USE OR INABILITY TO USE THE LIBRARY +(INCLUDING BUT NOT LIMITED TO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR LOSSES +SUSTAINED BY YOU OR THIRD PARTIES OR A FAILURE OF THE LIBRARY TO OPERATE WITH ANY OTHER +SOFTWARE), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH +DAMAGES. + +END OF TERMS AND CONDITIONS + +How to Apply These Terms to Your New Libraries + +If you develop a new library, and you want it to be of the greatest possible use to the public, we +recommend making it free software that everyone can redistribute and change. You can do so by +permitting redistribution under these terms (or, alternatively, under the terms of the ordinary General +Public License). + +To apply these terms, attach the following notices to the library. It is safest to attach them to the start +of each source file to most effectively convey the exclusion of warranty; and each file should have at +least the "copyright" line and a pointer to where the full notice is found. + +one line to give the library's name and an idea of what it does. +Copyright (C) year name of author + +This library is free software; you can redistribute it and/or +modify it under the terms of the GNU Lesser General Public +License as published by the Free Software Foundation; either +version 2.1 of the License, or (at your option) any later version. + +This library is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +Lesser General Public License for more details. + +You should have received a copy of the GNU Lesser General Public +License along with this library; if not, write to the Free Software +Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA +Also add information on how to contact you by electronic and paper mail. + +You should also get your employer (if you work as a programmer) or your school, if any, to sign a +"copyright disclaimer" for the library, if necessary. Here is a sample; alter the names: + +Yoyodyne, Inc., hereby disclaims all copyright interest in +the library `Frob' (a library for tweaking knobs) written +by James Random Hacker. + +signature of Ty Coon, 1 April 1990 +Ty Coon, President of Vice +That's all there is to it! +*************************************************************************** +%%The following software may be included in this product: +Boo +Use of any of this software is governed by the terms of the license below: +Copyright (c) 2003, 2004, Rodrigo B. de Oliveira (rbo@acm.org) +All rights reserved. + +Redistribution and use in source and binary forms, with or without modification, +are permitted provided that the following conditions are met: + +* Redistributions of source code must retain the above copyright notice, +this list of conditions and the following disclaimer. +* Redistributions in binary form must reproduce the above copyright notice, +this list of conditions and the following disclaimer in the documentation +and/or other materials provided with the distribution. +* Neither the name of Rodrigo B. de Oliveira nor the names of its +contributors may be used to endorse or promote products derived from this +software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND +ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED +WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE +FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL +DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR +SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER +CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, +OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF +THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +*************************************************************************** + diff --git a/license.txt b/license.txt new file mode 100644 index 00000000..1717cf28 --- /dev/null +++ b/license.txt @@ -0,0 +1,383 @@ + + +COMMON DEVELOPMENT AND DISTRIBUTION LICENSE (CDDL) Version 1.0 + +1. Definitions. + +1.1. "Contributor" means each individual or entity that +creates or contributes to the creation of Modifications. + +1.2. "Contributor Version" means the combination of the +Original Software, prior Modifications used by a +Contributor (if any), and the Modifications made by that +particular Contributor. + +1.3. "Covered Software" means (a) the Original Software, or +(b) Modifications, or (c) the combination of files +containing Original Software with files containing +Modifications, in each case including portions thereof. + +1.4. "Executable" means the Covered Software in any form +other than Source Code. + +1.5. "Initial Developer" means the individual or entity +that first makes Original Software available under this +License. + +1.6. "Larger Work" means a work which combines Covered +Software or portions thereof with code not governed by the +terms of this License. + +1.7. "License" means this document. + +1.8. "Licensable" means having the right to grant, to the +maximum extent possible, whether at the time of the initial +grant or subsequently acquired, any and all of the rights +conveyed herein. + +1.9. "Modifications" means the Source Code and Executable +form of any of the following: + +A. Any file that results from an addition to, +deletion from or modification of the contents of a +file containing Original Software or previous +Modifications; + +B. Any new file that contains any part of the +Original Software or previous Modification; or + +C. Any new file that is contributed or otherwise made +available under the terms of this License. + +1.10. "Original Software" means the Source Code and +Executable form of computer software code that is +originally released under this License. + +1.11. "Patent Claims" means any patent claim(s), now owned +or hereafter acquired, including without limitation, +method, process, and apparatus claims, in any patent +Licensable by grantor. + +1.12. "Source Code" means (a) the common form of computer +software code in which modifications are made and (b) +associated documentation included in or with such code. + +1.13. "You" (or "Your") means an individual or a legal +entity exercising rights under, and complying with all of +the terms of, this License. For legal entities, "You" +includes any entity which controls, is controlled by, or is +under common control with You. For purposes of this +definition, "control" means (a) the power, direct or +indirect, to cause the direction or management of such +entity, whether by contract or otherwise, or (b) ownership +of more than fifty percent (50%) of the outstanding shares +or beneficial ownership of such entity. + +2. License Grants. + +2.1. The Initial Developer Grant. + +Conditioned upon Your compliance with Section 3.1 below and +subject to third party intellectual property claims, the +Initial Developer hereby grants You a world-wide, +royalty-free, non-exclusive license: + +(a) under intellectual property rights (other than +patent or trademark) Licensable by Initial Developer, +to use, reproduce, modify, display, perform, +sublicense and distribute the Original Software (or +portions thereof), with or without Modifications, +and/or as part of a Larger Work; and + +(b) under Patent Claims infringed by the making, +using or selling of Original Software, to make, have +made, use, practice, sell, and offer for sale, and/or +otherwise dispose of the Original Software (or +portions thereof). + +(c) The licenses granted in Sections 2.1(a) and (b) +are effective on the date Initial Developer first +distributes or otherwise makes the Original Software +available to a third party under the terms of this +License. + +(d) Notwithstanding Section 2.1(b) above, no patent +license is granted: (1) for code that You delete from +the Original Software, or (2) for infringements +caused by: (i) the modification of the Original +Software, or (ii) the combination of the Original +Software with other software or devices. + +2.2. Contributor Grant. + +Conditioned upon Your compliance with Section 3.1 below and +subject to third party intellectual property claims, each +Contributor hereby grants You a world-wide, royalty-free, +non-exclusive license: + +(a) under intellectual property rights (other than +patent or trademark) Licensable by Contributor to +use, reproduce, modify, display, perform, sublicense +and distribute the Modifications created by such +Contributor (or portions thereof), either on an +unmodified basis, with other Modifications, as +Covered Software and/or as part of a Larger Work; and + +(b) under Patent Claims infringed by the making, +using, or selling of Modifications made by that +Contributor either alone and/or in combination with +its Contributor Version (or portions of such +combination), to make, use, sell, offer for sale, +have made, and/or otherwise dispose of: (1) +Modifications made by that Contributor (or portions +thereof); and (2) the combination of Modifications +made by that Contributor with its Contributor Version +(or portions of such combination). + +(c) The licenses granted in Sections 2.2(a) and +2.2(b) are effective on the date Contributor first +distributes or otherwise makes the Modifications +available to a third party. + +(d) Notwithstanding Section 2.2(b) above, no patent +license is granted: (1) for any code that Contributor +has deleted from the Contributor Version; (2) for +infringements caused by: (i) third party +modifications of Contributor Version, or (ii) the +combination of Modifications made by that Contributor +with other software (except as part of the +Contributor Version) or other devices; or (3) under +Patent Claims infringed by Covered Software in the +absence of Modifications made by that Contributor. + +3. Distribution Obligations. + +3.1. Availability of Source Code. + +Any Covered Software that You distribute or otherwise make +available in Executable form must also be made available in +Source Code form and that Source Code form must be +distributed only under the terms of this License. You must +include a copy of this License with every copy of the +Source Code form of the Covered Software You distribute or +otherwise make available. You must inform recipients of any +such Covered Software in Executable form as to how they can +obtain such Covered Software in Source Code form in a +reasonable manner on or through a medium customarily used +for software exchange. + +3.2. Modifications. + +The Modifications that You create or to which You +contribute are governed by the terms of this License. You +represent that You believe Your Modifications are Your +original creation(s) and/or You have sufficient rights to +grant the rights conveyed by this License. + +3.3. Required Notices. + +You must include a notice in each of Your Modifications +that identifies You as the Contributor of the Modification. +You may not remove or alter any copyright, patent or +trademark notices contained within the Covered Software, or +any notices of licensing or any descriptive text giving +attribution to any Contributor or the Initial Developer. + +3.4. Application of Additional Terms. + +You may not offer or impose any terms on any Covered +Software in Source Code form that alters or restricts the +applicable version of this License or the recipients' +rights hereunder. You may choose to offer, and to charge a +fee for, warranty, support, indemnity or liability +obligations to one or more recipients of Covered Software. +However, you may do so only on Your own behalf, and not on +behalf of the Initial Developer or any Contributor. You +must make it absolutely clear that any such warranty, +support, indemnity or liability obligation is offered by +You alone, and You hereby agree to indemnify the Initial +Developer and every Contributor for any liability incurred +by the Initial Developer or such Contributor as a result of +warranty, support, indemnity or liability terms You offer. + +3.5. Distribution of Executable Versions. + +You may distribute the Executable form of the Covered +Software under the terms of this License or under the terms +of a license of Your choice, which may contain terms +different from this License, provided that You are in +compliance with the terms of this License and that the +license for the Executable form does not attempt to limit +or alter the recipient's rights in the Source Code form +from the rights set forth in this License. If You +distribute the Covered Software in Executable form under a +different license, You must make it absolutely clear that +any terms which differ from this License are offered by You +alone, not by the Initial Developer or Contributor. You +hereby agree to indemnify the Initial Developer and every +Contributor for any liability incurred by the Initial +Developer or such Contributor as a result of any such terms +You offer. + +3.6. Larger Works. + +You may create a Larger Work by combining Covered Software +with other code not governed by the terms of this License +and distribute the Larger Work as a single product. In such +a case, You must make sure the requirements of this License +are fulfilled for the Covered Software. + +4. Versions of the License. + +4.1. New Versions. + +Sun Microsystems, Inc. is the initial license steward and +may publish revised and/or new versions of this License +from time to time. Each version will be given a +distinguishing version number. Except as provided in +Section 4.3, no one other than the license steward has the +right to modify this License. + +4.2. Effect of New Versions. + +You may always continue to use, distribute or otherwise +make the Covered Software available under the terms of the +version of the License under which You originally received +the Covered Software. If the Initial Developer includes a +notice in the Original Software prohibiting it from being +distributed or otherwise made available under any +subsequent version of the License, You must distribute and +make the Covered Software available under the terms of the +version of the License under which You originally received +the Covered Software. Otherwise, You may also choose to +use, distribute or otherwise make the Covered Software +available under the terms of any subsequent version of the +License published by the license steward. + +4.3. Modified Versions. + +When You are an Initial Developer and You want to create a +new license for Your Original Software, You may create and +use a modified version of this License if You: (a) rename +the license and remove any references to the name of the +license steward (except to note that the license differs +from this License); and (b) otherwise make it clear that +the license contains terms which differ from this License. + +5. DISCLAIMER OF WARRANTY. + +COVERED SOFTWARE IS PROVIDED UNDER THIS LICENSE ON AN "AS IS" +BASIS, WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED OR IMPLIED, +INCLUDING, WITHOUT LIMITATION, WARRANTIES THAT THE COVERED +SOFTWARE IS FREE OF DEFECTS, MERCHANTABLE, FIT FOR A PARTICULAR +PURPOSE OR NON-INFRINGING. THE ENTIRE RISK AS TO THE QUALITY AND +PERFORMANCE OF THE COVERED SOFTWARE IS WITH YOU. SHOULD ANY +COVERED SOFTWARE PROVE DEFECTIVE IN ANY RESPECT, YOU (NOT THE +INITIAL DEVELOPER OR ANY OTHER CONTRIBUTOR) ASSUME THE COST OF +ANY NECESSARY SERVICING, REPAIR OR CORRECTION. THIS DISCLAIMER OF +WARRANTY CONSTITUTES AN ESSENTIAL PART OF THIS LICENSE. NO USE OF +ANY COVERED SOFTWARE IS AUTHORIZED HEREUNDER EXCEPT UNDER THIS +DISCLAIMER. + +6. TERMINATION. + +6.1. This License and the rights granted hereunder will +terminate automatically if You fail to comply with terms +herein and fail to cure such breach within 30 days of +becoming aware of the breach. Provisions which, by their +nature, must remain in effect beyond the termination of +this License shall survive. + +6.2. If You assert a patent infringement claim (excluding +declaratory judgment actions) against Initial Developer or +a Contributor (the Initial Developer or Contributor against +whom You assert such claim is referred to as "Participant") +alleging that the Participant Software (meaning the +Contributor Version where the Participant is a Contributor +or the Original Software where the Participant is the +Initial Developer) directly or indirectly infringes any +patent, then any and all rights granted directly or +indirectly to You by such Participant, the Initial +Developer (if the Initial Developer is not the Participant) +and all Contributors under Sections 2.1 and/or 2.2 of this +License shall, upon 60 days notice from Participant +terminate prospectively and automatically at the expiration +of such 60 day notice period, unless if within such 60 day +period You withdraw Your claim with respect to the +Participant Software against such Participant either +unilaterally or pursuant to a written agreement with +Participant. + +6.3. In the event of termination under Sections 6.1 or 6.2 +above, all end user licenses that have been validly granted +by You or any distributor hereunder prior to termination +(excluding licenses granted to You by any distributor) +shall survive termination. + +7. LIMITATION OF LIABILITY. + +UNDER NO CIRCUMSTANCES AND UNDER NO LEGAL THEORY, WHETHER TORT +(INCLUDING NEGLIGENCE), CONTRACT, OR OTHERWISE, SHALL YOU, THE +INITIAL DEVELOPER, ANY OTHER CONTRIBUTOR, OR ANY DISTRIBUTOR OF +COVERED SOFTWARE, OR ANY SUPPLIER OF ANY OF SUCH PARTIES, BE +LIABLE TO ANY PERSON FOR ANY INDIRECT, SPECIAL, INCIDENTAL, OR +CONSEQUENTIAL DAMAGES OF ANY CHARACTER INCLUDING, WITHOUT +LIMITATION, DAMAGES FOR LOST PROFITS, LOSS OF GOODWILL, WORK +STOPPAGE, COMPUTER FAILURE OR MALFUNCTION, OR ANY AND ALL OTHER +COMMERCIAL DAMAGES OR LOSSES, EVEN IF SUCH PARTY SHALL HAVE BEEN +INFORMED OF THE POSSIBILITY OF SUCH DAMAGES. THIS LIMITATION OF +LIABILITY SHALL NOT APPLY TO LIABILITY FOR DEATH OR PERSONAL +INJURY RESULTING FROM SUCH PARTY'S NEGLIGENCE TO THE EXTENT +APPLICABLE LAW PROHIBITS SUCH LIMITATION. SOME JURISDICTIONS DO +NOT ALLOW THE EXCLUSION OR LIMITATION OF INCIDENTAL OR +CONSEQUENTIAL DAMAGES, SO THIS EXCLUSION AND LIMITATION MAY NOT +APPLY TO YOU. + +8. U.S. GOVERNMENT END USERS. + +The Covered Software is a "commercial item," as that term is +defined in 48 C.F.R. 2.101 (Oct. 1995), consisting of "commercial +computer software" (as that term is defined at 48 C.F.R. $ +252.227-7014(a)(1)) and "commercial computer software +documentation" as such terms are used in 48 C.F.R. 12.212 (Sept. +1995). Consistent with 48 C.F.R. 12.212 and 48 C.F.R. 227.7202-1 +through 227.7202-4 (June 1995), all U.S. Government End Users +acquire Covered Software with only those rights set forth herein. +This U.S. Government Rights clause is in lieu of, and supersedes, +any other FAR, DFAR, or other clause or provision that addresses +Government rights in computer software under this License. + +9. MISCELLANEOUS. + +This License represents the complete agreement concerning subject +matter hereof. If any provision of this License is held to be +unenforceable, such provision shall be reformed only to the +extent necessary to make it enforceable. This License shall be +governed by the law of the jurisdiction specified in a notice +contained within the Original Software (except to the extent +applicable law, if any, provides otherwise), excluding such +jurisdiction's conflict-of-law provisions. Any litigation +relating to this License shall be subject to the jurisdiction of +the courts located in the jurisdiction and venue specified in a +notice contained within the Original Software, with the losing +party responsible for costs, including, without limitation, court +costs and reasonable attorneys' fees and expenses. The +application of the United Nations Convention on Contracts for the +International Sale of Goods is expressly excluded. Any law or +regulation which provides that the language of a contract shall +be construed against the drafter shall not apply to this License. +You agree that You alone are responsible for compliance with the +United States export administration regulations (and the export +control laws and regulation of any other countries) when You use, +distribute or otherwise make available any Covered Software. + +10. RESPONSIBILITY FOR CLAIMS. + +As between Initial Developer and the Contributors, each party is +responsible for claims and damages arising, directly or +indirectly, out of its utilization of rights under this License +and You agree to work with Initial Developer and Contributors to +distribute such responsibility on an equitable basis. Nothing +herein is intended or shall be deemed to constitute any admission +of liability. + From 32deb8a680b79cb870f3379a780bcbcd981128f7 Mon Sep 17 00:00:00 2001 From: tknappek Date: Wed, 18 Feb 2009 14:35:31 +0000 Subject: [PATCH 193/342] Issue #428: Move test utilities to a separate library, dotnet side --- .../ActiveDirectoryConnectorTest.cs | 22 ++--- .../ActiveDirectoryConnectorTests.csproj | 16 ++-- DotNetConnectors.sln | 82 +++++++++---------- Framework/Framework.csproj | 3 +- FrameworkInternal/FrameworkInternal.csproj | 8 +- FrameworkInternal/Test.cs | 24 +++--- FrameworkTests/FrameworkTests.csproj | 8 +- FrameworkTests/TestHelperTests.cs | 4 +- ServiceInstall/ExtBuild.proj | 1 + TestCommon/FrameworkInternalBridge.cs | 58 +++++++++++++ {Framework => TestCommon}/Test.cs | 48 ++++++----- TestCommon/TestCommon.csproj | 64 +++++++++++++++ TestCommon/TestHelpersSpi.cs | 49 +++++++++++ TestCommon/version.txt | 1 + 14 files changed, 280 insertions(+), 108 deletions(-) create mode 100644 TestCommon/FrameworkInternalBridge.cs rename {Framework => TestCommon}/Test.cs (89%) create mode 100644 TestCommon/TestCommon.csproj create mode 100644 TestCommon/TestHelpersSpi.cs create mode 100644 TestCommon/version.txt diff --git a/ActiveDirectoryConnectorTests/ActiveDirectoryConnectorTest.cs b/ActiveDirectoryConnectorTests/ActiveDirectoryConnectorTest.cs index 9fcab2f7..f13c83cc 100644 --- a/ActiveDirectoryConnectorTests/ActiveDirectoryConnectorTest.cs +++ b/ActiveDirectoryConnectorTests/ActiveDirectoryConnectorTest.cs @@ -22,24 +22,20 @@ */ using System; using System.Collections.Generic; -using System.Linq; -using System.Text; -using NUnit.Framework; -using Org.IdentityConnectors.Framework; -using Org.IdentityConnectors.Framework.Spi; -using Org.IdentityConnectors.Framework.Spi.Operations; -using Org.IdentityConnectors.Framework.Common.Objects; -using Org.IdentityConnectors.Framework.Common.Objects.Filters; -using Org.IdentityConnectors.Framework.Test; -using Org.IdentityConnectors.Common.Security; using System.Diagnostics; -using System.Resources; using System.IO; +using System.Linq; using System.Security.AccessControl; using System.Security.Principal; -using Org.IdentityConnectors.Framework.Common.Exceptions; -using Org.IdentityConnectors.Common; +using System.Text; using System.Threading; +using NUnit.Framework; +using Org.IdentityConnectors.Common.Security; +using Org.IdentityConnectors.Framework.Common.Exceptions; +using Org.IdentityConnectors.Framework.Common.Objects; +using Org.IdentityConnectors.Framework.Common.Objects.Filters; +using Org.IdentityConnectors.Framework.Spi; +using Org.IdentityConnectors.Test.Common; namespace Org.IdentityConnectors.ActiveDirectory { diff --git a/ActiveDirectoryConnectorTests/ActiveDirectoryConnectorTests.csproj b/ActiveDirectoryConnectorTests/ActiveDirectoryConnectorTests.csproj index c8b2a110..43022473 100644 --- a/ActiveDirectoryConnectorTests/ActiveDirectoryConnectorTests.csproj +++ b/ActiveDirectoryConnectorTests/ActiveDirectoryConnectorTests.csproj @@ -1,4 +1,4 @@ - + diff --git a/TestCommon/FrameworkInternalBridge.cs b/TestCommon/FrameworkInternalBridge.cs new file mode 100644 index 00000000..ee942345 --- /dev/null +++ b/TestCommon/FrameworkInternalBridge.cs @@ -0,0 +1,58 @@ +/* + * ==================== + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER. + * + * Copyright 2007-2008 Sun Microsystems, Inc. All rights reserved. + * + * The contents of this file are subject to the terms of the Common Development + * and Distribution License("CDDL") (the "License"). You may not use this file + * except in compliance with the License. + * + * You can obtain a copy of the License at + * http://IdentityConnectors.dev.java.net/legal/license.txt + * See the License for the specific language governing permissions and limitations + * under the License. + * + * When distributing the Covered Code, include this CDDL Header Notice in each file + * and include the License file at identityconnectors/legal/license.txt. + * If applicable, add the following below this CDDL Header, with the fields + * enclosed by brackets [] replaced by your own identifying information: + * "Portions Copyrighted [year] [name of copyright owner]" + * ==================== + */ +using System; +using System.Reflection; +using System.Collections.Generic; +using Org.IdentityConnectors.Common; +using Org.IdentityConnectors.Common.Security; +using Org.IdentityConnectors.Framework.Spi; +using Org.IdentityConnectors.Framework.Api.Operations; +using Org.IdentityConnectors.Framework.Spi.Operations; +using Org.IdentityConnectors.Framework.Common.Objects; +namespace Org.IdentityConnectors.Test.Common +{ + internal static class FrameworkInternalBridge { + private static readonly Object LOCK = new Object(); + private static Assembly _assembly = null; + /// + /// Loads a class from the FrameworkInternal module + /// + /// + /// + public static SafeType LoadType(String typeName) where T : class { + + Assembly assembly; + lock(LOCK) { + if (_assembly == null) { + AssemblyName assemName = new AssemblyName(); + assemName.Name = "FrameworkInternal"; + _assembly = Assembly.Load(assemName); + } + assembly = _assembly; + } + + return SafeType.ForRawType(assembly.GetType(typeName,true)); + + } + } +} diff --git a/Framework/Test.cs b/TestCommon/Test.cs similarity index 89% rename from Framework/Test.cs rename to TestCommon/Test.cs index 1e2f0aeb..353183f4 100644 --- a/Framework/Test.cs +++ b/TestCommon/Test.cs @@ -31,14 +31,18 @@ using Org.IdentityConnectors.Common; using Org.IdentityConnectors.Framework.Api; using Org.IdentityConnectors.Framework.Api.Operations; -using Org.IdentityConnectors.Framework.Common; using Org.IdentityConnectors.Framework.Common.Objects; using Org.IdentityConnectors.Framework.Common.Objects.Filters; using Org.IdentityConnectors.Framework.Spi; using Org.IdentityConnectors.Framework.Spi.Operations; +using Org.IdentityConnectors.Test.Common.Spi; -namespace Org.IdentityConnectors.Framework.Test -{ +namespace Org.IdentityConnectors.Test.Common +{ + /// + /// which stores all connector objects into + /// list retrievable with . + /// public sealed class ToListResultsHandler { private IList _objects = new List(); @@ -53,14 +57,21 @@ public IList Objects { } } } - public abstract class TestHelpers { + + /// + /// Bag of utility methods useful to connector tests. + /// + public sealed class TestHelpers { + + private TestHelpers() { + } /** * Method for convenient testing of local connectors. */ public static APIConfiguration CreateTestConfiguration(SafeType clazz, Configuration config) { - return GetInstance().CreateTestConfigurationImpl(clazz, config); + return GetSpi().CreateTestConfiguration(clazz, config); } /** @@ -71,7 +82,7 @@ public static APIConfiguration CreateTestConfiguration(SafeType clazz * @return A dummy message catalog. */ public static ConnectorMessages CreateDummyMessages() { - return GetInstance().CreateDummyMessagesImpl(); + return GetSpi().CreateDummyMessages(); } public static IList SearchToList(SearchApiOp search, @@ -148,40 +159,29 @@ public static void Search(SearchOp search, Filter filter, ResultsHandler handler, OperationOptions options) where T : class { - GetInstance().SearchImpl(search, oclass, filter, handler, options); + GetSpi().Search(search, oclass, filter, handler, options); } - - + //At some point we might make this pluggable, but for now, hard-code private const String IMPL_NAME = "Org.IdentityConnectors.Framework.Impl.Test.TestHelpersImpl"; private static readonly object LOCK = new object(); - private static TestHelpers _instance; + private static TestHelpersSpi _instance; /** * Returns the instance of this factory. * @return The instance of this factory */ - private static TestHelpers GetInstance() { + private static TestHelpersSpi GetSpi() { lock(LOCK) { if (_instance == null) { - SafeType type = FrameworkInternalBridge.LoadType(IMPL_NAME); + SafeType type = FrameworkInternalBridge.LoadType(IMPL_NAME); _instance = type.CreateInstance(); } return _instance; } } - - abstract protected APIConfiguration CreateTestConfigurationImpl(SafeType clazz, - Configuration config); - abstract protected void SearchImpl(SearchOp search, - ObjectClass oclass, - Filter filter, - ResultsHandler handler, - OperationOptions options) where T : class; - - private static IDictionary _properties = null; private static readonly string PREFIX = Environment.GetEnvironmentVariable("USERPROFILE") + "/.connectors/"; public static readonly string GLOBAL_PROPS = "connectors.xml"; @@ -289,8 +289,6 @@ public static IDictionary LoadPropertiesFile(string filename) { reader.Close(); } return ret; - } - - abstract protected ConnectorMessages CreateDummyMessagesImpl(); + } } } diff --git a/TestCommon/TestCommon.csproj b/TestCommon/TestCommon.csproj new file mode 100644 index 00000000..593b0dc4 --- /dev/null +++ b/TestCommon/TestCommon.csproj @@ -0,0 +1,64 @@ + + + + Debug + AnyCPU + 9.0.21022 + 2.0 + {E6A207D2-E083-41BF-B522-D9D3EC09323E} + Library + Properties + Org.IdentityConnectors.Test.Common + TestCommon + v3.5 + 512 + + + true + full + false + bin\Debug\ + DEBUG;TRACE + prompt + 4 + + + pdbonly + true + bin\Release\ + TRACE + prompt + 4 + + + + + + 3.5 + + + 3.5 + + + 3.5 + + + + + + + + + + + + + {F140E8DA-52B4-4159-992A-9DA10EA8EEFB} + Common + + + {8B24461B-456A-4032-89A1-CD418F7B5B62} + Framework + + + \ No newline at end of file diff --git a/TestCommon/TestHelpersSpi.cs b/TestCommon/TestHelpersSpi.cs new file mode 100644 index 00000000..bab10bf7 --- /dev/null +++ b/TestCommon/TestHelpersSpi.cs @@ -0,0 +1,49 @@ +/* + * ==================== + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER. + * + * Copyright 2007-2008 Sun Microsystems, Inc. All rights reserved. + * + * The contents of this file are subject to the terms of the Common Development + * and Distribution License("CDDL") (the "License"). You may not use this file + * except in compliance with the License. + * + * You can obtain a copy of the License at + * http://IdentityConnectors.dev.java.net/legal/license.txt + * See the License for the specific language governing permissions and limitations + * under the License. + * + * When distributing the Covered Code, include this CDDL Header Notice in each file + * and include the License file at identityconnectors/legal/license.txt. + * If applicable, add the following below this CDDL Header, with the fields + * enclosed by brackets [] replaced by your own identifying information: + * "Portions Copyrighted [year] [name of copyright owner]" + * ==================== + */ +using Org.IdentityConnectors.Common; +using Org.IdentityConnectors.Framework.Api; +using Org.IdentityConnectors.Framework.Common.Objects; +using Org.IdentityConnectors.Framework.Common.Objects.Filters; +using Org.IdentityConnectors.Framework.Spi; +using Org.IdentityConnectors.Framework.Spi.Operations; + +namespace Org.IdentityConnectors.Test.Common.Spi +{ + /// + /// Private use only, do not implement! Use the methods in + /// instead. + /// + public interface TestHelpersSpi { + + APIConfiguration CreateTestConfiguration(SafeType clazz, + Configuration config); + + void Search(SearchOp search, + ObjectClass oclass, + Filter filter, + ResultsHandler handler, + OperationOptions options) where T : class; + + ConnectorMessages CreateDummyMessages(); + } +} \ No newline at end of file diff --git a/TestCommon/version.txt b/TestCommon/version.txt new file mode 100644 index 00000000..bd2666ab --- /dev/null +++ b/TestCommon/version.txt @@ -0,0 +1 @@ +1.0.0.0 \ No newline at end of file From 9e9ab1d9ce7db298c770bbf8645815292bb12efe Mon Sep 17 00:00:00 2001 From: kyarbro Date: Wed, 18 Feb 2009 22:29:11 +0000 Subject: [PATCH 194/342] Localized resources for ActiveDiretory connector --- ActiveDirectoryConnector/Messages.de-DE.resx | 282 +++++++++++++++++++ ActiveDirectoryConnector/Messages.fr-FR.resx | 282 +++++++++++++++++++ ActiveDirectoryConnector/Messages.it-IT.resx | 282 +++++++++++++++++++ ActiveDirectoryConnector/Messages.ja-JP.resx | 282 +++++++++++++++++++ ActiveDirectoryConnector/Messages.ko-KR.resx | 282 +++++++++++++++++++ ActiveDirectoryConnector/Messages.pt-BR.resx | 282 +++++++++++++++++++ ActiveDirectoryConnector/Messages.zh-CN.resx | 282 +++++++++++++++++++ ActiveDirectoryConnector/Messages.zh-TW.resx | 282 +++++++++++++++++++ 8 files changed, 2256 insertions(+) create mode 100755 ActiveDirectoryConnector/Messages.de-DE.resx create mode 100755 ActiveDirectoryConnector/Messages.fr-FR.resx create mode 100755 ActiveDirectoryConnector/Messages.it-IT.resx create mode 100755 ActiveDirectoryConnector/Messages.ja-JP.resx create mode 100755 ActiveDirectoryConnector/Messages.ko-KR.resx create mode 100755 ActiveDirectoryConnector/Messages.pt-BR.resx create mode 100755 ActiveDirectoryConnector/Messages.zh-CN.resx create mode 100755 ActiveDirectoryConnector/Messages.zh-TW.resx diff --git a/ActiveDirectoryConnector/Messages.de-DE.resx b/ActiveDirectoryConnector/Messages.de-DE.resx new file mode 100755 index 00000000..6c3f405e --- /dev/null +++ b/ActiveDirectoryConnector/Messages.de-DE.resx @@ -0,0 +1,282 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + text/microsoft-resx + + + 2.0 + + + System.Resources.ResXResourceReader, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + System.Resources.ResXResourceWriter, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + Windows Active Directory-Connector + + + Ausgangsverzeichnis erstellen + + + Konto des Verzeichnisadministrators + + + Passwort des Verzeichnisadministrators + + + Domänenname + + + Active Directory Domänencontroller-Hostname + + + Objektklasse für Benutzerobjekte + + + Untergeordnete Domänen durchsuchen + + + Container + + + Domänencontroller synchronisieren + + + Globalen Katalogserver synchronisieren + + + Suchkontext + + + Geben Sie an, ob das Ausgangsverzeichnis des Benutzers erstellt werden soll oder nicht. + + + Geben Sie den Benutzernamen des Administrators ein, mit dem das System authentifiziert werden soll. Die Einstellung kann entweder ein Benutzername oder 'domänenname'\'benutzername' sein. + + + Geben Sie das Passwort ein, dass bei der Authentifizierung verwendet werden soll. + + + Der Name der Windows-Domäne (z. B. windowsdomaene.eigenefirma.com) + + + Für die domänenübergreifende Administration geben Sie den Hostnamen, die IP-Adresse oder den Domänennamen des LDAP-Servers ein. + + + Geben Sie die Aktive Directory-Objektklasse für die Benutzerobjekte an, die auf dieser Ressource verwaltet werden. Die Standardeinstellung lautet User. Für die meisten Anwendungen ist diese Einstellung ausreichend. + + + Wählen Sie, ob bei Suchabfragen am Active Directory auch untergeordnete Domänen eingeschlossen werden sollen. Außerdem müssen die Attribute "Suchcontainer" und "Suchkontext synchronisieren" (siehe Synchronisationseinstellungen) auf die oberste übergeordnete Domäne festgelegt werden, z. B. DC=mydomain,DC=com. + + + Geben Sie ein Containerobjekt an, das als Standard-Root für alle Suchvorgänge fungiert. Es werden nur Objekte unter diesem Container durchsucht, es sei denn, in dem Suchvorgang werden explizit weitere Kriterien übergeben. Wenn Sie beispielsweise Benutzer aus dem Container "Users" abrufen wollen, geben Sie Folgendes ein: CN=Users,DC=MYDOMAIN,DC=COM. Wenn kein Wert angegeben wurde, wird der Wert des ''Base Container''-Attributs verwendet. + + + Bei einer aktiven Synchronisation zu verwendender Domänencontroller. Wird nur dann verwendet, wenn keine untergeordneten Domänen durchsucht werden. + + + Name des globalen Katalogservers. Dieser Name ist nur dann erforderlich, wenn untergeordnete Domänen durchsucht werden. + + + Geben Sie an, wo im ADSI-Verzeichnis gesucht werden soll, wenn versucht wird, das durch searchAttributes festgelegte Attribut in das native DN-Format aufzulösen. + + + Der Connector wurde nicht konfiguriert + + + Für die Objektklasse {0} wird kein Löschvorgang unterstützt. + + + Ungültige Objektklasse: {0} + + + Das Attribut {0} ist nicht im Connector-Objekt vorhanden. Synchronisierung kann nicht fortgesetzt werden + + + Der Name des funktionsbereiten Attributs kann nicht Null lauten + + + Für die Objektklasse {0} ist kein Synchronisierungsvorgang vorhanden. + + + Keine UID vorhanden + + + In der Connector-Konfiguration wurde eine ungültige Objektklasse angegeben. Die Objektklasse \'{0}\' konnte im Active Directory nicht gefunden werden + + + Zu dem Suchergebnis <Null> konnte kein Connector-Attribut hinzugefügt werden + + + Zu dem Verzeichniseintrag <Null> konnte kein Connector-Attribut hinzugefügt werden + + + Einzelner Wert wurde erwartet, es wurden aber mehrere Werte für das Attribut {0} gefunden + + + Die Objektklasse \'{0}\' ist für diesen Connector nicht zulässig + + + Für den Benutzer {0} wurden ungültige Anmeldedaten angegeben + + + Die Passwortablauffrist kann nur durch Zurücksetzen des Passworts zurückgesetzt werden. + + + Active Directory unterstützt das Sperren von Benutzern nicht. Der Benutzer kann nur freigegeben werden + + + Es wurde ein ungültiger Suchbereich angegeben: {0} + + + Bei der Validierung des Benutzers {0} ist ein Ausnahmefehler aufgetreten. Der Benutzer wurde erfolgreich authentifiziert, aber die GUI des Benutzers konnte nicht ermittelt werden. + + + Bei der Validierung des Benutzers {0} ist ein Ausnahmefehler aufgetreten. Der Benutzer wurde erfolgreich authentifiziert, aber die SID des Benutzers konnte nicht ermittelt werden. + + + Der Name des Verzeichnisadministrators wurde nicht angegeben. + + + Das Passwort des Verzeichnisadministrators wurde nicht angegeben. + + + Der Domänenname wurde nicht angegeben. + + + Die Objektklasse wurde nicht angegeben. + + + Der Suchcontainer wurde nicht angegeben. + + + Verwenden der Identity Manager Ressourcenadapter-Stilabfrage '{0}'. Dies sollte aktualisiert werden, um die neue Connector-Abfragesyntax zu verwenden. + + + Es wurde ein ungültiger Container angegeben: {0} + + + Shell-Skript Variablenpräfix + + + Ein Präfix, der zu allen Shell-Skript-Argumentnamen hinzugefügt wird. Beispiel: Wenn das Präfix auf "Connector_" gesetzt ist, wird das Argument "User" zu "Connector_User". + + + Das Benutzerkonto wurde gesperrt + + + Das Benutzerpasswort muss geändert werden + + + Für den Benutzer {0} ist das Benutzerkonto abgelaufen + + + Es wurde ein ungültiger Suchkontext angegeben: {0} + + \ No newline at end of file diff --git a/ActiveDirectoryConnector/Messages.fr-FR.resx b/ActiveDirectoryConnector/Messages.fr-FR.resx new file mode 100755 index 00000000..415467fa --- /dev/null +++ b/ActiveDirectoryConnector/Messages.fr-FR.resx @@ -0,0 +1,282 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + text/microsoft-resx + + + 2.0 + + + System.Resources.ResXResourceReader, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + System.Resources.ResXResourceWriter, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + Connecteur Windows Active Directory + + + Créer un répertoire personnel + + + Compte de l’administrateur de répertoires + + + Mot de passe de l’administrateur de répertoires + + + Nom du domaine + + + Nom d’hôte du contrôleur de domaine Active Directory + + + Classe d’objet utilisateur + + + Rechercher dans les domaines enfant + + + Conteneur + + + Synchr. le contrôleur de domaine + + + Synchr. le serveur de catalogue global + + + Contexte de recherche + + + Spécifiez si le répertoire personnel de l’utilisateur sera créé ou non. + + + Saisissez le nom d’utilisateur de l’administrateur que le système doit utiliser pour l’authentification. Ce paramètre peut correspondre à un nom d’utilisateur ou à nom_domaine\nom_utilisateur. + + + Saisissez le mot de passe utilisateur devant servir à l’authentification. + + + Nom du domaine Windows (par ex., domainewindows.monentreprise.com) + + + Pour l’administration interdomaine, saisissez le nom d’hôte, l’adresse IP ou le nom de domaine du serveur LDAP. + + + Spécifiez la classe d’objet Active Directory destinée aux objets utilisateur à gérer sur cette ressource. La classe par défaut est User (Utilisateur). Elle devrait convenir dans la majorité des cas. + + + Indiquez si les recherches effectuées dans Active Directory doivent inclure les domaines enfant. Assurez-vous par ailleurs que les attributs Conteneur de recherche et Synchr. le contexte de recherche (voir les paramètres de synchronisation) sont définis sur la racine du domaine parent (par ex., DC=mondomaine,DC=com). + + + Spécifiez un objet conteneur qui servira de racine par défaut à toutes les recherches. À moins qu’une recherche ne passe explicitement d’autres critères, seuls les objets situés sous ce conteneur feront l’objet d’une recherche. Par exemple, pour extraire les utilisateurs du conteneur Utilisateurs, saisissez CN=Utilisateurs,DC=MONDOMAINE,DC=COM. Si aucune valeur n’est spécifiée, c’est celle de l’attribut Conteneur de base qui est utilisée. + + + Contrôleur de domaine à utiliser lors d’une synchronisation active. Uniquement utilisé lorsque la recherche ne porte pas sur les domaines enfant. + + + Nom du serveur de catalogue global. Uniquement utilisé lorsque la recherche porte sur les domaines enfant. + + + Spécifiez où rechercher dans le répertoire ADSI lorsque vous tentez de résoudre l’attribut spécifié par searchAttributes au format DN natif. + + + Le connecteur n’a pas été configuré. + + + La suppression n’est pas prise en charge pour la classe d’objet {0}. + + + Classe d’objet incorrecte : {0} + + + L’attribut {0} ne figure pas dans l’objet connecteur. Impossible de poursuivre la synchronisation. + + + L’attribut opérationnel du nom doit être spécifié. + + + L’opération de synchronisation n’est pas disponible pour la classe d’objet {0}. + + + UID introuvable + + + Une classe d’objet erronée a été spécifiée dans la configuration du connecteur. Impossible de trouver la classe d’objet {0} dans Active Directory. + + + Impossible d’ajouter l’attribut de connecteur à un résultat de recherche <nul>. + + + Impossible d’ajouter l’attribut de connecteur à une entrée de répertoire <nul>. + + + Valeur unique attendue, alors que plusieurs valeurs ont été trouvées pour l’attribut {0}. + + + La classe d’objet {0} est incompatible avec ce connecteur. + + + Informations d’authentification incorrectes fournies pour l’utilisateur {0} + + + La date d’expiration du mot de passe peut uniquement être réinitialisée via la réinitialisation du mot de passe lui-même. + + + Active Directory ne prend pas en charge le verrouillage des utilisateurs. L’utilisateur doit impérativement être déverrouillé. + + + Une étendue de recherche incorrecte a été spécifiée : {0} + + + Une exception s’est produite lors de la validation de l’utilisateur {0}. L’utilisateur a bien été authentifié, mais son GUID n’a pas pu être déterminé. + + + Une exception s’est produite lors de la validation de l’utilisateur {0}. L’utilisateur a bien été authentifié, mais son SID n’a pas pu être déterminé. + + + Le nom de l’administrateur de répertoires n’a pas été fourni. + + + Le mot de passe de l’administrateur de répertoires n’a pas été fourni. + + + Le nom de domaine n’a pas été fourni. + + + La classe d’objet n’a pas été fournie. + + + Le conteneur de recherche n’a pas été fourni. + + + Utilisation de la requête de type Adaptateur de ressources Identity Manager {0}. Elle devrait être mise à jour afin d’utiliser la nouvelle syntaxe de requête du connecteur. + + + Un conteneur erroné a été fourni : {0} + + + Préfixe de script shell variable + + + Préfixe ajouté à tous les noms d’arguments de script shell. Par exemple, si le préfixe est défini sur Connector_, un argument appelé User est converti en Connector_User. + + + Le compte de l’utilisateur a été verrouillé. + + + Le mot de passe utilisateur doit être changé. + + + Le compte de l’utilisateur {0} a expiré. + + + Un contexte de recherche erroné a été fourni : {0} + + \ No newline at end of file diff --git a/ActiveDirectoryConnector/Messages.it-IT.resx b/ActiveDirectoryConnector/Messages.it-IT.resx new file mode 100755 index 00000000..519638f6 --- /dev/null +++ b/ActiveDirectoryConnector/Messages.it-IT.resx @@ -0,0 +1,282 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + text/microsoft-resx + + + 2.0 + + + System.Resources.ResXResourceReader, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + System.Resources.ResXResourceWriter, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + Connettore Windows Active Directory + + + Crea directory home + + + Account amministratore directory + + + Password amministratore directory + + + Nome dominio + + + Nome host controller di dominio Active Directory + + + Classe oggetto per oggetti utente + + + Ricerca nei domini figli + + + Contenitore + + + Sincronizza controller di dominio + + + Sincronizza server di catalogo globale + + + Contesto di ricerca + + + Specificare se verrà creata la directory home dell’utente. + + + Immettere il nome utente dell’amministratore con cui il sistema deve eseguire l’autenticazione. È possibile inserire direttamente il nome utente o usare il formato ’nome_dominio\’nome_utente’. + + + Immettere la password da utilizzare per l’autenticazione. + + + Nome del dominio Windows (ad es. nomedominio.azienda.com) + + + Per l’amministrazione interdominio, immettere nome host, indirizzo IP o nome dominio del server LDAP. + + + Specificare la classe oggetto di Active Directory per gli oggetti utente che verranno gestiti su questa risorsa. L’impostazione predefinita, User, dovrebbe essere appropriata per la maggior parte delle situazioni. + + + Selezionare questa opzione se le ricerche in Active Directory devono includere i domini figli. Inoltre, gli attributi Contenitore di ricerca e Sincronizza contesto di ricerca (vedere le impostazioni di sincronizzazione) devono essere impostati sul livello più elevato del dominio padre, es. DC=nomedominio,DC=com. + + + Specificare un oggetto contenitore che fungerà da radice predefinita per tutte le ricerche. A meno che la ricerca non passi esplicitamente altri criteri, verranno ricercati solo gli oggetti inclusi in questo contenitore. Ad esempio, se si desidera richiamare gli utenti dal contenitore Users, immettere CN=Users,DC=NOMEDOMINIO,DC=COM Se non si specifica un valore, viene usato quello dell’attributo ’Base Container’. + + + Il controller di dominio da usare durante Active Sync. Usato solo se non viene eseguita la ricerca nei domini figli. + + + Nome del server di catalogo globale. È richiesto solo se si esegue la ricerca nei domini figli. + + + Specificare dove cercare nella directory ADSI quando si cerca di risolvere l’attributo specificato da searchAttributes nel formato DN nativo. + + + Il connettore non è stato configurato + + + Eliminazione non supportata per la classe oggetto {0} + + + Classe oggetto non valida: {0} + + + L’attributo {0} non è presente nell’oggetto connettore. Impossibile procedere con la sincronizzazione + + + L’attributo operativo del nome non può essere nullo + + + L’operazione di sincronizzazione non è disponibile per la classe oggetto {0} + + + UID non presente + + + È stata specificata una classe oggetto non valida nella configurazione del connettore. Impossibile trovare la classe oggetto \’{0}\’ in Active Directory + + + Impossibile aggiungere l’attributo del connettore a risultati di ricerca nulli + + + Impossibile aggiungere l’attributo del connettore a una voce di directory nulla + + + Era atteso un valore singolo ma sono stati trovati più valori per l’attributo {0} + + + La classe oggetto \’{0}\’ non è valida per questo connettore + + + Le credenziali fornite per l’utente {0} non sono valide + + + La scadenza della password può essere modificata solo ripristinando la password + + + Active Directory non supporta il blocco degli utenti. Gli utenti possono solo essere sbloccati + + + È stato fornito un ambito di ricerca non valido: {0} + + + Si è verificata un’eccezione nella convalida dell’utente {0}. L’utente è stato autenticato correttamente ma non è stato possibile determinare il relativo GUID. + + + Si è verificata un’eccezione nella convalida dell’utente {0}. L’utente è stato autenticato correttamente ma non è stato possibile determinare il relativo SID. + + + Non è stato fornito il nome dell’amministratore della directory. + + + Non è stata fornita la password dell’amministratore della directory. + + + Non è stato fornito il nome del dominio. + + + Non è stata fornita la classe oggetto. + + + Non è stato fornito il contenitore di ricerca. + + + La query utilizza lo stile dell’adattatore di risorsa di Identity Manager ’{0}’. Deve essere aggiornata in modo da utilizzare la nuova sintassi di query del connettore. + + + È stato fornito un contenitore non valido: {0} + + + Prefisso variabili script della shell + + + Il prefisso che verrà aggiunto a tutti i nomi degli argomenti degli script della shell. Ad esempio, se il prefisso è impostato su "Connettore_" un argomento di nome "Utente" verrà trasformato in "Connettore_Utente" + + + L’account utente è stato bloccato + + + La password deve essere modificata + + + Account utente scaduto per l’utente {0} + + + È stato fornito un contesto di ricerca non valido: {0} + + \ No newline at end of file diff --git a/ActiveDirectoryConnector/Messages.ja-JP.resx b/ActiveDirectoryConnector/Messages.ja-JP.resx new file mode 100755 index 00000000..df865228 --- /dev/null +++ b/ActiveDirectoryConnector/Messages.ja-JP.resx @@ -0,0 +1,282 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + text/microsoft-resx + + + 2.0 + + + System.Resources.ResXResourceReader, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + System.Resources.ResXResourceWriter, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + Windows Active Directory コネクタ + + + ホームディレクトリの作成 + + + ディレクトリ管理者のアカウント + + + ディレクトリ管理者のパスワード + + + ドメイン名 + + + Active Directory ドメインコントローラのホスト名 + + + ユーザーオブジェクトのオブジェクトクラス + + + 子ドメインの検索 + + + コンテナ + + + ドメインコントローラの同期 + + + グローバルカタログサーバーの同期 + + + 検索コンテキスト + + + ユーザー用ホームディレクトリを作成するかどうかを指定します。 + + + システムが認証に使用する管理者のユーザー名を入力します。ユーザー名または 'ドメイン名'\ユーザー名' の形式で設定できます。 + + + 認証に使用するパスワードを入力します。 + + + Windows のドメイン名 (例: windowsdomain.mycompany.com) + + + クロスドメイン管理を行う場合は、ホスト名、IP アドレス、または LDAP サーバーのドメイン名を入力します。 + + + このリソース上で管理するユーザーオブジェクトの Active Directory オブジェクトクラスを指定します。デフォルトは User で、多くの場合、この設定が適切です。 + + + Active Directory の検索に子ドメインを含めるかどうかを選択します。さらに、親ドメインの先頭に、DC=mydomain,DC=com のように、検索コンテナおよび検索コンテキスト (同期設定を参照) 属性を設定してください。 + + + すべての検索のデフォルトルートとなるコンテナオブジェクトを指定します。検索を明示的に他の条件に渡さない場合、このコンテナ内のオブジェクトのみが検索されます。たとえば、Users コンテナからユーザーを取得する場合は、CN=Users,DC=MYDOMAIN,DC=COM を入力します。値を指定しないと、'Base Container' 属性の値が使用されます。 + + + アクティブ同期中に使用するドメインコントローラ。子ドメインを検索しない場合にのみ使用します。 + + + グローバルカタログサーバーの名前。子ドメインを検索する場合にのみ必要です。 + + + 検索属性で指定された属性のネイティブ DN 形式への解決を試みる場合に、ADSI ディレクトリ中でどこを検索するかを指定します。 + + + コネクタが設定されていません + + + オブジェクトクラス {0} では削除はサポートされていません + + + 無効なオブジェクトクラス: {0} + + + コネクタオブジェクトに属性 {0} がありません。同期を続行できません + + + 名前のオペレーショナル属性を null にすることはできません + + + オブジェクトクラス {0} では同期動作はできません + + + UID がありません + + + コネクタ設定に無効なオブジェクトクラスが指定されました。Active Directory からオブジェクトクラス \'{0}\' が見つかりませんでした + + + <null> の検索結果にコネクタ属性を追加できませんでした + + + <null> のディレクトリ入力にコネクタ属性を追加できませんでした + + + 単一の値を要求しているときに、属性 {0} に対して複数の値が検索されました + + + オブジェクトクラス \'{0}\' はこのコネクタでは無効です + + + ユーザー {0} に無効な認証情報が指定されました + + + パスワードをリセットすることでのみパスワードを期限切れにすることができます + + + Active Directory はユーザーのロックをサポートしません。ユーザーのロック解除のみができます + + + 無効な検索範囲が指定されました: {0} + + + ユーザー {0} の検証時に例外が発生しました。ユーザーの認証は正常に実行されましたが、ユーザーの GUID を特定できませんでした。 + + + ユーザー {0} の検証時に例外が発生しました。ユーザーの認証は正常に実行されましたが、ユーザーの SID を特定できませんでした。 + + + ディレクトリ管理者の名前が指定されていません。 + + + ディレクトリ管理者のパスワードが指定されていません。 + + + ドメイン名が指定されていません。 + + + オブジェクトクラスが指定されていません。 + + + 検索コンテナが指定されていません。 + + + 識別情報マネージャーリソースアダプタのスタイルクエリー '{0}' を使用しています。新規コネクタのクエリー構文を使用するには、このスタイルクエリーを更新してください。 + + + 無効なコンテナが指定されました: {0} + + + シェルスクリプトの変数のプレフィックス + + + シェルスクリプトのすべての引数名に追加されるプレフィックス。たとえば、プレフィックスを "Connector_" に設定すると、引数 "User" は "Connector_User" になります + + + ユーザーのアカウントがロックされています + + + ユーザーのパスワードを変更してください + + + ユーザー {0} のユーザーアカウントの期限が切れています + + + 無効な検索コンテキストが指定されました: {0} + + \ No newline at end of file diff --git a/ActiveDirectoryConnector/Messages.ko-KR.resx b/ActiveDirectoryConnector/Messages.ko-KR.resx new file mode 100755 index 00000000..c8854a6f --- /dev/null +++ b/ActiveDirectoryConnector/Messages.ko-KR.resx @@ -0,0 +1,282 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + text/microsoft-resx + + + 2.0 + + + System.Resources.ResXResourceReader, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + System.Resources.ResXResourceWriter, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + Windows Active Directory Connector + + + 홈 디렉토리 작성 + + + 디렉토리 관리자 계정 + + + 디렉토리 관리자 비밀번호 + + + 도메인 이름 + + + Active Directory 도메인 컨트롤러 호스트 이름 + + + 사용자 객체의 객체 클래스 + + + 하위 도메인 검색 + + + 컨테이너 + + + 도메인 컨트롤러 동기화 + + + 전역 카탈로그 서버 동기화 + + + 검색 컨텍스트 + + + 사용자의 홈 디렉토리를 만들지 여부를 지정합니다. + + + 시스템이 인증해야 하는 관리자 사용자 이름을 입력합니다. 설정은 사용자 이름 또는 'domainname'\'username'일 수 있습니다. + + + 인증할 때 사용해야 하는 비밀번호를 입력합니다. + + + Windows 도메인 이름(예: windowsdomain.mycompany.com) + + + 교차 도메인 관리의 경우 LDAP 서버의 호스트 이름, IP 주소 또는 도메인 이름을 입력합니다. + + + 이 자원에서 관리될 사용자 객체의 Active Directory 객체 클래스를 지정합니다. 기본값은 User이며, 대부분의 경우 기본값이 적절합니다. + + + Active Directory의 검색에 하위 도메인을 포함하려면 선택합니다. 또한 검색 컨테이너 및 검색 컨텍스트 동기화(동기화 설정 참조) 속성을 상위 도메인의 최상위로 설정해야 합니다(예: DC=mydomain,DC=com). + + + 모든 검색의 기본 루트가 되는 컨테이너 객체를 지정합니다. 검색이 명백히 다른 기준을 충족하지 않으면 이 컨테이너 아래에 있는 객체만 검색됩니다. 예를 들어 Users 컨테이너에서 사용자를 검색하려면 CN=Users,DC=MYDOMAIN,DC=COM을 입력합니다. 값을 지정하지 않으면 '기본 컨테이너' 속성 값이 사용됩니다. + + + 활성 동기화 중에 사용할 도메인 컨트롤러입니다. 하위 도메인을 검색하지 않는 경우에만 사용됩니다. + + + 전역 카탈로그 서버의 이름입니다. 이 이름은 하위 도메인을 검색하는 경우에만 필요합니다. + + + searchAttributes에 의하여 지정된 속성을 원래 DN 형식으로 변환할 때 찾을 ADSI의 디렉토리를 지정합니다. + + + 커넥터가 구성되지 않았습니다. + + + ObjectClass {0}은(는) 삭제할 수 없습니다. + + + 잘못된 객체 클래스: {0} + + + 속성 {0}이(가) 커넥터 객체에 없습니다. 동기화를 계속할 수 없습니다. + + + 이름 작업 속성은 null일 수 없습니다. + + + 동기화 작업을 ObjectClass {0}에 사용할 수 없습니다. + + + Uid가 없습니다. + + + 커넥터 구성에 잘못된 객체 클래스가 지정되었습니다. 객체 클래스 \'{0}\'이(가) Active Directory에 없습니다. + + + 커넥터 속성을 <null> 검색 결과에 추가할 수 없습니다. + + + 커넥터 속성을 <null> 디렉토리 입력 항목에 추가할 수 없습니다. + + + 속성 {0}에 대해 하나의 값이 검색되어야 하는데, 여러 개의 값이 검색되었습니다. + + + ObjectClass \'{0}\'이(가) 이 커넥터에 유효하지 않습니다. + + + 사용자 {0}에 대해 잘못된 자격 증명이 제공되었습니다. + + + 비밀번호 만료는 비밀번호 재설정을 통해서만 재설정할 수 있습니다. + + + Active Directory는 사용자 잠금을 지원하지 않습니다. 사용자의 잠금을 해제만 할 수 있습니다. + + + 잘못된 검색 범위가 입력되었습니다. {0} + + + 사용자 {0}의 유효성을 검사하는 동안 예외가 발생했습니다. 사용자가 인증되었지만 사용자의 guid는 확인하지 못했습니다. + + + 사용자 {0}의 유효성을 검사하는 동안 예외가 발생했습니다. 사용자가 인증되었지만 사용자의 sid는 확인하지 못했습니다. + + + 디렉토리 관리자 이름을 제공하지 않았습니다. + + + 디렉토리 관리자 비밀번호를 제공하지 않았습니다. + + + 도메인 이름을 제공하지 않았습니다. + + + ObjectClass를 제공하지 않았습니다. + + + 검색 컨테이너를 제공하지 않았습니다. + + + Identity Manger 자원 어댑터 스타일 쿼리 '{0}'을(를) 사용하고 있습니다. 새 커넥터 쿼리 구문을 사용하려면 이 쿼리를 업데이트해야 합니다. + + + 잘못된 컨테이너를 제공했습니다. {0} + + + 쉘 스크립트 변수 접두어 + + + 모든 쉘 스크립트 인수 이름에 추가되는 접두어입니다. 예를 들어 접두어가 "Connector_"로 설정된 경우 "User"라는 인수는 "Connector_User"가 됩니다. + + + 사용자의 계정이 잠겼습니다. + + + 사용자 비밀번호를 변경해야 합니다. + + + 사용자 {0}에 대한 사용자 계정이 만료되었습니다. + + + 잘못된 검색 컨텍스트를 제공했습니다. {0} + + \ No newline at end of file diff --git a/ActiveDirectoryConnector/Messages.pt-BR.resx b/ActiveDirectoryConnector/Messages.pt-BR.resx new file mode 100755 index 00000000..86ac023d --- /dev/null +++ b/ActiveDirectoryConnector/Messages.pt-BR.resx @@ -0,0 +1,282 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + text/microsoft-resx + + + 2.0 + + + System.Resources.ResXResourceReader, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + System.Resources.ResXResourceWriter, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + Conector do Windows Active Directory + + + Criar diretório principal + + + Conta do administrador do diretório + + + Senha do administrador do diretório + + + Nome do domínio + + + Nome do host do controlador de domínio do Active Directory + + + Classe de objeto para objetos Usuário + + + Pesquisar domínios filhos + + + Recipiente + + + Controlador de domínio de sincronização + + + Servidor de catálogo global de sincronização + + + Contexto da pesquisa + + + Especifique se o diretório principal do usuário será criado ou não. + + + Digite o nome do usuário administrador com o qual o sistema deve fazer a autenticação. A configuração pode ser um nome do usuário ou 'domainname'\'username'. + + + Digite a senha que deverá ser usada na autenticação. + + + Nome do domínio do Windows (por exemplo, windowsdomain.mycompany.com) + + + Para administração de domínio cruzado, digite o nome do host, o endereço IP ou o nome de domínio do servidor LDAP. + + + Especifique a classe de objeto do Active Directory para objetos Usuário que serão gerenciados neste recurso. O padrão é Usuário e, na maioria das situações, isso deverá ser adequado. + + + Selecione se você desejar que as pesquisas do Active Directory incluam domínios filhos. Além disso, os atributos Recipiente de pesquisas e Contexto da pesquisa de sincronização (consulte as configurações de sincronização) devem ser configurados para o topo do domínio pai; por exemplo, DC=mydomain,DC=com. + + + Especifique um objeto de recipiente que será a raiz padrão de todas as pesquisas. A menos que a pesquisa passe outros critérios explicitamente, somente objetos neste recipiente serão pesquisados. Por exemplo, se desejar recuperar usuários do recipiente Usuários, digite CN=Users,DC=MYDOMAIN,DC=COM. Se nenhum valor for especificado, será usado o valor do atributo 'Base Container'. + + + O controlador de domínio a ser usado durante o Active Sync. Usado apenas quando não está sendo feita uma pesquisa de domínios filhos. + + + Nome do servidor de catálogo global. Isso é necessário apenas para pesquisa de domínios filhos. + + + Especifica onde procurar no diretório ADSI ao tentar resolver o atributo especificado por searchAttributes para o formato original de dn. + + + O conector não foi configurado + + + Exclusão sem suporte para ObjectClass {0} + + + Classe de objeto inválida: {0} + + + O atributo {0} não está presente no objeto de conector. Não é possível continuar a sincronização. + + + O atributo operacional de nome não pode ser nulo + + + A operação de sincronização não está disponível para ObjectClass {0} + + + A UID não estava presente + + + Uma classe de objeto inválida foi especificada na configuração do conector. A classe de objeto \'{0}\' não foi encontrada no Active Directory + + + Não foi possível adicionar o atributo do conector ao resultado da pesquisa <nulo> + + + Não foi possível adicionar o atributo do conector à entrada de diretório <nula> + + + Esperando um valor único, mas foram encontrados vários valores para o atributo {0} + + + ObjectClass \'{0}\' não é válida para este conector + + + Credenciais inválidas fornecidas para o usuário {0} + + + Para redefinir a expiração da senha, é preciso redefinir a senha + + + O Active Directory não oferece suporte a bloqueio de usuários. O usuário só pode ser desbloqueado + + + Foi fornecido um âmbito de pesquisa inválido: {0} + + + Ocorreu uma exceção durante a validação do usuário {0}. O usuário foi autenticado com êxito, mas não foi possível determinar sua GUID. + + + Ocorreu uma exceção durante a validação do usuário {0}. O usuário foi autenticado com êxito, mas não foi possível determinar sua SID. + + + O nome do administrador do diretório não foi fornecido. + + + A senha do administrador do diretório não foi fornecida. + + + O nome do domínio não foi fornecido. + + + A ObjectClass não foi fornecida. + + + O recipiente de pesquisas não foi fornecido. + + + Usando consulta de estilo do adaptador de recurso do Identity Manager '{0}'. Isso deve ser atualizado para usar a nova sintaxe de consulta de conector. + + + Foi fornecido um recipiente inválido: {0} + + + Prefixo de variável de script de shell + + + O prefixo que será adicionado a todos os nomes de argumentos do script de shell. Por exemplo, se o prefixo for definido como "Connector_", um argumento denominado "User" se tornará "Connector_User" + + + A conta do usuário foi bloqueada + + + A senha do usuário deve ser alterada + + + A conta de usuário expirou para o usuário {0} + + + Foi fornecido um contexto de pesquisa inválido: {0} + + \ No newline at end of file diff --git a/ActiveDirectoryConnector/Messages.zh-CN.resx b/ActiveDirectoryConnector/Messages.zh-CN.resx new file mode 100755 index 00000000..98e986c3 --- /dev/null +++ b/ActiveDirectoryConnector/Messages.zh-CN.resx @@ -0,0 +1,282 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + text/microsoft-resx + + + 2.0 + + + System.Resources.ResXResourceReader, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + System.Resources.ResXResourceWriter, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + Windows Active Directory 连接器 + + + 创建主目录 + + + 目录管理员的帐户 + + + 目录管理员的密码 + + + 域名 + + + Active Directory 域控制器主机名 + + + 用户对象的对象类 + + + 搜索子域 + + + 容器 + + + 同步域控制器 + + + 同步全局目录服务器 + + + 搜索上下文 + + + 指定是否为用户创建主目录。 + + + 输入系统用以进行验证的管理员用户名。该设置可以为用户名或“域名”\“用户名”。 + + + 输入进行验证时应使用的密码。 + + + Windows 域的名称(例如 windowsdomain.mycompany.com) + + + 对于跨域管理,请输入 LDAP 服务器的主机名、IP 地址或域名。 + + + 指定将在此资源上管理的用户对象的 Active Directory 对象类。默认值为 User,在大多数情况下该值均适用。 + + + 如果要在 Active Directory 搜索中包括子域,请选择此项。此外,必须将“搜索容器”和“同步搜索上下文”(请参见同步设置)属性设置为父域的顶级,例如 DC=mydomain,DC=com。 + + + 指定将作为所有搜索的默认根目录的容器对象。除非搜索明确指定其他条件,否则只会搜索此容器下的对象。例如,如果要从 Users 容器中检索用户,请输入 CN=Users,DC=MYDOMAIN,DC=COM。如果没有指定任何值,则将使用“基容器”属性的值。 + + + 活动同步期间要使用的域控制器。仅在不搜索子域的情况下使用此项。 + + + 全局目录服务器的名称。仅在搜索子域的情况下需要此项。 + + + 指定要将由 searchAttributes 指定的属性解析为本机标识名格式时,从何处查找 ADSI 目录。 + + + 尚未配置连接器 + + + 对于对象类 {0},不支持删除操作 + + + 无效的对象类: {0} + + + 连接器对象中不存在属性 {0}。无法进行同步 + + + 名称操作属性不能为 null + + + 对于对象类 {0},无法执行同步操作 + + + 不存在 UID + + + 在连接器配置中指定了无效的对象类。在 Active Directory 中未找到对象类 \'{0}\' + + + 无法将连接器属性添加到 <null> 搜索结果 + + + 无法将连接器属性添加到 <null> 目录条目 + + + 属性 {0} 应有一个值,但却找到了多个值 + + + 对象类 \'{0}\' 对此连接器无效 + + + 为用户 {0} 提供的无效凭证 + + + 只能通过重置密码来重置密码到期 + + + Active Directory 不支持锁定用户。只能解除对用户的锁定 + + + 提供了无效的搜索范围: {0} + + + 验证用户 {0} 期间出现异常。已成功验证该用户,但无法确定该用户的 GUID。 + + + 验证用户 {0} 期间出现异常。已成功验证该用户,但无法确定该用户的 SID。 + + + 未提供目录管理员名称。 + + + 未提供目录管理员密码。 + + + 未提供域名。 + + + 未提供对象类。 + + + 未提供搜索容器。 + + + 使用 Identity Manger 资源适配器样式查询“{0}”。应对此进行更新,以使用新的连接器查询语法。 + + + 提供了无效的容器: {0} + + + Shell 脚本变量前缀 + + + 将添加到所有 Shell 脚本参数名称的前缀。例如,如果将该前缀设置为 "Connector_",则名为 "User" 的参数将变为 "Connector_User" + + + 用户帐户已被锁定 + + + 必须更改用户密码 + + + 用户 {0} 的用户帐户已到期 + + + 提供了无效的搜索上下文: {0} + + \ No newline at end of file diff --git a/ActiveDirectoryConnector/Messages.zh-TW.resx b/ActiveDirectoryConnector/Messages.zh-TW.resx new file mode 100755 index 00000000..4f744341 --- /dev/null +++ b/ActiveDirectoryConnector/Messages.zh-TW.resx @@ -0,0 +1,282 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + text/microsoft-resx + + + 2.0 + + + System.Resources.ResXResourceReader, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + System.Resources.ResXResourceWriter, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + Windows Active Directory Connector + + + 建立主目錄 + + + 目錄管理員的帳號 + + + 目錄管理員的密碼 + + + 網域名稱 + + + Active Directory 網域控制器主機名稱 + + + 使用者物件的物件類別 + + + 搜尋子網域 + + + 容器 + + + 同步化網域控制器 + + + 同步化全域目錄伺服器 + + + 搜尋上下文 + + + 指定是否要建立使用者的主目錄。 + + + 輸入系統進行認證時採用的管理員使用者名稱。此設定可以是「使用者名稱」或「網域名稱」\「使用者名稱」。 + + + 輸入進行認證時應使用的密碼。 + + + Windows 網域的名稱 (例如 windowsdomain.mycompany.com) + + + 若是跨網域進行管理,請輸入 LDAP 伺服器的主機名稱、IP 位址或網域名稱。 + + + 針對要在此資源上管理的使用者物件,指定 Active Directory 物件類別。預設值為【User】,且在大部分情況下均適用。 + + + 若要 Active Directory 的搜尋包含子網域,請選取此選項。此外此外您必須在父網域頂端設定【搜尋容器】及【同步化搜尋上下文】(請參閱同步化設定) 屬性 (例如 DC=mydomain,DC=com)。 + + + 指定將成為所有搜尋之預設根目錄的容器物件。除非搜尋明確指定有其他條件,否則只會搜尋此容器中的物件。例如,若想要擷取【Users】容器中的使用者,請輸入 CN=Users,DC=MYDOMAIN,DC=COM。若未指定任何值,將使用「基底容器」屬性值。 + + + Active Sync 期間要使用的網域控制器。只有在不搜尋子網域時才使用。 + + + 全域目錄伺服器的名稱。只有在搜尋子網域時才需要。 + + + 指定將 searchAttributes 所指定的屬性解析為本機 DN 格式時,要在何處查詢 ADSI 目錄。 + + + 尚未配置連接器 + + + 不支援刪除物件類別 {0} + + + 無效的物件類別: {0} + + + 連接器物件中沒有屬性 {0}。無法繼續同步化 + + + 名稱作業屬性不得為空 + + + 同步化作業不適用於物件類別 {0} + + + uid 不存在 + + + 連接器配置中指定了無效的物件類別。Active Directory 中找不到物件類別「{0}」 + + + 無法將連接器屬性增加至 <null> 搜尋結果 + + + 無法將連接器屬性增加至 <null> 目錄項目 + + + 屬性 {0} 應僅有單一值,但卻出現多個值 + + + 物件類別「{0}」對此連接器無效 + + + 為使用者 {0} 提供的憑證無效 + + + 必須重設密碼才可重設密碼過期功能 + + + Active Directory 不支援鎖定使用者。只能解除鎖定使用者 + + + 提供的搜尋範圍無效: {0} + + + 驗證使用者 {0} 期間發生異常。已成功認證使用者,但無法確定使用者的 GUID。 + + + 驗證使用者 {0} 期間發生異常。已成功認證使用者,但無法確定使用者的 SID。 + + + 未提供目錄管理員名稱。 + + + 未提供目錄管理員密碼。 + + + 未提供網域名稱。 + + + 未提供物件類別。 + + + 未提供搜尋容器。 + + + 使用 Identity Manger 資源配接卡樣式查詢「{0}」。應更新為使用新的連接器查詢語法。 + + + 提供的容器無效: {0} + + + shell 程序檔變數前綴 + + + 將增加至所有 shell 程序檔引數名稱的前綴。例如,若將前綴設定為「Connector_」,則稱為「User」的引數會變成「Connector_User」 + + + 使用者的帳號已鎖定 + + + 必須變更使用者密碼 + + + 使用者 {0} 的使用者帳號已過期。 + + + 提供的搜尋上下文無效: {0} + + \ No newline at end of file From c94145232abd990a31def0d7f0fd56bcfd2ae850 Mon Sep 17 00:00:00 2001 From: dvernon Date: Wed, 18 Feb 2009 23:01:56 +0000 Subject: [PATCH 195/342] Committing localized resources for Active Directory --- .../ActiveDirectoryConnector.csproj | 27 +++++++++++++++++++ 1 file changed, 27 insertions(+) diff --git a/ActiveDirectoryConnector/ActiveDirectoryConnector.csproj b/ActiveDirectoryConnector/ActiveDirectoryConnector.csproj index 66e9c4a8..8ffc8853 100644 --- a/ActiveDirectoryConnector/ActiveDirectoryConnector.csproj +++ b/ActiveDirectoryConnector/ActiveDirectoryConnector.csproj @@ -127,6 +127,33 @@ + + Designer + + + Designer + + + Designer + + + Designer + + + Designer + + + Designer + + + Designer + + + Designer + + + Designer + \ No newline at end of file From 33b4999d0d635ee24b789f2e5fc0f56e02b8a06d Mon Sep 17 00:00:00 2001 From: dvernon Date: Wed, 18 Feb 2009 23:19:28 +0000 Subject: [PATCH 196/342] Localized resources for ActiveDiretory connector --- ActiveDirectoryConnector/Messages.es-ES.resx | 163 ++++++++++++++++++- 1 file changed, 161 insertions(+), 2 deletions(-) diff --git a/ActiveDirectoryConnector/Messages.es-ES.resx b/ActiveDirectoryConnector/Messages.es-ES.resx index 03886a03..3e98a6bf 100644 --- a/ActiveDirectoryConnector/Messages.es-ES.resx +++ b/ActiveDirectoryConnector/Messages.es-ES.resx @@ -1,4 +1,4 @@ - + \ No newline at end of file diff --git a/ExchangeConnector/ExchangeConnector.cs b/ExchangeConnector/ExchangeConnector.cs index af4a3110..c9d225e2 100644 --- a/ExchangeConnector/ExchangeConnector.cs +++ b/ExchangeConnector/ExchangeConnector.cs @@ -318,6 +318,11 @@ internal sealed class CommandInfo /// internal static readonly CommandInfo GetUser = new CommandInfo("Get-User"); + /// + /// Get-Mailbox command meta info + /// + internal static readonly CommandInfo GetMailbox = new CommandInfo("Get-Mailbox"); + /// /// List of SerializableCommandInfo object - will be read from persistence /// diff --git a/ExchangeConnector/LegacyExchangeConnector.cs b/ExchangeConnector/LegacyExchangeConnector.cs index d0678690..44e167d1 100644 --- a/ExchangeConnector/LegacyExchangeConnector.cs +++ b/ExchangeConnector/LegacyExchangeConnector.cs @@ -309,7 +309,7 @@ public override void Sync( { // replace the ad attributes with exchange one and add recipient type ConnectorObject updated = ExchangeUtility.ReplaceAttributes(delta.Object, attsToGet, AttMapFromAD); - updated = this.AddRecipientType(objClass, updated, attsToGet); + updated = this.AddExchangeAttributes(objClass, updated, attsToGet); if (updated != delta.Object) { // build new sync delta, cause we changed the object @@ -351,7 +351,7 @@ public override void ExecuteQuery( { ConnectorObject filtered = ExchangeUtility.ReplaceAttributes( cobject, attsToGet, AttMapFromAD); - filtered = this.AddRecipientType(oclass, filtered, attsToGet); + filtered = this.AddExchangeAttributes(oclass, filtered, attsToGet); return handler(filtered); }; @@ -510,33 +510,43 @@ private static ICollection FilterOut(ICollection - /// Gets Recipient Type from Exchange database, this method can be more general, but it is ok + /// Gets Recipient Type/Database from Exchange database, this method can be more general, but it is ok /// for out needs /// /// object class, currently the moethod works for only - /// connector object to get the recipient type for + /// connector object to get the recipient type/database for /// attributes to get /// Connector Object with recipient type added /// In case of some troubles in powershell (if the /// user is not found we get this exception too) - private ConnectorObject AddRecipientType(ObjectClass oc, ConnectorObject cobject, ArrayList attToGet) + private ConnectorObject AddExchangeAttributes(ObjectClass oc, ConnectorObject cobject, IList attToGet) { ExchangeUtility.NullCheck(oc, "name", this.configuration); ExchangeUtility.NullCheck(oc, "cobject", this.configuration); - // we support ACCOUNT only and if the recipient type is in att to get - if (!oc.Is(ObjectClass.ACCOUNT_NAME) || !attToGet.Contains(AttRecipientType)) + // we support ACCOUNT only and if the recipient type and database is in att to get + if (!oc.Is(ObjectClass.ACCOUNT_NAME) || + (!attToGet.Contains(AttRecipientType) && !attToGet.Contains(AttDatabase))) { return cobject; } + bool getDatabase = false; + ExchangeConnector.CommandInfo cmdInfo = ExchangeConnector.CommandInfo.GetUser; + if (cobject.GetAttributeByName(AttDatabase) != null || cobject.GetAttributeByName(AttDatabaseADName) != null) + { + // we need to get database attribute, it is mailbox for sure + getDatabase = true; + cmdInfo = ExchangeConnector.CommandInfo.GetMailbox; + } + ConnectorObject retCObject = cobject; // prepare the connector attribute list to get the command ICollection attributes = new Collection { cobject.Name }; // get the command - Command cmd = ExchangeUtility.GetCommand(ExchangeConnector.CommandInfo.GetUser, attributes); + Command cmd = ExchangeUtility.GetCommand(cmdInfo, attributes); ICollection foundObjects = this.InvokePipeline(cmd); @@ -544,14 +554,34 @@ private ConnectorObject AddRecipientType(ObjectClass oc, ConnectorObject cobject if (foundObjects != null && foundObjects.Count == 1) { string rcptName = RcptTypeUser; + string database = null; foreach (PSObject obj in foundObjects) { rcptName = obj.Members[AttRecipientType].Value.ToString(); + database = obj.Members[AttDatabase] != null ? obj.Members[AttDatabase].Value.ToString() : null; break; } ConnectorObjectBuilder cobjBuilder = new ConnectorObjectBuilder(); - cobjBuilder.AddAttributes(cobject.GetAttributes()); + if (getDatabase) + { + foreach (ConnectorAttribute attribute in cobject.GetAttributes()) + { + if ((attribute.Is(AttDatabase) || attribute.Is(AttDatabaseADName)) && database != null) + { + cobjBuilder.AddAttribute(ConnectorAttributeBuilder.Build(AttDatabase, database)); + } + else + { + cobjBuilder.AddAttribute(attribute); + } + } + } + else + { + cobjBuilder.AddAttributes(cobject.GetAttributes()); + } + cobjBuilder.AddAttribute(ConnectorAttributeBuilder.Build(AttRecipientType, rcptName)); retCObject = cobjBuilder.Build(); } From 010c5b52bf29efdab6da2236285ffd19b8617e20 Mon Sep 17 00:00:00 2001 From: tknappek Date: Sun, 22 Mar 2009 11:40:06 +0000 Subject: [PATCH 205/342] Issue #455: ExchangeConnector - UserMailbox update fixed --- ExchangeConnector/Data/CommandInfos.xml | 1 - ExchangeConnector/ExchangeUtility.cs | 23 +++- ExchangeConnector/LegacyExchangeConnector.cs | 115 ++++++++++++++++--- ExchangeConnector/Messages.resx | 6 + 4 files changed, 126 insertions(+), 19 deletions(-) diff --git a/ExchangeConnector/Data/CommandInfos.xml b/ExchangeConnector/Data/CommandInfos.xml index 2a4728b3..b5caaddb 100644 --- a/ExchangeConnector/Data/CommandInfos.xml +++ b/ExchangeConnector/Data/CommandInfos.xml @@ -256,7 +256,6 @@ Anr Credential - Database DomainController Filter IgnoreDefaultScope diff --git a/ExchangeConnector/ExchangeUtility.cs b/ExchangeConnector/ExchangeUtility.cs index bdbdb969..a9a2bcf0 100644 --- a/ExchangeConnector/ExchangeUtility.cs +++ b/ExchangeConnector/ExchangeUtility.cs @@ -30,6 +30,7 @@ namespace Org.IdentityConnectors.Exchange using System.Diagnostics; using System.Globalization; using System.IO; + using System.Linq; using System.Management.Automation.Runspaces; using System.Reflection; using Microsoft.Win32; @@ -351,6 +352,26 @@ internal static void NullCheck(object obj, string param, Configuration config) throw new ArgumentNullException(config.ConnectorMessages.Format( "ex_argument_null", "The Argument [{0}] can't be null", param)); } - } + } + + /// + /// Builds new Operation options and add the specified attribute names as + /// AttributesToGet (add to existing AttributesToGet) + /// + /// Existing Operation Options + /// attribute names to be add to AttributeToGet + /// New Operation Options + internal static OperationOptions AddAttributeToOptions(OperationOptions options, params string[] attNames) + { + OperationOptionsBuilder optionsBuilder = new OperationOptionsBuilder(options); + ICollection attsToGet = new HashSet(options.AttributesToGet); + foreach (string attName in attNames) + { + attsToGet.Add(attName); + } + + optionsBuilder.AttributesToGet = attsToGet.ToArray(); + return optionsBuilder.Build(); + } } } diff --git a/ExchangeConnector/LegacyExchangeConnector.cs b/ExchangeConnector/LegacyExchangeConnector.cs index 44e167d1..4b4f08c9 100644 --- a/ExchangeConnector/LegacyExchangeConnector.cs +++ b/ExchangeConnector/LegacyExchangeConnector.cs @@ -76,7 +76,7 @@ public class LegacyExchangeConnector : ActiveDirectoryConnector /// /// Attribute mapping constant /// - internal static readonly IDictionary AttMap2AD = new Dictionary() + internal static readonly IDictionary AttMap2AD = new Dictionary { { AttDatabase, AttDatabaseADName }, { AttExternalMail, AttExternalMailADName } @@ -85,7 +85,7 @@ public class LegacyExchangeConnector : ActiveDirectoryConnector /// /// Attribute mapping constant /// - internal static readonly IDictionary AttMapFromAD = new Dictionary() + internal static readonly IDictionary AttMapFromAD = new Dictionary { { AttDatabaseADName, AttDatabase }, { AttExternalMailADName, AttExternalMail } @@ -103,7 +103,7 @@ public class LegacyExchangeConnector : ActiveDirectoryConnector ConnectorAttributeInfoBuilder.Build( AttRecipientType, typeof(string), - ConnectorAttributeInfo.Flags.REQUIRED | ConnectorAttributeInfo.Flags.NOT_UPDATEABLE | ConnectorAttributeInfo.Flags.NOT_RETURNED_BY_DEFAULT); + ConnectorAttributeInfo.Flags.REQUIRED | ConnectorAttributeInfo.Flags.NOT_RETURNED_BY_DEFAULT); /// /// External Mail attribute info @@ -185,7 +185,6 @@ public override Uid Create( try { - // execute the command this.InvokePipeline(cmd); } catch @@ -239,27 +238,22 @@ public override Uid Update( // update in AD first Uid uid = base.Update(type, oclass, FilterOut(attributes), options); - // get recipient type + // get recipient type and database string rcptType = ExchangeUtility.GetAttValue(AttRecipientType, attributes) as string; + string database = ExchangeUtility.GetAttValue(AttDatabase, attributes) as string; - // update is possible for mailuser's external email only if (rcptType == RcptTypeMailUser) { if (type == UpdateType.REPLACE) { // get name attribute - string name = ExchangeUtility.GetAttValue(Name.NAME, attributes) as string; - if (name == null) - { - // we don't know name, but we need it - NOTE: searching for all the default attributes, we need only Name here, it can be improved - ConnectorObject co = this.ADSearchByUid(uid, oclass, null); - ExchangeUtility.NullCheck(co, "co", this.configuration); + attributes = this.EnsureName(oclass, attributes, uid); - // add to attributes - attributes.Add(co.Name); - } - - Command cmd = ExchangeUtility.GetCommand(ExchangeConnector.CommandInfo.SetMailUser, attributes); + PSObject psuser = this.GetUser(ExchangeConnector.CommandInfo.GetUser, attributes); + string origRcptType = psuser.Members[AttRecipientType].Value.ToString(); + Command cmd = origRcptType != rcptType ? + ExchangeUtility.GetCommand(ExchangeConnector.CommandInfo.EnableMailUser, attributes) + : ExchangeUtility.GetCommand(ExchangeConnector.CommandInfo.SetMailUser, attributes); this.InvokePipeline(cmd); } else @@ -268,6 +262,43 @@ public override Uid Update( "ex_wrong_update_type", "Update type [{0}] not supported", type)); } } + else if (rcptType == RcptTypeMailBox) + { + // we should execute something like this here: + // get-user -identity id|?{$_.RecipientType -eq "User"}|enable-mailbox -database "db" + // unfortunately I was not able to get it working with the pipeline... that's why there are two commands + // executed :-( + // alternatively there can be something like: + // get-user -identity id -RecipientTypeDetails User|enable-mailbox -database "db", but we have then trouble + // with detecting attempt to change the database attribute + ConnectorObject aduser = this.ADSearchByUid(uid, oclass, ExchangeUtility.AddAttributeToOptions(options, AttDatabaseADName)); + attributes.Add(aduser.Name); + ExchangeConnector.CommandInfo cmdInfo = ExchangeConnector.CommandInfo.GetUser; + if (aduser.GetAttributeByName(AttDatabaseADName) != null) + { + // we can be sure it is user mailbox type + cmdInfo = ExchangeConnector.CommandInfo.GetMailbox; + } + + PSObject psuser = this.GetUser(cmdInfo, attributes); + string origRcptType = psuser.Members[AttRecipientType].Value.ToString(); + string origDatabase = psuser.Members[AttDatabase] != null ? psuser.Members[AttDatabase].Value.ToString() : null; + if (origRcptType != rcptType) + { + Command cmd2 = ExchangeUtility.GetCommand(ExchangeConnector.CommandInfo.EnableMailbox, attributes); + this.InvokePipeline(cmd2); + } + else + { + // trying to update the database? + if (database != null && database != origDatabase) + { + throw new ArgumentException( + this.configuration.ConnectorMessages.Format( + "ex_not_updatable", "Update of [{0}] attribute is not supported", AttDatabase)); + } + } + } Debug.WriteLine(METHOD + ":exit", ClassName); return uid; @@ -644,6 +675,56 @@ private ConnectorObject ADSearchByUid(Uid uid, ObjectClass oclass, OperationOpti return ret; } + /// + /// Gets the Exchange user using powershell Get-User command + /// + /// command info to get the user + /// attributes containing the Name + /// with user info + private PSObject GetUser(ExchangeConnector.CommandInfo cmdInfo, ICollection attributes) + { + // assert we have user name + string name = ExchangeUtility.GetAttValue(Name.NAME, attributes) as string; + ExchangeUtility.NullCheck(name, "User name", this.configuration); + + Command cmdUser = ExchangeUtility.GetCommand(cmdInfo, attributes); + ICollection users = this.InvokePipeline(cmdUser); + if (users.Count == 1) + { + foreach (PSObject obj in users) + { + return obj; + } + } + + throw new ArgumentException( + this.configuration.ConnectorMessages.Format( + "ex_bad_username", "Provided User name is not unique or not existing")); + } + + /// + /// Ensures we have Name attribute in attributes collection, if not present, it uses AD search to get it + /// + /// object class + /// Collection of attributes + /// object Uid + /// Collection of attributes conta + private ICollection EnsureName(ObjectClass oclass, ICollection attributes, Uid uid) + { + string name = ExchangeUtility.GetAttValue(Name.NAME, attributes) as string; + if (name == null) + { + // we don't know name, but we need it - NOTE: searching for all the default attributes, we need only Name here, it can be improved + ConnectorObject co = this.ADSearchByUid(uid, oclass, null); + ExchangeUtility.NullCheck(co, "co", this.configuration); + + // add to attributes + attributes.Add(co.Name); + } + return attributes; + } + + /// /// Filter translator which does MS Exchange specific translation /// diff --git a/ExchangeConnector/Messages.resx b/ExchangeConnector/Messages.resx index 9de64bc4..cfd6a596 100644 --- a/ExchangeConnector/Messages.resx +++ b/ExchangeConnector/Messages.resx @@ -123,6 +123,12 @@ The Argument [{0}] can't be null + + Provided User name is not unique or not existing + + + Update of [{0}] attribute is not supported + Problem while PowerShell execution {0} From 8fe718202213576ce867b8f851ad90ee31969d95 Mon Sep 17 00:00:00 2001 From: tknappek Date: Mon, 23 Mar 2009 19:56:52 +0000 Subject: [PATCH 206/342] ExchangeConnector: Fixed issue with null attributes to get --- ExchangeConnector/ExchangeUtility.cs | 7 ++++++- ExchangeConnector/LegacyExchangeConnector.cs | 2 +- 2 files changed, 7 insertions(+), 2 deletions(-) diff --git a/ExchangeConnector/ExchangeUtility.cs b/ExchangeConnector/ExchangeUtility.cs index a9a2bcf0..f43847d7 100644 --- a/ExchangeConnector/ExchangeUtility.cs +++ b/ExchangeConnector/ExchangeUtility.cs @@ -364,7 +364,12 @@ internal static void NullCheck(object obj, string param, Configuration config) internal static OperationOptions AddAttributeToOptions(OperationOptions options, params string[] attNames) { OperationOptionsBuilder optionsBuilder = new OperationOptionsBuilder(options); - ICollection attsToGet = new HashSet(options.AttributesToGet); + List attsToGet = new List(); + if (options.AttributesToGet != null) + { + attsToGet.AddRange(options.AttributesToGet); + } + foreach (string attName in attNames) { attsToGet.Add(attName); diff --git a/ExchangeConnector/LegacyExchangeConnector.cs b/ExchangeConnector/LegacyExchangeConnector.cs index 4b4f08c9..1f5eaac3 100644 --- a/ExchangeConnector/LegacyExchangeConnector.cs +++ b/ExchangeConnector/LegacyExchangeConnector.cs @@ -171,7 +171,7 @@ public override Uid Create( if (rcptType != RcptTypeMailBox && rcptType != RcptTypeMailUser) { // AD account only, we do nothing - return base.Create(oclass, attributes, options); + return base.Create(oclass, FilterOut(attributes), options); } // first create the object in AD From 523a8dc6ca3076dbb133983c5b843b26f87a3e60 Mon Sep 17 00:00:00 2001 From: tknappek Date: Thu, 26 Mar 2009 19:48:29 +0000 Subject: [PATCH 207/342] ExchangeConnector: Fixed not supported update scenario MailUser,UserMailbox->User --- ExchangeConnector/LegacyExchangeConnector.cs | 22 +++++++++++++++++++- ExchangeConnector/Messages.resx | 6 ++++++ 2 files changed, 27 insertions(+), 1 deletion(-) diff --git a/ExchangeConnector/LegacyExchangeConnector.cs b/ExchangeConnector/LegacyExchangeConnector.cs index 1f5eaac3..6d4e0f8f 100644 --- a/ExchangeConnector/LegacyExchangeConnector.cs +++ b/ExchangeConnector/LegacyExchangeConnector.cs @@ -299,6 +299,27 @@ public override Uid Update( } } } + else if (rcptType == RcptTypeUser) + { + // get name attribute + attributes = this.EnsureName(oclass, attributes, uid); + + PSObject psuser = this.GetUser(ExchangeConnector.CommandInfo.GetUser, attributes); + string origRcptType = psuser.Members[AttRecipientType].Value.ToString(); + if (origRcptType != rcptType) + { + throw new ArgumentException( + this.configuration.ConnectorMessages.Format( + "ex_update_notsupported", "Update of [{0}] to [{1}] is not supported", AttRecipientType, rcptType)); + } + } + else + { + // unsupported rcpt type + throw new ArgumentException( + this.configuration.ConnectorMessages.Format( + "ex_bad_rcpt", "Recipient type [{0}] is not supported", rcptType)); + } Debug.WriteLine(METHOD + ":exit", ClassName); return uid; @@ -724,7 +745,6 @@ private ICollection EnsureName(ObjectClass oclass, ICollecti return attributes; } - /// /// Filter translator which does MS Exchange specific translation /// diff --git a/ExchangeConnector/Messages.resx b/ExchangeConnector/Messages.resx index cfd6a596..56b2c4e6 100644 --- a/ExchangeConnector/Messages.resx +++ b/ExchangeConnector/Messages.resx @@ -123,6 +123,9 @@ The Argument [{0}] can't be null + + Recipient type [{0}] is not supported + Provided User name is not unique or not existing @@ -132,6 +135,9 @@ Problem while PowerShell execution {0} + + Update of [{0}] to [{1}] is not supported + Update type [{0}] not supported From 3b1a06c5a95bcd62845405ef043affdcae654cff Mon Sep 17 00:00:00 2001 From: tknappek Date: Wed, 1 Apr 2009 17:43:11 +0000 Subject: [PATCH 208/342] Issue #461 and Issue #462: All Exchange attributes support added, Enable/Disable update fixed --- ExchangeConnector/Data/CommandInfos.xml | 140 ++++++---- ExchangeConnector/ExchangeConnector.cs | 10 + ExchangeConnector/ExchangeUtility.cs | 9 +- ExchangeConnector/LegacyExchangeConnector.cs | 263 ++++++++++++------- 4 files changed, 279 insertions(+), 143 deletions(-) diff --git a/ExchangeConnector/Data/CommandInfos.xml b/ExchangeConnector/Data/CommandInfos.xml index b5caaddb..cc78cf21 100644 --- a/ExchangeConnector/Data/CommandInfos.xml +++ b/ExchangeConnector/Data/CommandInfos.xml @@ -102,58 +102,13 @@ Get-MailUser + Identity Identity OrganizationalUnit ReadFromDomainController - - Filter - - AcceptMessagesOnlyFrom - AcceptMessagesOnlyFromDLMembers - Alias - CustomAttribute1 - CustomAttribute2 - CustomAttribute3 - CustomAttribute4 - CustomAttribute5 - CustomAttribute6 - CustomAttribute7 - CustomAttribute8 - CustomAttribute9 - CustomAttribute10 - CustomAttribute11 - CustomAttribute12 - CustomAttribute13 - CustomAttribute14 - CustomAttribute15 - DisplayName - DistinguishedName - EmailAddresses - EmailAddressPolicyEnabled - ExchangeVersion - ExternalEmailAddress - GrantSendOnBehalfTo - Guid - HiddenFromAddressListsEnabled - MaxReceiveSize - MaxSendSize - Name - PrimarySmtpAddress - RecipientLimits - RecipientType - RecipientTypeDetails - RejectMessagesFrom - RejectMessagesFromDLMembers - SamAccountName - SimpleDisplayName - UMDtmfMap - UserPrincipalName - WhenChanged - WhenCreated - WindowsEmailAddress - + Enable-MailUser @@ -266,6 +221,97 @@ SortBy + + Set-Mailbox + Identity + + AcceptMessagesOnlyFrom + AcceptMessagesOnlyFromDLMembers + Alias + AntispamBypassEnabled + ApplyMandatoryProperties + Confirm + CreateDTMFMap + CustomAttribute1 + CustomAttribute2 + CustomAttribute3 + CustomAttribute4 + CustomAttribute5 + CustomAttribute6 + CustomAttribute7 + CustomAttribute8 + CustomAttribute9 + CustomAttribute10 + CustomAttribute11 + CustomAttribute12 + CustomAttribute13 + CustomAttribute14 + CustomAttribute15 + DeliverToMailboxAndForward + DisplayName + DomainController + DowngradeHighPriorityMessagesEnabled + EmailAddresses + EmailAddressPolicyEnabled + EndDateForRetentionHold + Extensions + ExternalOofOptions + ForwardingAddress + GrantSendOnBehalfTo + HiddenFromAddressListsEnabled + IgnoreDefaultScope + Instance + IssueWarningQuota + Languages + LinkedCredential + LinkedDomainController + LinkedMasterAccount + ManagedFolderMailboxPolicy + ManagedFolderMailboxPolicyAllowed + MaxBlockedSenders + MaxReceiveSize + MaxSafeSenders + MaxSendSize + Name + Office + OfflineAddressBook + PrimarySmtpAddress + ProhibitSendQuota + ProhibitSendReceiveQuota + RecipientLimits + RejectMessagesFrom + RejectMessagesFromDLMembers + RemoveManagedFolderAndPolicy + RequireSenderAuthenticationEnabled + ResourceCapacity + ResourceCustom + RetainDeletedItemsFor + RetainDeletedItemsUntilBackup + RetentionHoldEnabled + RulesQuota + SamAccountName + SCLDeleteEnabled + SCLDeleteThreshold + SCLJunkEnabled + SCLJunkThreshold + SCLQuarantineEnabled + SCLQuarantineThreshold + SCLRejectEnabled + SCLRejectThreshold + SecondaryAddress + SecondaryDialPlan + SimpleDisplayName + StartDateForRetentionHold + Type + UMDtmfMap + UseDatabaseQuotaDefaults + UseDatabaseRetentionDefaults + UserPrincipalName + UseRusServer + WhatIf + WindowsEmailAddress + + \ No newline at end of file diff --git a/ExchangeConnector/ExchangeConnector.cs b/ExchangeConnector/ExchangeConnector.cs index c9d225e2..bff9fd46 100644 --- a/ExchangeConnector/ExchangeConnector.cs +++ b/ExchangeConnector/ExchangeConnector.cs @@ -303,6 +303,11 @@ internal sealed class CommandInfo /// internal static readonly CommandInfo EnableMailbox = new CommandInfo("Enable-Mailbox"); + /// + /// Set-Mailbox command meta info + /// + internal static readonly CommandInfo SetMailbox = new CommandInfo("Set-Mailbox"); + /// /// Enable-MailUser command meta info /// @@ -313,6 +318,11 @@ internal sealed class CommandInfo /// internal static readonly CommandInfo SetMailUser = new CommandInfo("Set-MailUser"); + /// + /// Get-MailUser command meta info + /// + internal static readonly CommandInfo GetMailUser = new CommandInfo("Get-MailUser"); + /// /// Get-User command meta info /// diff --git a/ExchangeConnector/ExchangeUtility.cs b/ExchangeConnector/ExchangeUtility.cs index f43847d7..6e60829a 100644 --- a/ExchangeConnector/ExchangeUtility.cs +++ b/ExchangeConnector/ExchangeUtility.cs @@ -235,17 +235,16 @@ internal static object GetAttValue(string attName, ICollection /// Collection of attributes - /// Attribute names to be filtered out + /// Attribute names to be filtered out /// Filtered collection of attributes - internal static ICollection FilterOut(ICollection attributes, params string[] attName) + internal static ICollection FilterOut(ICollection attributes, IList names) { Assertions.NullCheck(attributes, "attributes"); - if (attName == null || attName.Length == 0) + if (names == null || names.Count == 0) { return attributes; } - - IList names = new ArrayList(attName); + ICollection filtered = new List(); foreach (ConnectorAttribute attribute in attributes) { diff --git a/ExchangeConnector/LegacyExchangeConnector.cs b/ExchangeConnector/LegacyExchangeConnector.cs index 6d4e0f8f..0e135091 100644 --- a/ExchangeConnector/LegacyExchangeConnector.cs +++ b/ExchangeConnector/LegacyExchangeConnector.cs @@ -31,7 +31,9 @@ namespace Org.IdentityConnectors.Exchange using System.Diagnostics; using System.Management.Automation; using System.Management.Automation.Runspaces; + using Org.IdentityConnectors.ActiveDirectory; + using Org.IdentityConnectors.Common; using Org.IdentityConnectors.Framework.Common.Exceptions; using Org.IdentityConnectors.Framework.Common.Objects; using Org.IdentityConnectors.Framework.Common.Objects.Filters; @@ -168,24 +170,43 @@ public override Uid Create( // get recipient type string rcptType = ExchangeUtility.GetAttValue(AttRecipientType, attributes) as string; - if (rcptType != RcptTypeMailBox && rcptType != RcptTypeMailUser) + ExchangeConnector.CommandInfo cmdInfoEnable = null; + ExchangeConnector.CommandInfo cmdInfoSet = null; + switch (rcptType) { - // AD account only, we do nothing - return base.Create(oclass, FilterOut(attributes), options); + case RcptTypeMailBox: + cmdInfoEnable = ExchangeConnector.CommandInfo.EnableMailbox; + cmdInfoSet = ExchangeConnector.CommandInfo.SetMailbox; + break; + case RcptTypeMailUser: + cmdInfoEnable = ExchangeConnector.CommandInfo.EnableMailUser; + cmdInfoSet = ExchangeConnector.CommandInfo.SetMailUser; + break; + case RcptTypeUser: + break; + default: + throw new ArgumentException( + this.configuration.ConnectorMessages.Format( + "ex_bad_rcpt", "Recipient type [{0}] is not supported", rcptType)); } // first create the object in AD - Uid uid = base.Create(oclass, FilterOut(attributes), options); + Uid uid = base.Create(oclass, FilterOut(attributes, cmdInfoEnable, cmdInfoSet), options); - // prepare the command - ExchangeConnector.CommandInfo cmdInfo = rcptType == RcptTypeMailBox - ? ExchangeConnector.CommandInfo.EnableMailbox - : ExchangeConnector.CommandInfo.EnableMailUser; - Command cmd = ExchangeUtility.GetCommand(cmdInfo, attributes); + if (rcptType == RcptTypeUser) + { + // AD account only, we do nothing + return uid; + } + + // prepare the command + Command cmdEnable = ExchangeUtility.GetCommand(cmdInfoEnable, attributes); + Command cmdSet = ExchangeUtility.GetCommand(cmdInfoSet, attributes); try { - this.InvokePipeline(cmd); + this.InvokePipeline(cmdEnable); + this.InvokePipeline(cmdSet); } catch { @@ -235,26 +256,48 @@ public override Uid Update( ExchangeUtility.NullCheck(oclass, "oclass", this.configuration); ExchangeUtility.NullCheck(attributes, "attributes", this.configuration); - // update in AD first - Uid uid = base.Update(type, oclass, FilterOut(attributes), options); - // get recipient type and database string rcptType = ExchangeUtility.GetAttValue(AttRecipientType, attributes) as string; string database = ExchangeUtility.GetAttValue(AttDatabase, attributes) as string; + // update in AD first + var filtered = FilterOut( + attributes, + ExchangeConnector.CommandInfo.EnableMailbox, + ExchangeConnector.CommandInfo.EnableMailUser, + ExchangeConnector.CommandInfo.SetMailbox, + ExchangeConnector.CommandInfo.SetMailUser); + Uid uid = base.Update(type, oclass, filtered, options); + + ConnectorObject aduser = this.ADSearchByUid(uid, oclass, ExchangeUtility.AddAttributeToOptions(options, AttDatabaseADName)); + attributes.Add(aduser.Name); + ExchangeConnector.CommandInfo cmdInfo = ExchangeConnector.CommandInfo.GetUser; + if (aduser.GetAttributeByName(AttDatabaseADName) != null) + { + // we can be sure it is user mailbox type + cmdInfo = ExchangeConnector.CommandInfo.GetMailbox; + } + + PSObject psuser = this.GetUser(cmdInfo, attributes); + string origRcptType = psuser.Members[AttRecipientType].Value.ToString(); + if (String.IsNullOrEmpty(rcptType)) + { + rcptType = origRcptType; + } + if (rcptType == RcptTypeMailUser) { if (type == UpdateType.REPLACE) - { - // get name attribute - attributes = this.EnsureName(oclass, attributes, uid); - - PSObject psuser = this.GetUser(ExchangeConnector.CommandInfo.GetUser, attributes); - string origRcptType = psuser.Members[AttRecipientType].Value.ToString(); - Command cmd = origRcptType != rcptType ? - ExchangeUtility.GetCommand(ExchangeConnector.CommandInfo.EnableMailUser, attributes) - : ExchangeUtility.GetCommand(ExchangeConnector.CommandInfo.SetMailUser, attributes); - this.InvokePipeline(cmd); + { + if (origRcptType != rcptType) + { + Command cmdEnable = ExchangeUtility.GetCommand( + ExchangeConnector.CommandInfo.EnableMailUser, attributes); + this.InvokePipeline(cmdEnable); + } + + Command cmdSet = ExchangeUtility.GetCommand(ExchangeConnector.CommandInfo.SetMailUser, attributes); + this.InvokePipeline(cmdSet); } else { @@ -270,23 +313,12 @@ public override Uid Update( // executed :-( // alternatively there can be something like: // get-user -identity id -RecipientTypeDetails User|enable-mailbox -database "db", but we have then trouble - // with detecting attempt to change the database attribute - ConnectorObject aduser = this.ADSearchByUid(uid, oclass, ExchangeUtility.AddAttributeToOptions(options, AttDatabaseADName)); - attributes.Add(aduser.Name); - ExchangeConnector.CommandInfo cmdInfo = ExchangeConnector.CommandInfo.GetUser; - if (aduser.GetAttributeByName(AttDatabaseADName) != null) - { - // we can be sure it is user mailbox type - cmdInfo = ExchangeConnector.CommandInfo.GetMailbox; - } - - PSObject psuser = this.GetUser(cmdInfo, attributes); - string origRcptType = psuser.Members[AttRecipientType].Value.ToString(); + // with detecting attempt to change the database attribute string origDatabase = psuser.Members[AttDatabase] != null ? psuser.Members[AttDatabase].Value.ToString() : null; if (origRcptType != rcptType) { - Command cmd2 = ExchangeUtility.GetCommand(ExchangeConnector.CommandInfo.EnableMailbox, attributes); - this.InvokePipeline(cmd2); + Command cmdEnable = ExchangeUtility.GetCommand(ExchangeConnector.CommandInfo.EnableMailbox, attributes); + this.InvokePipeline(cmdEnable); } else { @@ -298,20 +330,15 @@ public override Uid Update( "ex_not_updatable", "Update of [{0}] attribute is not supported", AttDatabase)); } } + + Command cmdSet = ExchangeUtility.GetCommand(ExchangeConnector.CommandInfo.SetMailbox, attributes); + this.InvokePipeline(cmdSet); } - else if (rcptType == RcptTypeUser) + else if (rcptType == RcptTypeUser && origRcptType != rcptType) { - // get name attribute - attributes = this.EnsureName(oclass, attributes, uid); - - PSObject psuser = this.GetUser(ExchangeConnector.CommandInfo.GetUser, attributes); - string origRcptType = psuser.Members[AttRecipientType].Value.ToString(); - if (origRcptType != rcptType) - { - throw new ArgumentException( - this.configuration.ConnectorMessages.Format( - "ex_update_notsupported", "Update of [{0}] to [{1}] is not supported", AttRecipientType, rcptType)); - } + throw new ArgumentException( + this.configuration.ConnectorMessages.Format( + "ex_update_notsupported", "Update of [{0}] to [{1}] is not supported", AttRecipientType, rcptType)); } else { @@ -555,10 +582,45 @@ protected override ObjectClassInfo GetObjectClassInfo(ObjectClass oc) /// helper method to filter out all attributes used in LegacyExchangeConnector only /// /// Connector attributes - /// Filtered connector attributes - private static ICollection FilterOut(ICollection attributes) + /// CommandInfo whose parameters will be used and filtered out from attributes + /// + /// Filtered connector attributes + /// + private static ICollection FilterOut(ICollection attributes, params ExchangeConnector.CommandInfo[] cmdInfos) + { + IList attsToRemove = new List { AttRecipientType, AttDatabase, AttExternalMail }; + if (cmdInfos != null) + { + foreach (ExchangeConnector.CommandInfo cmdInfo in cmdInfos) + { + CollectionUtil.AddAll(attsToRemove, cmdInfo.Parameters); + } + } + + return ExchangeUtility.FilterOut(attributes, attsToRemove); + } + + /// + /// This method tries to get name and value from and + /// creates out of it + /// + /// PSMemberInfo to get the data from + /// Created ConnectorAttribute or null if not possible to create it + private static ConnectorAttribute GetAsAttribute(PSMemberInfo info) { - return ExchangeUtility.FilterOut(attributes, AttRecipientType, AttDatabase, AttExternalMail); + Assertions.NullCheck(info, "param"); + if (info.Value != null) + { + string value = info.Value.ToString(); + + // TODO: add type recognition, currently only string is supported + if (value != info.Value.GetType().ToString() && !string.IsNullOrEmpty(value)) + { + return ConnectorAttributeBuilder.Build(info.Name, value); + } + } + + return null; } /// @@ -576,69 +638,88 @@ private ConnectorObject AddExchangeAttributes(ObjectClass oc, ConnectorObject co ExchangeUtility.NullCheck(oc, "name", this.configuration); ExchangeUtility.NullCheck(oc, "cobject", this.configuration); - // we support ACCOUNT only and if the recipient type and database is in att to get - if (!oc.Is(ObjectClass.ACCOUNT_NAME) || - (!attToGet.Contains(AttRecipientType) && !attToGet.Contains(AttDatabase))) + // we support ACCOUNT only + if (!oc.Is(ObjectClass.ACCOUNT_NAME)) { return cobject; } - bool getDatabase = false; + ConnectorObjectBuilder cobjBuilder = new ConnectorObjectBuilder(); + cobjBuilder.AddAttributes(cobject.GetAttributes()); + ExchangeConnector.CommandInfo cmdInfo = ExchangeConnector.CommandInfo.GetUser; - if (cobject.GetAttributeByName(AttDatabase) != null || cobject.GetAttributeByName(AttDatabaseADName) != null) - { - // we need to get database attribute, it is mailbox for sure - getDatabase = true; - cmdInfo = ExchangeConnector.CommandInfo.GetMailbox; - } - - ConnectorObject retCObject = cobject; // prepare the connector attribute list to get the command ICollection attributes = new Collection { cobject.Name }; // get the command Command cmd = ExchangeUtility.GetCommand(cmdInfo, attributes); - ICollection foundObjects = this.InvokePipeline(cmd); - - // it has to be only one or zero objects in this case + PSObject user = null; if (foundObjects != null && foundObjects.Count == 1) { - string rcptName = RcptTypeUser; - string database = null; - foreach (PSObject obj in foundObjects) + user = GetFirstElement(foundObjects); + foreach (var info in user.Properties) { - rcptName = obj.Members[AttRecipientType].Value.ToString(); - database = obj.Members[AttDatabase] != null ? obj.Members[AttDatabase].Value.ToString() : null; - break; + ConnectorAttribute att = GetAsAttribute(info); + if (att != null) + { + cobjBuilder.AddAttribute(att); + } } + } - ConnectorObjectBuilder cobjBuilder = new ConnectorObjectBuilder(); - if (getDatabase) + if (user == null) + { + // nothing to do + return cobject; + } + + string rcptType = user.Members[AttRecipientType].Value.ToString(); + foundObjects = null; + + // get detailed information + PSObject userDetails = null; + if (rcptType == RcptTypeMailBox) + { + foundObjects = this.InvokePipeline(ExchangeUtility.GetCommand(ExchangeConnector.CommandInfo.GetMailbox, attributes)); + } + else if (rcptType == RcptTypeMailUser) + { + foundObjects = this.InvokePipeline(ExchangeUtility.GetCommand(ExchangeConnector.CommandInfo.GetMailUser, attributes)); + } + + if (foundObjects != null && foundObjects.Count == 1) + { + userDetails = GetFirstElement(foundObjects); + foreach (var info in userDetails.Properties) { - foreach (ConnectorAttribute attribute in cobject.GetAttributes()) + + ConnectorAttribute att = GetAsAttribute(info); + if (att != null) { - if ((attribute.Is(AttDatabase) || attribute.Is(AttDatabaseADName)) && database != null) - { - cobjBuilder.AddAttribute(ConnectorAttributeBuilder.Build(AttDatabase, database)); - } - else - { - cobjBuilder.AddAttribute(attribute); - } + cobjBuilder.AddAttribute(att); } } - else - { - cobjBuilder.AddAttributes(cobject.GetAttributes()); - } + } - cobjBuilder.AddAttribute(ConnectorAttributeBuilder.Build(AttRecipientType, rcptName)); - retCObject = cobjBuilder.Build(); + return cobjBuilder.Build(); + } + + /// + /// Returns first element of the collection + /// + /// Object Type stored in collection + /// Collection to get the first element from + /// First element in the collection, null if the collection is empty + private T GetFirstElement(IEnumerable collection) where T : class + { + foreach (T o in collection) + { + return o; } - return retCObject; + return null; } /// From 7e2e8a1b4cd780749850ce41d0d7dfad0fd2fe33 Mon Sep 17 00:00:00 2001 From: tknappek Date: Wed, 1 Apr 2009 19:01:26 +0000 Subject: [PATCH 209/342] Issue #463: handling deleted objects in sync fixed --- ExchangeConnector/LegacyExchangeConnector.cs | 77 +++++++++----------- 1 file changed, 33 insertions(+), 44 deletions(-) diff --git a/ExchangeConnector/LegacyExchangeConnector.cs b/ExchangeConnector/LegacyExchangeConnector.cs index 0e135091..0e920fc4 100644 --- a/ExchangeConnector/LegacyExchangeConnector.cs +++ b/ExchangeConnector/LegacyExchangeConnector.cs @@ -65,6 +65,11 @@ public class LegacyExchangeConnector : ActiveDirectoryConnector /// internal const string AttDatabase = "Database"; + /// + /// Deleted atrribute name + /// + internal const string AttIsDeleted = "isDeleted"; + /// /// External Mail attribute name as in AD /// @@ -623,6 +628,22 @@ private static ConnectorAttribute GetAsAttribute(PSMemberInfo info) return null; } + /// + /// Returns first element of the collection + /// + /// Object Type stored in collection + /// Collection to get the first element from + /// First element in the collection, null if the collection is empty + private static T GetFirstElement(IEnumerable collection) where T : class + { + foreach (T o in collection) + { + return o; + } + + return null; + } + /// /// Gets Recipient Type/Database from Exchange database, this method can be more general, but it is ok /// for out needs @@ -644,6 +665,14 @@ private ConnectorObject AddExchangeAttributes(ObjectClass oc, ConnectorObject co return cobject; } + // check it is not deleted object + bool? deleted = ExchangeUtility.GetAttValue(AttIsDeleted, cobject.GetAttributes()) as bool?; + if (deleted != null && deleted == true) + { + // do nothing, it is deleted object + return cobject; + } + ConnectorObjectBuilder cobjBuilder = new ConnectorObjectBuilder(); cobjBuilder.AddAttributes(cobject.GetAttributes()); @@ -678,8 +707,7 @@ private ConnectorObject AddExchangeAttributes(ObjectClass oc, ConnectorObject co string rcptType = user.Members[AttRecipientType].Value.ToString(); foundObjects = null; - // get detailed information - PSObject userDetails = null; + // get detailed information if (rcptType == RcptTypeMailBox) { foundObjects = this.InvokePipeline(ExchangeUtility.GetCommand(ExchangeConnector.CommandInfo.GetMailbox, attributes)); @@ -691,10 +719,9 @@ private ConnectorObject AddExchangeAttributes(ObjectClass oc, ConnectorObject co if (foundObjects != null && foundObjects.Count == 1) { - userDetails = GetFirstElement(foundObjects); + PSObject userDetails = GetFirstElement(foundObjects); foreach (var info in userDetails.Properties) { - ConnectorAttribute att = GetAsAttribute(info); if (att != null) { @@ -704,23 +731,7 @@ private ConnectorObject AddExchangeAttributes(ObjectClass oc, ConnectorObject co } return cobjBuilder.Build(); - } - - /// - /// Returns first element of the collection - /// - /// Object Type stored in collection - /// Collection to get the first element from - /// First element in the collection, null if the collection is empty - private T GetFirstElement(IEnumerable collection) where T : class - { - foreach (T o in collection) - { - return o; - } - - return null; - } + } /// /// Invokes command in PowerShell runspace, this method is just helper @@ -802,29 +813,7 @@ private PSObject GetUser(ExchangeConnector.CommandInfo cmdInfo, ICollection - /// Ensures we have Name attribute in attributes collection, if not present, it uses AD search to get it - /// - /// object class - /// Collection of attributes - /// object Uid - /// Collection of attributes conta - private ICollection EnsureName(ObjectClass oclass, ICollection attributes, Uid uid) - { - string name = ExchangeUtility.GetAttValue(Name.NAME, attributes) as string; - if (name == null) - { - // we don't know name, but we need it - NOTE: searching for all the default attributes, we need only Name here, it can be improved - ConnectorObject co = this.ADSearchByUid(uid, oclass, null); - ExchangeUtility.NullCheck(co, "co", this.configuration); - - // add to attributes - attributes.Add(co.Name); - } - return attributes; - } + } /// /// Filter translator which does MS Exchange specific translation From e83cd59a4559b228aa169560b1d3935968d4c202 Mon Sep 17 00:00:00 2001 From: dvernon Date: Wed, 1 Apr 2009 20:22:58 +0000 Subject: [PATCH 210/342] Fixing code to detect and allow move from one container to another --- .../ActiveDirectoryConnector.cs | 13 ++- .../ActiveDirectoryUtils.cs | 67 +++++------ .../ActiveDirectoryConnectorTest.cs | 110 +++++++++++++----- 3 files changed, 120 insertions(+), 70 deletions(-) diff --git a/ActiveDirectoryConnector/ActiveDirectoryConnector.cs b/ActiveDirectoryConnector/ActiveDirectoryConnector.cs index a6a23a0f..13535e6d 100644 --- a/ActiveDirectoryConnector/ActiveDirectoryConnector.cs +++ b/ActiveDirectoryConnector/ActiveDirectoryConnector.cs @@ -228,7 +228,12 @@ public virtual Uid Create(ObjectClass oclass, if (!oclass.Equals(ObjectClass.ACCOUNT)) { // uid will be the dn for non account objects - return new Uid(nameAttribute.GetNameValue()); + String dnUid = nameAttribute.GetNameValue(); + if((dnUid != null) && (dnUid.Length > 0)) + { + dnUid = ActiveDirectoryUtils.NormalizeLdapString(dnUid); + } + return new Uid(dnUid); } return uid; } @@ -827,7 +832,11 @@ public virtual Uid Update(UpdateType type, ObjectClass oclass, if(!ObjectClass.ACCOUNT.Equals(oclass)) { // other objects use dn as guid for idm backward compatibility - updatedUid = new Uid((string)updateEntry.Properties["distinguishedName"][0]); + String dnUid = (string)updateEntry.Properties["distinguishedName"][0]; + if ((dnUid != null) && (dnUid.Length > 0)) + { + updatedUid = new Uid(ActiveDirectoryUtils.NormalizeLdapString(dnUid)); + } } return updatedUid; } diff --git a/ActiveDirectoryConnector/ActiveDirectoryUtils.cs b/ActiveDirectoryConnector/ActiveDirectoryUtils.cs index 06763cf7..1fdb765d 100644 --- a/ActiveDirectoryConnector/ActiveDirectoryUtils.cs +++ b/ActiveDirectoryConnector/ActiveDirectoryUtils.cs @@ -579,57 +579,42 @@ private static void HandleContainerChange(UpdateType type, DirectoryEntry directoryEntry, ICollection attributes, ActiveDirectoryConfiguration config) { - // this return means that te connector attribute is ignored for - // the purpose of moving an object to a different container - return; - - // this code seems right, but doesnt work. The DirectoryEntry.Move() - // method always throws an Exception with the text 'unspecified error' + Name nameAttribute = ConnectorAttributeUtil.GetNameFromAttributes(attributes); + if(nameAttribute == null) + { + // no name, so must not be a container change + return; + } - ConnectorAttribute containerAttribute = - ConnectorAttributeUtil.Find(ActiveDirectoryConnector.ATT_CONTAINER, attributes); - if (containerAttribute != null) + if (!type.Equals(UpdateType.REPLACE)) { // this only make sense for replace. you can't // add a name or delete a name - if (type.Equals(UpdateType.REPLACE)) + return; + } + + String oldContainer = GetParentDn(directoryEntry.Path); + String newContainer = GetParentDn(nameAttribute.GetNameValue()); + + if (!NormalizeLdapString(oldContainer).Equals(NormalizeLdapString(newContainer))) + { + if (newContainer != null) { - DirectoryEntry parent = directoryEntry.Parent; - String oldContainer = null; - if (parent != null) + try { - PropertyValueCollection parentDNValues = - parent.Properties[ActiveDirectoryConnector.ATT_DISTINGUISHED_NAME]; - if ((parentDNValues.Count == 1) && (parentDNValues[0] is String)) + if (!NormalizeLdapString(oldContainer).Equals( + NormalizeLdapString(newContainer))) { - oldContainer = (String)parentDNValues[0]; - } - else - { - String msg = String.Format("Unable to retrieve the distinguished name for {0}.", - parent.Path); - throw new ConnectorException(msg); + String newContainerLdapPath = ActiveDirectoryUtils.GetLDAPPath( + config.LDAPHostName, newContainer); + DirectoryEntry newContainerDe = new DirectoryEntry(newContainerLdapPath, + config.DirectoryAdminName, config.DirectoryAdminPassword); + directoryEntry.MoveTo(newContainerDe); } } - - String newContainer = ConnectorAttributeUtil.GetStringValue(containerAttribute); - - if (newContainer != null) + catch (Exception e) { - try - { - if (!NormalizeLdapString(oldContainer).Equals( - NormalizeLdapString(newContainer))) - { - DirectoryEntry newContainerDe = new DirectoryEntry(newContainer, - config.DirectoryAdminName, config.DirectoryAdminPassword); - directoryEntry.MoveTo(newContainerDe); - } - } - catch (Exception e) - { - throw e; - } + throw e; } } } diff --git a/ActiveDirectoryConnectorTests/ActiveDirectoryConnectorTest.cs b/ActiveDirectoryConnectorTests/ActiveDirectoryConnectorTest.cs index 836b52bc..ab119c08 100644 --- a/ActiveDirectoryConnectorTests/ActiveDirectoryConnectorTest.cs +++ b/ActiveDirectoryConnectorTests/ActiveDirectoryConnectorTest.cs @@ -1092,38 +1092,39 @@ public void TestRemoveAttributeValue() } } - - [Ignore] [Test] public void TestContainerChange_account() { ActiveDirectoryConnector connector = new ActiveDirectoryConnector(); connector.Init(GetConfiguration()); - Uid createGroupUid = null; + Uid createOuUid = null; Uid createUserUid = null; try { // create container for this test - ICollection groupAttributes = GetNormalAttributes_Group(); - createGroupUid = CreateAndVerifyObject(connector, - ActiveDirectoryConnector.groupObjectClass, groupAttributes); - ICollection groupResults = TestHelpers.SearchToList( - connector, ActiveDirectoryConnector.groupObjectClass, FilterBuilder.EqualTo(createGroupUid)); - Assert.AreEqual(1, groupResults.Count); - Assert.AreEqual(createGroupUid, groupResults.ElementAt(0).Uid); - ConnectorAttribute groupDnAttr = - groupResults.ElementAt(0).GetAttributeByName("distinguishedName"); - Assert.IsNotNull(groupDnAttr); - String groupPath = ConnectorAttributeUtil.GetStringValue(groupDnAttr); + ICollection ouAttributes = GetNormalAttributes_OrganizationalUnit(); + createOuUid = CreateAndVerifyObject(connector, + ActiveDirectoryConnector.ouObjectClass, ouAttributes); + ICollection ouResults = TestHelpers.SearchToList( + connector, ActiveDirectoryConnector.ouObjectClass, FilterBuilder.EqualTo(createOuUid)); + Assert.AreEqual(1, ouResults.Count); + Assert.AreEqual(createOuUid, ouResults.ElementAt(0).Uid); + + // as a reminder, the uid is the dn for non account objects (idm backward compatiblity) + String ouPath = createOuUid.GetUidValue(); // create user + ICollection userAttributes = GetNormalAttributes_Account(); createUserUid = CreateAndVerifyObject(connector, - ObjectClass.ACCOUNT, GetNormalAttributes_Account()); + ObjectClass.ACCOUNT, userAttributes); //now change user container to the newly created one + Name createdName = ConnectorAttributeUtil.GetNameFromAttributes(userAttributes); + String newName = ActiveDirectoryUtils.GetRelativeName(createdName); + newName += ", " + ouPath; ICollection updateAttrs = new HashSet(); - updateAttrs.Add(ConnectorAttributeBuilder.Build("ad_container", groupPath)); + updateAttrs.Add(new Name(newName)); updateAttrs.Add(createUserUid); connector.Update(UpdateType.REPLACE, ObjectClass.ACCOUNT, updateAttrs, null); @@ -1135,7 +1136,7 @@ public void TestContainerChange_account() ConnectorAttribute foundContainerAttr = results.ElementAt(0).GetAttributeByName("ad_container"); Assert.IsNotNull(foundContainerAttr); - String lhs = ActiveDirectoryUtils.NormalizeLdapString(groupPath); + String lhs = ActiveDirectoryUtils.NormalizeLdapString(ouPath); String rhs = ActiveDirectoryUtils.NormalizeLdapString(ConnectorAttributeUtil.GetStringValue(foundContainerAttr)); Assert.AreEqual(lhs, rhs); } @@ -1148,31 +1149,86 @@ public void TestContainerChange_account() createUserUid, false, true); } - if (createGroupUid != null) + if (createOuUid != null) { //remove the one we created - DeleteAndVerifyObject(connector, ActiveDirectoryConnector.groupObjectClass, - createGroupUid, false, true); + DeleteAndVerifyObject(connector, ActiveDirectoryConnector.ouObjectClass, + createOuUid, false, true); } } } - [Ignore] [Test] public void TestContainerChange_group() { ActiveDirectoryConnector connector = new ActiveDirectoryConnector(); connector.Init(GetConfiguration()); - // create user - Uid createUid = CreateAndVerifyObject(connector, - ObjectClass.ACCOUNT, GetNormalAttributes_Account()); + Uid createOuUid = null; + Uid createGroupUid = null; + Uid updateGroupUid = null; + + try { + // create container for this test + ICollection ouAttributes = GetNormalAttributes_OrganizationalUnit(); + createOuUid = CreateAndVerifyObject(connector, + ActiveDirectoryConnector.ouObjectClass, ouAttributes); + ICollection ouResults = TestHelpers.SearchToList( + connector, ActiveDirectoryConnector.ouObjectClass, FilterBuilder.EqualTo(createOuUid)); + Assert.AreEqual(1, ouResults.Count); + Assert.AreEqual(createOuUid, ouResults.ElementAt(0).Uid); - // create container for this test + // as a reminder, the uid is the dn for non account objects (idm backward compatiblity) + String ouPath = createOuUid.GetUidValue(); - //now change user container to the newly created one + // create group + ICollection groupAttributes = GetNormalAttributes_Group(); + createGroupUid = CreateAndVerifyObject(connector, + ActiveDirectoryConnector.groupObjectClass, groupAttributes); - throw new NotImplementedException(); + //now change group's container to the newly created one + Name createdName = ConnectorAttributeUtil.GetNameFromAttributes(groupAttributes); + String newName = ActiveDirectoryUtils.GetRelativeName(createdName); + newName += ", " + ouPath; + ICollection updateAttrs = new HashSet(); + updateAttrs.Add(new Name(newName)); + updateAttrs.Add(createGroupUid); + + updateGroupUid = connector.Update(UpdateType.REPLACE, + ActiveDirectoryConnector.groupObjectClass, updateAttrs, null); + + ICollection results = TestHelpers.SearchToList( + connector, ActiveDirectoryConnector.groupObjectClass, FilterBuilder.EqualTo(updateGroupUid)); + Assert.AreEqual(1, results.Count); + Assert.AreEqual(updateGroupUid, results.ElementAt(0).Uid); + ConnectorAttribute foundContainerAttr = results.ElementAt(0).GetAttributeByName("ad_container"); + Assert.IsNotNull(foundContainerAttr); + + String lhs = ActiveDirectoryUtils.NormalizeLdapString(ouPath); + String rhs = ActiveDirectoryUtils.NormalizeLdapString(ConnectorAttributeUtil.GetStringValue(foundContainerAttr)); + Assert.AreEqual(lhs, rhs); + } + finally + { + if(updateGroupUid != null) + { + //remove the one. if we updated, this is the id + DeleteAndVerifyObject(connector, ActiveDirectoryConnector.groupObjectClass, + updateGroupUid, false, true); + } else if (createGroupUid != null) + { + //remove the one. if we didn't update, this is the id + DeleteAndVerifyObject(connector, ActiveDirectoryConnector.groupObjectClass, + createGroupUid, false, true); + } + + if (createOuUid != null) + { + //remove the one we created + DeleteAndVerifyObject(connector, ActiveDirectoryConnector.ouObjectClass, + createOuUid, false, true); + } + } } [Ignore] From 0d09afb6909315e220740bd5c0ebc39b00f0d28a Mon Sep 17 00:00:00 2001 From: tknappek Date: Thu, 2 Apr 2009 08:23:57 +0000 Subject: [PATCH 211/342] DotNetCommonBuild.Targets ziplevel removed from zip target --- DotNetCommonBuild.Targets | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/DotNetCommonBuild.Targets b/DotNetCommonBuild.Targets index c69042d5..7a1e029d 100644 --- a/DotNetCommonBuild.Targets +++ b/DotNetCommonBuild.Targets @@ -67,8 +67,7 @@ + ZipFileName="$(CommonBuildDir)\$(AssemblyName)-$(Major).$(Minor).$(Build).$(SVN_Revision).zip" /> From b8a0eb15a31fbbc676b89d372c1c226365452941 Mon Sep 17 00:00:00 2001 From: tknappek Date: Thu, 2 Apr 2009 11:16:28 +0000 Subject: [PATCH 212/342] Fixed DotNetCommonBuild.Targets to work on all windows platforms --- DotNetCommonBuild.Targets | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/DotNetCommonBuild.Targets b/DotNetCommonBuild.Targets index 7a1e029d..f68ef01c 100644 --- a/DotNetCommonBuild.Targets +++ b/DotNetCommonBuild.Targets @@ -61,8 +61,8 @@ DestinationFolder="$(OutputPath)" /> - - + + Date: Thu, 2 Apr 2009 15:20:25 +0000 Subject: [PATCH 213/342] ExchangeConnector - fixed handling of empty attributes --- ExchangeConnector/ExchangeUtility.cs | 2 +- ExchangeConnector/LegacyExchangeConnector.cs | 5 ++++- 2 files changed, 5 insertions(+), 2 deletions(-) diff --git a/ExchangeConnector/ExchangeUtility.cs b/ExchangeConnector/ExchangeUtility.cs index 6e60829a..336ff060 100644 --- a/ExchangeConnector/ExchangeUtility.cs +++ b/ExchangeConnector/ExchangeUtility.cs @@ -225,7 +225,7 @@ internal static object GetAttValue(string attName, ICollection FilterOut(ICollection Date: Tue, 7 Apr 2009 13:52:36 +0000 Subject: [PATCH 214/342] License year update --- ActiveDirectoryConnector/ActiveDirectoryConfiguration.cs | 2 +- ActiveDirectoryConnector/ActiveDirectoryConnector.cs | 2 +- ActiveDirectoryConnector/ActiveDirectoryConnector.csproj | 4 ++-- ActiveDirectoryConnector/ActiveDirectoryFilterTranslator.cs | 2 +- ActiveDirectoryConnector/ActiveDirectorySyncToken.cs | 2 +- ActiveDirectoryConnector/ActiveDirectoryUtils.cs | 2 +- ActiveDirectoryConnector/CommonUtils.cs | 2 +- ActiveDirectoryConnector/CustomAttributeHandlers.cs | 2 +- ActiveDirectoryConnector/PasswordChangeHandler.cs | 2 +- ActiveDirectoryConnector/TerminalServicesUtils.cs | 2 +- ActiveDirectoryConnector/UserAccountControl.cs | 2 +- ActiveDirectoryConnector/build.xml | 2 +- ActiveDirectoryConnectorTests/ActiveDirectoryConnectorTest.cs | 2 +- .../ActiveDirectoryConnectorTests.csproj | 4 ++-- BooScriptExecutorFactory/BooScriptExecutorFactory.cs | 2 +- BooScriptExecutorFactory/BooScriptExecutorFactory.csproj | 2 +- Common/Assertions.cs | 2 +- Common/CollectionUtil.cs | 2 +- Common/Common.csproj | 2 +- Common/DateTimeUtil.cs | 2 +- Common/FrameworkInternalBridge.cs | 2 +- Common/IOUtil.cs | 2 +- Common/Locale.cs | 2 +- Common/Pair.cs | 2 +- Common/Pooling.cs | 2 +- Common/Proxy.cs | 2 +- Common/ReflectionUtil.cs | 2 +- Common/SafeType.cs | 2 +- Common/Script.cs | 2 +- Common/Security.cs | 2 +- Common/StringUtil.cs | 2 +- Common/TraceUtil.cs | 2 +- Common/XmlUtil.cs | 2 +- Console/Console.csproj | 2 +- Console/MainForm.cs | 2 +- Console/Program.cs | 2 +- DotNetCommonBuild.Targets | 4 ++-- ExchangeConnector/Data/CommandInfos.xml | 4 ++-- ExchangeConnector/Data/PersistenceUtility.cs | 4 ++-- ExchangeConnector/Data/SerializableCommandInfo.cs | 4 ++-- ExchangeConnector/ExchangeConfiguration.cs | 2 +- ExchangeConnector/ExchangeConnector.cs | 2 +- ExchangeConnector/ExchangeConnector.csproj | 4 ++-- ExchangeConnector/ExchangeUtility.cs | 2 +- ExchangeConnector/LegacyExchangeConnector.cs | 2 +- ExchangeConnector/ObjectClasses.xml | 2 +- ExchangeConnector/RunSpaceInstance.cs | 2 +- ExchangeConnector/build.xml | 2 +- Framework/Api.cs | 2 +- Framework/ApiOperations.cs | 2 +- Framework/Common.cs | 2 +- Framework/CommonExceptions.cs | 2 +- Framework/CommonObjects.cs | 2 +- Framework/CommonObjectsFilter.cs | 2 +- Framework/CommonSerializer.cs | 2 +- Framework/Framework.csproj | 2 +- Framework/Spi.cs | 2 +- Framework/SpiOperations.cs | 2 +- FrameworkInternal/Api.cs | 2 +- FrameworkInternal/ApiLocal.cs | 2 +- FrameworkInternal/ApiLocalOperations.cs | 2 +- FrameworkInternal/ApiRemote.cs | 2 +- FrameworkInternal/ApiRemoteMessages.cs | 2 +- FrameworkInternal/FrameworkInternal.csproj | 4 ++-- FrameworkInternal/Security.cs | 2 +- FrameworkInternal/Serializer.cs | 2 +- FrameworkInternal/SerializerBinary.cs | 2 +- FrameworkInternal/SerializerXml.cs | 2 +- FrameworkInternal/Server.cs | 2 +- FrameworkInternal/Test.cs | 2 +- FrameworkTests/CollectionUtilTests.cs | 2 +- FrameworkTests/ConnectorAttributeUtilTests.cs | 2 +- FrameworkTests/ConnectorInfoManagerTests.cs | 2 +- FrameworkTests/FilterTranslatorTests.cs | 2 +- FrameworkTests/FrameworkTests.csproj | 4 ++-- FrameworkTests/GuardedStringTests.cs | 2 +- FrameworkTests/LocaleTests.cs | 2 +- FrameworkTests/ObjectClassUtilTests.cs | 2 +- FrameworkTests/ObjectNormalizerFacadeTests.cs | 2 +- FrameworkTests/ObjectPoolTests.cs | 2 +- FrameworkTests/ObjectSerializationTests.cs | 2 +- FrameworkTests/ProxyTests.cs | 2 +- FrameworkTests/SafeTypeTest.cs | 2 +- FrameworkTests/ScriptTests.cs | 2 +- FrameworkTests/TestHelperTests.cs | 2 +- FrameworkTests/TestUtil.cs | 2 +- FrameworkTests/UpdateImplTests.cs | 2 +- Service/Program.cs | 2 +- Service/ProjectInstaller.cs | 2 +- Service/Service.cs | 2 +- Service/Service.csproj | 4 ++-- ServiceInstall/ExtBuild.proj | 2 +- ServiceInstall/ServiceInstall.wixproj | 2 +- ShellScriptExecutorFactory/ShellScriptExecutorFactory.cs | 2 +- ShellScriptExecutorFactory/ShellScriptExecutorFactory.csproj | 2 +- TestBundleV1/AssemblyInfo.cs | 2 +- TestBundleV1/TestBundleV1.csproj | 2 +- TestBundleV1/TestConnector.cs | 2 +- TestBundleV2/AssemblyInfo.cs | 2 +- TestBundleV2/TestBundleV2.csproj | 2 +- TestBundleV2/TestConnector.cs | 2 +- TestCommon/FrameworkInternalBridge.cs | 2 +- TestCommon/Test.cs | 2 +- TestCommon/TestHelpersSpi.cs | 4 ++-- build.xml | 2 +- 105 files changed, 116 insertions(+), 116 deletions(-) diff --git a/ActiveDirectoryConnector/ActiveDirectoryConfiguration.cs b/ActiveDirectoryConnector/ActiveDirectoryConfiguration.cs index 63aafbea..6fa201dd 100644 --- a/ActiveDirectoryConnector/ActiveDirectoryConfiguration.cs +++ b/ActiveDirectoryConnector/ActiveDirectoryConfiguration.cs @@ -2,7 +2,7 @@ * ==================== * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER. * - * Copyright 2007-2008 Sun Microsystems, Inc. All rights reserved. + * Copyright 2008-2009 Sun Microsystems, Inc. All rights reserved. * * The contents of this file are subject to the terms of the Common Development * and Distribution License("CDDL") (the "License"). You may not use this file diff --git a/ActiveDirectoryConnector/ActiveDirectoryConnector.cs b/ActiveDirectoryConnector/ActiveDirectoryConnector.cs index 13535e6d..4d3436c7 100644 --- a/ActiveDirectoryConnector/ActiveDirectoryConnector.cs +++ b/ActiveDirectoryConnector/ActiveDirectoryConnector.cs @@ -2,7 +2,7 @@ * ==================== * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER. * - * Copyright 2007-2008 Sun Microsystems, Inc. All rights reserved. + * Copyright 2008-2009 Sun Microsystems, Inc. All rights reserved. * * The contents of this file are subject to the terms of the Common Development * and Distribution License("CDDL") (the "License"). You may not use this file diff --git a/ActiveDirectoryConnector/ActiveDirectoryConnector.csproj b/ActiveDirectoryConnector/ActiveDirectoryConnector.csproj index 8ffc8853..517bb05d 100644 --- a/ActiveDirectoryConnector/ActiveDirectoryConnector.csproj +++ b/ActiveDirectoryConnector/ActiveDirectoryConnector.csproj @@ -2,7 +2,7 @@ ==================== DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER. - Copyright 2007-2008 Sun Microsystems, Inc. All rights reserved. + Copyright 2008-2009 Sun Microsystems, Inc. All rights reserved. The contents of this file are subject to the terms of the Common Development and Distribution License("CDDL") (the "License"). You may not use this file @@ -156,4 +156,4 @@ - \ No newline at end of file + diff --git a/ActiveDirectoryConnector/ActiveDirectoryFilterTranslator.cs b/ActiveDirectoryConnector/ActiveDirectoryFilterTranslator.cs index 2b0978d7..20b0dc8e 100644 --- a/ActiveDirectoryConnector/ActiveDirectoryFilterTranslator.cs +++ b/ActiveDirectoryConnector/ActiveDirectoryFilterTranslator.cs @@ -2,7 +2,7 @@ * ==================== * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER. * - * Copyright 2007-2008 Sun Microsystems, Inc. All rights reserved. + * Copyright 2008-2009 Sun Microsystems, Inc. All rights reserved. * * The contents of this file are subject to the terms of the Common Development * and Distribution License("CDDL") (the "License"). You may not use this file diff --git a/ActiveDirectoryConnector/ActiveDirectorySyncToken.cs b/ActiveDirectoryConnector/ActiveDirectorySyncToken.cs index 9449ba52..50871f69 100644 --- a/ActiveDirectoryConnector/ActiveDirectorySyncToken.cs +++ b/ActiveDirectoryConnector/ActiveDirectorySyncToken.cs @@ -2,7 +2,7 @@ * ==================== * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER. * - * Copyright 2007-2008 Sun Microsystems, Inc. All rights reserved. + * Copyright 2008-2009 Sun Microsystems, Inc. All rights reserved. * * The contents of this file are subject to the terms of the Common Development * and Distribution License("CDDL") (the "License"). You may not use this file diff --git a/ActiveDirectoryConnector/ActiveDirectoryUtils.cs b/ActiveDirectoryConnector/ActiveDirectoryUtils.cs index 1fdb765d..08e57068 100644 --- a/ActiveDirectoryConnector/ActiveDirectoryUtils.cs +++ b/ActiveDirectoryConnector/ActiveDirectoryUtils.cs @@ -2,7 +2,7 @@ * ==================== * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER. * - * Copyright 2007-2008 Sun Microsystems, Inc. All rights reserved. + * Copyright 2008-2009 Sun Microsystems, Inc. All rights reserved. * * The contents of this file are subject to the terms of the Common Development * and Distribution License("CDDL") (the "License"). You may not use this file diff --git a/ActiveDirectoryConnector/CommonUtils.cs b/ActiveDirectoryConnector/CommonUtils.cs index d42bb8cd..ae61e3b4 100644 --- a/ActiveDirectoryConnector/CommonUtils.cs +++ b/ActiveDirectoryConnector/CommonUtils.cs @@ -2,7 +2,7 @@ * ==================== * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER. * - * Copyright 2007-2008 Sun Microsystems, Inc. All rights reserved. + * Copyright 2008-2009 Sun Microsystems, Inc. All rights reserved. * * The contents of this file are subject to the terms of the Common Development * and Distribution License("CDDL") (the "License"). You may not use this file diff --git a/ActiveDirectoryConnector/CustomAttributeHandlers.cs b/ActiveDirectoryConnector/CustomAttributeHandlers.cs index 0cc4658a..74f30f8a 100644 --- a/ActiveDirectoryConnector/CustomAttributeHandlers.cs +++ b/ActiveDirectoryConnector/CustomAttributeHandlers.cs @@ -2,7 +2,7 @@ * ==================== * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER. * - * Copyright 2007-2008 Sun Microsystems, Inc. All rights reserved. + * Copyright 2008-2009 Sun Microsystems, Inc. All rights reserved. * * The contents of this file are subject to the terms of the Common Development * and Distribution License("CDDL") (the "License"). You may not use this file diff --git a/ActiveDirectoryConnector/PasswordChangeHandler.cs b/ActiveDirectoryConnector/PasswordChangeHandler.cs index e050170a..771b28d0 100644 --- a/ActiveDirectoryConnector/PasswordChangeHandler.cs +++ b/ActiveDirectoryConnector/PasswordChangeHandler.cs @@ -2,7 +2,7 @@ * ==================== * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER. * - * Copyright 2007-2008 Sun Microsystems, Inc. All rights reserved. + * Copyright 2008-2009 Sun Microsystems, Inc. All rights reserved. * * The contents of this file are subject to the terms of the Common Development * and Distribution License("CDDL") (the "License"). You may not use this file diff --git a/ActiveDirectoryConnector/TerminalServicesUtils.cs b/ActiveDirectoryConnector/TerminalServicesUtils.cs index 03b70f39..72ca6396 100644 --- a/ActiveDirectoryConnector/TerminalServicesUtils.cs +++ b/ActiveDirectoryConnector/TerminalServicesUtils.cs @@ -2,7 +2,7 @@ * ==================== * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER. * - * Copyright 2007-2008 Sun Microsystems, Inc. All rights reserved. + * Copyright 2008-2009 Sun Microsystems, Inc. All rights reserved. * * The contents of this file are subject to the terms of the Common Development * and Distribution License("CDDL") (the "License"). You may not use this file diff --git a/ActiveDirectoryConnector/UserAccountControl.cs b/ActiveDirectoryConnector/UserAccountControl.cs index e0aed510..476359f1 100644 --- a/ActiveDirectoryConnector/UserAccountControl.cs +++ b/ActiveDirectoryConnector/UserAccountControl.cs @@ -2,7 +2,7 @@ * ==================== * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER. * - * Copyright 2007-2008 Sun Microsystems, Inc. All rights reserved. + * Copyright 2008-2009 Sun Microsystems, Inc. All rights reserved. * * The contents of this file are subject to the terms of the Common Development * and Distribution License("CDDL") (the "License"). You may not use this file diff --git a/ActiveDirectoryConnector/build.xml b/ActiveDirectoryConnector/build.xml index 9b6bed6d..e44f008e 100644 --- a/ActiveDirectoryConnector/build.xml +++ b/ActiveDirectoryConnector/build.xml @@ -2,7 +2,7 @@ ==================== DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER. - Copyright 2007-2008 Sun Microsystems, Inc. All rights reserved. + Copyright 2008-2009 Sun Microsystems, Inc. All rights reserved. The contents of this file are subject to the terms of the Common Development and Distribution License("CDDL") (the "License"). You may not use this file diff --git a/ActiveDirectoryConnectorTests/ActiveDirectoryConnectorTest.cs b/ActiveDirectoryConnectorTests/ActiveDirectoryConnectorTest.cs index ab119c08..391d9b50 100644 --- a/ActiveDirectoryConnectorTests/ActiveDirectoryConnectorTest.cs +++ b/ActiveDirectoryConnectorTests/ActiveDirectoryConnectorTest.cs @@ -2,7 +2,7 @@ * ==================== * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER. * - * Copyright 2007-2008 Sun Microsystems, Inc. All rights reserved. + * Copyright 2008-2009 Sun Microsystems, Inc. All rights reserved. * * The contents of this file are subject to the terms of the Common Development * and Distribution License("CDDL") (the "License"). You may not use this file diff --git a/ActiveDirectoryConnectorTests/ActiveDirectoryConnectorTests.csproj b/ActiveDirectoryConnectorTests/ActiveDirectoryConnectorTests.csproj index 9c096440..3c12f81b 100644 --- a/ActiveDirectoryConnectorTests/ActiveDirectoryConnectorTests.csproj +++ b/ActiveDirectoryConnectorTests/ActiveDirectoryConnectorTests.csproj @@ -2,7 +2,7 @@ ==================== DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER. - Copyright 2007-2008 Sun Microsystems, Inc. All rights reserved. + Copyright 2008-2009 Sun Microsystems, Inc. All rights reserved. The contents of this file are subject to the terms of the Common Development and Distribution License("CDDL") (the "License"). You may not use this file @@ -113,4 +113,4 @@ if EXIST "$(SolutionDir)\ShellScriptExecutorFactory\$(OutDir)\Shell.ScriptExecutorFactory.dll". (copy "$(SolutionDir)\ShellScriptExecutorFactory\$(OutDir)\Shell.ScriptExecutorFactory.dll" "$(TargetDir)") else (echo yuck > .\delete.me.ad) - \ No newline at end of file + diff --git a/BooScriptExecutorFactory/BooScriptExecutorFactory.cs b/BooScriptExecutorFactory/BooScriptExecutorFactory.cs index ff822ba7..72e5664e 100644 --- a/BooScriptExecutorFactory/BooScriptExecutorFactory.cs +++ b/BooScriptExecutorFactory/BooScriptExecutorFactory.cs @@ -2,7 +2,7 @@ * ==================== * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER. * - * Copyright 2007-2008 Sun Microsystems, Inc. All rights reserved. + * Copyright 2008-2009 Sun Microsystems, Inc. All rights reserved. * * The contents of this file are subject to the terms of the Common Development * and Distribution License("CDDL") (the "License"). You may not use this file diff --git a/BooScriptExecutorFactory/BooScriptExecutorFactory.csproj b/BooScriptExecutorFactory/BooScriptExecutorFactory.csproj index b560a68e..81bda59b 100644 --- a/BooScriptExecutorFactory/BooScriptExecutorFactory.csproj +++ b/BooScriptExecutorFactory/BooScriptExecutorFactory.csproj @@ -2,7 +2,7 @@ ==================== DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER. - Copyright 2007-2008 Sun Microsystems, Inc. All rights reserved. + Copyright 2008-2009 Sun Microsystems, Inc. All rights reserved. The contents of this file are subject to the terms of the Common Development and Distribution License("CDDL") (the "License"). You may not use this file diff --git a/Common/Assertions.cs b/Common/Assertions.cs index 9c927a7c..4510a153 100644 --- a/Common/Assertions.cs +++ b/Common/Assertions.cs @@ -2,7 +2,7 @@ * ==================== * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER. * - * Copyright 2007-2008 Sun Microsystems, Inc. All rights reserved. + * Copyright 2008-2009 Sun Microsystems, Inc. All rights reserved. * * The contents of this file are subject to the terms of the Common Development * and Distribution License("CDDL") (the "License"). You may not use this file diff --git a/Common/CollectionUtil.cs b/Common/CollectionUtil.cs index 12dcb3b6..93122f14 100644 --- a/Common/CollectionUtil.cs +++ b/Common/CollectionUtil.cs @@ -2,7 +2,7 @@ * ==================== * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER. * - * Copyright 2007-2008 Sun Microsystems, Inc. All rights reserved. + * Copyright 2008-2009 Sun Microsystems, Inc. All rights reserved. * * The contents of this file are subject to the terms of the Common Development * and Distribution License("CDDL") (the "License"). You may not use this file diff --git a/Common/Common.csproj b/Common/Common.csproj index ec840c45..bc5dee31 100644 --- a/Common/Common.csproj +++ b/Common/Common.csproj @@ -2,7 +2,7 @@ ==================== DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER. - Copyright 2007-2008 Sun Microsystems, Inc. All rights reserved. + Copyright 2008-2009 Sun Microsystems, Inc. All rights reserved. The contents of this file are subject to the terms of the Common Development and Distribution License("CDDL") (the "License"). You may not use this file diff --git a/Common/DateTimeUtil.cs b/Common/DateTimeUtil.cs index 55c086b7..12fca8e8 100644 --- a/Common/DateTimeUtil.cs +++ b/Common/DateTimeUtil.cs @@ -2,7 +2,7 @@ * ==================== * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER. * - * Copyright 2007-2008 Sun Microsystems, Inc. All rights reserved. + * Copyright 2008-2009 Sun Microsystems, Inc. All rights reserved. * * The contents of this file are subject to the terms of the Common Development * and Distribution License("CDDL") (the "License"). You may not use this file diff --git a/Common/FrameworkInternalBridge.cs b/Common/FrameworkInternalBridge.cs index a72c0e71..5610f5d5 100644 --- a/Common/FrameworkInternalBridge.cs +++ b/Common/FrameworkInternalBridge.cs @@ -2,7 +2,7 @@ * ==================== * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER. * - * Copyright 2007-2008 Sun Microsystems, Inc. All rights reserved. + * Copyright 2008-2009 Sun Microsystems, Inc. All rights reserved. * * The contents of this file are subject to the terms of the Common Development * and Distribution License("CDDL") (the "License"). You may not use this file diff --git a/Common/IOUtil.cs b/Common/IOUtil.cs index 41483294..9497fd54 100644 --- a/Common/IOUtil.cs +++ b/Common/IOUtil.cs @@ -2,7 +2,7 @@ * ==================== * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER. * - * Copyright 2007-2008 Sun Microsystems, Inc. All rights reserved. + * Copyright 2008-2009 Sun Microsystems, Inc. All rights reserved. * * The contents of this file are subject to the terms of the Common Development * and Distribution License("CDDL") (the "License"). You may not use this file diff --git a/Common/Locale.cs b/Common/Locale.cs index b1620142..293ac539 100644 --- a/Common/Locale.cs +++ b/Common/Locale.cs @@ -2,7 +2,7 @@ * ==================== * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER. * - * Copyright 2007-2008 Sun Microsystems, Inc. All rights reserved. + * Copyright 2008-2009 Sun Microsystems, Inc. All rights reserved. * * The contents of this file are subject to the terms of the Common Development * and Distribution License("CDDL") (the "License"). You may not use this file diff --git a/Common/Pair.cs b/Common/Pair.cs index cad448f7..7faf48d9 100644 --- a/Common/Pair.cs +++ b/Common/Pair.cs @@ -2,7 +2,7 @@ * ==================== * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER. * - * Copyright 2007-2008 Sun Microsystems, Inc. All rights reserved. + * Copyright 2008-2009 Sun Microsystems, Inc. All rights reserved. * * The contents of this file are subject to the terms of the Common Development * and Distribution License("CDDL") (the "License"). You may not use this file diff --git a/Common/Pooling.cs b/Common/Pooling.cs index 063c8610..93cf9981 100644 --- a/Common/Pooling.cs +++ b/Common/Pooling.cs @@ -2,7 +2,7 @@ * ==================== * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER. * - * Copyright 2007-2008 Sun Microsystems, Inc. All rights reserved. + * Copyright 2008-2009 Sun Microsystems, Inc. All rights reserved. * * The contents of this file are subject to the terms of the Common Development * and Distribution License("CDDL") (the "License"). You may not use this file diff --git a/Common/Proxy.cs b/Common/Proxy.cs index 2534fdd0..da7feca2 100644 --- a/Common/Proxy.cs +++ b/Common/Proxy.cs @@ -2,7 +2,7 @@ * ==================== * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER. * - * Copyright 2007-2008 Sun Microsystems, Inc. All rights reserved. + * Copyright 2008-2009 Sun Microsystems, Inc. All rights reserved. * * The contents of this file are subject to the terms of the Common Development * and Distribution License("CDDL") (the "License"). You may not use this file diff --git a/Common/ReflectionUtil.cs b/Common/ReflectionUtil.cs index 38a455a3..48d515b3 100644 --- a/Common/ReflectionUtil.cs +++ b/Common/ReflectionUtil.cs @@ -2,7 +2,7 @@ * ==================== * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER. * - * Copyright 2007-2008 Sun Microsystems, Inc. All rights reserved. + * Copyright 2008-2009 Sun Microsystems, Inc. All rights reserved. * * The contents of this file are subject to the terms of the Common Development * and Distribution License("CDDL") (the "License"). You may not use this file diff --git a/Common/SafeType.cs b/Common/SafeType.cs index 31c4621e..4370162e 100644 --- a/Common/SafeType.cs +++ b/Common/SafeType.cs @@ -2,7 +2,7 @@ * ==================== * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER. * - * Copyright 2007-2008 Sun Microsystems, Inc. All rights reserved. + * Copyright 2008-2009 Sun Microsystems, Inc. All rights reserved. * * The contents of this file are subject to the terms of the Common Development * and Distribution License("CDDL") (the "License"). You may not use this file diff --git a/Common/Script.cs b/Common/Script.cs index 4d1bbc36..0ae4ac77 100644 --- a/Common/Script.cs +++ b/Common/Script.cs @@ -2,7 +2,7 @@ * ==================== * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER. * - * Copyright 2007-2008 Sun Microsystems, Inc. All rights reserved. + * Copyright 2008-2009 Sun Microsystems, Inc. All rights reserved. * * The contents of this file are subject to the terms of the Common Development * and Distribution License("CDDL") (the "License"). You may not use this file diff --git a/Common/Security.cs b/Common/Security.cs index 4d7473bf..d43e1aa6 100644 --- a/Common/Security.cs +++ b/Common/Security.cs @@ -2,7 +2,7 @@ * ==================== * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER. * - * Copyright 2007-2008 Sun Microsystems, Inc. All rights reserved. + * Copyright 2008-2009 Sun Microsystems, Inc. All rights reserved. * * The contents of this file are subject to the terms of the Common Development * and Distribution License("CDDL") (the "License"). You may not use this file diff --git a/Common/StringUtil.cs b/Common/StringUtil.cs index 2fef0f7b..ae13619a 100644 --- a/Common/StringUtil.cs +++ b/Common/StringUtil.cs @@ -2,7 +2,7 @@ * ==================== * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER. * - * Copyright 2007-2008 Sun Microsystems, Inc. All rights reserved. + * Copyright 2008-2009 Sun Microsystems, Inc. All rights reserved. * * The contents of this file are subject to the terms of the Common Development * and Distribution License("CDDL") (the "License"). You may not use this file diff --git a/Common/TraceUtil.cs b/Common/TraceUtil.cs index d12f5e70..bb093054 100644 --- a/Common/TraceUtil.cs +++ b/Common/TraceUtil.cs @@ -2,7 +2,7 @@ * ==================== * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER. * - * Copyright 2007-2008 Sun Microsystems, Inc. All rights reserved. + * Copyright 2008-2009 Sun Microsystems, Inc. All rights reserved. * * The contents of this file are subject to the terms of the Common Development * and Distribution License("CDDL") (the "License"). You may not use this file diff --git a/Common/XmlUtil.cs b/Common/XmlUtil.cs index 30ea8040..94e31a08 100644 --- a/Common/XmlUtil.cs +++ b/Common/XmlUtil.cs @@ -2,7 +2,7 @@ * ==================== * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER. * - * Copyright 2007-2008 Sun Microsystems, Inc. All rights reserved. + * Copyright 2008-2009 Sun Microsystems, Inc. All rights reserved. * * The contents of this file are subject to the terms of the Common Development * and Distribution License("CDDL") (the "License"). You may not use this file diff --git a/Console/Console.csproj b/Console/Console.csproj index 4132bb5e..91a41b09 100644 --- a/Console/Console.csproj +++ b/Console/Console.csproj @@ -2,7 +2,7 @@ ==================== DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER. - Copyright 2007-2008 Sun Microsystems, Inc. All rights reserved. + Copyright 2008-2009 Sun Microsystems, Inc. All rights reserved. The contents of this file are subject to the terms of the Common Development and Distribution License("CDDL") (the "License"). You may not use this file diff --git a/Console/MainForm.cs b/Console/MainForm.cs index ca92a32a..ac181ad5 100644 --- a/Console/MainForm.cs +++ b/Console/MainForm.cs @@ -2,7 +2,7 @@ * ==================== * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER. * - * Copyright 2007-2008 Sun Microsystems, Inc. All rights reserved. + * Copyright 2008-2009 Sun Microsystems, Inc. All rights reserved. * * The contents of this file are subject to the terms of the Common Development * and Distribution License("CDDL") (the "License"). You may not use this file diff --git a/Console/Program.cs b/Console/Program.cs index 23030249..6bf701ac 100644 --- a/Console/Program.cs +++ b/Console/Program.cs @@ -2,7 +2,7 @@ * ==================== * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER. * - * Copyright 2007-2008 Sun Microsystems, Inc. All rights reserved. + * Copyright 2008-2009 Sun Microsystems, Inc. All rights reserved. * * The contents of this file are subject to the terms of the Common Development * and Distribution License("CDDL") (the "License"). You may not use this file diff --git a/DotNetCommonBuild.Targets b/DotNetCommonBuild.Targets index f68ef01c..fc7d5b25 100644 --- a/DotNetCommonBuild.Targets +++ b/DotNetCommonBuild.Targets @@ -2,7 +2,7 @@ ==================== DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER. - Copyright 2007-2008 Sun Microsystems, Inc. All rights reserved. + Copyright 2008-2009 Sun Microsystems, Inc. All rights reserved. The contents of this file are subject to the terms of the Common Development and Distribution License("CDDL") (the "License"). You may not use this file @@ -26,7 +26,7 @@ 0 $(MSBuildProjectDirectory)\version.txt Sun Microsystems, Inc. - Copyright 2007-2008 Sun Microsystems, Inc. All rights reserved. + Copyright 2008-2009 Sun Microsystems, Inc. All rights reserved. $(MSBuildProjectDirectory)\..\Build diff --git a/ExchangeConnector/Data/CommandInfos.xml b/ExchangeConnector/Data/CommandInfos.xml index cc78cf21..c0cf0cfe 100644 --- a/ExchangeConnector/Data/CommandInfos.xml +++ b/ExchangeConnector/Data/CommandInfos.xml @@ -3,7 +3,7 @@ // ==================== // DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER. // -// Copyright 2007-2008 Sun Microsystems, Inc. All rights reserved. +// Copyright 2008-2009 Sun Microsystems, Inc. All rights reserved. // // The contents of this file are subject to the terms of the Common Development // and Distribution License("CDDL") (the "License"). You may not use this file @@ -314,4 +314,4 @@ - \ No newline at end of file + diff --git a/ExchangeConnector/Data/PersistenceUtility.cs b/ExchangeConnector/Data/PersistenceUtility.cs index f7cc3fe1..7e4e4685 100644 --- a/ExchangeConnector/Data/PersistenceUtility.cs +++ b/ExchangeConnector/Data/PersistenceUtility.cs @@ -2,7 +2,7 @@ // ==================== // DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER. // -// Copyright 2007-2008 Sun Microsystems, Inc. All rights reserved. +// Copyright 2008-2009 Sun Microsystems, Inc. All rights reserved. // // The contents of this file are subject to the terms of the Common Development // and Distribution License("CDDL") (the "License"). You may not use this file @@ -69,4 +69,4 @@ internal static IList ReadCommandInfo() } } } -} \ No newline at end of file +} diff --git a/ExchangeConnector/Data/SerializableCommandInfo.cs b/ExchangeConnector/Data/SerializableCommandInfo.cs index e6d74015..da08b437 100644 --- a/ExchangeConnector/Data/SerializableCommandInfo.cs +++ b/ExchangeConnector/Data/SerializableCommandInfo.cs @@ -2,7 +2,7 @@ // ==================== // DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER. // -// Copyright 2007-2008 Sun Microsystems, Inc. All rights reserved. +// Copyright 2008-2009 Sun Microsystems, Inc. All rights reserved. // // The contents of this file are subject to the terms of the Common Development // and Distribution License("CDDL") (the "License"). You may not use this file @@ -80,4 +80,4 @@ public void AddParameter(string parameter) this.parameters.Add(parameter); } } -} \ No newline at end of file +} diff --git a/ExchangeConnector/ExchangeConfiguration.cs b/ExchangeConnector/ExchangeConfiguration.cs index a963d667..a06c78c0 100644 --- a/ExchangeConnector/ExchangeConfiguration.cs +++ b/ExchangeConnector/ExchangeConfiguration.cs @@ -2,7 +2,7 @@ // ==================== // DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER. // -// Copyright 2007-2008 Sun Microsystems, Inc. All rights reserved. +// Copyright 2008-2009 Sun Microsystems, Inc. All rights reserved. // // The contents of this file are subject to the terms of the Common Development // and Distribution License("CDDL") (the "License"). You may not use this file diff --git a/ExchangeConnector/ExchangeConnector.cs b/ExchangeConnector/ExchangeConnector.cs index bff9fd46..1de76fef 100644 --- a/ExchangeConnector/ExchangeConnector.cs +++ b/ExchangeConnector/ExchangeConnector.cs @@ -2,7 +2,7 @@ // ==================== // DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER. // -// Copyright 2007-2008 Sun Microsystems, Inc. All rights reserved. +// Copyright 2008-2009 Sun Microsystems, Inc. All rights reserved. // // The contents of this file are subject to the terms of the Common Development // and Distribution License("CDDL") (the "License"). You may not use this file diff --git a/ExchangeConnector/ExchangeConnector.csproj b/ExchangeConnector/ExchangeConnector.csproj index 6be03cbf..56501eef 100644 --- a/ExchangeConnector/ExchangeConnector.csproj +++ b/ExchangeConnector/ExchangeConnector.csproj @@ -2,7 +2,7 @@ ==================== DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER. - Copyright 2007-2008 Sun Microsystems, Inc. All rights reserved. + Copyright 2008-2009 Sun Microsystems, Inc. All rights reserved. The contents of this file are subject to the terms of the Common Development and Distribution License("CDDL") (the "License"). You may not use this file @@ -105,4 +105,4 @@ - \ No newline at end of file + diff --git a/ExchangeConnector/ExchangeUtility.cs b/ExchangeConnector/ExchangeUtility.cs index 336ff060..bf093478 100644 --- a/ExchangeConnector/ExchangeUtility.cs +++ b/ExchangeConnector/ExchangeUtility.cs @@ -2,7 +2,7 @@ // ==================== // DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER. // -// Copyright 2007-2008 Sun Microsystems, Inc. All rights reserved. +// Copyright 2008-2009 Sun Microsystems, Inc. All rights reserved. // // The contents of this file are subject to the terms of the Common Development // and Distribution License("CDDL") (the "License"). You may not use this file diff --git a/ExchangeConnector/LegacyExchangeConnector.cs b/ExchangeConnector/LegacyExchangeConnector.cs index 23d834be..c4ab6340 100644 --- a/ExchangeConnector/LegacyExchangeConnector.cs +++ b/ExchangeConnector/LegacyExchangeConnector.cs @@ -2,7 +2,7 @@ // ==================== // DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER. // -// Copyright 2007-2008 Sun Microsystems, Inc. All rights reserved. +// Copyright 2008-2009 Sun Microsystems, Inc. All rights reserved. // // The contents of this file are subject to the terms of the Common Development // and Distribution License("CDDL") (the "License"). You may not use this file diff --git a/ExchangeConnector/ObjectClasses.xml b/ExchangeConnector/ObjectClasses.xml index e03c70e9..55f8db45 100644 --- a/ExchangeConnector/ObjectClasses.xml +++ b/ExchangeConnector/ObjectClasses.xml @@ -3,7 +3,7 @@ ==================== DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER. - Copyright 2007-2008 Sun Microsystems, Inc. All rights reserved. + Copyright 2008-2009 Sun Microsystems, Inc. All rights reserved. The contents of this file are subject to the terms of the Common Development and Distribution License("CDDL") (the "License"). You may not use this file diff --git a/ExchangeConnector/RunSpaceInstance.cs b/ExchangeConnector/RunSpaceInstance.cs index 9bee7671..c64b0c78 100644 --- a/ExchangeConnector/RunSpaceInstance.cs +++ b/ExchangeConnector/RunSpaceInstance.cs @@ -2,7 +2,7 @@ // ==================== // DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER. // -// Copyright 2007-2008 Sun Microsystems, Inc. All rights reserved. +// Copyright 2008-2009 Sun Microsystems, Inc. All rights reserved. // // The contents of this file are subject to the terms of the Common Development // and Distribution License("CDDL") (the "License"). You may not use this file diff --git a/ExchangeConnector/build.xml b/ExchangeConnector/build.xml index 06f6f903..e28e6851 100644 --- a/ExchangeConnector/build.xml +++ b/ExchangeConnector/build.xml @@ -2,7 +2,7 @@ ==================== DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER. - Copyright 2007-2008 Sun Microsystems, Inc. All rights reserved. + Copyright 2008-2009 Sun Microsystems, Inc. All rights reserved. The contents of this file are subject to the terms of the Common Development and Distribution License("CDDL") (the "License"). You may not use this file diff --git a/Framework/Api.cs b/Framework/Api.cs index d8e0cf23..b1b526f6 100644 --- a/Framework/Api.cs +++ b/Framework/Api.cs @@ -2,7 +2,7 @@ * ==================== * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER. * - * Copyright 2007-2008 Sun Microsystems, Inc. All rights reserved. + * Copyright 2008-2009 Sun Microsystems, Inc. All rights reserved. * * The contents of this file are subject to the terms of the Common Development * and Distribution License("CDDL") (the "License"). You may not use this file diff --git a/Framework/ApiOperations.cs b/Framework/ApiOperations.cs index bf1ce08f..83f50a10 100644 --- a/Framework/ApiOperations.cs +++ b/Framework/ApiOperations.cs @@ -2,7 +2,7 @@ * ==================== * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER. * - * Copyright 2007-2008 Sun Microsystems, Inc. All rights reserved. + * Copyright 2008-2009 Sun Microsystems, Inc. All rights reserved. * * The contents of this file are subject to the terms of the Common Development * and Distribution License("CDDL") (the "License"). You may not use this file diff --git a/Framework/Common.cs b/Framework/Common.cs index 2da8fa0d..bb5407d7 100644 --- a/Framework/Common.cs +++ b/Framework/Common.cs @@ -2,7 +2,7 @@ * ==================== * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER. * - * Copyright 2007-2008 Sun Microsystems, Inc. All rights reserved. + * Copyright 2008-2009 Sun Microsystems, Inc. All rights reserved. * * The contents of this file are subject to the terms of the Common Development * and Distribution License("CDDL") (the "License"). You may not use this file diff --git a/Framework/CommonExceptions.cs b/Framework/CommonExceptions.cs index 08043091..4b5420d2 100644 --- a/Framework/CommonExceptions.cs +++ b/Framework/CommonExceptions.cs @@ -2,7 +2,7 @@ * ==================== * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER. * - * Copyright 2007-2008 Sun Microsystems, Inc. All rights reserved. + * Copyright 2008-2009 Sun Microsystems, Inc. All rights reserved. * * The contents of this file are subject to the terms of the Common Development * and Distribution License("CDDL") (the "License"). You may not use this file diff --git a/Framework/CommonObjects.cs b/Framework/CommonObjects.cs index bfd8c908..ba99241a 100644 --- a/Framework/CommonObjects.cs +++ b/Framework/CommonObjects.cs @@ -2,7 +2,7 @@ * ==================== * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER. * - * Copyright 2007-2008 Sun Microsystems, Inc. All rights reserved. + * Copyright 2008-2009 Sun Microsystems, Inc. All rights reserved. * * The contents of this file are subject to the terms of the Common Development * and Distribution License("CDDL") (the "License"). You may not use this file diff --git a/Framework/CommonObjectsFilter.cs b/Framework/CommonObjectsFilter.cs index 2e32a68f..3e53c8bc 100644 --- a/Framework/CommonObjectsFilter.cs +++ b/Framework/CommonObjectsFilter.cs @@ -2,7 +2,7 @@ * ==================== * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER. * - * Copyright 2007-2008 Sun Microsystems, Inc. All rights reserved. + * Copyright 2008-2009 Sun Microsystems, Inc. All rights reserved. * * The contents of this file are subject to the terms of the Common Development * and Distribution License("CDDL") (the "License"). You may not use this file diff --git a/Framework/CommonSerializer.cs b/Framework/CommonSerializer.cs index 261429cb..f13e7d53 100644 --- a/Framework/CommonSerializer.cs +++ b/Framework/CommonSerializer.cs @@ -2,7 +2,7 @@ * ==================== * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER. * - * Copyright 2007-2008 Sun Microsystems, Inc. All rights reserved. + * Copyright 2008-2009 Sun Microsystems, Inc. All rights reserved. * * The contents of this file are subject to the terms of the Common Development * and Distribution License("CDDL") (the "License"). You may not use this file diff --git a/Framework/Framework.csproj b/Framework/Framework.csproj index 1e3e38f2..3a2e05f4 100644 --- a/Framework/Framework.csproj +++ b/Framework/Framework.csproj @@ -2,7 +2,7 @@ ==================== DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER. - Copyright 2007-2008 Sun Microsystems, Inc. All rights reserved. + Copyright 2008-2009 Sun Microsystems, Inc. All rights reserved. The contents of this file are subject to the terms of the Common Development and Distribution License("CDDL") (the "License"). You may not use this file diff --git a/Framework/Spi.cs b/Framework/Spi.cs index 974246d0..ee4ebc39 100644 --- a/Framework/Spi.cs +++ b/Framework/Spi.cs @@ -2,7 +2,7 @@ * ==================== * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER. * - * Copyright 2007-2008 Sun Microsystems, Inc. All rights reserved. + * Copyright 2008-2009 Sun Microsystems, Inc. All rights reserved. * * The contents of this file are subject to the terms of the Common Development * and Distribution License("CDDL") (the "License"). You may not use this file diff --git a/Framework/SpiOperations.cs b/Framework/SpiOperations.cs index 236f1124..f8520012 100644 --- a/Framework/SpiOperations.cs +++ b/Framework/SpiOperations.cs @@ -2,7 +2,7 @@ * ==================== * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER. * - * Copyright 2007-2008 Sun Microsystems, Inc. All rights reserved. + * Copyright 2008-2009 Sun Microsystems, Inc. All rights reserved. * * The contents of this file are subject to the terms of the Common Development * and Distribution License("CDDL") (the "License"). You may not use this file diff --git a/FrameworkInternal/Api.cs b/FrameworkInternal/Api.cs index 09f7f320..fbda9413 100644 --- a/FrameworkInternal/Api.cs +++ b/FrameworkInternal/Api.cs @@ -2,7 +2,7 @@ * ==================== * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER. * - * Copyright 2007-2008 Sun Microsystems, Inc. All rights reserved. + * Copyright 2008-2009 Sun Microsystems, Inc. All rights reserved. * * The contents of this file are subject to the terms of the Common Development * and Distribution License("CDDL") (the "License"). You may not use this file diff --git a/FrameworkInternal/ApiLocal.cs b/FrameworkInternal/ApiLocal.cs index 2485e132..b01547c9 100644 --- a/FrameworkInternal/ApiLocal.cs +++ b/FrameworkInternal/ApiLocal.cs @@ -2,7 +2,7 @@ * ==================== * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER. * - * Copyright 2007-2008 Sun Microsystems, Inc. All rights reserved. + * Copyright 2008-2009 Sun Microsystems, Inc. All rights reserved. * * The contents of this file are subject to the terms of the Common Development * and Distribution License("CDDL") (the "License"). You may not use this file diff --git a/FrameworkInternal/ApiLocalOperations.cs b/FrameworkInternal/ApiLocalOperations.cs index 6ed4c594..42328e76 100644 --- a/FrameworkInternal/ApiLocalOperations.cs +++ b/FrameworkInternal/ApiLocalOperations.cs @@ -2,7 +2,7 @@ * ==================== * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER. * - * Copyright 2007-2008 Sun Microsystems, Inc. All rights reserved. + * Copyright 2008-2009 Sun Microsystems, Inc. All rights reserved. * * The contents of this file are subject to the terms of the Common Development * and Distribution License("CDDL") (the "License"). You may not use this file diff --git a/FrameworkInternal/ApiRemote.cs b/FrameworkInternal/ApiRemote.cs index b9ba8dbe..aacb21f9 100644 --- a/FrameworkInternal/ApiRemote.cs +++ b/FrameworkInternal/ApiRemote.cs @@ -2,7 +2,7 @@ * ==================== * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER. * - * Copyright 2007-2008 Sun Microsystems, Inc. All rights reserved. + * Copyright 2008-2009 Sun Microsystems, Inc. All rights reserved. * * The contents of this file are subject to the terms of the Common Development * and Distribution License("CDDL") (the "License"). You may not use this file diff --git a/FrameworkInternal/ApiRemoteMessages.cs b/FrameworkInternal/ApiRemoteMessages.cs index 62c58ab0..038e0a78 100644 --- a/FrameworkInternal/ApiRemoteMessages.cs +++ b/FrameworkInternal/ApiRemoteMessages.cs @@ -2,7 +2,7 @@ * ==================== * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER. * - * Copyright 2007-2008 Sun Microsystems, Inc. All rights reserved. + * Copyright 2008-2009 Sun Microsystems, Inc. All rights reserved. * * The contents of this file are subject to the terms of the Common Development * and Distribution License("CDDL") (the "License"). You may not use this file diff --git a/FrameworkInternal/FrameworkInternal.csproj b/FrameworkInternal/FrameworkInternal.csproj index a682bda3..4ae33963 100644 --- a/FrameworkInternal/FrameworkInternal.csproj +++ b/FrameworkInternal/FrameworkInternal.csproj @@ -2,7 +2,7 @@ ==================== DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER. - Copyright 2007-2008 Sun Microsystems, Inc. All rights reserved. + Copyright 2008-2009 Sun Microsystems, Inc. All rights reserved. The contents of this file are subject to the terms of the Common Development and Distribution License("CDDL") (the "License"). You may not use this file @@ -105,4 +105,4 @@ TestCommon - \ No newline at end of file + diff --git a/FrameworkInternal/Security.cs b/FrameworkInternal/Security.cs index 9520c701..772d3f3b 100644 --- a/FrameworkInternal/Security.cs +++ b/FrameworkInternal/Security.cs @@ -2,7 +2,7 @@ * ==================== * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER. * - * Copyright 2007-2008 Sun Microsystems, Inc. All rights reserved. + * Copyright 2008-2009 Sun Microsystems, Inc. All rights reserved. * * The contents of this file are subject to the terms of the Common Development * and Distribution License("CDDL") (the "License"). You may not use this file diff --git a/FrameworkInternal/Serializer.cs b/FrameworkInternal/Serializer.cs index a3ef47d6..0357b5a7 100644 --- a/FrameworkInternal/Serializer.cs +++ b/FrameworkInternal/Serializer.cs @@ -2,7 +2,7 @@ * ==================== * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER. * - * Copyright 2007-2008 Sun Microsystems, Inc. All rights reserved. + * Copyright 2008-2009 Sun Microsystems, Inc. All rights reserved. * * The contents of this file are subject to the terms of the Common Development * and Distribution License("CDDL") (the "License"). You may not use this file diff --git a/FrameworkInternal/SerializerBinary.cs b/FrameworkInternal/SerializerBinary.cs index c98692ac..c685a403 100644 --- a/FrameworkInternal/SerializerBinary.cs +++ b/FrameworkInternal/SerializerBinary.cs @@ -2,7 +2,7 @@ * ==================== * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER. * - * Copyright 2007-2008 Sun Microsystems, Inc. All rights reserved. + * Copyright 2008-2009 Sun Microsystems, Inc. All rights reserved. * * The contents of this file are subject to the terms of the Common Development * and Distribution License("CDDL") (the "License"). You may not use this file diff --git a/FrameworkInternal/SerializerXml.cs b/FrameworkInternal/SerializerXml.cs index 44556848..b8b851a9 100644 --- a/FrameworkInternal/SerializerXml.cs +++ b/FrameworkInternal/SerializerXml.cs @@ -2,7 +2,7 @@ * ==================== * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER. * - * Copyright 2007-2008 Sun Microsystems, Inc. All rights reserved. + * Copyright 2008-2009 Sun Microsystems, Inc. All rights reserved. * * The contents of this file are subject to the terms of the Common Development * and Distribution License("CDDL") (the "License"). You may not use this file diff --git a/FrameworkInternal/Server.cs b/FrameworkInternal/Server.cs index 25ed9432..4a9125d0 100644 --- a/FrameworkInternal/Server.cs +++ b/FrameworkInternal/Server.cs @@ -2,7 +2,7 @@ * ==================== * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER. * - * Copyright 2007-2008 Sun Microsystems, Inc. All rights reserved. + * Copyright 2008-2009 Sun Microsystems, Inc. All rights reserved. * * The contents of this file are subject to the terms of the Common Development * and Distribution License("CDDL") (the "License"). You may not use this file diff --git a/FrameworkInternal/Test.cs b/FrameworkInternal/Test.cs index 31eba7ba..265f9755 100644 --- a/FrameworkInternal/Test.cs +++ b/FrameworkInternal/Test.cs @@ -2,7 +2,7 @@ * ==================== * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER. * - * Copyright 2007-2008 Sun Microsystems, Inc. All rights reserved. + * Copyright 2008-2009 Sun Microsystems, Inc. All rights reserved. * * The contents of this file are subject to the terms of the Common Development * and Distribution License("CDDL") (the "License"). You may not use this file diff --git a/FrameworkTests/CollectionUtilTests.cs b/FrameworkTests/CollectionUtilTests.cs index 289a91ca..7ec17517 100644 --- a/FrameworkTests/CollectionUtilTests.cs +++ b/FrameworkTests/CollectionUtilTests.cs @@ -2,7 +2,7 @@ * ==================== * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER. * - * Copyright 2007-2008 Sun Microsystems, Inc. All rights reserved. + * Copyright 2008-2009 Sun Microsystems, Inc. All rights reserved. * * The contents of this file are subject to the terms of the Common Development * and Distribution License("CDDL") (the "License"). You may not use this file diff --git a/FrameworkTests/ConnectorAttributeUtilTests.cs b/FrameworkTests/ConnectorAttributeUtilTests.cs index 8b9d6c51..882ce4ee 100644 --- a/FrameworkTests/ConnectorAttributeUtilTests.cs +++ b/FrameworkTests/ConnectorAttributeUtilTests.cs @@ -2,7 +2,7 @@ * ==================== * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER. * - * Copyright 2007-2008 Sun Microsystems, Inc. All rights reserved. + * Copyright 2008-2009 Sun Microsystems, Inc. All rights reserved. * * The contents of this file are subject to the terms of the Common Development * and Distribution License("CDDL") (the "License"). You may not use this file diff --git a/FrameworkTests/ConnectorInfoManagerTests.cs b/FrameworkTests/ConnectorInfoManagerTests.cs index 16b8f43f..97d0fd54 100644 --- a/FrameworkTests/ConnectorInfoManagerTests.cs +++ b/FrameworkTests/ConnectorInfoManagerTests.cs @@ -2,7 +2,7 @@ * ==================== * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER. * - * Copyright 2007-2008 Sun Microsystems, Inc. All rights reserved. + * Copyright 2008-2009 Sun Microsystems, Inc. All rights reserved. * * The contents of this file are subject to the terms of the Common Development * and Distribution License("CDDL") (the "License"). You may not use this file diff --git a/FrameworkTests/FilterTranslatorTests.cs b/FrameworkTests/FilterTranslatorTests.cs index f545c6b2..1286e763 100644 --- a/FrameworkTests/FilterTranslatorTests.cs +++ b/FrameworkTests/FilterTranslatorTests.cs @@ -2,7 +2,7 @@ * ==================== * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER. * - * Copyright 2007-2008 Sun Microsystems, Inc. All rights reserved. + * Copyright 2008-2009 Sun Microsystems, Inc. All rights reserved. * * The contents of this file are subject to the terms of the Common Development * and Distribution License("CDDL") (the "License"). You may not use this file diff --git a/FrameworkTests/FrameworkTests.csproj b/FrameworkTests/FrameworkTests.csproj index a9506e40..125b3474 100644 --- a/FrameworkTests/FrameworkTests.csproj +++ b/FrameworkTests/FrameworkTests.csproj @@ -2,7 +2,7 @@ ==================== DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER. - Copyright 2007-2008 Sun Microsystems, Inc. All rights reserved. + Copyright 2008-2009 Sun Microsystems, Inc. All rights reserved. The contents of this file are subject to the terms of the Common Development and Distribution License("CDDL") (the "License"). You may not use this file @@ -156,4 +156,4 @@ - \ No newline at end of file + diff --git a/FrameworkTests/GuardedStringTests.cs b/FrameworkTests/GuardedStringTests.cs index 789cfc60..8e15b28e 100644 --- a/FrameworkTests/GuardedStringTests.cs +++ b/FrameworkTests/GuardedStringTests.cs @@ -2,7 +2,7 @@ * ==================== * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER. * - * Copyright 2007-2008 Sun Microsystems, Inc. All rights reserved. + * Copyright 2008-2009 Sun Microsystems, Inc. All rights reserved. * * The contents of this file are subject to the terms of the Common Development * and Distribution License("CDDL") (the "License"). You may not use this file diff --git a/FrameworkTests/LocaleTests.cs b/FrameworkTests/LocaleTests.cs index 0176a1af..1119afec 100644 --- a/FrameworkTests/LocaleTests.cs +++ b/FrameworkTests/LocaleTests.cs @@ -2,7 +2,7 @@ * ==================== * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER. * - * Copyright 2007-2008 Sun Microsystems, Inc. All rights reserved. + * Copyright 2008-2009 Sun Microsystems, Inc. All rights reserved. * * The contents of this file are subject to the terms of the Common Development * and Distribution License("CDDL") (the "License"). You may not use this file diff --git a/FrameworkTests/ObjectClassUtilTests.cs b/FrameworkTests/ObjectClassUtilTests.cs index 27e771d3..b74f2eda 100755 --- a/FrameworkTests/ObjectClassUtilTests.cs +++ b/FrameworkTests/ObjectClassUtilTests.cs @@ -2,7 +2,7 @@ * ==================== * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER. * - * Copyright 2007-2008 Sun Microsystems, Inc. All rights reserved. + * Copyright 2008-2009 Sun Microsystems, Inc. All rights reserved. * * The contents of this file are subject to the terms of the Common Development * and Distribution License("CDDL") (the "License"). You may not use this file diff --git a/FrameworkTests/ObjectNormalizerFacadeTests.cs b/FrameworkTests/ObjectNormalizerFacadeTests.cs index 475f770e..6fdfc6f8 100644 --- a/FrameworkTests/ObjectNormalizerFacadeTests.cs +++ b/FrameworkTests/ObjectNormalizerFacadeTests.cs @@ -2,7 +2,7 @@ * ==================== * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER. * - * Copyright 2007-2008 Sun Microsystems, Inc. All rights reserved. + * Copyright 2008-2009 Sun Microsystems, Inc. All rights reserved. * * The contents of this file are subject to the terms of the Common Development * and Distribution License("CDDL") (the "License"). You may not use this file diff --git a/FrameworkTests/ObjectPoolTests.cs b/FrameworkTests/ObjectPoolTests.cs index c34afc48..90c1e7ed 100644 --- a/FrameworkTests/ObjectPoolTests.cs +++ b/FrameworkTests/ObjectPoolTests.cs @@ -2,7 +2,7 @@ * ==================== * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER. * - * Copyright 2007-2008 Sun Microsystems, Inc. All rights reserved. + * Copyright 2008-2009 Sun Microsystems, Inc. All rights reserved. * * The contents of this file are subject to the terms of the Common Development * and Distribution License("CDDL") (the "License"). You may not use this file diff --git a/FrameworkTests/ObjectSerializationTests.cs b/FrameworkTests/ObjectSerializationTests.cs index 5e35fc4a..2e0b8ea3 100644 --- a/FrameworkTests/ObjectSerializationTests.cs +++ b/FrameworkTests/ObjectSerializationTests.cs @@ -2,7 +2,7 @@ * ==================== * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER. * - * Copyright 2007-2008 Sun Microsystems, Inc. All rights reserved. + * Copyright 2008-2009 Sun Microsystems, Inc. All rights reserved. * * The contents of this file are subject to the terms of the Common Development * and Distribution License("CDDL") (the "License"). You may not use this file diff --git a/FrameworkTests/ProxyTests.cs b/FrameworkTests/ProxyTests.cs index 863ba446..8431c563 100644 --- a/FrameworkTests/ProxyTests.cs +++ b/FrameworkTests/ProxyTests.cs @@ -2,7 +2,7 @@ * ==================== * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER. * - * Copyright 2007-2008 Sun Microsystems, Inc. All rights reserved. + * Copyright 2008-2009 Sun Microsystems, Inc. All rights reserved. * * The contents of this file are subject to the terms of the Common Development * and Distribution License("CDDL") (the "License"). You may not use this file diff --git a/FrameworkTests/SafeTypeTest.cs b/FrameworkTests/SafeTypeTest.cs index 49567e30..3eafcae7 100644 --- a/FrameworkTests/SafeTypeTest.cs +++ b/FrameworkTests/SafeTypeTest.cs @@ -2,7 +2,7 @@ * ==================== * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER. * - * Copyright 2007-2008 Sun Microsystems, Inc. All rights reserved. + * Copyright 2008-2009 Sun Microsystems, Inc. All rights reserved. * * The contents of this file are subject to the terms of the Common Development * and Distribution License("CDDL") (the "License"). You may not use this file diff --git a/FrameworkTests/ScriptTests.cs b/FrameworkTests/ScriptTests.cs index a22d85b9..62baf918 100644 --- a/FrameworkTests/ScriptTests.cs +++ b/FrameworkTests/ScriptTests.cs @@ -2,7 +2,7 @@ * ==================== * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER. * - * Copyright 2007-2008 Sun Microsystems, Inc. All rights reserved. + * Copyright 2008-2009 Sun Microsystems, Inc. All rights reserved. * * The contents of this file are subject to the terms of the Common Development * and Distribution License("CDDL") (the "License"). You may not use this file diff --git a/FrameworkTests/TestHelperTests.cs b/FrameworkTests/TestHelperTests.cs index 61600d35..00025926 100644 --- a/FrameworkTests/TestHelperTests.cs +++ b/FrameworkTests/TestHelperTests.cs @@ -2,7 +2,7 @@ * ==================== * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER. * - * Copyright 2007-2008 Sun Microsystems, Inc. All rights reserved. + * Copyright 2008-2009 Sun Microsystems, Inc. All rights reserved. * * The contents of this file are subject to the terms of the Common Development * and Distribution License("CDDL") (the "License"). You may not use this file diff --git a/FrameworkTests/TestUtil.cs b/FrameworkTests/TestUtil.cs index 309f8b91..0d413da2 100644 --- a/FrameworkTests/TestUtil.cs +++ b/FrameworkTests/TestUtil.cs @@ -2,7 +2,7 @@ * ==================== * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER. * - * Copyright 2007-2008 Sun Microsystems, Inc. All rights reserved. + * Copyright 2008-2009 Sun Microsystems, Inc. All rights reserved. * * The contents of this file are subject to the terms of the Common Development * and Distribution License("CDDL") (the "License"). You may not use this file diff --git a/FrameworkTests/UpdateImplTests.cs b/FrameworkTests/UpdateImplTests.cs index f71f7c52..79974783 100644 --- a/FrameworkTests/UpdateImplTests.cs +++ b/FrameworkTests/UpdateImplTests.cs @@ -2,7 +2,7 @@ * ==================== * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER. * - * Copyright 2007-2008 Sun Microsystems, Inc. All rights reserved. + * Copyright 2008-2009 Sun Microsystems, Inc. All rights reserved. * * The contents of this file are subject to the terms of the Common Development * and Distribution License("CDDL") (the "License"). You may not use this file diff --git a/Service/Program.cs b/Service/Program.cs index aff6237a..6b465a36 100644 --- a/Service/Program.cs +++ b/Service/Program.cs @@ -2,7 +2,7 @@ * ==================== * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER. * - * Copyright 2007-2008 Sun Microsystems, Inc. All rights reserved. + * Copyright 2008-2009 Sun Microsystems, Inc. All rights reserved. * * The contents of this file are subject to the terms of the Common Development * and Distribution License("CDDL") (the "License"). You may not use this file diff --git a/Service/ProjectInstaller.cs b/Service/ProjectInstaller.cs index c119abc6..103917f2 100644 --- a/Service/ProjectInstaller.cs +++ b/Service/ProjectInstaller.cs @@ -2,7 +2,7 @@ * ==================== * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER. * - * Copyright 2007-2008 Sun Microsystems, Inc. All rights reserved. + * Copyright 2008-2009 Sun Microsystems, Inc. All rights reserved. * * The contents of this file are subject to the terms of the Common Development * and Distribution License("CDDL") (the "License"). You may not use this file diff --git a/Service/Service.cs b/Service/Service.cs index 2855a94e..54a43dc0 100644 --- a/Service/Service.cs +++ b/Service/Service.cs @@ -2,7 +2,7 @@ * ==================== * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER. * - * Copyright 2007-2008 Sun Microsystems, Inc. All rights reserved. + * Copyright 2008-2009 Sun Microsystems, Inc. All rights reserved. * * The contents of this file are subject to the terms of the Common Development * and Distribution License("CDDL") (the "License"). You may not use this file diff --git a/Service/Service.csproj b/Service/Service.csproj index d8271ae6..6e1f939c 100644 --- a/Service/Service.csproj +++ b/Service/Service.csproj @@ -2,7 +2,7 @@ ==================== DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER. - Copyright 2007-2008 Sun Microsystems, Inc. All rights reserved. + Copyright 2008-2009 Sun Microsystems, Inc. All rights reserved. The contents of this file are subject to the terms of the Common Development and Distribution License("CDDL") (the "License"). You may not use this file @@ -111,4 +111,4 @@ Framework - \ No newline at end of file + diff --git a/ServiceInstall/ExtBuild.proj b/ServiceInstall/ExtBuild.proj index 376bf745..d8d676bb 100644 --- a/ServiceInstall/ExtBuild.proj +++ b/ServiceInstall/ExtBuild.proj @@ -2,7 +2,7 @@ ==================== DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER. - Copyright 2007-2008 Sun Microsystems, Inc. All rights reserved. + Copyright 2008-2009 Sun Microsystems, Inc. All rights reserved. The contents of this file are subject to the terms of the Common Development and Distribution License("CDDL") (the "License"). You may not use this file diff --git a/ServiceInstall/ServiceInstall.wixproj b/ServiceInstall/ServiceInstall.wixproj index 217eb36d..45e20954 100644 --- a/ServiceInstall/ServiceInstall.wixproj +++ b/ServiceInstall/ServiceInstall.wixproj @@ -2,7 +2,7 @@ ==================== DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER. - Copyright 2007-2008 Sun Microsystems, Inc. All rights reserved. + Copyright 2008-2009 Sun Microsystems, Inc. All rights reserved. The contents of this file are subject to the terms of the Common Development and Distribution License("CDDL") (the "License"). You may not use this file diff --git a/ShellScriptExecutorFactory/ShellScriptExecutorFactory.cs b/ShellScriptExecutorFactory/ShellScriptExecutorFactory.cs index 60be5ff6..082bcea5 100644 --- a/ShellScriptExecutorFactory/ShellScriptExecutorFactory.cs +++ b/ShellScriptExecutorFactory/ShellScriptExecutorFactory.cs @@ -2,7 +2,7 @@ * ==================== * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER. * - * Copyright 2007-2008 Sun Microsystems, Inc. All rights reserved. + * Copyright 2008-2009 Sun Microsystems, Inc. All rights reserved. * * The contents of this file are subject to the terms of the Common Development * and Distribution License("CDDL") (the "License"). You may not use this file diff --git a/ShellScriptExecutorFactory/ShellScriptExecutorFactory.csproj b/ShellScriptExecutorFactory/ShellScriptExecutorFactory.csproj index 53200b04..b914956d 100644 --- a/ShellScriptExecutorFactory/ShellScriptExecutorFactory.csproj +++ b/ShellScriptExecutorFactory/ShellScriptExecutorFactory.csproj @@ -2,7 +2,7 @@ ==================== DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER. - Copyright 2007-2008 Sun Microsystems, Inc. All rights reserved. + Copyright 2008-2009 Sun Microsystems, Inc. All rights reserved. The contents of this file are subject to the terms of the Common Development and Distribution License("CDDL") (the "License"). You may not use this file diff --git a/TestBundleV1/AssemblyInfo.cs b/TestBundleV1/AssemblyInfo.cs index f1b27d89..680a4514 100644 --- a/TestBundleV1/AssemblyInfo.cs +++ b/TestBundleV1/AssemblyInfo.cs @@ -2,7 +2,7 @@ * ==================== * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER. * - * Copyright 2007-2008 Sun Microsystems, Inc. All rights reserved. + * Copyright 2008-2009 Sun Microsystems, Inc. All rights reserved. * * The contents of this file are subject to the terms of the Common Development * and Distribution License("CDDL") (the "License"). You may not use this file diff --git a/TestBundleV1/TestBundleV1.csproj b/TestBundleV1/TestBundleV1.csproj index 0c67a441..271ff4ed 100644 --- a/TestBundleV1/TestBundleV1.csproj +++ b/TestBundleV1/TestBundleV1.csproj @@ -2,7 +2,7 @@ ==================== DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER. - Copyright 2007-2008 Sun Microsystems, Inc. All rights reserved. + Copyright 2008-2009 Sun Microsystems, Inc. All rights reserved. The contents of this file are subject to the terms of the Common Development and Distribution License("CDDL") (the "License"). You may not use this file diff --git a/TestBundleV1/TestConnector.cs b/TestBundleV1/TestConnector.cs index abc8f446..da928cf2 100644 --- a/TestBundleV1/TestConnector.cs +++ b/TestBundleV1/TestConnector.cs @@ -2,7 +2,7 @@ * ==================== * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER. * - * Copyright 2007-2008 Sun Microsystems, Inc. All rights reserved. + * Copyright 2008-2009 Sun Microsystems, Inc. All rights reserved. * * The contents of this file are subject to the terms of the Common Development * and Distribution License("CDDL") (the "License"). You may not use this file diff --git a/TestBundleV2/AssemblyInfo.cs b/TestBundleV2/AssemblyInfo.cs index 9c8de8e4..f2c39b46 100644 --- a/TestBundleV2/AssemblyInfo.cs +++ b/TestBundleV2/AssemblyInfo.cs @@ -2,7 +2,7 @@ * ==================== * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER. * - * Copyright 2007-2008 Sun Microsystems, Inc. All rights reserved. + * Copyright 2008-2009 Sun Microsystems, Inc. All rights reserved. * * The contents of this file are subject to the terms of the Common Development * and Distribution License("CDDL") (the "License"). You may not use this file diff --git a/TestBundleV2/TestBundleV2.csproj b/TestBundleV2/TestBundleV2.csproj index 2b6e3cfe..b2b913ef 100644 --- a/TestBundleV2/TestBundleV2.csproj +++ b/TestBundleV2/TestBundleV2.csproj @@ -2,7 +2,7 @@ ==================== DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER. - Copyright 2007-2008 Sun Microsystems, Inc. All rights reserved. + Copyright 2008-2009 Sun Microsystems, Inc. All rights reserved. The contents of this file are subject to the terms of the Common Development and Distribution License("CDDL") (the "License"). You may not use this file diff --git a/TestBundleV2/TestConnector.cs b/TestBundleV2/TestConnector.cs index cce29c4a..9ed69370 100644 --- a/TestBundleV2/TestConnector.cs +++ b/TestBundleV2/TestConnector.cs @@ -2,7 +2,7 @@ * ==================== * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER. * - * Copyright 2007-2008 Sun Microsystems, Inc. All rights reserved. + * Copyright 2008-2009 Sun Microsystems, Inc. All rights reserved. * * The contents of this file are subject to the terms of the Common Development * and Distribution License("CDDL") (the "License"). You may not use this file diff --git a/TestCommon/FrameworkInternalBridge.cs b/TestCommon/FrameworkInternalBridge.cs index ee942345..cb661694 100644 --- a/TestCommon/FrameworkInternalBridge.cs +++ b/TestCommon/FrameworkInternalBridge.cs @@ -2,7 +2,7 @@ * ==================== * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER. * - * Copyright 2007-2008 Sun Microsystems, Inc. All rights reserved. + * Copyright 2008-2009 Sun Microsystems, Inc. All rights reserved. * * The contents of this file are subject to the terms of the Common Development * and Distribution License("CDDL") (the "License"). You may not use this file diff --git a/TestCommon/Test.cs b/TestCommon/Test.cs index 353183f4..4a9b3088 100644 --- a/TestCommon/Test.cs +++ b/TestCommon/Test.cs @@ -2,7 +2,7 @@ * ==================== * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER. * - * Copyright 2007-2008 Sun Microsystems, Inc. All rights reserved. + * Copyright 2008-2009 Sun Microsystems, Inc. All rights reserved. * * The contents of this file are subject to the terms of the Common Development * and Distribution License("CDDL") (the "License"). You may not use this file diff --git a/TestCommon/TestHelpersSpi.cs b/TestCommon/TestHelpersSpi.cs index bab10bf7..4b000af1 100644 --- a/TestCommon/TestHelpersSpi.cs +++ b/TestCommon/TestHelpersSpi.cs @@ -2,7 +2,7 @@ * ==================== * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER. * - * Copyright 2007-2008 Sun Microsystems, Inc. All rights reserved. + * Copyright 2008-2009 Sun Microsystems, Inc. All rights reserved. * * The contents of this file are subject to the terms of the Common Development * and Distribution License("CDDL") (the "License"). You may not use this file @@ -46,4 +46,4 @@ void Search(SearchOp search, ConnectorMessages CreateDummyMessages(); } -} \ No newline at end of file +} diff --git a/build.xml b/build.xml index fe46e1c6..00dbfaeb 100644 --- a/build.xml +++ b/build.xml @@ -2,7 +2,7 @@ ==================== DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER. - Copyright 2007-2008 Sun Microsystems, Inc. All rights reserved. + Copyright 2008-2009 Sun Microsystems, Inc. All rights reserved. The contents of this file are subject to the terms of the Common Development and Distribution License("CDDL") (the "License"). You may not use this file From 5b2028b55c7160df959ae293847b7fbdae051d82 Mon Sep 17 00:00:00 2001 From: tknappek Date: Tue, 7 Apr 2009 18:41:25 +0000 Subject: [PATCH 215/342] ExchangeConnector: Renamed ExchangeConnector -> PSExchangeConnector --- ExchangeConnector/ExchangeConnector.csproj | 4 +- ExchangeConnector/ExchangeUtility.cs | 2 +- ExchangeConnector/LegacyExchangeConnector.cs | 46 +++++++++---------- ...ngeConnector.cs => PSExchangeConnector.cs} | 4 +- 4 files changed, 28 insertions(+), 28 deletions(-) rename ExchangeConnector/{ExchangeConnector.cs => PSExchangeConnector.cs} (96%) diff --git a/ExchangeConnector/ExchangeConnector.csproj b/ExchangeConnector/ExchangeConnector.csproj index 56501eef..107a58da 100644 --- a/ExchangeConnector/ExchangeConnector.csproj +++ b/ExchangeConnector/ExchangeConnector.csproj @@ -73,7 +73,7 @@ - + @@ -105,4 +105,4 @@ - + \ No newline at end of file diff --git a/ExchangeConnector/ExchangeUtility.cs b/ExchangeConnector/ExchangeUtility.cs index bf093478..6624231f 100644 --- a/ExchangeConnector/ExchangeUtility.cs +++ b/ExchangeConnector/ExchangeUtility.cs @@ -178,7 +178,7 @@ internal static IDictionary GetOCInfo() /// Attribute values /// Ready to execute Command /// if some of the param is null - internal static Command GetCommand(ExchangeConnector.CommandInfo cmdInfo, ICollection attributes) + internal static Command GetCommand(PSExchangeConnector.CommandInfo cmdInfo, ICollection attributes) { Assertions.NullCheck(cmdInfo, "cmdInfo"); Assertions.NullCheck(attributes, "attributes"); diff --git a/ExchangeConnector/LegacyExchangeConnector.cs b/ExchangeConnector/LegacyExchangeConnector.cs index c4ab6340..ede41b2b 100644 --- a/ExchangeConnector/LegacyExchangeConnector.cs +++ b/ExchangeConnector/LegacyExchangeConnector.cs @@ -175,17 +175,17 @@ public override Uid Create( // get recipient type string rcptType = ExchangeUtility.GetAttValue(AttRecipientType, attributes) as string; - ExchangeConnector.CommandInfo cmdInfoEnable = null; - ExchangeConnector.CommandInfo cmdInfoSet = null; + PSExchangeConnector.CommandInfo cmdInfoEnable = null; + PSExchangeConnector.CommandInfo cmdInfoSet = null; switch (rcptType) { case RcptTypeMailBox: - cmdInfoEnable = ExchangeConnector.CommandInfo.EnableMailbox; - cmdInfoSet = ExchangeConnector.CommandInfo.SetMailbox; + cmdInfoEnable = PSExchangeConnector.CommandInfo.EnableMailbox; + cmdInfoSet = PSExchangeConnector.CommandInfo.SetMailbox; break; case RcptTypeMailUser: - cmdInfoEnable = ExchangeConnector.CommandInfo.EnableMailUser; - cmdInfoSet = ExchangeConnector.CommandInfo.SetMailUser; + cmdInfoEnable = PSExchangeConnector.CommandInfo.EnableMailUser; + cmdInfoSet = PSExchangeConnector.CommandInfo.SetMailUser; break; case RcptTypeUser: break; @@ -268,19 +268,19 @@ public override Uid Update( // update in AD first var filtered = FilterOut( attributes, - ExchangeConnector.CommandInfo.EnableMailbox, - ExchangeConnector.CommandInfo.EnableMailUser, - ExchangeConnector.CommandInfo.SetMailbox, - ExchangeConnector.CommandInfo.SetMailUser); + PSExchangeConnector.CommandInfo.EnableMailbox, + PSExchangeConnector.CommandInfo.EnableMailUser, + PSExchangeConnector.CommandInfo.SetMailbox, + PSExchangeConnector.CommandInfo.SetMailUser); Uid uid = base.Update(type, oclass, filtered, options); ConnectorObject aduser = this.ADSearchByUid(uid, oclass, ExchangeUtility.AddAttributeToOptions(options, AttDatabaseADName)); attributes.Add(aduser.Name); - ExchangeConnector.CommandInfo cmdInfo = ExchangeConnector.CommandInfo.GetUser; + PSExchangeConnector.CommandInfo cmdInfo = PSExchangeConnector.CommandInfo.GetUser; if (aduser.GetAttributeByName(AttDatabaseADName) != null) { // we can be sure it is user mailbox type - cmdInfo = ExchangeConnector.CommandInfo.GetMailbox; + cmdInfo = PSExchangeConnector.CommandInfo.GetMailbox; } PSObject psuser = this.GetUser(cmdInfo, attributes); @@ -297,11 +297,11 @@ public override Uid Update( if (origRcptType != rcptType) { Command cmdEnable = ExchangeUtility.GetCommand( - ExchangeConnector.CommandInfo.EnableMailUser, attributes); + PSExchangeConnector.CommandInfo.EnableMailUser, attributes); this.InvokePipeline(cmdEnable); } - Command cmdSet = ExchangeUtility.GetCommand(ExchangeConnector.CommandInfo.SetMailUser, attributes); + Command cmdSet = ExchangeUtility.GetCommand(PSExchangeConnector.CommandInfo.SetMailUser, attributes); this.InvokePipeline(cmdSet); } else @@ -322,7 +322,7 @@ public override Uid Update( string origDatabase = psuser.Members[AttDatabase] != null ? psuser.Members[AttDatabase].Value.ToString() : null; if (origRcptType != rcptType) { - Command cmdEnable = ExchangeUtility.GetCommand(ExchangeConnector.CommandInfo.EnableMailbox, attributes); + Command cmdEnable = ExchangeUtility.GetCommand(PSExchangeConnector.CommandInfo.EnableMailbox, attributes); this.InvokePipeline(cmdEnable); } else @@ -336,7 +336,7 @@ public override Uid Update( } } - Command cmdSet = ExchangeUtility.GetCommand(ExchangeConnector.CommandInfo.SetMailbox, attributes); + Command cmdSet = ExchangeUtility.GetCommand(PSExchangeConnector.CommandInfo.SetMailbox, attributes); this.InvokePipeline(cmdSet); } else if (rcptType == RcptTypeUser && origRcptType != rcptType) @@ -544,7 +544,7 @@ public override ConnectorAttribute NormalizeAttribute(ObjectClass oclass, Connec /// /// Dispose the resources we use /// - /// true if called from + /// true if called from protected virtual void Dispose(bool disposing) { if (disposing) @@ -591,12 +591,12 @@ protected override ObjectClassInfo GetObjectClassInfo(ObjectClass oc) /// /// Filtered connector attributes /// - private static ICollection FilterOut(ICollection attributes, params ExchangeConnector.CommandInfo[] cmdInfos) + private static ICollection FilterOut(ICollection attributes, params PSExchangeConnector.CommandInfo[] cmdInfos) { IList attsToRemove = new List { AttRecipientType, AttDatabase, AttExternalMail }; if (cmdInfos != null) { - foreach (ExchangeConnector.CommandInfo cmdInfo in cmdInfos) + foreach (PSExchangeConnector.CommandInfo cmdInfo in cmdInfos) { if (cmdInfo != null) { @@ -679,7 +679,7 @@ private ConnectorObject AddExchangeAttributes(ObjectClass oc, ConnectorObject co ConnectorObjectBuilder cobjBuilder = new ConnectorObjectBuilder(); cobjBuilder.AddAttributes(cobject.GetAttributes()); - ExchangeConnector.CommandInfo cmdInfo = ExchangeConnector.CommandInfo.GetUser; + PSExchangeConnector.CommandInfo cmdInfo = PSExchangeConnector.CommandInfo.GetUser; // prepare the connector attribute list to get the command ICollection attributes = new Collection { cobject.Name }; @@ -713,11 +713,11 @@ private ConnectorObject AddExchangeAttributes(ObjectClass oc, ConnectorObject co // get detailed information if (rcptType == RcptTypeMailBox) { - foundObjects = this.InvokePipeline(ExchangeUtility.GetCommand(ExchangeConnector.CommandInfo.GetMailbox, attributes)); + foundObjects = this.InvokePipeline(ExchangeUtility.GetCommand(PSExchangeConnector.CommandInfo.GetMailbox, attributes)); } else if (rcptType == RcptTypeMailUser) { - foundObjects = this.InvokePipeline(ExchangeUtility.GetCommand(ExchangeConnector.CommandInfo.GetMailUser, attributes)); + foundObjects = this.InvokePipeline(ExchangeUtility.GetCommand(PSExchangeConnector.CommandInfo.GetMailUser, attributes)); } if (foundObjects != null && foundObjects.Count == 1) @@ -797,7 +797,7 @@ private ConnectorObject ADSearchByUid(Uid uid, ObjectClass oclass, OperationOpti /// command info to get the user /// attributes containing the Name /// with user info - private PSObject GetUser(ExchangeConnector.CommandInfo cmdInfo, ICollection attributes) + private PSObject GetUser(PSExchangeConnector.CommandInfo cmdInfo, ICollection attributes) { // assert we have user name string name = ExchangeUtility.GetAttValue(Name.NAME, attributes) as string; diff --git a/ExchangeConnector/ExchangeConnector.cs b/ExchangeConnector/PSExchangeConnector.cs similarity index 96% rename from ExchangeConnector/ExchangeConnector.cs rename to ExchangeConnector/PSExchangeConnector.cs index 1de76fef..1edf8621 100644 --- a/ExchangeConnector/ExchangeConnector.cs +++ b/ExchangeConnector/PSExchangeConnector.cs @@ -41,7 +41,7 @@ namespace Org.IdentityConnectors.Exchange /// Full featured connector, for limited functionality connector. /// LegacyExchangeConnector will be extension of this class, once ready. /// - public class ExchangeConnector : ActiveDirectoryConnector + public class PSExchangeConnector : ActiveDirectoryConnector { /// /// MailBox object class name @@ -66,7 +66,7 @@ public class ExchangeConnector : ActiveDirectoryConnector /// /// This Class name - used for logging purposes /// - private static readonly string ClassName = typeof(ExchangeConnector).ToString(); + private static readonly string ClassName = typeof(PSExchangeConnector).ToString(); /// /// Configuration instance variable, method for assignment From a7d3da4c227846e1cc715fc396787fec28c8333e Mon Sep 17 00:00:00 2001 From: tknappek Date: Tue, 7 Apr 2009 18:58:26 +0000 Subject: [PATCH 216/342] ExchangeConnector: Renamed LegacyExchangeConnector -> ExchangeConnector --- .../{LegacyExchangeConnector.cs => ExchangeConnector.cs} | 8 ++++---- ExchangeConnector/ExchangeConnector.csproj | 4 ++-- ExchangeConnector/PSExchangeConnector.cs | 6 +++--- 3 files changed, 9 insertions(+), 9 deletions(-) rename ExchangeConnector/{LegacyExchangeConnector.cs => ExchangeConnector.cs} (96%) diff --git a/ExchangeConnector/LegacyExchangeConnector.cs b/ExchangeConnector/ExchangeConnector.cs similarity index 96% rename from ExchangeConnector/LegacyExchangeConnector.cs rename to ExchangeConnector/ExchangeConnector.cs index ede41b2b..9f5791fb 100644 --- a/ExchangeConnector/LegacyExchangeConnector.cs +++ b/ExchangeConnector/ExchangeConnector.cs @@ -1,4 +1,4 @@ -// +// // ==================== // DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER. // @@ -46,7 +46,7 @@ namespace Org.IdentityConnectors.Exchange typeof(ExchangeConfiguration), MessageCatalogPaths = new[] { "Org.IdentityConnectors.Exchange.Messages", "Org.IdentityConnectors.ActiveDirectory.Messages" })] - public class LegacyExchangeConnector : ActiveDirectoryConnector + public class ExchangeConnector : ActiveDirectoryConnector { #region Fields Definition @@ -101,7 +101,7 @@ public class LegacyExchangeConnector : ActiveDirectoryConnector /// /// ClassName - used for debugging purposes /// - private static readonly string ClassName = typeof(LegacyExchangeConnector).ToString(); + private static readonly string ClassName = typeof(ExchangeConnector).ToString(); /// /// Recipient type attribute info @@ -584,7 +584,7 @@ protected override ObjectClassInfo GetObjectClassInfo(ObjectClass oc) } /// - /// helper method to filter out all attributes used in LegacyExchangeConnector only + /// helper method to filter out all attributes used in ExchangeConnector only /// /// Connector attributes /// CommandInfo whose parameters will be used and filtered out from attributes diff --git a/ExchangeConnector/ExchangeConnector.csproj b/ExchangeConnector/ExchangeConnector.csproj index 107a58da..68cf3399 100644 --- a/ExchangeConnector/ExchangeConnector.csproj +++ b/ExchangeConnector/ExchangeConnector.csproj @@ -1,4 +1,4 @@ - + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + text/microsoft-resx + + + 2.0 + + + System.Resources.ResXResourceReader, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + System.Resources.ResXResourceWriter, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + de-DE: Exchange Connector + + + de-DE: The Argument [{0}] can't be null + + + de-DE: Recipient type [{0}] is not supported + + + de-DE: Provided User name is not unique or not existing + + + de-DE: Update of [{0}] attribute is not supported + + + de-DE: Problem while PowerShell execution {0} + + + de-DE: Update of [{0}] to [{1}] is not supported + + + de-DE: Update type [{0}] not supported + + \ No newline at end of file diff --git a/ExchangeConnector/Messages.es-ES.resx b/ExchangeConnector/Messages.es-ES.resx new file mode 100644 index 00000000..ff4fd84a --- /dev/null +++ b/ExchangeConnector/Messages.es-ES.resx @@ -0,0 +1,144 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + text/microsoft-resx + + + 2.0 + + + System.Resources.ResXResourceReader, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + System.Resources.ResXResourceWriter, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + es-ES: Exchange Connector + + + es-ES: The Argument [{0}] can't be null + + + es-ES: Recipient type [{0}] is not supported + + + es-ES: Provided User name is not unique or not existing + + + es-ES: Update of [{0}] attribute is not supported + + + es-ES: Problem while PowerShell execution {0} + + + es-ES: Update of [{0}] to [{1}] is not supported + + + es-ES: Update type [{0}] not supported + + \ No newline at end of file diff --git a/ExchangeConnector/Messages.fr-FR.resx b/ExchangeConnector/Messages.fr-FR.resx new file mode 100644 index 00000000..67b829df --- /dev/null +++ b/ExchangeConnector/Messages.fr-FR.resx @@ -0,0 +1,144 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + text/microsoft-resx + + + 2.0 + + + System.Resources.ResXResourceReader, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + System.Resources.ResXResourceWriter, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + fr-FR: Exchange Connector + + + fr-FR: The Argument [{0}] can't be null + + + fr-FR: Recipient type [{0}] is not supported + + + fr-FR: Provided User name is not unique or not existing + + + fr-FR: Update of [{0}] attribute is not supported + + + fr-FR: Problem while PowerShell execution {0} + + + fr-FR: Update of [{0}] to [{1}] is not supported + + + fr-FR: Update type [{0}] not supported + + \ No newline at end of file diff --git a/ExchangeConnector/Messages.it-IT.resx b/ExchangeConnector/Messages.it-IT.resx new file mode 100644 index 00000000..6538676e --- /dev/null +++ b/ExchangeConnector/Messages.it-IT.resx @@ -0,0 +1,144 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + text/microsoft-resx + + + 2.0 + + + System.Resources.ResXResourceReader, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + System.Resources.ResXResourceWriter, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + it-IT: Exchange Connector + + + it-IT: The Argument [{0}] can't be null + + + it-IT: Recipient type [{0}] is not supported + + + it-IT: Provided User name is not unique or not existing + + + it-IT: Update of [{0}] attribute is not supported + + + it-IT: Problem while PowerShell execution {0} + + + it-IT: Update of [{0}] to [{1}] is not supported + + + it-IT: Update type [{0}] not supported + + \ No newline at end of file diff --git a/ExchangeConnector/Messages.ja-JP.resx b/ExchangeConnector/Messages.ja-JP.resx new file mode 100644 index 00000000..43b21f60 --- /dev/null +++ b/ExchangeConnector/Messages.ja-JP.resx @@ -0,0 +1,144 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + text/microsoft-resx + + + 2.0 + + + System.Resources.ResXResourceReader, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + System.Resources.ResXResourceWriter, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + ja-JP: Exchange Connector + + + ja-JP: The Argument [{0}] can't be null + + + ja-JP: Recipient type [{0}] is not supported + + + ja-JP: Provided User name is not unique or not existing + + + ja-JP: Update of [{0}] attribute is not supported + + + ja-JP: Problem while PowerShell execution {0} + + + ja-JP: Update of [{0}] to [{1}] is not supported + + + ja-JP: Update type [{0}] not supported + + \ No newline at end of file diff --git a/ExchangeConnector/Messages.ko-KR.resx b/ExchangeConnector/Messages.ko-KR.resx new file mode 100644 index 00000000..ab1ec65b --- /dev/null +++ b/ExchangeConnector/Messages.ko-KR.resx @@ -0,0 +1,144 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + text/microsoft-resx + + + 2.0 + + + System.Resources.ResXResourceReader, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + System.Resources.ResXResourceWriter, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + ko-KR: Exchange Connector + + + ko-KR: The Argument [{0}] can't be null + + + ko-KR: Recipient type [{0}] is not supported + + + ko-KR: Provided User name is not unique or not existing + + + ko-KR: Update of [{0}] attribute is not supported + + + ko-KR: Problem while PowerShell execution {0} + + + ko-KR: Update of [{0}] to [{1}] is not supported + + + ko-KR: Update type [{0}] not supported + + \ No newline at end of file diff --git a/ExchangeConnector/Messages.pt-BR.resx b/ExchangeConnector/Messages.pt-BR.resx new file mode 100644 index 00000000..c27deead --- /dev/null +++ b/ExchangeConnector/Messages.pt-BR.resx @@ -0,0 +1,144 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + text/microsoft-resx + + + 2.0 + + + System.Resources.ResXResourceReader, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + System.Resources.ResXResourceWriter, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + pt-BR: Exchange Connector + + + pt-BR: The Argument [{0}] can't be null + + + pt-BR: Recipient type [{0}] is not supported + + + pt-BR: Provided User name is not unique or not existing + + + pt-BR: Update of [{0}] attribute is not supported + + + pt-BR: Problem while PowerShell execution {0} + + + pt-BR: Update of [{0}] to [{1}] is not supported + + + pt-BR: Update type [{0}] not supported + + \ No newline at end of file diff --git a/ExchangeConnector/Messages.zh-CN.resx b/ExchangeConnector/Messages.zh-CN.resx new file mode 100644 index 00000000..ae5680a7 --- /dev/null +++ b/ExchangeConnector/Messages.zh-CN.resx @@ -0,0 +1,144 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + text/microsoft-resx + + + 2.0 + + + System.Resources.ResXResourceReader, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + System.Resources.ResXResourceWriter, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + zh-CN: Exchange Connector + + + zh-CN: The Argument [{0}] can't be null + + + zh-CN: Recipient type [{0}] is not supported + + + zh-CN: Provided User name is not unique or not existing + + + zh-CN: Update of [{0}] attribute is not supported + + + zh-CN: Problem while PowerShell execution {0} + + + zh-CN: Update of [{0}] to [{1}] is not supported + + + zh-CN: Update type [{0}] not supported + + \ No newline at end of file diff --git a/ExchangeConnector/Messages.zh-TW.resx b/ExchangeConnector/Messages.zh-TW.resx new file mode 100644 index 00000000..856f71af --- /dev/null +++ b/ExchangeConnector/Messages.zh-TW.resx @@ -0,0 +1,144 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + text/microsoft-resx + + + 2.0 + + + System.Resources.ResXResourceReader, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + System.Resources.ResXResourceWriter, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + zh-TW: Exchange Connector + + + zh-TW: The Argument [{0}] can't be null + + + zh-TW: Recipient type [{0}] is not supported + + + zh-TW: Provided User name is not unique or not existing + + + zh-TW: Update of [{0}] attribute is not supported + + + zh-TW: Problem while PowerShell execution {0} + + + zh-TW: Update of [{0}] to [{1}] is not supported + + + zh-TW: Update type [{0}] not supported + + \ No newline at end of file From fba57a7a191b06235cdfd3cd05db2c1d4b52c815 Mon Sep 17 00:00:00 2001 From: tknappek Date: Wed, 8 Apr 2009 13:29:12 +0000 Subject: [PATCH 218/342] object class check added --- ExchangeConnector/ExchangeConnector.cs | 33 ++++++++++++++++++++++++++ 1 file changed, 33 insertions(+) diff --git a/ExchangeConnector/ExchangeConnector.cs b/ExchangeConnector/ExchangeConnector.cs index 9f5791fb..bf5f2b9e 100644 --- a/ExchangeConnector/ExchangeConnector.cs +++ b/ExchangeConnector/ExchangeConnector.cs @@ -169,6 +169,15 @@ public class ExchangeConnector : ActiveDirectoryConnector public override Uid Create( ObjectClass oclass, ICollection attributes, OperationOptions options) { + ExchangeUtility.NullCheck(oclass, "oclass", this.configuration); + ExchangeUtility.NullCheck(attributes, "attributes", this.configuration); + + // we handle accounts only + if (!oclass.Is(ObjectClass.ACCOUNT_NAME)) + { + return base.Create(oclass, attributes, options); + } + const string METHOD = "Create"; Debug.WriteLine(METHOD + ":entry", ClassName); @@ -261,6 +270,12 @@ public override Uid Update( ExchangeUtility.NullCheck(oclass, "oclass", this.configuration); ExchangeUtility.NullCheck(attributes, "attributes", this.configuration); + // we handle accounts only + if (!oclass.Is(ObjectClass.ACCOUNT_NAME)) + { + return base.Update(type, oclass, attributes, options); + } + // get recipient type and database string rcptType = ExchangeUtility.GetAttValue(AttRecipientType, attributes) as string; string database = ExchangeUtility.GetAttValue(AttDatabase, attributes) as string; @@ -382,6 +397,15 @@ public override void Test() public override void Sync( ObjectClass objClass, SyncToken token, SyncResultsHandler handler, OperationOptions options) { + ExchangeUtility.NullCheck(objClass, "oclass", this.configuration); + + // we handle accounts only + if (!objClass.Is(ObjectClass.ACCOUNT_NAME)) + { + base.Sync(objClass, token, handler, options); + return; + } + ArrayList attsToGet = null; if (options != null && options.AttributesToGet != null) { @@ -424,6 +448,15 @@ public override void Sync( public override void ExecuteQuery( ObjectClass oclass, string query, ResultsHandler handler, OperationOptions options) { + ExchangeUtility.NullCheck(oclass, "oclass", this.configuration); + + // we handle accounts only + if (!oclass.Is(ObjectClass.ACCOUNT_NAME)) + { + base.ExecuteQuery(oclass, query, handler, options); + return; + } + ArrayList attsToGet = null; if (options != null && options.AttributesToGet != null) { From f96c2a02706e17e343c299dd1efd3e8a83628ccd Mon Sep 17 00:00:00 2001 From: tknappek Date: Wed, 8 Apr 2009 14:42:56 +0000 Subject: [PATCH 219/342] ExchangeConnector - checking of rcptType adjusted in update --- ExchangeConnector/ExchangeConnector.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/ExchangeConnector/ExchangeConnector.cs b/ExchangeConnector/ExchangeConnector.cs index bf5f2b9e..9290a8df 100644 --- a/ExchangeConnector/ExchangeConnector.cs +++ b/ExchangeConnector/ExchangeConnector.cs @@ -360,7 +360,7 @@ public override Uid Update( this.configuration.ConnectorMessages.Format( "ex_update_notsupported", "Update of [{0}] to [{1}] is not supported", AttRecipientType, rcptType)); } - else + else if (rcptType != RcptTypeUser) { // unsupported rcpt type throw new ArgumentException( From 93b01fd5cff9d4a09442ee02bf829b7c695318de Mon Sep 17 00:00:00 2001 From: tknappek Date: Wed, 8 Apr 2009 22:43:18 +0000 Subject: [PATCH 220/342] ExchangeConnector: fixed sync of deleted objects --- ExchangeConnector/ExchangeConnector.cs | 5 +++++ ExchangeConnector/ExchangeUtility.cs | 5 ++++- 2 files changed, 9 insertions(+), 1 deletion(-) diff --git a/ExchangeConnector/ExchangeConnector.cs b/ExchangeConnector/ExchangeConnector.cs index 9290a8df..a594bc05 100644 --- a/ExchangeConnector/ExchangeConnector.cs +++ b/ExchangeConnector/ExchangeConnector.cs @@ -415,6 +415,11 @@ public override void Sync( // delegate to get the exchange attributes if requested SyncResultsHandler xchangeHandler = delegate(SyncDelta delta) { + if (delta.DeltaType == SyncDeltaType.DELETE) + { + return handler(delta); + } + // replace the ad attributes with exchange one and add recipient type ConnectorObject updated = ExchangeUtility.ReplaceAttributes(delta.Object, attsToGet, AttMapFromAD); updated = this.AddExchangeAttributes(objClass, updated, attsToGet); diff --git a/ExchangeConnector/ExchangeUtility.cs b/ExchangeConnector/ExchangeUtility.cs index 28d623a9..6f2dba68 100644 --- a/ExchangeConnector/ExchangeUtility.cs +++ b/ExchangeConnector/ExchangeUtility.cs @@ -293,8 +293,11 @@ internal static ArrayList FilterReplace(ArrayList col, IDictionary map) { Assertions.NullCheck(cobject, "cobject"); - Assertions.NullCheck(attsToGet, "attsToGet"); Assertions.NullCheck(map, "map"); + if (attsToGet == null) + { + return cobject; + } var attributes = cobject.GetAttributes(); var builder = new ConnectorObjectBuilder(); From 2b3ca487c9d1fdcc17bff8ed755053ba4287c5a3 Mon Sep 17 00:00:00 2001 From: dvernon Date: Wed, 8 Apr 2009 23:00:17 +0000 Subject: [PATCH 221/342] Issue #467 - use IDM format for UID --- ActiveDirectoryConnector/ActiveDirectoryConnector.cs | 5 +++-- ActiveDirectoryConnector/ActiveDirectoryUtils.cs | 4 ++-- 2 files changed, 5 insertions(+), 4 deletions(-) diff --git a/ActiveDirectoryConnector/ActiveDirectoryConnector.cs b/ActiveDirectoryConnector/ActiveDirectoryConnector.cs index 4d3436c7..dbe5eb11 100644 --- a/ActiveDirectoryConnector/ActiveDirectoryConnector.cs +++ b/ActiveDirectoryConnector/ActiveDirectoryConnector.cs @@ -1204,7 +1204,7 @@ public virtual ConnectorAttribute NormalizeAttribute(ObjectClass oclass, Connect if (oclass.Equals(ObjectClass.ACCOUNT)) { // convert to upper case - uidValue = uidValue.ToUpper(); + uidValue = uidValue.ToLower(); // now remove spaces foreach (Char nextChar in uidValue) @@ -1214,8 +1214,9 @@ public virtual ConnectorAttribute NormalizeAttribute(ObjectClass oclass, Connect normalizedUidValue.Append(nextChar); } } + String tempGuid = normalizedUidValue.ToString(); - return new Uid(normalizedUidValue.ToString()); + return new Uid(tempGuid.Replace("guid", "GUID")); } else { diff --git a/ActiveDirectoryConnector/ActiveDirectoryUtils.cs b/ActiveDirectoryConnector/ActiveDirectoryUtils.cs index 08e57068..5396ec13 100644 --- a/ActiveDirectoryConnector/ActiveDirectoryUtils.cs +++ b/ActiveDirectoryConnector/ActiveDirectoryUtils.cs @@ -71,7 +71,7 @@ internal static String ConvertUIDBytesToSearchString(Byte[] guidBytes) for (int i = 0; i < guidBytes.Length; i++) { - searchGuid += String.Format("\\{0:X2}", guidBytes[i]); + searchGuid += String.Format("\\{0:x2}", guidBytes[i]); } return searchGuid; @@ -100,7 +100,7 @@ internal static String ConvertBytesToADSpecialString(string attribute, Byte[] by for (int i = 0; i < bytes.Length; i++) { - guidString += String.Format("{0:X2}", bytes[i]); + guidString += String.Format("{0:x2}", bytes[i]); } guidString += ">"; From bf0ec856a7b00ed1357c63513abae6d3e80b3aec Mon Sep 17 00:00:00 2001 From: tknappek Date: Thu, 9 Apr 2009 15:44:54 +0000 Subject: [PATCH 222/342] ExchangeConnector: RecipientType removing added to FilterTranslator --- ExchangeConnector/ExchangeConnector.cs | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/ExchangeConnector/ExchangeConnector.cs b/ExchangeConnector/ExchangeConnector.cs index a594bc05..0910a6af 100644 --- a/ExchangeConnector/ExchangeConnector.cs +++ b/ExchangeConnector/ExchangeConnector.cs @@ -878,6 +878,11 @@ protected override string[] GetLdapNamesForAttribute(ConnectorAttribute attr) return new[] { AttExternalMailADName }; } + if (attr.Is(AttRecipientType)) + { + return null; + } + return base.GetLdapNamesForAttribute(attr); } } From a3b828412b3a2fc5bf9995461539360d1d3659e0 Mon Sep 17 00:00:00 2001 From: kyarbro Date: Thu, 9 Apr 2009 16:14:01 +0000 Subject: [PATCH 223/342] Localizations --- ExchangeConnector/Messages.de-DE.resx | 20 +++++++------------- ExchangeConnector/Messages.es-ES.resx | 20 +++++++------------- ExchangeConnector/Messages.fr-FR.resx | 20 +++++++------------- ExchangeConnector/Messages.ja-JP.resx | 20 +++++++------------- ExchangeConnector/Messages.ko-KR.resx | 20 +++++++------------- ExchangeConnector/Messages.zh-CN.resx | 20 +++++++------------- ExchangeConnector/Messages.zh-TW.resx | 20 +++++++------------- 7 files changed, 49 insertions(+), 91 deletions(-) diff --git a/ExchangeConnector/Messages.de-DE.resx b/ExchangeConnector/Messages.de-DE.resx index 9be8d0f4..9b38bf5a 100644 --- a/ExchangeConnector/Messages.de-DE.resx +++ b/ExchangeConnector/Messages.de-DE.resx @@ -1,4 +1,4 @@ - + - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - text/microsoft-resx - - - 2.0 - - - System.Resources.ResXResourceReader, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 - - - System.Resources.ResXResourceWriter, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 - - - it-IT: Exchange Connector - - - it-IT: The Argument [{0}] can't be null - - - it-IT: Recipient type [{0}] is not supported - - - it-IT: Provided User name is not unique or not existing - - - it-IT: Update of [{0}] attribute is not supported - - - it-IT: Problem while PowerShell execution {0} - - - it-IT: Update of [{0}] to [{1}] is not supported - - - it-IT: Update type [{0}] not supported - - \ No newline at end of file diff --git a/ExchangeConnector/Messages.pt-BR.resx b/ExchangeConnector/Messages.pt-BR.resx deleted file mode 100644 index c27deead..00000000 --- a/ExchangeConnector/Messages.pt-BR.resx +++ /dev/null @@ -1,144 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - text/microsoft-resx - - - 2.0 - - - System.Resources.ResXResourceReader, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 - - - System.Resources.ResXResourceWriter, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 - - - pt-BR: Exchange Connector - - - pt-BR: The Argument [{0}] can't be null - - - pt-BR: Recipient type [{0}] is not supported - - - pt-BR: Provided User name is not unique or not existing - - - pt-BR: Update of [{0}] attribute is not supported - - - pt-BR: Problem while PowerShell execution {0} - - - pt-BR: Update of [{0}] to [{1}] is not supported - - - pt-BR: Update type [{0}] not supported - - \ No newline at end of file From b39212e07c380afd8bcbf0b3dd87b72eb981f3ba Mon Sep 17 00:00:00 2001 From: tknappek Date: Thu, 9 Apr 2009 17:58:46 +0000 Subject: [PATCH 225/342] ExchangeConnector: reflecting of atts to get added --- ExchangeConnector/ExchangeConnector.cs | 41 ++++++++++++++++++++------ ExchangeConnector/ExchangeUtility.cs | 6 ++-- 2 files changed, 35 insertions(+), 12 deletions(-) diff --git a/ExchangeConnector/ExchangeConnector.cs b/ExchangeConnector/ExchangeConnector.cs index 0910a6af..67fc0dcc 100644 --- a/ExchangeConnector/ExchangeConnector.cs +++ b/ExchangeConnector/ExchangeConnector.cs @@ -405,11 +405,11 @@ public override void Sync( base.Sync(objClass, token, handler, options); return; } - - ArrayList attsToGet = null; + + ICollection attsToGet = null; if (options != null && options.AttributesToGet != null) { - attsToGet = new ArrayList(options.AttributesToGet); + attsToGet = CollectionUtil.NewSet(options.AttributesToGet); } // delegate to get the exchange attributes if requested @@ -462,10 +462,10 @@ public override void ExecuteQuery( return; } - ArrayList attsToGet = null; + ICollection attsToGet = null; if (options != null && options.AttributesToGet != null) { - attsToGet = new ArrayList(options.AttributesToGet); + attsToGet = CollectionUtil.NewList(options.AttributesToGet); } // delegate to get the exchange attributes if requested @@ -492,7 +492,9 @@ public override void ExecuteQuery( // build new op options var builder = new OperationOptionsBuilder(options); - builder.AttributesToGet = (string[])newAttsToGet.ToArray(typeof(string)); + string[] attributesToGet = new string[newAttsToGet.Count]; + newAttsToGet.CopyTo(attributesToGet, 0); + builder.AttributesToGet = attributesToGet; options2use = builder.Build(); handler2use = filter; } @@ -695,7 +697,7 @@ private static T GetFirstElement(IEnumerable collection) where T : class /// Connector Object with recipient type added /// In case of some troubles in powershell (if the /// user is not found we get this exception too) - private ConnectorObject AddExchangeAttributes(ObjectClass oc, ConnectorObject cobject, IList attToGet) + private ConnectorObject AddExchangeAttributes(ObjectClass oc, ConnectorObject cobject, IEnumerable attToGet) { ExchangeUtility.NullCheck(oc, "name", this.configuration); ExchangeUtility.NullCheck(oc, "cobject", this.configuration); @@ -714,6 +716,20 @@ private ConnectorObject AddExchangeAttributes(ObjectClass oc, ConnectorObject co return cobject; } + IList lattToGet = CollectionUtil.NewList(attToGet); + foreach (string att in lattToGet) + { + if (cobject.GetAttributeByName(att) != null && att != AttDatabase) + { + lattToGet.Remove(att); + } + } + + if (lattToGet.Count == 0) + { + return cobject; + } + ConnectorObjectBuilder cobjBuilder = new ConnectorObjectBuilder(); cobjBuilder.AddAttributes(cobject.GetAttributes()); @@ -732,11 +748,17 @@ private ConnectorObject AddExchangeAttributes(ObjectClass oc, ConnectorObject co foreach (var info in user.Properties) { ConnectorAttribute att = GetAsAttribute(info); - if (att != null) + if (att != null && lattToGet.Contains(att.Name)) { cobjBuilder.AddAttribute(att); + lattToGet.Remove(att.Name); } } + + if (lattToGet.Count == 0) + { + return cobjBuilder.Build(); + } } if (user == null) @@ -764,9 +786,10 @@ private ConnectorObject AddExchangeAttributes(ObjectClass oc, ConnectorObject co foreach (var info in userDetails.Properties) { ConnectorAttribute att = GetAsAttribute(info); - if (att != null) + if (att != null && lattToGet.Contains(att.Name)) { cobjBuilder.AddAttribute(att); + lattToGet.Remove(att.Name); } } } diff --git a/ExchangeConnector/ExchangeUtility.cs b/ExchangeConnector/ExchangeUtility.cs index 6f2dba68..034d08d2 100644 --- a/ExchangeConnector/ExchangeUtility.cs +++ b/ExchangeConnector/ExchangeUtility.cs @@ -263,12 +263,12 @@ internal static ICollection FilterOut(ICollectionReplace mappings /// Replaced /// If some of the params is null - internal static ArrayList FilterReplace(ArrayList col, IDictionary map) + internal static ICollection FilterReplace(ICollection col, IDictionary map) { Assertions.NullCheck(col, "col"); Assertions.NullCheck(map, "map"); - ArrayList newcol = (ArrayList) col.Clone(); + ICollection newcol = CollectionUtil.NewList(col); foreach (KeyValuePair pair in map) { if (newcol.Contains(pair.Key)) @@ -290,7 +290,7 @@ internal static ArrayList FilterReplace(ArrayList col, IDictionaryReplace mapping /// ConnectorObject with replaced attributes /// If some of the params is null - internal static ConnectorObject ReplaceAttributes(ConnectorObject cobject, IList attsToGet, IDictionary map) + internal static ConnectorObject ReplaceAttributes(ConnectorObject cobject, ICollection attsToGet, IDictionary map) { Assertions.NullCheck(cobject, "cobject"); Assertions.NullCheck(map, "map"); From 512fdcb7933083d6a9066e142836af6484eac484 Mon Sep 17 00:00:00 2001 From: dvernon Date: Thu, 9 Apr 2009 18:00:32 +0000 Subject: [PATCH 226/342] Adding unit test for GUID compatibility (especially for IDM). --- .../ActiveDirectoryConnectorTest.cs | 11 +++++++++++ 1 file changed, 11 insertions(+) diff --git a/ActiveDirectoryConnectorTests/ActiveDirectoryConnectorTest.cs b/ActiveDirectoryConnectorTests/ActiveDirectoryConnectorTest.cs index 391d9b50..3a0c8f70 100644 --- a/ActiveDirectoryConnectorTests/ActiveDirectoryConnectorTest.cs +++ b/ActiveDirectoryConnectorTests/ActiveDirectoryConnectorTest.cs @@ -2626,6 +2626,17 @@ public ICollection GetNormalAttributes_OrganizationalUnit() private static void VerifyObject(ICollection requestedAttributes, ConnectorObject returnedObject) { + // verify guid is in the proper format. This is important for IDM. + if (returnedObject.ObjectClass.Equals(ObjectClass.ACCOUNT)) + { + + Uid uid = returnedObject.Uid; + String uidValue = uid.GetUidValue(); + Assert.That(uidValue.StartsWith((""), "GUID for account objects must end with >"); + Assert.That(uidValue.ToLower().Replace("guid", "GUID").Equals(uidValue), + "GUID for account objects must have lowercase hex strings"); + } // for now, skipping values that are very difficult to // determine equality ... or they are not returned like From ba5694814e7bb5d66817786b9949cf97315cd151 Mon Sep 17 00:00:00 2001 From: tknappek Date: Thu, 9 Apr 2009 19:05:45 +0000 Subject: [PATCH 227/342] ExchangeConnector: fixed atts to get --- ExchangeConnector/ExchangeConnector.cs | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/ExchangeConnector/ExchangeConnector.cs b/ExchangeConnector/ExchangeConnector.cs index 67fc0dcc..7a8951f6 100644 --- a/ExchangeConnector/ExchangeConnector.cs +++ b/ExchangeConnector/ExchangeConnector.cs @@ -716,8 +716,9 @@ private ConnectorObject AddExchangeAttributes(ObjectClass oc, ConnectorObject co return cobject; } - IList lattToGet = CollectionUtil.NewList(attToGet); - foreach (string att in lattToGet) + ICollection lattToGet = CollectionUtil.NewCaseInsensitiveSet(); + CollectionUtil.AddAll(lattToGet, attToGet); + foreach (string att in attToGet) { if (cobject.GetAttributeByName(att) != null && att != AttDatabase) { From 30eb1f3949c762a92d8e0cc422be28e1bc009024 Mon Sep 17 00:00:00 2001 From: tknappek Date: Thu, 9 Apr 2009 20:04:35 +0000 Subject: [PATCH 228/342] ExchangeConnector: fixed atts to get --- ExchangeConnector/ExchangeConnector.cs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/ExchangeConnector/ExchangeConnector.cs b/ExchangeConnector/ExchangeConnector.cs index 7a8951f6..bfe7a82f 100644 --- a/ExchangeConnector/ExchangeConnector.cs +++ b/ExchangeConnector/ExchangeConnector.cs @@ -702,8 +702,8 @@ private ConnectorObject AddExchangeAttributes(ObjectClass oc, ConnectorObject co ExchangeUtility.NullCheck(oc, "name", this.configuration); ExchangeUtility.NullCheck(oc, "cobject", this.configuration); - // we support ACCOUNT only - if (!oc.Is(ObjectClass.ACCOUNT_NAME)) + // we support ACCOUNT only or there is nothing to add + if (!oc.Is(ObjectClass.ACCOUNT_NAME) || attToGet == null) { return cobject; } From e1cd38ee05d8e0860cc922c12c860779d15b476d Mon Sep 17 00:00:00 2001 From: tknappek Date: Wed, 15 Apr 2009 12:30:30 +0000 Subject: [PATCH 229/342] ExchangeConnector messages quick fix because of Issue#469 --- ExchangeConnector/Messages.de-DE.resx | 161 ++++++++++++++++++++++++++ ExchangeConnector/Messages.es-ES.resx | 161 ++++++++++++++++++++++++++ ExchangeConnector/Messages.fr-FR.resx | 161 ++++++++++++++++++++++++++ ExchangeConnector/Messages.ja-JP.resx | 161 ++++++++++++++++++++++++++ ExchangeConnector/Messages.ko-KR.resx | 161 ++++++++++++++++++++++++++ ExchangeConnector/Messages.resx | 161 ++++++++++++++++++++++++++ ExchangeConnector/Messages.zh-CN.resx | 161 ++++++++++++++++++++++++++ ExchangeConnector/Messages.zh-TW.resx | 161 ++++++++++++++++++++++++++ 8 files changed, 1288 insertions(+) diff --git a/ExchangeConnector/Messages.de-DE.resx b/ExchangeConnector/Messages.de-DE.resx index 9b38bf5a..c8ca72dd 100644 --- a/ExchangeConnector/Messages.de-DE.resx +++ b/ExchangeConnector/Messages.de-DE.resx @@ -135,4 +135,165 @@ Aktualisierungstyp [{0}] wird nicht unterstützt + + + Ausgangsverzeichnis erstellen + + + Konto des Verzeichnisadministrators + + + Passwort des Verzeichnisadministrators + + + Domänenname + + + Active Directory Domänencontroller-Hostname + + + Objektklasse für Benutzerobjekte + + + Untergeordnete Domänen durchsuchen + + + Container + + + Domänencontroller synchronisieren + + + Globalen Katalogserver synchronisieren + + + Suchkontext + + + Geben Sie an, ob das Ausgangsverzeichnis des Benutzers erstellt werden soll oder nicht. + + + Geben Sie den Benutzernamen des Administrators ein, mit dem das System authentifiziert werden soll. Die Einstellung kann entweder ein Benutzername oder 'domänenname'\'benutzername' sein. + + + Geben Sie das Passwort ein, dass bei der Authentifizierung verwendet werden soll. + + + Der Name der Windows-Domäne (z. B. windowsdomaene.eigenefirma.com) + + + Für die domänenübergreifende Administration geben Sie den Hostnamen, die IP-Adresse oder den Domänennamen des LDAP-Servers ein. + + + Geben Sie die Aktive Directory-Objektklasse für die Benutzerobjekte an, die auf dieser Ressource verwaltet werden. Die Standardeinstellung lautet User. Für die meisten Anwendungen ist diese Einstellung ausreichend. + + + Wählen Sie, ob bei Suchabfragen am Active Directory auch untergeordnete Domänen eingeschlossen werden sollen. Außerdem müssen die Attribute "Suchcontainer" und "Suchkontext synchronisieren" (siehe Synchronisationseinstellungen) auf die oberste übergeordnete Domäne festgelegt werden, z. B. DC=mydomain,DC=com. + + + Geben Sie ein Containerobjekt an, das als Standard-Root für alle Suchvorgänge fungiert. Es werden nur Objekte unter diesem Container durchsucht, es sei denn, in dem Suchvorgang werden explizit weitere Kriterien übergeben. Wenn Sie beispielsweise Benutzer aus dem Container "Users" abrufen wollen, geben Sie Folgendes ein: CN=Users,DC=MYDOMAIN,DC=COM. Wenn kein Wert angegeben wurde, wird der Wert des ''Base Container''-Attributs verwendet. + + + Bei einer aktiven Synchronisation zu verwendender Domänencontroller. Wird nur dann verwendet, wenn keine untergeordneten Domänen durchsucht werden. + + + Name des globalen Katalogservers. Dieser Name ist nur dann erforderlich, wenn untergeordnete Domänen durchsucht werden. + + + Geben Sie an, wo im ADSI-Verzeichnis gesucht werden soll, wenn versucht wird, das durch searchAttributes festgelegte Attribut in das native DN-Format aufzulösen. + + + Der Connector wurde nicht konfiguriert + + + Für die Objektklasse {0} wird kein Löschvorgang unterstützt. + + + Ungültige Objektklasse: {0} + + + Das Attribut {0} ist nicht im Connector-Objekt vorhanden. Synchronisierung kann nicht fortgesetzt werden + + + Der Name des funktionsbereiten Attributs kann nicht Null lauten + + + Für die Objektklasse {0} ist kein Synchronisierungsvorgang vorhanden. + + + Keine UID vorhanden + + + In der Connector-Konfiguration wurde eine ungültige Objektklasse angegeben. Die Objektklasse \'{0}\' konnte im Active Directory nicht gefunden werden + + + Zu dem Suchergebnis <Null> konnte kein Connector-Attribut hinzugefügt werden + + + Zu dem Verzeichniseintrag <Null> konnte kein Connector-Attribut hinzugefügt werden + + + Einzelner Wert wurde erwartet, es wurden aber mehrere Werte für das Attribut {0} gefunden + + + Die Objektklasse \'{0}\' ist für diesen Connector nicht zulässig + + + Für den Benutzer {0} wurden ungültige Anmeldedaten angegeben + + + Die Passwortablauffrist kann nur durch Zurücksetzen des Passworts zurückgesetzt werden. + + + Active Directory unterstützt das Sperren von Benutzern nicht. Der Benutzer kann nur freigegeben werden + + + Es wurde ein ungültiger Suchbereich angegeben: {0} + + + Bei der Validierung des Benutzers {0} ist ein Ausnahmefehler aufgetreten. Der Benutzer wurde erfolgreich authentifiziert, aber die GUI des Benutzers konnte nicht ermittelt werden. + + + Bei der Validierung des Benutzers {0} ist ein Ausnahmefehler aufgetreten. Der Benutzer wurde erfolgreich authentifiziert, aber die SID des Benutzers konnte nicht ermittelt werden. + + + Der Name des Verzeichnisadministrators wurde nicht angegeben. + + + Das Passwort des Verzeichnisadministrators wurde nicht angegeben. + + + Der Domänenname wurde nicht angegeben. + + + Die Objektklasse wurde nicht angegeben. + + + Der Suchcontainer wurde nicht angegeben. + + + Verwenden der Identity Manager Ressourcenadapter-Stilabfrage '{0}'. Dies sollte aktualisiert werden, um die neue Connector-Abfragesyntax zu verwenden. + + + Es wurde ein ungültiger Container angegeben: {0} + + + Shell-Skript Variablenpräfix + + + Ein Präfix, der zu allen Shell-Skript-Argumentnamen hinzugefügt wird. Beispiel: Wenn das Präfix auf "Connector_" gesetzt ist, wird das Argument "User" zu "Connector_User". + + + Das Benutzerkonto wurde gesperrt + + + Das Benutzerpasswort muss geändert werden + + + Für den Benutzer {0} ist das Benutzerkonto abgelaufen + + + Es wurde ein ungültiger Suchkontext angegeben: {0} + + \ No newline at end of file diff --git a/ExchangeConnector/Messages.es-ES.resx b/ExchangeConnector/Messages.es-ES.resx index 0e3b3fb0..96ea7aec 100644 --- a/ExchangeConnector/Messages.es-ES.resx +++ b/ExchangeConnector/Messages.es-ES.resx @@ -135,4 +135,165 @@ Tipo de actualización [{0}] no compatible + + + Crear directorio principal + + + Cuenta de administrador de Directory + + + Contraseña de administrador de Directory + + + Nombre de dominio + + + Nombre de host del controlador de dominio de Active Directory + + + Clase de objeto para objetos de usuario + + + Dominios secundarios de búsqueda + + + Contenedor + + + Sincronizar controlador de dominio + + + Sincronizar servidor del catálogo global + + + Contexto de la búsqueda + + + Especifique si se debe crear el directorio principal para el usuario. + + + Introduzca el nombre de usuario del administrador con el que debe autenticarse el sistema. El valor puede ser un nombre de usuario o 'nombredominio'\'nombreusuario'. + + + Introduzca la contraseña que se debe usar al autenticar. + + + Nombre del dominio de Windows (ej. dominiowindows.miempresa.com) + + + En la administración de dominios cruzados, introduzca el nombre de host, la dirección IP o el nombre de dominio del servidor LDAP. + + + Especifique la clase de objeto de Active Directory para los objetos de usuario que se administrarán en este recurso. El valor predeterminado es User, que debe ser adecuado en la mayoría de los casos. + + + Indique si quiere que las búsquedas de Active Directory incluyan dominios secundarios. Además, los atributos Contenedor de búsqueda y Sincronizar contexto de búsqueda (véase la configuración de sincronización) deben estar configurados en la parte superior del dominio principal; por ejemplo, DC=mydomain,DC=com. + + + Especifique un objeto de contenedor que será el root predeterminado de todas la búsquedas. A no ser que se produzca una búsqueda explícita con otros criterios, sólo se buscarán los objetos de este contenedor. Por ejemplo, si desea recuperar usuarios del contenedor de usuarios, escriba CN=Users,DC=MYDOMAIN,DC=COM. Si no se especifica ningún valor, se utiliza el valor del atributo 'Base Container'. + + + Controlador de dominio que se debe usar al activar la sincronización. Sólo se utiliza cuando no se busca en dominios secundarios. + + + Nombre del servidor del catálogo global. Sólo es necesario cuando se busca en dominios secundarios. + + + Especifique dónde buscar en el directorio ADSI cuando se intente resolver el atributo especificado por Atributos de búsqueda al formato dn nativo. + + + No se ha configurado el conector + + + No se admite la eliminación para la clase de objeto {0} + + + Clase de objeto no válida: {0} + + + El atributo {0} no está presente en el objeto de conector. No se puede proceder a la sincronización. + + + El atributo operativo de nombre no puede ser nulo + + + La operación de sincronización no está disponible para la clase de objeto {0} + + + No había Uid + + + Se había especificado una clase de objeto no válida en la configuración del conector. La clase de objeto \'{0}\' no se ha encontrado en Active Directory + + + No se pudo agregar el atributo de conector al resultado de búsqueda <null> + + + No se pudo agregar el atributo de conector a la entrada de directorio <null> + + + Se esperaba un solo valor, pero se encontraron varios para el atributo {0} + + + La clase de objeto \'{0}\' no es válida para este conector + + + Credenciales suministradas para usuario {0} no válidas + + + La caducidad de la contraseña sólo puede restablecerse al restablecer la contraseña + + + Active Directory no admite bloqueo de usuarios. El usuario sólo se puede desbloquear + + + Se ha suministrado un ámbito de búsqueda no válido: {0} + + + Ha ocurrido una excepción durante la validación del usuario {0}. El usuario se ha autenticado satisfactoriamente, pero no se ha podido determinar su guid. + + + Ha ocurrido una excepción durante la validación del usuario {0}. El usuario se ha autenticado satisfactoriamente, pero no se ha podido determinar su sid. + + + No se ha suministrado el nombre del administrador de Directory. + + + No se ha suministrado la contraseña del administrador de Directory. + + + No se ha suministrado el nombre de dominio. + + + No se ha suministrado la clase de objeto. + + + No se ha suministrado el contenedor de búsqueda. + + + Se está usando el estilo de consulta del adaptador de recursos de Identity Manager '{0}'. Debe actualizarse para utilizar la sintaxis de consulta del nuevo conector. + + + Se ha suministrado un contenedor no válido: {0} + + + Prefijo variable de secuencia de comandos shell + + + Prefijo que se añadirá a todos los nombres de argumento de secuencia de comandos shell. Por ejemplo, si el prefijo se configura en "Conector_", un argumento llamado "Usuario" se convertirá en "Conector_Usuario" + + + La cuenta del usuario ha sido bloqueada + + + La contraseña de usuario se debe cambiar + + + Ha caducado la cuenta de usuario del usuario {0} + + + Se ha suministrado un contexto de búsqueda no válido: {0} + + \ No newline at end of file diff --git a/ExchangeConnector/Messages.fr-FR.resx b/ExchangeConnector/Messages.fr-FR.resx index d029a344..9ec54c58 100644 --- a/ExchangeConnector/Messages.fr-FR.resx +++ b/ExchangeConnector/Messages.fr-FR.resx @@ -135,4 +135,165 @@ La mise à jour du type [{0}] n’est pas prise en charge. + + + Créer un répertoire personnel + + + Compte de l’administrateur de répertoires + + + Mot de passe de l’administrateur de répertoires + + + Nom du domaine + + + Nom d’hôte du contrôleur de domaine Active Directory + + + Classe d’objet utilisateur + + + Rechercher dans les domaines enfant + + + Conteneur + + + Synchr. le contrôleur de domaine + + + Synchr. le serveur de catalogue global + + + Contexte de recherche + + + Spécifiez si le répertoire personnel de l’utilisateur sera créé ou non. + + + Saisissez le nom d’utilisateur de l’administrateur que le système doit utiliser pour l’authentification. Ce paramètre peut correspondre à un nom d’utilisateur ou à nom_domaine\nom_utilisateur. + + + Saisissez le mot de passe utilisateur devant servir à l’authentification. + + + Nom du domaine Windows (par ex., domainewindows.monentreprise.com) + + + Pour l’administration interdomaine, saisissez le nom d’hôte, l’adresse IP ou le nom de domaine du serveur LDAP. + + + Spécifiez la classe d’objet Active Directory destinée aux objets utilisateur à gérer sur cette ressource. La classe par défaut est User (Utilisateur). Elle devrait convenir dans la majorité des cas. + + + Indiquez si les recherches effectuées dans Active Directory doivent inclure les domaines enfant. Assurez-vous par ailleurs que les attributs Conteneur de recherche et Synchr. le contexte de recherche (voir les paramètres de synchronisation) sont définis sur la racine du domaine parent (par ex., DC=mondomaine,DC=com). + + + Spécifiez un objet conteneur qui servira de racine par défaut à toutes les recherches. À moins qu’une recherche ne passe explicitement d’autres critères, seuls les objets situés sous ce conteneur feront l’objet d’une recherche. Par exemple, pour extraire les utilisateurs du conteneur Utilisateurs, saisissez CN=Utilisateurs,DC=MONDOMAINE,DC=COM. Si aucune valeur n’est spécifiée, c’est celle de l’attribut Conteneur de base qui est utilisée. + + + Contrôleur de domaine à utiliser lors d’une synchronisation active. Uniquement utilisé lorsque la recherche ne porte pas sur les domaines enfant. + + + Nom du serveur de catalogue global. Uniquement utilisé lorsque la recherche porte sur les domaines enfant. + + + Spécifiez où rechercher dans le répertoire ADSI lorsque vous tentez de résoudre l’attribut spécifié par searchAttributes au format DN natif. + + + Le connecteur n’a pas été configuré. + + + La suppression n’est pas prise en charge pour la classe d’objet {0}. + + + Classe d’objet incorrecte : {0} + + + L’attribut {0} ne figure pas dans l’objet connecteur. Impossible de poursuivre la synchronisation. + + + L’attribut opérationnel du nom doit être spécifié. + + + L’opération de synchronisation n’est pas disponible pour la classe d’objet {0}. + + + UID introuvable + + + Une classe d’objet erronée a été spécifiée dans la configuration du connecteur. Impossible de trouver la classe d’objet {0} dans Active Directory. + + + Impossible d’ajouter l’attribut de connecteur à un résultat de recherche <nul>. + + + Impossible d’ajouter l’attribut de connecteur à une entrée de répertoire <nul>. + + + Valeur unique attendue, alors que plusieurs valeurs ont été trouvées pour l’attribut {0}. + + + La classe d’objet {0} est incompatible avec ce connecteur. + + + Informations d’authentification incorrectes fournies pour l’utilisateur {0} + + + La date d’expiration du mot de passe peut uniquement être réinitialisée via la réinitialisation du mot de passe lui-même. + + + Active Directory ne prend pas en charge le verrouillage des utilisateurs. L’utilisateur doit impérativement être déverrouillé. + + + Une étendue de recherche incorrecte a été spécifiée : {0} + + + Une exception s’est produite lors de la validation de l’utilisateur {0}. L’utilisateur a bien été authentifié, mais son GUID n’a pas pu être déterminé. + + + Une exception s’est produite lors de la validation de l’utilisateur {0}. L’utilisateur a bien été authentifié, mais son SID n’a pas pu être déterminé. + + + Le nom de l’administrateur de répertoires n’a pas été fourni. + + + Le mot de passe de l’administrateur de répertoires n’a pas été fourni. + + + Le nom de domaine n’a pas été fourni. + + + La classe d’objet n’a pas été fournie. + + + Le conteneur de recherche n’a pas été fourni. + + + Utilisation de la requête de type Adaptateur de ressources Identity Manager {0}. Elle devrait être mise à jour afin d’utiliser la nouvelle syntaxe de requête du connecteur. + + + Un conteneur erroné a été fourni : {0} + + + Préfixe de script shell variable + + + Préfixe ajouté à tous les noms d’arguments de script shell. Par exemple, si le préfixe est défini sur Connector_, un argument appelé User est converti en Connector_User. + + + Le compte de l’utilisateur a été verrouillé. + + + Le mot de passe utilisateur doit être changé. + + + Le compte de l’utilisateur {0} a expiré. + + + Un contexte de recherche erroné a été fourni : {0} + + \ No newline at end of file diff --git a/ExchangeConnector/Messages.ja-JP.resx b/ExchangeConnector/Messages.ja-JP.resx index 941ec498..9490bc7d 100644 --- a/ExchangeConnector/Messages.ja-JP.resx +++ b/ExchangeConnector/Messages.ja-JP.resx @@ -135,4 +135,165 @@ 更新タイプ [{0}] はサポートされません + + + ホームディレクトリの作成 + + + ディレクトリ管理者のアカウント + + + ディレクトリ管理者のパスワード + + + ドメイン名 + + + Active Directory ドメインコントローラのホスト名 + + + ユーザーオブジェクトのオブジェクトクラス + + + 子ドメインの検索 + + + コンテナ + + + ドメインコントローラの同期 + + + グローバルカタログサーバーの同期 + + + 検索コンテキスト + + + ユーザー用ホームディレクトリを作成するかどうかを指定します。 + + + システムが認証に使用する管理者のユーザー名を入力します。ユーザー名または 'ドメイン名'\ユーザー名' の形式で設定できます。 + + + 認証に使用するパスワードを入力します。 + + + Windows のドメイン名 (例: windowsdomain.mycompany.com) + + + クロスドメイン管理を行う場合は、ホスト名、IP アドレス、または LDAP サーバーのドメイン名を入力します。 + + + このリソース上で管理するユーザーオブジェクトの Active Directory オブジェクトクラスを指定します。デフォルトは User で、多くの場合、この設定が適切です。 + + + Active Directory の検索に子ドメインを含めるかどうかを選択します。さらに、親ドメインの先頭に、DC=mydomain,DC=com のように、検索コンテナおよび検索コンテキスト (同期設定を参照) 属性を設定してください。 + + + すべての検索のデフォルトルートとなるコンテナオブジェクトを指定します。検索を明示的に他の条件に渡さない場合、このコンテナ内のオブジェクトのみが検索されます。たとえば、Users コンテナからユーザーを取得する場合は、CN=Users,DC=MYDOMAIN,DC=COM を入力します。値を指定しないと、'Base Container' 属性の値が使用されます。 + + + アクティブ同期中に使用するドメインコントローラ。子ドメインを検索しない場合にのみ使用します。 + + + グローバルカタログサーバーの名前。子ドメインを検索する場合にのみ必要です。 + + + 検索属性で指定された属性のネイティブ DN 形式への解決を試みる場合に、ADSI ディレクトリ中でどこを検索するかを指定します。 + + + コネクタが設定されていません + + + オブジェクトクラス {0} では削除はサポートされていません + + + 無効なオブジェクトクラス: {0} + + + コネクタオブジェクトに属性 {0} がありません。同期を続行できません + + + 名前のオペレーショナル属性を null にすることはできません + + + オブジェクトクラス {0} では同期動作はできません + + + UID がありません + + + コネクタ設定に無効なオブジェクトクラスが指定されました。Active Directory からオブジェクトクラス \'{0}\' が見つかりませんでした + + + <null> の検索結果にコネクタ属性を追加できませんでした + + + <null> のディレクトリ入力にコネクタ属性を追加できませんでした + + + 単一の値を要求しているときに、属性 {0} に対して複数の値が検索されました + + + オブジェクトクラス \'{0}\' はこのコネクタでは無効です + + + ユーザー {0} に無効な認証情報が指定されました + + + パスワードをリセットすることでのみパスワードを期限切れにすることができます + + + Active Directory はユーザーのロックをサポートしません。ユーザーのロック解除のみができます + + + 無効な検索範囲が指定されました: {0} + + + ユーザー {0} の検証時に例外が発生しました。ユーザーの認証は正常に実行されましたが、ユーザーの GUID を特定できませんでした。 + + + ユーザー {0} の検証時に例外が発生しました。ユーザーの認証は正常に実行されましたが、ユーザーの SID を特定できませんでした。 + + + ディレクトリ管理者の名前が指定されていません。 + + + ディレクトリ管理者のパスワードが指定されていません。 + + + ドメイン名が指定されていません。 + + + オブジェクトクラスが指定されていません。 + + + 検索コンテナが指定されていません。 + + + 識別情報マネージャーリソースアダプタのスタイルクエリー '{0}' を使用しています。新規コネクタのクエリー構文を使用するには、このスタイルクエリーを更新してください。 + + + 無効なコンテナが指定されました: {0} + + + シェルスクリプトの変数のプレフィックス + + + シェルスクリプトのすべての引数名に追加されるプレフィックス。たとえば、プレフィックスを "Connector_" に設定すると、引数 "User" は "Connector_User" になります + + + ユーザーのアカウントがロックされています + + + ユーザーのパスワードを変更してください + + + ユーザー {0} のユーザーアカウントの期限が切れています + + + 無効な検索コンテキストが指定されました: {0} + + \ No newline at end of file diff --git a/ExchangeConnector/Messages.ko-KR.resx b/ExchangeConnector/Messages.ko-KR.resx index 2069da2f..b070b4b7 100644 --- a/ExchangeConnector/Messages.ko-KR.resx +++ b/ExchangeConnector/Messages.ko-KR.resx @@ -135,4 +135,165 @@ 업데이트 유형 [{0}]은(는) 지원되지 않습니다. + + + 홈 디렉토리 작성 + + + 디렉토리 관리자 계정 + + + 디렉토리 관리자 비밀번호 + + + 도메인 이름 + + + Active Directory 도메인 컨트롤러 호스트 이름 + + + 사용자 객체의 객체 클래스 + + + 하위 도메인 검색 + + + 컨테이너 + + + 도메인 컨트롤러 동기화 + + + 전역 카탈로그 서버 동기화 + + + 검색 컨텍스트 + + + 사용자의 홈 디렉토리를 만들지 여부를 지정합니다. + + + 시스템이 인증해야 하는 관리자 사용자 이름을 입력합니다. 설정은 사용자 이름 또는 'domainname'\'username'일 수 있습니다. + + + 인증할 때 사용해야 하는 비밀번호를 입력합니다. + + + Windows 도메인 이름(예: windowsdomain.mycompany.com) + + + 교차 도메인 관리의 경우 LDAP 서버의 호스트 이름, IP 주소 또는 도메인 이름을 입력합니다. + + + 이 자원에서 관리될 사용자 객체의 Active Directory 객체 클래스를 지정합니다. 기본값은 User이며, 대부분의 경우 기본값이 적절합니다. + + + Active Directory의 검색에 하위 도메인을 포함하려면 선택합니다. 또한 검색 컨테이너 및 검색 컨텍스트 동기화(동기화 설정 참조) 속성을 상위 도메인의 최상위로 설정해야 합니다(예: DC=mydomain,DC=com). + + + 모든 검색의 기본 루트가 되는 컨테이너 객체를 지정합니다. 검색이 명백히 다른 기준을 충족하지 않으면 이 컨테이너 아래에 있는 객체만 검색됩니다. 예를 들어 Users 컨테이너에서 사용자를 검색하려면 CN=Users,DC=MYDOMAIN,DC=COM을 입력합니다. 값을 지정하지 않으면 '기본 컨테이너' 속성 값이 사용됩니다. + + + 활성 동기화 중에 사용할 도메인 컨트롤러입니다. 하위 도메인을 검색하지 않는 경우에만 사용됩니다. + + + 전역 카탈로그 서버의 이름입니다. 이 이름은 하위 도메인을 검색하는 경우에만 필요합니다. + + + searchAttributes에 의하여 지정된 속성을 원래 DN 형식으로 변환할 때 찾을 ADSI의 디렉토리를 지정합니다. + + + 커넥터가 구성되지 않았습니다. + + + ObjectClass {0}은(는) 삭제할 수 없습니다. + + + 잘못된 객체 클래스: {0} + + + 속성 {0}이(가) 커넥터 객체에 없습니다. 동기화를 계속할 수 없습니다. + + + 이름 작업 속성은 null일 수 없습니다. + + + 동기화 작업을 ObjectClass {0}에 사용할 수 없습니다. + + + Uid가 없습니다. + + + 커넥터 구성에 잘못된 객체 클래스가 지정되었습니다. 객체 클래스 \'{0}\'이(가) Active Directory에 없습니다. + + + 커넥터 속성을 <null> 검색 결과에 추가할 수 없습니다. + + + 커넥터 속성을 <null> 디렉토리 입력 항목에 추가할 수 없습니다. + + + 속성 {0}에 대해 하나의 값이 검색되어야 하는데, 여러 개의 값이 검색되었습니다. + + + ObjectClass \'{0}\'이(가) 이 커넥터에 유효하지 않습니다. + + + 사용자 {0}에 대해 잘못된 자격 증명이 제공되었습니다. + + + 비밀번호 만료는 비밀번호 재설정을 통해서만 재설정할 수 있습니다. + + + Active Directory는 사용자 잠금을 지원하지 않습니다. 사용자의 잠금을 해제만 할 수 있습니다. + + + 잘못된 검색 범위가 입력되었습니다. {0} + + + 사용자 {0}의 유효성을 검사하는 동안 예외가 발생했습니다. 사용자가 인증되었지만 사용자의 guid는 확인하지 못했습니다. + + + 사용자 {0}의 유효성을 검사하는 동안 예외가 발생했습니다. 사용자가 인증되었지만 사용자의 sid는 확인하지 못했습니다. + + + 디렉토리 관리자 이름을 제공하지 않았습니다. + + + 디렉토리 관리자 비밀번호를 제공하지 않았습니다. + + + 도메인 이름을 제공하지 않았습니다. + + + ObjectClass를 제공하지 않았습니다. + + + 검색 컨테이너를 제공하지 않았습니다. + + + Identity Manger 자원 어댑터 스타일 쿼리 '{0}'을(를) 사용하고 있습니다. 새 커넥터 쿼리 구문을 사용하려면 이 쿼리를 업데이트해야 합니다. + + + 잘못된 컨테이너를 제공했습니다. {0} + + + 쉘 스크립트 변수 접두어 + + + 모든 쉘 스크립트 인수 이름에 추가되는 접두어입니다. 예를 들어 접두어가 "Connector_"로 설정된 경우 "User"라는 인수는 "Connector_User"가 됩니다. + + + 사용자의 계정이 잠겼습니다. + + + 사용자 비밀번호를 변경해야 합니다. + + + 사용자 {0}에 대한 사용자 계정이 만료되었습니다. + + + 잘못된 검색 컨텍스트를 제공했습니다. {0} + + \ No newline at end of file diff --git a/ExchangeConnector/Messages.resx b/ExchangeConnector/Messages.resx index 56b2c4e6..d296d52d 100644 --- a/ExchangeConnector/Messages.resx +++ b/ExchangeConnector/Messages.resx @@ -141,4 +141,165 @@ Update type [{0}] not supported + + + Create Home Directory + + + Directory Adminstrator''s Account + + + Directory Administrator''s Password + + + Domain Name + + + Active Directory Domain Controller Hostname + + + Object Class for User Objects + + + Search Child Domains + + + Container + + + Sync Domain Controller + + + Sync Global Catalog Server + + + Search Context + + + Specify whether or not the home directory for the user will be created. + + + Enter the administrator user name with which the system should authenticate. The setting can be either be a username or 'domainname'\'username'. + + + Enter the password that should be used when authenticating. + + + Name of the windows domain (e.g. windowsdomain.mycompany.com) + + + For cross-domain administration, enter the hostname, IP address, or domain name of the LDAP server. + + + Specify the Active Directory object class for user objects that will be managed on this resource. The default is User, and for most situations, this should be fine. + + + Select if you want searches of Active Directory to include child domains. In addition, the Search Container and Sync Search Context (see sync settings) attributes must be set to the top of the parent domain, e.g. DC=mydomain,DC=com. + + + Specify a container object which will be the default root of all searches. Unless a search explicitly passes in other criteria, only objects under this container will be searched. For example, if you want to retrieve users from the Users container, enter CN=Users,DC=MYDOMAIN,DC=COM. If no value is specified, the value of the 'Base Container' attribute is used. + + + Domain controller to use during active sync. Only used if not searching child domains. + + + Name of the global catalog server. This is needed only if searching child domains. + + + Specify where to look in the ADSI directory when attempting to resolve the attribute specified by searchAttributes to the native dn format. + + + Connector has not been configured + + + Delete is not supported for ObjectClass {0} + + + Invalid object class: {0} + + + Attribute {0} is not present in connector object. Cannot proceed with synchronization + + + The name operational attribute cannot be null + + + Sync operation is not available for ObjectClass {0} + + + Uid was not present + + + Invalid Object Class was specified in the connector configuration. Object Class \'{0}\' was not found in Active Directory + + + Could not add connector attribute to <null> search result + + + Could not add connector attribute to <null> directory entry + + + Expecting single value, but found multiple values for attribute {0} + + + ObjectClass \'{0}\' is not valid for this connector + + + Invalid credentials supplied for user {0} + + + Password expiration can only be reset by reseting the password + + + Active Directory does not support locking users. User may be unlocked only + + + An invalid searchscope was supplied: {0} + + + An execption occurred during validation of user {0}. The user was successfully authenticated, but the user's guid could not be determined. + + + An execption occurred during validation of user {0}. The user was successfully authenticated, but the user's sid could not be determined. + + + Directory administrator name not supplied. + + + Directory administrator password not supplied. + + + Domain name not supplied. + + + ObjectClass was not supplied. + + + Search Container was not supplied. + + + Using Identity Manger Resource Adapter style query '{0}'. This should be updated to use the new connector query syntax. + + + An invalid container was supplied: {0} + + + Shell Script Variable Prefix + + + Prefix that will be added to all shell script argument names. For example, if the prefix is set to "Connector_" an argument called "User" would become "Connector_User" + + + User's account has been locked + + + User password must be changed + + + User account expired for user {0} + + + An invalid search context was supplied: {0} + + \ No newline at end of file diff --git a/ExchangeConnector/Messages.zh-CN.resx b/ExchangeConnector/Messages.zh-CN.resx index 1028b301..60d839d8 100644 --- a/ExchangeConnector/Messages.zh-CN.resx +++ b/ExchangeConnector/Messages.zh-CN.resx @@ -135,4 +135,165 @@ 不支持更新类型 [{0}] + + + 创建主目录 + + + 目录管理员的帐户 + + + 目录管理员的密码 + + + 域名 + + + Active Directory 域控制器主机名 + + + 用户对象的对象类 + + + 搜索子域 + + + 容器 + + + 同步域控制器 + + + 同步全局目录服务器 + + + 搜索上下文 + + + 指定是否为用户创建主目录。 + + + 输入系统用以进行验证的管理员用户名。该设置可以为用户名或“域名”\“用户名”。 + + + 输入进行验证时应使用的密码。 + + + Windows 域的名称(例如 windowsdomain.mycompany.com) + + + 对于跨域管理,请输入 LDAP 服务器的主机名、IP 地址或域名。 + + + 指定将在此资源上管理的用户对象的 Active Directory 对象类。默认值为 User,在大多数情况下该值均适用。 + + + 如果要在 Active Directory 搜索中包括子域,请选择此项。此外,必须将“搜索容器”和“同步搜索上下文”(请参见同步设置)属性设置为父域的顶级,例如 DC=mydomain,DC=com。 + + + 指定将作为所有搜索的默认根目录的容器对象。除非搜索明确指定其他条件,否则只会搜索此容器下的对象。例如,如果要从 Users 容器中检索用户,请输入 CN=Users,DC=MYDOMAIN,DC=COM。如果没有指定任何值,则将使用“基容器”属性的值。 + + + 活动同步期间要使用的域控制器。仅在不搜索子域的情况下使用此项。 + + + 全局目录服务器的名称。仅在搜索子域的情况下需要此项。 + + + 指定要将由 searchAttributes 指定的属性解析为本机标识名格式时,从何处查找 ADSI 目录。 + + + 尚未配置连接器 + + + 对于对象类 {0},不支持删除操作 + + + 无效的对象类: {0} + + + 连接器对象中不存在属性 {0}。无法进行同步 + + + 名称操作属性不能为 null + + + 对于对象类 {0},无法执行同步操作 + + + 不存在 UID + + + 在连接器配置中指定了无效的对象类。在 Active Directory 中未找到对象类 \'{0}\' + + + 无法将连接器属性添加到 <null> 搜索结果 + + + 无法将连接器属性添加到 <null> 目录条目 + + + 属性 {0} 应有一个值,但却找到了多个值 + + + 对象类 \'{0}\' 对此连接器无效 + + + 为用户 {0} 提供的无效凭证 + + + 只能通过重置密码来重置密码到期 + + + Active Directory 不支持锁定用户。只能解除对用户的锁定 + + + 提供了无效的搜索范围: {0} + + + 验证用户 {0} 期间出现异常。已成功验证该用户,但无法确定该用户的 GUID。 + + + 验证用户 {0} 期间出现异常。已成功验证该用户,但无法确定该用户的 SID。 + + + 未提供目录管理员名称。 + + + 未提供目录管理员密码。 + + + 未提供域名。 + + + 未提供对象类。 + + + 未提供搜索容器。 + + + 使用 Identity Manger 资源适配器样式查询“{0}”。应对此进行更新,以使用新的连接器查询语法。 + + + 提供了无效的容器: {0} + + + Shell 脚本变量前缀 + + + 将添加到所有 Shell 脚本参数名称的前缀。例如,如果将该前缀设置为 "Connector_",则名为 "User" 的参数将变为 "Connector_User" + + + 用户帐户已被锁定 + + + 必须更改用户密码 + + + 用户 {0} 的用户帐户已到期 + + + 提供了无效的搜索上下文: {0} + + \ No newline at end of file diff --git a/ExchangeConnector/Messages.zh-TW.resx b/ExchangeConnector/Messages.zh-TW.resx index bbddd61f..2b645d5b 100644 --- a/ExchangeConnector/Messages.zh-TW.resx +++ b/ExchangeConnector/Messages.zh-TW.resx @@ -135,4 +135,165 @@ 不支援更新類型 [{0}] + + + 建立主目錄 + + + 目錄管理員的帳號 + + + 目錄管理員的密碼 + + + 網域名稱 + + + Active Directory 網域控制器主機名稱 + + + 使用者物件的物件類別 + + + 搜尋子網域 + + + 容器 + + + 同步化網域控制器 + + + 同步化全域目錄伺服器 + + + 搜尋上下文 + + + 指定是否要建立使用者的主目錄。 + + + 輸入系統進行認證時採用的管理員使用者名稱。此設定可以是「使用者名稱」或「網域名稱」\「使用者名稱」。 + + + 輸入進行認證時應使用的密碼。 + + + Windows 網域的名稱 (例如 windowsdomain.mycompany.com) + + + 若是跨網域進行管理,請輸入 LDAP 伺服器的主機名稱、IP 位址或網域名稱。 + + + 針對要在此資源上管理的使用者物件,指定 Active Directory 物件類別。預設值為【User】,且在大部分情況下均適用。 + + + 若要 Active Directory 的搜尋包含子網域,請選取此選項。此外此外您必須在父網域頂端設定【搜尋容器】及【同步化搜尋上下文】(請參閱同步化設定) 屬性 (例如 DC=mydomain,DC=com)。 + + + 指定將成為所有搜尋之預設根目錄的容器物件。除非搜尋明確指定有其他條件,否則只會搜尋此容器中的物件。例如,若想要擷取【Users】容器中的使用者,請輸入 CN=Users,DC=MYDOMAIN,DC=COM。若未指定任何值,將使用「基底容器」屬性值。 + + + Active Sync 期間要使用的網域控制器。只有在不搜尋子網域時才使用。 + + + 全域目錄伺服器的名稱。只有在搜尋子網域時才需要。 + + + 指定將 searchAttributes 所指定的屬性解析為本機 DN 格式時,要在何處查詢 ADSI 目錄。 + + + 尚未配置連接器 + + + 不支援刪除物件類別 {0} + + + 無效的物件類別: {0} + + + 連接器物件中沒有屬性 {0}。無法繼續同步化 + + + 名稱作業屬性不得為空 + + + 同步化作業不適用於物件類別 {0} + + + uid 不存在 + + + 連接器配置中指定了無效的物件類別。Active Directory 中找不到物件類別「{0}」 + + + 無法將連接器屬性增加至 <null> 搜尋結果 + + + 無法將連接器屬性增加至 <null> 目錄項目 + + + 屬性 {0} 應僅有單一值,但卻出現多個值 + + + 物件類別「{0}」對此連接器無效 + + + 為使用者 {0} 提供的憑證無效 + + + 必須重設密碼才可重設密碼過期功能 + + + Active Directory 不支援鎖定使用者。只能解除鎖定使用者 + + + 提供的搜尋範圍無效: {0} + + + 驗證使用者 {0} 期間發生異常。已成功認證使用者,但無法確定使用者的 GUID。 + + + 驗證使用者 {0} 期間發生異常。已成功認證使用者,但無法確定使用者的 SID。 + + + 未提供目錄管理員名稱。 + + + 未提供目錄管理員密碼。 + + + 未提供網域名稱。 + + + 未提供物件類別。 + + + 未提供搜尋容器。 + + + 使用 Identity Manger 資源配接卡樣式查詢「{0}」。應更新為使用新的連接器查詢語法。 + + + 提供的容器無效: {0} + + + shell 程序檔變數前綴 + + + 將增加至所有 shell 程序檔引數名稱的前綴。例如,若將前綴設定為「Connector_」,則稱為「User」的引數會變成「Connector_User」 + + + 使用者的帳號已鎖定 + + + 必須變更使用者密碼 + + + 使用者 {0} 的使用者帳號已過期。 + + + 提供的搜尋上下文無效: {0} + + \ No newline at end of file From a307814704fab34a0e396c0d87bd15065f220d0f Mon Sep 17 00:00:00 2001 From: abadea Date: Thu, 7 May 2009 15:29:34 +0000 Subject: [PATCH 230/342] Issue 481: Support guarded byte arrays --- Common/Security.cs | 277 +++++++++++++++++++-- Framework/Common.cs | 2 + FrameworkInternal/Resources.resx | 3 +- FrameworkInternal/Serializer.cs | 91 +++++-- FrameworkTests/FrameworkTests.csproj | 5 +- FrameworkTests/ObjectSerializationTests.cs | 28 ++- 6 files changed, 368 insertions(+), 38 deletions(-) diff --git a/Common/Security.cs b/Common/Security.cs index d43e1aa6..b141aef5 100644 --- a/Common/Security.cs +++ b/Common/Security.cs @@ -41,6 +41,207 @@ public interface UnmanagedArray : IDisposable } #endregion + /** + * Secure byte array implementation that solves the problems associated with + * keeping confidential data as byte[]. That is, anything + * represented as a byte[] is kept in memory in clear + * text and stays in memory at least until it is garbage collected. + *

+ * The GuardedByteArray class alleviates this problem by storing the bytes in + * memory in an encrypted form. The encryption key will be a randomly-generated + * key. + *

+ * In their serialized form, GuardedByteArray will be encrypted using a known + * default key. This is to provide a minimum level of protection regardless + * of the transport. For communications with the Remote Connector Framework + * it is recommended that deployments enable SSL for true encryption. + *

+ * Applications may also wish to persist GuardedByteArrays. In the case of + * Identity Manager, it should convert GuardedByteArrays to EncryptedData so + * that they can be stored and managed using the Manage Encryption features + * of Identity Manager. Other applications may wish to serialize APIConfiguration + * as a whole. These applications are responsible for encrypting the APIConfiguration + * blob for an additional layer of security (beyond the basic default key encryption + * provided by GuardedByteArray). + */ + public sealed class GuardedByteArray : IDisposable + { + /** + * This method will be called with the clear text of the byte array. + * After the call the clearBytes array will be automatically zeroed + * out, thus keeping the window of potential exposure to a bare-minimum. + * @param clearChars + */ + public delegate void Accessor(UnmanagedArray clearBytes); + + + private SecureString _target; + private String _base64SHA1Hash; + + /** + * Creates an empty secure byte array. + */ + public GuardedByteArray() + { + _target = new SecureString(); + ComputeHash(); + } + + public GuardedByteArray(UnmanagedArray clearBytes) + { + _target = new SecureString(); + AppendBytes(clearBytes); + } + + private GuardedByteArray(SecureString str) + { + _target = str.Copy(); + ComputeHash(); + } + + + /** + * Provides access to the clear-text value of the bytes in a controlled fashion. + * The clear-text bytes will only be available for the duration of the call + * and automatically zeroed out following the call. + * + *

+ * NOTE: Callers are encouraged to use {@link #verifyBase64SHA1Hash(String)} + * where possible if the intended use is merely to verify the contents of + * the string match an expected hash value. + * @param accessor Accessor callback. + * @throws IllegalStateException If the byte array has been disposed + */ + public void Access(Accessor accessor) + { + using (SecureStringToByteArrayAdapter adapter = new SecureStringToByteArrayAdapter(_target)) + { + accessor(adapter); + } + } + + /** + * Appends a single clear-text byte to the secure byte array. + * The in-memory data will be decrypted, the character will be + * appended, and then it will be re-encrypted. + * @param b The byte to append. + * @throws IllegalStateException If the byte array is read-only + * @throws IllegalStateException If the byte array has been disposed + */ + public void AppendByte(byte b) + { + _target.AppendChar((char)b); + ComputeHash(); + } + + private void AppendBytes(UnmanagedArray clearBytes) + { + for (int i = 0; i < clearBytes.Length; i++) + { + _target.AppendChar((char)clearBytes[i]); + } + ComputeHash(); + } + + /** + * Clears the in-memory representation of the byte array. + */ + public void Dispose() + { + _target.Dispose(); + } + + /** + * Returns true iff this byte array has been marked read-only + * @return true iff this byte array has been marked read-only + * @throws IllegalStateException If the byte array has been disposed + */ + public bool IsReadOnly() + { + return _target.IsReadOnly(); + } + + /** + * Mark this byte array as read-only. + * @throws IllegalStateException If the byte array has been disposed + */ + public void MakeReadOnly() + { + _target.MakeReadOnly(); + } + + /** + * Create a copy of the byte array. If this instance is read-only, + * the copy will not be read-only. + * @return A copy of the byte array. + * @throws IllegalStateException If the byte array has been disposed + */ + public GuardedByteArray Copy() + { + SecureString t2 = _target.Copy(); + GuardedByteArray rv = new GuardedByteArray(t2); + return rv; + } + + /** + * Verifies that this base-64 encoded SHA1 hash of this byte array + * matches the given value. + * @param hash The hash to verify against. + * @return True if the hash matches the given parameter. + * @throws IllegalStateException If the byte array has been disposed + */ + public bool VerifyBase64SHA1Hash(String hash) + { + CheckNotDisposed(); + return _base64SHA1Hash.Equals(hash); + } + + public string GetBase64SHA1Hash() + { + CheckNotDisposed(); + return _base64SHA1Hash; + } + + + + private void CheckNotDisposed() + { + //this throws if disposed + _target.IsReadOnly(); + } + + + public override bool Equals(Object o) + { + if (o is GuardedByteArray) + { + GuardedByteArray other = (GuardedByteArray)o; + //not the true contract of equals. however, + //due to the high mathematical improbability of + //two unequal strings having the same secure hash, + //this approach feels good. the alternative, + //decrypting for comparison, is simply too + //performance intensive to be used for equals + return _base64SHA1Hash.Equals(other._base64SHA1Hash); + } + return false; + } + + public override int GetHashCode() + { + return _base64SHA1Hash.GetHashCode(); + } + + private void ComputeHash() + { + Access(array => + { + _base64SHA1Hash = SecurityUtil.ComputeBase64SHA1Hash(array); + }); + } + + } + /** * Secure string implementation that solves the problems associated with * keeping passwords as java.lang.String. That is, anything @@ -300,6 +501,39 @@ protected override void FreeMemory() } #endregion + #region SecureStringToByteArrayAdapter + internal class SecureStringToByteArrayAdapter : AbstractUnmanagedArray + { + private IntPtr _bstrPtr; + public SecureStringToByteArrayAdapter(SecureString secureString) + : base(secureString.Length) + { + Assertions.NullCheck(secureString, "secureString"); + _bstrPtr = Marshal.SecureStringToBSTR(secureString); + } + protected override byte GetValue(int index) + { + unsafe + { + char* charPtr = (char*)_bstrPtr; + return (byte)*(charPtr + index); + } + } + protected override void SetValue(int index, byte b) + { + unsafe + { + char* charPtr = (char*)_bstrPtr; + *(charPtr + index) = (char)b; + } + } + protected override void FreeMemory() + { + Marshal.ZeroFreeBSTR(_bstrPtr); + } + } + #endregion + #region UnmanagedCharArray public class UnmanagedCharArray : AbstractUnmanagedArray { @@ -405,24 +639,33 @@ public static UnmanagedArray BytesToChars(UnmanagedArray bytes) return chars; } - + public unsafe static string ComputeBase64SHA1Hash(UnmanagedArray input) - { - using (UnmanagedArray bytes = SecurityUtil.CharsToBytes(input)) { - byte [] managedBytes = new byte[bytes.Length]; - fixed (byte*dummy=managedBytes) { //pin it - try { - //populate it in pinned block - SecurityUtil.UnmanagedBytesToManagedBytes(bytes,managedBytes); - SHA1 hasher = SHA1.Create(); - byte[] data = hasher.ComputeHash(managedBytes); - return Convert.ToBase64String(data); - } - finally { - //clear it before we leave pinned block - SecurityUtil.Clear(managedBytes); - } - } + { + using (UnmanagedArray bytes = SecurityUtil.CharsToBytes(input)) + { + return ComputeBase64SHA1Hash(bytes); + } + } + + public unsafe static string ComputeBase64SHA1Hash(UnmanagedArray input) + { + byte[] managedBytes = new byte[input.Length]; + fixed (byte* dummy = managedBytes) + { //pin it + try + { + //populate it in pinned block + SecurityUtil.UnmanagedBytesToManagedBytes(input, managedBytes); + SHA1 hasher = SHA1.Create(); + byte[] data = hasher.ComputeHash(managedBytes); + return Convert.ToBase64String(data); + } + finally + { + //clear it before we leave pinned block + SecurityUtil.Clear(managedBytes); + } } } diff --git a/Framework/Common.cs b/Framework/Common.cs index bb5407d7..1a2776b2 100644 --- a/Framework/Common.cs +++ b/Framework/Common.cs @@ -105,6 +105,7 @@ static FrameworkUtil() { typeof(bool?), typeof(Uri), typeof(FileName), + typeof(GuardedByteArray), typeof(GuardedString) ); ATTR_SUPPORTED_TYPES = CollectionUtil.NewReadOnlySet @@ -125,6 +126,7 @@ static FrameworkUtil() { typeof(byte[]), typeof(BigDecimal), typeof(BigInteger), + typeof(GuardedByteArray), typeof(GuardedString) ); diff --git a/FrameworkInternal/Resources.resx b/FrameworkInternal/Resources.resx index 1187f7d7..20e1cf38 100644 --- a/FrameworkInternal/Resources.resx +++ b/FrameworkInternal/Resources.resx @@ -161,7 +161,7 @@ "null | Array | Boolean | boolean | Character | char | Integer | int | Long | long | Float | float | Double | double | String | URI | File | BigDecimal | BigInteger | ByteArray | Class | - Map | List | Set | Locale | GuardedString + Map | List | Set | Locale | GuardedByteArray | GuardedString "> <!ENTITY % xmlObject @@ -241,6 +241,7 @@ OperationOptionInfo | SyncDeltaType | SyncToken | SyncDelta | QualifiedUid country CDATA #IMPLIED variant CDATA #IMPLIED > +<!ELEMENT GuardedByteArray (#PCDATA)> <!ELEMENT GuardedString (#PCDATA)> <!--=======================================================--> diff --git a/FrameworkInternal/Serializer.cs b/FrameworkInternal/Serializer.cs index 0357b5a7..b263ca23 100644 --- a/FrameworkInternal/Serializer.cs +++ b/FrameworkInternal/Serializer.cs @@ -550,6 +550,7 @@ static Primitives() { HANDLERS.Add(new ListHandler()); HANDLERS.Add(new SetHandler()); HANDLERS.Add(new LocaleHandler()); + HANDLERS.Add(new GuardedByteArrayHandler()); HANDLERS.Add(new GuardedStringHandler()); } private class BooleanHandler : AbstractObjectSerializationHandler { @@ -989,22 +990,20 @@ public override void Serialize(Object obj, ObjectEncoder encoder) { } } - private class GuardedStringHandler : AbstractObjectSerializationHandler { - public GuardedStringHandler() - : base(typeof(GuardedString),"GuardedString") { + private class GuardedByteArrayHandler : AbstractObjectSerializationHandler { + public GuardedByteArrayHandler() + : base(typeof(GuardedByteArray),"GuardedByteArray") { } public override Object Deserialize(ObjectDecoder decoder) { byte [] encryptedBytes = null; UnmanagedArray clearBytes = null; - UnmanagedArray clearChars = null; try { encryptedBytes = decoder.ReadByteArrayContents(); clearBytes = EncryptorFactory.GetInstance().GetDefaultEncryptor().Decrypt(encryptedBytes); - clearChars = SecurityUtil.BytesToChars(clearBytes); - GuardedString rv = new GuardedString(); - for ( int i = 0; i < clearChars.Length; i++ ) { - rv.AppendChar(clearChars[i]); + GuardedByteArray rv = new GuardedByteArray(); + for ( int i = 0; i < clearBytes.Length; i++ ) { + rv.AppendByte(clearBytes[i]); } return rv; } @@ -1012,34 +1011,90 @@ public override Object Deserialize(ObjectDecoder decoder) { if ( clearBytes != null ) { clearBytes.Dispose(); } - if ( clearChars != null ) { - clearChars.Dispose(); - } SecurityUtil.Clear(encryptedBytes); } } public override void Serialize(Object obj, ObjectEncoder encoder) { - GuardedString str = (GuardedString)obj; + GuardedByteArray str = (GuardedByteArray)obj; str.Access( - clearChars=> + clearBytes=> { - UnmanagedArray clearBytes = null; byte [] encryptedBytes = null; try { - clearBytes = SecurityUtil.CharsToBytes(clearChars); encryptedBytes = EncryptorFactory.GetInstance().GetDefaultEncryptor().Encrypt(clearBytes); encoder.WriteByteArrayContents(encryptedBytes); } finally { - if ( clearBytes != null ) { - clearBytes.Dispose(); - } SecurityUtil.Clear(encryptedBytes); } }); } } + + private class GuardedStringHandler : AbstractObjectSerializationHandler + { + public GuardedStringHandler() + : base(typeof(GuardedString), "GuardedString") + { + + } + public override Object Deserialize(ObjectDecoder decoder) + { + byte[] encryptedBytes = null; + UnmanagedArray clearBytes = null; + UnmanagedArray clearChars = null; + try + { + encryptedBytes = decoder.ReadByteArrayContents(); + clearBytes = EncryptorFactory.GetInstance().GetDefaultEncryptor().Decrypt(encryptedBytes); + clearChars = SecurityUtil.BytesToChars(clearBytes); + GuardedString rv = new GuardedString(); + for (int i = 0; i < clearChars.Length; i++) + { + rv.AppendChar(clearChars[i]); + } + return rv; + } + finally + { + if (clearBytes != null) + { + clearBytes.Dispose(); + } + if (clearChars != null) + { + clearChars.Dispose(); + } + SecurityUtil.Clear(encryptedBytes); + } + } + + public override void Serialize(Object obj, ObjectEncoder encoder) + { + GuardedString str = (GuardedString)obj; + str.Access( + clearChars => + { + UnmanagedArray clearBytes = null; + byte[] encryptedBytes = null; + try + { + clearBytes = SecurityUtil.CharsToBytes(clearChars); + encryptedBytes = EncryptorFactory.GetInstance().GetDefaultEncryptor().Encrypt(clearBytes); + encoder.WriteByteArrayContents(encryptedBytes); + } + finally + { + if (clearBytes != null) + { + clearBytes.Dispose(); + } + SecurityUtil.Clear(encryptedBytes); + } + }); + } + } } #endregion diff --git a/FrameworkTests/FrameworkTests.csproj b/FrameworkTests/FrameworkTests.csproj index 125b3474..58d033e3 100644 --- a/FrameworkTests/FrameworkTests.csproj +++ b/FrameworkTests/FrameworkTests.csproj @@ -67,6 +67,9 @@ + + Code + @@ -156,4 +159,4 @@ - + diff --git a/FrameworkTests/ObjectSerializationTests.cs b/FrameworkTests/ObjectSerializationTests.cs index 2e0b8ea3..902f208d 100644 --- a/FrameworkTests/ObjectSerializationTests.cs +++ b/FrameworkTests/ObjectSerializationTests.cs @@ -1022,6 +1022,16 @@ public void TestNull() { Assert.IsNull(v2); } + [Test] + public void TestGuardedByteArray() { + GuardedByteArray v1 = new GuardedByteArray(); + v1.AppendByte(0x00); + v1.AppendByte(0x01); + v1.AppendByte(0x02); + GuardedByteArray v2 = (GuardedByteArray)CloneObject(v1); + Assert.AreEqual(new byte[] { 0x00, 0x01, 0x02 },DecryptToByteArray(v2)); + } + [Test] public void TestGuardedString() { GuardedString v1 = new GuardedString(); @@ -1062,7 +1072,23 @@ private String DecryptToString(GuardedString str) { return buf.ToString(); } - protected virtual Object CloneObject(Object o) { + private byte[] DecryptToByteArray(GuardedByteArray bytes) + { + byte[] result = null; + bytes.Access( + array => + { + result = new byte[array.Length]; + for (int i = 0; i < array.Length; i++) + { + result[i] = array[i]; + } + }); + return result; + } + + protected virtual Object CloneObject(Object o) + { return SerializerUtil.CloneObject(o); } } From ff62ad3ad6c20ca2e5141e5c529c6205ed290081 Mon Sep 17 00:00:00 2001 From: abadea Date: Thu, 7 May 2009 15:48:35 +0000 Subject: [PATCH 231/342] Adding forgotten file --- GuardedByteArrayTests.cs | 97 ++++++++++++++++++++++++++++++++++++++++ 1 file changed, 97 insertions(+) create mode 100755 GuardedByteArrayTests.cs diff --git a/GuardedByteArrayTests.cs b/GuardedByteArrayTests.cs new file mode 100755 index 00000000..2863dcb8 --- /dev/null +++ b/GuardedByteArrayTests.cs @@ -0,0 +1,97 @@ +/* + * ==================== + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER. + * + * Copyright 2008-2009 Sun Microsystems, Inc. All rights reserved. + * + * The contents of this file are subject to the terms of the Common Development + * and Distribution License("CDDL") (the "License"). You may not use this file + * except in compliance with the License. + * + * You can obtain a copy of the License at + * http://IdentityConnectors.dev.java.net/legal/license.txt + * See the License for the specific language governing permissions and limitations + * under the License. + * + * When distributing the Covered Code, include this CDDL Header Notice in each file + * and include the License file at identityconnectors/legal/license.txt. + * If applicable, add the following below this CDDL Header, with the fields + * enclosed by brackets [] replaced by your own identifying information: + * "Portions Copyrighted [year] [name of copyright owner]" + * ==================== + */ +using System; +using System.Security; +using System.Runtime.InteropServices; +using System.Text; +using Org.IdentityConnectors.Common.Security; +using Org.IdentityConnectors.Framework.Common.Serializer; +using NUnit.Framework; +using NUnit.Framework.SyntaxHelpers; + +namespace FrameworkTests +{ + [TestFixture] + public class GuardedByteArrayTests + { + [Test] + public void TestBasics() + { + GuardedByteArray ss = new GuardedByteArray(); + ss.AppendByte(0x00); + ss.AppendByte(0x01); + ss.AppendByte(0x02); + byte[] decrypted = DecryptToByteArray(ss); + Assert.AreEqual(new byte[] { 0x00, 0x01, 0x02} ,decrypted); + String hash = ss.GetBase64SHA1Hash(); + Assert.IsTrue(ss.VerifyBase64SHA1Hash(hash)); + ss.AppendByte(0x03); + Assert.IsFalse(ss.VerifyBase64SHA1Hash(hash)); + } + [Test] + public void TestRange() { + + for ( byte i = 0; i < 0xFF; i++) { + byte expected = i; + GuardedByteArray gba = new GuardedByteArray(); + gba = (GuardedByteArray)SerializerUtil.CloneObject(gba); + gba.AppendByte(i); + gba.Access(clearChars => { + int v = (byte)clearChars[0]; + Assert.AreEqual(expected, v); + }); + + } + } + + [Test] + public void TestEquals() { + GuardedByteArray arr1 = new GuardedByteArray(); + GuardedByteArray arr2 = new GuardedByteArray(); + Assert.AreEqual(arr1, arr2); + arr2.AppendByte(0x02); + Assert.AreNotEqual(arr1,arr2); + arr1.AppendByte(0x02); + Assert.AreEqual(arr1, arr2); + } + + + /** + * Highly insecure method! Do not do this in production + * code. This is only for test purposes + */ + private byte[] DecryptToByteArray(GuardedByteArray str) { + byte[] result = null; + str.Access( + array=> + { + result = new byte[array.Length]; + for ( int i = 0 ; i < array.Length; i++) + { + result[i] = array[i]; + } + }); + return result; + } + } +} From 7a5914886929c784160a78d4ab8e4720e761fb34 Mon Sep 17 00:00:00 2001 From: abadea Date: Thu, 7 May 2009 15:50:29 +0000 Subject: [PATCH 232/342] Adding forgotten file, now to the right directory --- .../GuardedByteArrayTests.cs | 0 1 file changed, 0 insertions(+), 0 deletions(-) rename GuardedByteArrayTests.cs => FrameworkTests/GuardedByteArrayTests.cs (100%) diff --git a/GuardedByteArrayTests.cs b/FrameworkTests/GuardedByteArrayTests.cs similarity index 100% rename from GuardedByteArrayTests.cs rename to FrameworkTests/GuardedByteArrayTests.cs From 31a4230351190190b68e1dd54343e97e0ffb1cc3 Mon Sep 17 00:00:00 2001 From: abadea Date: Thu, 14 May 2009 15:27:21 +0000 Subject: [PATCH 233/342] Issue 495: Introduce SyncDelta.previousUid --- Framework/CommonObjects.cs | 53 ++++++++++++++++++++-- FrameworkInternal/Resources.resx | 5 +- FrameworkInternal/Serializer.cs | 2 + FrameworkTests/ObjectSerializationTests.cs | 2 + 4 files changed, 58 insertions(+), 4 deletions(-) diff --git a/Framework/CommonObjects.cs b/Framework/CommonObjects.cs index ba99241a..58cabf50 100644 --- a/Framework/CommonObjects.cs +++ b/Framework/CommonObjects.cs @@ -3404,6 +3404,7 @@ public ScriptContext Build() { public sealed class SyncDelta { private readonly SyncToken _token; private readonly SyncDeltaType _deltaType; + private readonly Uid _previousUid; private readonly Uid _uid; private readonly ConnectorObject _object; @@ -3419,12 +3420,17 @@ public sealed class SyncDelta { * The object that has changed. May be null for delete. */ internal SyncDelta(SyncToken token, SyncDeltaType deltaType, - Uid uid, + Uid previousUid, Uid uid, ConnectorObject obj) { Assertions.NullCheck(token, "token"); Assertions.NullCheck(deltaType, "deltaType"); Assertions.NullCheck(uid, "uid"); + //do not allow previous Uid for anything else than create or update + if ( previousUid != null && deltaType != SyncDeltaType.CREATE_OR_UPDATE) { + throw new ArgumentException("The previous Uid can only be specified for create or update."); + } + //only allow null object for delete if ( obj == null && deltaType != SyncDeltaType.DELETE) { @@ -3441,10 +3447,26 @@ internal SyncDelta(SyncToken token, SyncDeltaType deltaType, _token = token; _deltaType = deltaType; + _previousUid = previousUid; _uid = uid; _object = obj; } + + /** + * If the change described by this SyncDelta modified the + * object's Uid, this method returns the Uid before the change. Not + * all resources can determine the previous Uid, so this method can + * return null. + * @return the previous Uid or null if it could not be determined + * or the change did not modify the Uid. + */ + public Uid PreviousUid + { + get { + return _previousUid; + } + } /** * Returns the Uid of the object that changed. @@ -3496,6 +3518,7 @@ public override String ToString() { IDictionary values = new Dictionary(); values["Token"] = _token; values["DeltaType"] = _deltaType; + values["PreviousUid"] = _previousUid; values["Uid"] = _uid; values["Object"] = _object; return values.ToString(); @@ -3514,6 +3537,14 @@ public override bool Equals(Object o) { if (!_deltaType.Equals(other._deltaType)) { return false; } + if (_previousUid == null) { + if ( other._previousUid != null ) { + return false; + } + } + else if (!_previousUid.Equals(other._previousUid)) { + return false; + } if (!_uid.Equals(other._uid)) { return false; } @@ -3539,6 +3570,7 @@ public override bool Equals(Object o) { public sealed class SyncDeltaBuilder { private SyncToken _token; private SyncDeltaType _deltaType; + private Uid _previousUid; private Uid _uid; private ConnectorObject _object; @@ -3557,8 +3589,9 @@ public SyncDeltaBuilder() { public SyncDeltaBuilder(SyncDelta delta) { _token = delta.Token; _deltaType = delta.DeltaType; - _object = delta.Object; + _previousUid = delta.PreviousUid; _uid = delta.Uid; + _object = delta.Object; } /** @@ -3588,6 +3621,20 @@ public SyncDeltaType DeltaType { _deltaType = value; } } + + /** + * Returns the Uid before the change. + * + * @return the Uid before the change. + */ + public Uid PreviousUid { + get { + return _previousUid; + } + set { + _previousUid = value; + } + } /** * Returns the Uid of the object that changed. @@ -3634,7 +3681,7 @@ public ConnectorObject Object { * */ public SyncDelta Build() { - return new SyncDelta(_token, _deltaType, _uid, _object); + return new SyncDelta(_token, _deltaType, _previousUid, _uid, _object); } } #endregion diff --git a/FrameworkInternal/Resources.resx b/FrameworkInternal/Resources.resx index 20e1cf38..9650d361 100644 --- a/FrameworkInternal/Resources.resx +++ b/FrameworkInternal/Resources.resx @@ -432,7 +432,10 @@ OperationOptionInfo | SyncDeltaType | SyncToken | SyncDelta | QualifiedUid > <!ELEMENT SyncToken (value)> -<!ELEMENT SyncDelta (SyncDeltaType,SyncToken,Uid,ConnectorObject?)> +<!ELEMENT SyncDelta (SyncDeltaType,SyncToken,PreviousUid,Uid,ConnectorObject?)> + +<!ELEMENT PreviousUid (#PCDATA)> + <!ELEMENT QualifiedUid (ObjectClass,Uid)> diff --git a/FrameworkInternal/Serializer.cs b/FrameworkInternal/Serializer.cs index b263ca23..9954fc95 100644 --- a/FrameworkInternal/Serializer.cs +++ b/FrameworkInternal/Serializer.cs @@ -2033,6 +2033,7 @@ public override Object Deserialize(ObjectDecoder decoder) { SyncDeltaBuilder builder = new SyncDeltaBuilder(); builder.DeltaType=((SyncDeltaType)decoder.ReadObjectField("SyncDeltaType",typeof(SyncDeltaType),null)); builder.Token=((SyncToken)decoder.ReadObjectField("SyncToken",typeof(SyncToken),null)); + builder.PreviousUid=((Uid)decoder.ReadObjectField("PreviousUid",typeof(Uid),null)); builder.Uid=((Uid)decoder.ReadObjectField("Uid",typeof(Uid),null)); builder.Object=((ConnectorObject)decoder.ReadObjectField("ConnectorObject",typeof(ConnectorObject),null)); return builder.Build(); @@ -2042,6 +2043,7 @@ public override void Serialize(Object obj, ObjectEncoder encoder) { SyncDelta val = (SyncDelta)obj; encoder.WriteObjectField("SyncDeltaType", val.DeltaType,true); encoder.WriteObjectField("SyncToken", val.Token,true); + encoder.WriteObjectField("PreviousUid", val.PreviousUid,true); encoder.WriteObjectField("Uid", val.Uid,true); encoder.WriteObjectField("ConnectorObject", val.Object, true); } diff --git a/FrameworkTests/ObjectSerializationTests.cs b/FrameworkTests/ObjectSerializationTests.cs index 902f208d..db950436 100644 --- a/FrameworkTests/ObjectSerializationTests.cs +++ b/FrameworkTests/ObjectSerializationTests.cs @@ -1003,12 +1003,14 @@ public void TestSyncDelta() { bld.SetUid("foo"); bld.SetName("name"); SyncDeltaBuilder builder = new SyncDeltaBuilder(); + builder.PreviousUid=(new Uid("mypreviousuid")); builder.Uid=(new Uid("myuid")); builder.DeltaType=(SyncDeltaType.CREATE_OR_UPDATE); builder.Token=(new SyncToken("mytoken")); builder.Object=(bld.Build()); SyncDelta v1 = builder.Build(); SyncDelta v2 = (SyncDelta)CloneObject(v1); + Assert.AreEqual(new Uid("mypreviousuid"),v2.PreviousUid); Assert.AreEqual(new Uid("foo"),v2.Uid); Assert.AreEqual(new SyncToken("mytoken"),v2.Token); Assert.AreEqual(SyncDeltaType.CREATE_OR_UPDATE,v2.DeltaType); From 0f700835c8b4d228dbc0bd6be006d933c3a8c527 Mon Sep 17 00:00:00 2001 From: abadea Date: Tue, 7 Jul 2009 16:00:56 +0000 Subject: [PATCH 234/342] Issue 502: Clarified that update operations must throw UnknownUidException when the uid does not exist --- Framework/ApiOperations.cs | 6 ++++++ Framework/SpiOperations.cs | 6 ++++++ 2 files changed, 12 insertions(+) diff --git a/Framework/ApiOperations.cs b/Framework/ApiOperations.cs index 83f50a10..c4a4a339 100644 --- a/Framework/ApiOperations.cs +++ b/Framework/ApiOperations.cs @@ -290,6 +290,8 @@ public interface UpdateApiOp : APIOperation { * May be null. * @return the {@link Uid} of the updated object in case the update changes * the formation of the unique identifier. + * @throws UnknownUidException + * iff the {@link Uid} does not exist on the resource. */ Uid Update(ObjectClass objclass, Uid uid, @@ -330,6 +332,8 @@ Uid Update(ObjectClass objclass, * May be null. * @return the {@link Uid} of the updated object in case the update changes * the formation of the unique identifier. + * @throws UnknownUidException + * iff the {@link Uid} does not exist on the resource. */ Uid AddAttributeValues(ObjectClass objclass, Uid uid, @@ -371,6 +375,8 @@ Uid AddAttributeValues(ObjectClass objclass, * May be null. * @return the {@link Uid} of the updated object in case the update changes * the formation of the unique identifier. + * @throws UnknownUidException + * iff the {@link Uid} does not exist on the resource. */ Uid RemoveAttributeValues(ObjectClass objclass, Uid uid, diff --git a/Framework/SpiOperations.cs b/Framework/SpiOperations.cs index f8520012..12f33c22 100644 --- a/Framework/SpiOperations.cs +++ b/Framework/SpiOperations.cs @@ -306,6 +306,8 @@ public interface UpdateOp : SPIOperation { * Will never be null. * @return the {@link Uid} of the updated object in case the update changes * the formation of the unique identifier. + * @throws UnknownUidException + * iff the {@link Uid} does not exist on the resource. */ Uid Update(ObjectClass objclass, Uid uid, @@ -349,6 +351,8 @@ public interface UpdateAttributeValuesOp : UpdateOp { * Will never be null. * @return the {@link Uid} of the updated object in case the update changes * the formation of the unique identifier. + * @throws UnknownUidException + * iff the {@link Uid} does not exist on the resource. */ Uid AddAttributeValues(ObjectClass objclass, Uid uid, @@ -383,6 +387,8 @@ Uid AddAttributeValues(ObjectClass objclass, * Will never be null.. * @return the {@link Uid} of the updated object in case the update changes * the formation of the unique identifier. + * @throws UnknownUidException + * iff the {@link Uid} does not exist on the resource. */ Uid RemoveAttributeValues(ObjectClass objclass, Uid uid, From f0f17579328513ec60b16f8230ffe731d8f63000 Mon Sep 17 00:00:00 2001 From: abadea Date: Tue, 28 Jul 2009 13:05:11 +0000 Subject: [PATCH 235/342] Issue 523: loadConfiguration from the GroovyDataProvider should support primitive types --- FrameworkInternal/ApiLocal.cs | 36 +++++++++++++++++----------- FrameworkInternal/Test.cs | 22 +++++++++++++++++ FrameworkTests/TestHelperTests.cs | 39 +++++++++++++++++++++++++++++++ TestCommon/Test.cs | 13 +++++++++++ TestCommon/TestHelpersSpi.cs | 9 +++++-- 5 files changed, 104 insertions(+), 15 deletions(-) diff --git a/FrameworkInternal/ApiLocal.cs b/FrameworkInternal/ApiLocal.cs index b01547c9..1ada4e51 100644 --- a/FrameworkInternal/ApiLocal.cs +++ b/FrameworkInternal/ApiLocal.cs @@ -240,20 +240,31 @@ private static ICollection> TranslateOperations(SafeType< public static Configuration CreateBean(ConfigurationPropertiesImpl properties, - SafeType config) { - Configuration rv = config.CreateInstance(); + SafeType configType) { + Configuration rv = configType.CreateInstance(); rv.ConnectorMessages=properties.Parent.ConnectorInfo.Messages; - IDictionary descriptors = - GetFilteredProperties(config); - foreach (ConfigurationPropertyImpl property in properties.Properties) { + MergeIntoBean(properties, rv); + return rv; + } + + public static void + MergeIntoBean(ConfigurationPropertiesImpl properties, + Configuration config) { + SafeType configType = + SafeType.Get(config); + IDictionary descriptors = + GetFilteredProperties(configType); + foreach (ConfigurationPropertyImpl property in properties.Properties) + { String name = property.Name; - PropertyInfo desc = - CollectionUtil.GetValue(descriptors,name,null); - if ( desc == null ) { - String FMT = + PropertyInfo desc = + CollectionUtil.GetValue(descriptors, name, null); + if (desc == null) + { + String FMT = "Class ''{0}'' does not have a property ''{1}''."; - String MSG = String.Format(FMT, - config.RawType.Name, + String MSG = String.Format(FMT, + configType.RawType.Name, name); throw new ArgumentException(MSG); } @@ -262,9 +273,8 @@ public static Configuration //are mutable. make sure the config object //has its own copy val = SerializerUtil.CloneObject(val); - desc.SetValue(rv,val,null); + desc.SetValue(config, val, null); } - return rv; } private static IDictionary diff --git a/FrameworkInternal/Test.cs b/FrameworkInternal/Test.cs index 265f9755..7427b1f7 100644 --- a/FrameworkInternal/Test.cs +++ b/FrameworkInternal/Test.cs @@ -21,6 +21,8 @@ * ==================== */ using System; +using System.Collections.Generic; +using System.Diagnostics; using System.Text; using Org.IdentityConnectors.Common; @@ -67,6 +69,26 @@ public APIConfiguration CreateTestConfiguration(SafeType clazz, return rv; } + public void FillConfiguration(Configuration config, IDictionary configData) { + IDictionary configDataCopy = new Dictionary(configData); + ConfigurationPropertiesImpl configProps = + CSharpClassProperties.CreateConfigurationProperties(config); + foreach (string propName in configProps.PropertyNames) { + object value; + if (configDataCopy.TryGetValue(propName, out value)) { + // Remove the entry from the config map, so that at the end + // the map only contains entries that were not assigned to a config property. + configDataCopy.Remove(propName); + configProps.SetPropertyValue(propName, value); + } + } + // The config map now contains entries that were not assigned to a config property. + foreach (string propName in configDataCopy.Keys) { + Trace.TraceWarning("Configuration property {0} does not exist!", propName); + } + CSharpClassProperties.MergeIntoBean(configProps, config); + } + private static bool IsConnectorPoolingSupported(SafeType clazz) { return ReflectionUtil.IsParentTypeOf(typeof(PoolableConnector),clazz.RawType); } diff --git a/FrameworkTests/TestHelperTests.cs b/FrameworkTests/TestHelperTests.cs index 00025926..f83037bd 100644 --- a/FrameworkTests/TestHelperTests.cs +++ b/FrameworkTests/TestHelperTests.cs @@ -28,6 +28,8 @@ using NUnit.Framework; using NUnit.Framework.SyntaxHelpers; +using Org.IdentityConnectors.Framework.Common.Objects; +using Org.IdentityConnectors.Framework.Spi; using Org.IdentityConnectors.Test.Common; namespace FrameworkTests @@ -68,5 +70,42 @@ public void testLoadProperties() public void testGetProperties() { Assert.IsTrue(TestHelpers.GetProperty("Help", null).Equals("Me")); } + + [Test] + public void testFillConfiguration() { + TestConfiguration testConfig = new TestConfiguration(); + // There is no "Foo" property in the config bean. We want to ensure + // that TestHelpers.FillConfiguration() does not fail for unknown properties. + IDictionary configData = new Dictionary(); + configData["Host"] = "example.com"; + configData["Port"] = 1234; + configData["Foo"] = "bar"; + TestHelpers.FillConfiguration(testConfig, configData); + + Assert.AreEqual("example.com", testConfig.Host); + Assert.AreEqual(1234, testConfig.Port); + } + + public class TestConfiguration : Configuration { + + public ConnectorMessages ConnectorMessages { + get { + return null; + } + set { + Assert.Fail("Should not set ConnectorMessages"); + } + } + + public void Validate() { + Assert.Fail("Should not call Validate()"); + } + + public String Host { get; set; } + + public int Port { get; set; } + + public String Unused { get; set; } + } } } diff --git a/TestCommon/Test.cs b/TestCommon/Test.cs index 4a9b3088..66483a94 100644 --- a/TestCommon/Test.cs +++ b/TestCommon/Test.cs @@ -73,6 +73,19 @@ public static APIConfiguration CreateTestConfiguration(SafeType clazz Configuration config) { return GetSpi().CreateTestConfiguration(clazz, config); } + + /** + * Fills a configuration bean with data from the given map. The map + * keys are configuration property names and the values are + * configuration property values. + * + * @param config the configuration bean. + * @param configData the map with configuration data. + */ + public static void FillConfiguration(Configuration config, + IDictionary configData) { + GetSpi().FillConfiguration(config, configData); + } /** * Creates an dummy message catalog ideal for unit testing. diff --git a/TestCommon/TestHelpersSpi.cs b/TestCommon/TestHelpersSpi.cs index 4b000af1..692c300f 100644 --- a/TestCommon/TestHelpersSpi.cs +++ b/TestCommon/TestHelpersSpi.cs @@ -20,6 +20,8 @@ * "Portions Copyrighted [year] [name of copyright owner]" * ==================== */ +using System.Collections.Generic; + using Org.IdentityConnectors.Common; using Org.IdentityConnectors.Framework.Api; using Org.IdentityConnectors.Framework.Common.Objects; @@ -37,7 +39,10 @@ public interface TestHelpersSpi { APIConfiguration CreateTestConfiguration(SafeType clazz, Configuration config); - + + void FillConfiguration(Configuration config, + IDictionary configData); + void Search(SearchOp search, ObjectClass oclass, Filter filter, @@ -46,4 +51,4 @@ void Search(SearchOp search, ConnectorMessages CreateDummyMessages(); } -} +} From 7ae7c8055ade784cf78ea9732340eaee17cdf5fa Mon Sep 17 00:00:00 2001 From: abadea Date: Tue, 28 Jul 2009 14:25:20 +0000 Subject: [PATCH 236/342] Issue 528: Make test cases in ConnectorInfoManagerTests independent of each other - .NET side --- FrameworkTests/ConnectorInfoManagerTests.cs | 22 ++++++--------------- 1 file changed, 6 insertions(+), 16 deletions(-) diff --git a/FrameworkTests/ConnectorInfoManagerTests.cs b/FrameworkTests/ConnectorInfoManagerTests.cs index 97d0fd54..b17700ce 100644 --- a/FrameworkTests/ConnectorInfoManagerTests.cs +++ b/FrameworkTests/ConnectorInfoManagerTests.cs @@ -60,6 +60,11 @@ private static ConnectorInfo FindConnectorInfo } return null; } + + [TearDown] + public void TearDown() { + ShutdownConnnectorInfoManager(); + } [Test] public void TestClassLoading() { @@ -86,8 +91,6 @@ public void TestClassLoading() { ICollection attrs = new HashSet(); Assert.AreEqual("1.0", facade1.Create(ObjectClass.ACCOUNT,attrs,null).GetUidValue()); Assert.AreEqual("2.0", facade2.Create(ObjectClass.ACCOUNT,attrs,null).GetUidValue()); - - ShutdownConnnectorInfoManager(); } [Test] @@ -136,7 +139,6 @@ public void TestAPIConfiguration() ConnectorFacade facade = facf.NewInstance(api); // call the various create/update/delete commands.. facade.Schema(); - ShutdownConnnectorInfoManager(); } [Test] @@ -174,11 +176,8 @@ public void TestValidate() { catch (ConnectorException e) { Assert.AreEqual("validation failed es", e.Message); } - ShutdownConnnectorInfoManager(); } - - /** * Main purpose of this is to test searching with * many results and that we can properly handle @@ -240,8 +239,6 @@ public void TestSearchWithManyResults() { Assert.AreEqual(i.ToString(), obj.Uid.GetUidValue()); } - - ShutdownConnnectorInfoManager(); } /** * Main purpose of this is to test sync with @@ -306,8 +303,6 @@ public void TestSyncWithManyResults() { Assert.AreEqual(i.ToString(), obj.Uid.GetUidValue()); } - - ShutdownConnnectorInfoManager(); } [Test] @@ -358,7 +353,6 @@ public void TestConnectionPooling() { Assert.AreEqual("5", facade1.Create(ObjectClass.ACCOUNT,attrs,options).GetUidValue()); Assert.AreEqual("5", facade1.Create(ObjectClass.ACCOUNT,attrs,options).GetUidValue()); Assert.AreEqual("5", facade1.Create(ObjectClass.ACCOUNT,attrs,options).GetUidValue()); - ShutdownConnnectorInfoManager(); } [Test] @@ -437,7 +431,6 @@ public void TestScripting() ConnectorAttribute attr = (ConnectorAttribute) facade.RunScriptOnConnector(builder.Build(), null); Assert.AreEqual("myattr", attr.Name); } - ShutdownConnnectorInfoManager(); } protected virtual ConnectorInfoManager GetConnectorInfoManager() { @@ -447,7 +440,7 @@ protected virtual ConnectorInfoManager GetConnectorInfoManager() { } protected virtual void ShutdownConnnectorInfoManager() { - + ConnectorFacadeFactory.GetInstance().Dispose(); } } @@ -459,8 +452,6 @@ public class RemoteConnectorInfoManagerClearTests : ConnectorInfoManagerTests { protected override ConnectorInfoManager GetConnectorInfoManager() { TestUtil.InitializeLogging(); - ShutdownConnnectorInfoManager(); - GuardedString str = new GuardedString(); str.AppendChar('c'); str.AppendChar('h'); @@ -526,7 +517,6 @@ public class RemoteConnectorInfoManagerSSLTests : ConnectorInfoManagerTests { protected override ConnectorInfoManager GetConnectorInfoManager() { TestUtil.InitializeLogging(); - ShutdownConnnectorInfoManager(); GuardedString str = new GuardedString(); str.AppendChar('c'); str.AppendChar('h'); From 90851362ffdbbdf5d1ac281ed4fe679052b736f5 Mon Sep 17 00:00:00 2001 From: abadea Date: Tue, 4 Aug 2009 14:26:17 +0000 Subject: [PATCH 237/342] Issue 518: Javadoc for AttributeUtil.getStringValue is incomplete --- Framework/CommonObjects.cs | 5 ++-- FrameworkTests/ConnectorAttributeUtilTests.cs | 24 +++++++++++++++++++ 2 files changed, 27 insertions(+), 2 deletions(-) diff --git a/Framework/CommonObjects.cs b/Framework/CommonObjects.cs index 58cabf50..af05247c 100644 --- a/Framework/CommonObjects.cs +++ b/Framework/CommonObjects.cs @@ -178,12 +178,13 @@ public static GuardedString GetGuardedStringValue(ConnectorAttribute attr) { } /** - * Get the single value from the ConnectorAttribute. + * Get the single value from the ConnectorAttribute. Return + * null if the attribute's list of values is null or empty. */ public static object GetSingleValue(ConnectorAttribute attr) { Object ret = null; IList val = attr.Value; - if (val != null) { + if (val != null && val.Count > 0) { // make sure this only called for single value.. if (val.Count > 1) { const string MSG = "The method is only for single value attributes."; diff --git a/FrameworkTests/ConnectorAttributeUtilTests.cs b/FrameworkTests/ConnectorAttributeUtilTests.cs index 882ce4ee..bc971b33 100644 --- a/FrameworkTests/ConnectorAttributeUtilTests.cs +++ b/FrameworkTests/ConnectorAttributeUtilTests.cs @@ -20,6 +20,9 @@ * "Portions Copyrighted [year] [name of copyright owner]" * ==================== */ +using System; +using System.Collections.Generic; + using NUnit.Framework; using Org.IdentityConnectors.Framework.Common.Objects; @@ -34,5 +37,26 @@ public void TestNamesEqual() { Assert.IsTrue(ConnectorAttributeUtil.NamesEqual("givenName", "givenname")); } + + [Test] + [ExpectedException(typeof(ArgumentException))] + public void TestGetSingleValue() + { + object TEST_VALUE = 1L; + ConnectorAttribute attr = ConnectorAttributeBuilder.Build("long", TEST_VALUE); + object value = ConnectorAttributeUtil.GetSingleValue(attr); + Assert.AreEqual(TEST_VALUE, value); + + // test null + attr = ConnectorAttributeBuilder.Build("long"); + value = ConnectorAttributeUtil.GetSingleValue(attr); + Assert.IsNull(value); + // test empty + attr = ConnectorAttributeBuilder.Build("long", new List()); + value = ConnectorAttributeUtil.GetSingleValue(attr); + Assert.IsNull(value); + // test illegal argument exception + ConnectorAttributeUtil.GetSingleValue(ConnectorAttributeBuilder.Build("bob", 1, 2, 3)); + } } } From fc5974cb404cf11de2bb37b7b46f983ab9b84011 Mon Sep 17 00:00:00 2001 From: abadea Date: Thu, 6 Aug 2009 15:23:51 +0000 Subject: [PATCH 238/342] Issue 535: Typo in .Net side's UpdateImpl --- FrameworkInternal/ApiLocalOperations.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/FrameworkInternal/ApiLocalOperations.cs b/FrameworkInternal/ApiLocalOperations.cs index 42328e76..4e1372e8 100644 --- a/FrameworkInternal/ApiLocalOperations.cs +++ b/FrameworkInternal/ApiLocalOperations.cs @@ -1275,7 +1275,7 @@ private ICollection FetchAndMerge(ObjectClass objclass, Uid OperationOptions options) { // check that this connector supports Search.. - if (ReflectionUtil.FindInHierarchyOf(typeof(SearchOp<>),GetConnector().GetType()) != null) { + if (ReflectionUtil.FindInHierarchyOf(typeof(SearchOp<>),GetConnector().GetType()) == null) { String MSG = "Connector must support search"; throw new InvalidOperationException(MSG); } From 97f8fc536c6e22c2e57c997e0e541fbfc0d2938f Mon Sep 17 00:00:00 2001 From: tknappek Date: Thu, 20 Aug 2009 09:56:19 +0000 Subject: [PATCH 239/342] Issue #546 fix: AD replication delay can cause exchange mailbox creates to fail --- .../ActiveDirectoryUtils.cs | 18 ++++++++++++- ExchangeConnector/ExchangeConnector.cs | 26 ++++++++++++------- ExchangeConnector/ExchangeUtility.cs | 15 ++++++++--- ExchangeConnector/PSExchangeConnector.cs | 4 +-- 4 files changed, 47 insertions(+), 16 deletions(-) diff --git a/ActiveDirectoryConnector/ActiveDirectoryUtils.cs b/ActiveDirectoryConnector/ActiveDirectoryUtils.cs index 5396ec13..76eee0f1 100644 --- a/ActiveDirectoryConnector/ActiveDirectoryUtils.cs +++ b/ActiveDirectoryConnector/ActiveDirectoryUtils.cs @@ -684,10 +684,26 @@ internal static DomainController GetDomainController(ActiveDirectoryConfiguratio configuration.DirectoryAdminPassword); controller = DomainController.GetDomainController(context); } - + return controller; } + public static string GetDomainControllerName(ActiveDirectoryConfiguration configuration) + { + string serverName = configuration.LDAPHostName; + if (string.IsNullOrEmpty(serverName)) + { + // serverless + using (DirectoryEntry rootDe = new DirectoryEntry("LDAP://RootDSE", + configuration.DirectoryAdminName, configuration.DirectoryAdminPassword)) + { + serverName = rootDe.Properties["dnsHostName"].Value as string; + } + } + + return serverName; + } + internal ActiveDirectorySchema GetADSchema() { String serverName = _configuration.LDAPHostName; diff --git a/ExchangeConnector/ExchangeConnector.cs b/ExchangeConnector/ExchangeConnector.cs index bfe7a82f..66476f6b 100644 --- a/ExchangeConnector/ExchangeConnector.cs +++ b/ExchangeConnector/ExchangeConnector.cs @@ -214,8 +214,8 @@ public override Uid Create( } // prepare the command - Command cmdEnable = ExchangeUtility.GetCommand(cmdInfoEnable, attributes); - Command cmdSet = ExchangeUtility.GetCommand(cmdInfoSet, attributes); + Command cmdEnable = ExchangeUtility.GetCommand(cmdInfoEnable, attributes, this.configuration); + Command cmdSet = ExchangeUtility.GetCommand(cmdInfoSet, attributes, this.configuration); try { @@ -312,11 +312,11 @@ public override Uid Update( if (origRcptType != rcptType) { Command cmdEnable = ExchangeUtility.GetCommand( - PSExchangeConnector.CommandInfo.EnableMailUser, attributes); + PSExchangeConnector.CommandInfo.EnableMailUser, attributes, this.configuration); this.InvokePipeline(cmdEnable); } - Command cmdSet = ExchangeUtility.GetCommand(PSExchangeConnector.CommandInfo.SetMailUser, attributes); + Command cmdSet = ExchangeUtility.GetCommand(PSExchangeConnector.CommandInfo.SetMailUser, attributes, this.configuration); this.InvokePipeline(cmdSet); } else @@ -337,7 +337,7 @@ public override Uid Update( string origDatabase = psuser.Members[AttDatabase] != null ? psuser.Members[AttDatabase].Value.ToString() : null; if (origRcptType != rcptType) { - Command cmdEnable = ExchangeUtility.GetCommand(PSExchangeConnector.CommandInfo.EnableMailbox, attributes); + Command cmdEnable = ExchangeUtility.GetCommand(PSExchangeConnector.CommandInfo.EnableMailbox, attributes, this.configuration); this.InvokePipeline(cmdEnable); } else @@ -351,7 +351,7 @@ public override Uid Update( } } - Command cmdSet = ExchangeUtility.GetCommand(PSExchangeConnector.CommandInfo.SetMailbox, attributes); + Command cmdSet = ExchangeUtility.GetCommand(PSExchangeConnector.CommandInfo.SetMailbox, attributes, this.configuration); this.InvokePipeline(cmdSet); } else if (rcptType == RcptTypeUser && origRcptType != rcptType) @@ -740,7 +740,7 @@ private ConnectorObject AddExchangeAttributes(ObjectClass oc, ConnectorObject co ICollection attributes = new Collection { cobject.Name }; // get the command - Command cmd = ExchangeUtility.GetCommand(cmdInfo, attributes); + Command cmd = ExchangeUtility.GetCommand(cmdInfo, attributes, this.configuration); ICollection foundObjects = this.InvokePipeline(cmd); PSObject user = null; if (foundObjects != null && foundObjects.Count == 1) @@ -774,11 +774,11 @@ private ConnectorObject AddExchangeAttributes(ObjectClass oc, ConnectorObject co // get detailed information if (rcptType == RcptTypeMailBox) { - foundObjects = this.InvokePipeline(ExchangeUtility.GetCommand(PSExchangeConnector.CommandInfo.GetMailbox, attributes)); + foundObjects = this.InvokePipeline(ExchangeUtility.GetCommand(PSExchangeConnector.CommandInfo.GetMailbox, attributes, this.configuration)); } else if (rcptType == RcptTypeMailUser) { - foundObjects = this.InvokePipeline(ExchangeUtility.GetCommand(PSExchangeConnector.CommandInfo.GetMailUser, attributes)); + foundObjects = this.InvokePipeline(ExchangeUtility.GetCommand(PSExchangeConnector.CommandInfo.GetMailUser, attributes, this.configuration)); } if (foundObjects != null && foundObjects.Count == 1) @@ -810,6 +810,12 @@ private ICollection InvokePipeline(Command cmd) { try { + Trace.TraceInformation("PowerShell Command: " + cmd); + foreach (CommandParameter parameter in cmd.Parameters) + { + Trace.TraceInformation("parameter: " + parameter.Name + " value:" + parameter.Value); + } + return this.runspace.InvokePipeline(cmd); } catch (Exception e) @@ -865,7 +871,7 @@ private PSObject GetUser(PSExchangeConnector.CommandInfo cmdInfo, ICollection users = this.InvokePipeline(cmdUser); if (users.Count == 1) { diff --git a/ExchangeConnector/ExchangeUtility.cs b/ExchangeConnector/ExchangeUtility.cs index 034d08d2..14f202d0 100644 --- a/ExchangeConnector/ExchangeUtility.cs +++ b/ExchangeConnector/ExchangeUtility.cs @@ -175,9 +175,12 @@ internal static IDictionary GetOCInfo() /// /// Command defition /// Attribute values - /// Ready to execute Command + /// Configuration object + /// + /// Ready to execute Command + /// /// if some of the param is null - internal static Command GetCommand(PSExchangeConnector.CommandInfo cmdInfo, ICollection attributes) + internal static Command GetCommand(PSExchangeConnector.CommandInfo cmdInfo, ICollection attributes, ExchangeConfiguration config) { Assertions.NullCheck(cmdInfo, "cmdInfo"); Assertions.NullCheck(attributes, "attributes"); @@ -198,10 +201,16 @@ internal static Command GetCommand(PSExchangeConnector.CommandInfo cmdInfo, ICol foreach (string attName in cmdInfo.Parameters) { object val = GetAttValue(attName, attributes); + if (val == null && attName.Equals("DomainController")) + { + // add domain controller if not provided + val = ActiveDirectoryUtils.GetDomainControllerName(config); + } + if (val != null) { cmd.Parameters.Add(attName, val); - } + } } return cmd; diff --git a/ExchangeConnector/PSExchangeConnector.cs b/ExchangeConnector/PSExchangeConnector.cs index 2febcb31..71e46a4c 100644 --- a/ExchangeConnector/PSExchangeConnector.cs +++ b/ExchangeConnector/PSExchangeConnector.cs @@ -106,13 +106,13 @@ public override Uid Create( if (oclass.Is(MailboxName)) { // enable mailbox for person - Command cmd = ExchangeUtility.GetCommand(CommandInfo.EnableMailbox, attributes); + Command cmd = ExchangeUtility.GetCommand(CommandInfo.EnableMailbox, attributes, this.configuration); this.runspace.InvokePipeline(cmd); } else if (oclass.Is(MailUserName)) { // enable mailuser - Command cmd = ExchangeUtility.GetCommand(CommandInfo.EnableMailUser, attributes); + Command cmd = ExchangeUtility.GetCommand(CommandInfo.EnableMailUser, attributes, this.configuration); this.runspace.InvokePipeline(cmd); } From dbb52f7ef78e1264a133ebbb546c7a8d07ff61ba Mon Sep 17 00:00:00 2001 From: abadea Date: Thu, 27 Aug 2009 13:47:40 +0000 Subject: [PATCH 240/342] Issue 536: Port ConnectorFacadeTests to .Net side --- FrameworkTests/ConnectorFacadeTests.cs | 398 +++++++++++++++++++++++++ FrameworkTests/FrameworkTests.csproj | 4 +- FrameworkTests/MockConnector.cs | 333 +++++++++++++++++++++ 3 files changed, 734 insertions(+), 1 deletion(-) create mode 100755 FrameworkTests/ConnectorFacadeTests.cs create mode 100755 FrameworkTests/MockConnector.cs diff --git a/FrameworkTests/ConnectorFacadeTests.cs b/FrameworkTests/ConnectorFacadeTests.cs new file mode 100755 index 00000000..04f0b68e --- /dev/null +++ b/FrameworkTests/ConnectorFacadeTests.cs @@ -0,0 +1,398 @@ +/* + * ==================== + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER. + * + * Copyright 2008-2009 Sun Microsystems, Inc. All rights reserved. + * + * The contents of this file are subject to the terms of the Common Development + * and Distribution License("CDDL") (the "License"). You may not use this file + * except in compliance with the License. + * + * You can obtain a copy of the License at + * http://IdentityConnectors.dev.java.net/legal/license.txt + * See the License for the specific language governing permissions and limitations + * under the License. + * + * When distributing the Covered Code, include this CDDL Header Notice in each file + * and include the License file at identityconnectors/legal/license.txt. + * If applicable, add the following below this CDDL Header, with the fields + * enclosed by brackets [] replaced by your own identifying information: + * "Portions Copyrighted [year] [name of copyright owner]" + * ==================== + */ + +using System; +using System.Collections.Generic; + +using NUnit.Framework; +using Org.IdentityConnectors.Common; +using Org.IdentityConnectors.Common.Security; +using Org.IdentityConnectors.Framework.Api; +using Org.IdentityConnectors.Framework.Api.Operations; +using Org.IdentityConnectors.Framework.Common; +using Org.IdentityConnectors.Framework.Common.Objects; +using Org.IdentityConnectors.Framework.Spi; +using Org.IdentityConnectors.Framework.Spi.Operations; +using Org.IdentityConnectors.Test.Common; + +namespace FrameworkTests { + + [TestFixture] + public class ConnectorFacadeTests + { + + [SetUp] + public void setup() + { + // always reset the call patterns.. + MockConnector.Reset(); + } + + private class TestOperationPattern + { + /// + /// Simple call back to make the 'facade' calls. + /// + public Action MakeCall; + + /// + /// Given the list of calls determine if they match expected values based + /// on the calls made in the method. + /// + public Action> CheckCalls; + } + + /// + /// Test the pattern of the common operations. + /// + private void TestCallPattern(TestOperationPattern pattern) + { + TestCallPattern(pattern, SafeType.Get()); + } + + private void TestCallPattern(TestOperationPattern pattern, + SafeType clazz) + { + Configuration config = new MockConfiguration(false); + ConnectorFacadeFactory factory = ConnectorFacadeFactory.GetInstance(); + // **test only** + APIConfiguration impl = TestHelpers.CreateTestConfiguration(clazz, config); + ConnectorFacade facade; + facade = factory.NewInstance(impl); + // make the call on the connector facade.. + pattern.MakeCall(facade); + // check the call structure.. + IList calls = MockConnector.GetCallPattern(); + // check the call pattern.. + Assert.AreEqual("Init", calls[0].MethodName); + calls.RemoveAt(0); + pattern.CheckCalls(calls); + Assert.AreEqual("Dispose", calls[0].MethodName); + calls.RemoveAt(0); + Assert.IsTrue(calls.Count == 0); + } + + /// + /// Tests that if an SPI operation is not implemented that the API will throw + /// an . + /// + [Test] + [ExpectedException(typeof(InvalidOperationException))] + public void UnsupportedOperationTest() + { + Configuration config = new MockConfiguration(false); + SafeType clazz = SafeType.Get(); + ConnectorFacadeFactory factory = ConnectorFacadeFactory.GetInstance(); + APIConfiguration impl = TestHelpers.CreateTestConfiguration(clazz, + config); + ConnectorFacade facade; + facade = factory.NewInstance(impl); + facade.Authenticate(ObjectClass.ACCOUNT, "fadf", new GuardedString(), null); + } + + [Test] + public void RunScriptOnConnectorCallPattern() + { + TestCallPattern(new TestOperationPattern() + { + MakeCall = facade => + { + facade.RunScriptOnConnector( + new ScriptContextBuilder("lang", "script").Build(), + null); + }, + CheckCalls = calls => + { + Assert.AreEqual("RunScriptOnConnector", GetAndRemoveMethodName(calls)); + } + }); + } + + [Test] + public void RunScriptOnResourceCallPattern() + { + TestCallPattern(new TestOperationPattern() + { + MakeCall = facade => + { + facade.RunScriptOnResource( + new ScriptContextBuilder("lang", "script").Build(), + null); + }, + CheckCalls = calls => + { + Assert.AreEqual("RunScriptOnResource", GetAndRemoveMethodName(calls)); + } + }); + } + + /// + /// Test the call pattern to get the schema. + /// + [Test] + public void SchemaCallPattern() + { + TestCallPattern(new TestOperationPattern() + { + MakeCall = facade => + { + facade.Schema(); + }, + CheckCalls = calls => + { + Assert.AreEqual("Schema", GetAndRemoveMethodName(calls)); + } + }); + } + + [Test] + public void AuthenticateCallPattern() + { + TestCallPattern(new TestOperationPattern() + { + MakeCall = facade => + { + facade.Authenticate(ObjectClass.ACCOUNT, "dfadf", new GuardedString(), null); + }, + CheckCalls = calls => + { + Assert.AreEqual("Authenticate", GetAndRemoveMethodName(calls)); + } + }); + } + + [Test] + public void CreateCallPattern() + { + TestCallPattern(new TestOperationPattern() + { + MakeCall = facade => + { + ICollection attrs = CollectionUtil.NewReadOnlySet(); + facade.Create(ObjectClass.ACCOUNT, attrs, null); + }, + CheckCalls = calls => + { + Assert.AreEqual("Create", GetAndRemoveMethodName(calls)); + } + }); + } + + [Test] + [ExpectedException(typeof(ArgumentNullException))] + public void CreateWithOutObjectClassPattern() + { + TestCallPattern(new TestOperationPattern() + { + MakeCall = facade => + { + ICollection attrs = new HashSet(); + facade.Create(null, attrs, null); + }, + CheckCalls = calls => + { + Assert.Fail("Should not get here.."); + } + }); + } + + [Test] + [ExpectedException(typeof(ArgumentException))] + public void createDuplicatConnectorAttributesPattern() + { + TestCallPattern(new TestOperationPattern() + { + MakeCall = facade => + { + ICollection attrs = new HashSet(); + attrs.Add(ConnectorAttributeBuilder.Build("abc", 1)); + attrs.Add(ConnectorAttributeBuilder.Build("abc", 2)); + facade.Create(ObjectClass.ACCOUNT, attrs, null); + }, + CheckCalls = calls => + { + Assert.Fail("Should not get here.."); + } + }); + } + + [Test] + public void UpdateCallPattern() + { + TestCallPattern(new TestOperationPattern() + { + MakeCall = facade => + { + ICollection attrs = new HashSet(); + attrs.Add(ConnectorAttributeBuilder.Build("accountid")); + facade.Update(ObjectClass.ACCOUNT, NewUid(0), attrs, null); + }, + CheckCalls = calls => + { + Assert.AreEqual("Update", GetAndRemoveMethodName(calls)); + } + }); + } + + [Test] + public void DeleteCallPattern() + { + TestCallPattern(new TestOperationPattern() + { + MakeCall = facade => + { + facade.Delete(ObjectClass.ACCOUNT, NewUid(0), null); + }, + CheckCalls = calls => + { + Assert.AreEqual("Delete", GetAndRemoveMethodName(calls)); + } + }); + } + + [Test] + public void SearchCallPattern() + { + TestCallPattern(new TestOperationPattern() + { + MakeCall = facade => + { + // create an empty results handler.. + ResultsHandler rh = obj => + { + return true; + }; + // call the search method.. + facade.Search(ObjectClass.ACCOUNT, null, rh, null); + }, + CheckCalls = calls => + { + Assert.AreEqual("CreateFilterTranslator", GetAndRemoveMethodName(calls)); + Assert.AreEqual("ExecuteQuery", GetAndRemoveMethodName(calls)); + } + }); + } + + [Test] + public void GetCallPattern() + { + TestCallPattern(new TestOperationPattern() + { + MakeCall = facade => + { + // create an empty results handler.. + // call the search method.. + facade.GetObject(ObjectClass.ACCOUNT, NewUid(0), null); + }, + CheckCalls = calls => + { + Assert.AreEqual("CreateFilterTranslator", GetAndRemoveMethodName(calls)); + Assert.AreEqual("ExecuteQuery", GetAndRemoveMethodName(calls)); + } + }); + } + + [Test] + public void TestOpCallPattern() + { + TestCallPattern(new TestOperationPattern() + { + MakeCall = facade => + { + facade.Test(); + }, + CheckCalls = calls => + { + Assert.AreEqual("Test", GetAndRemoveMethodName(calls)); + } + }); + } + + [Test] + public void UpdateMergeTests() + { + ConnectorAttribute expected, actual; + Configuration config = new MockConfiguration(false); + ConnectorFacadeFactory factory = ConnectorFacadeFactory.GetInstance(); + SafeType clazz = SafeType.Get(); + // **test only** + APIConfiguration impl = TestHelpers.CreateTestConfiguration(clazz, config); + impl.SetTimeout(SafeType.Get(), APIConstants.NO_TIMEOUT); + impl.SetTimeout(SafeType.Get(), APIConstants.NO_TIMEOUT); + impl.SetTimeout(SafeType.Get(), APIConstants.NO_TIMEOUT); + ConnectorFacade facade = factory.NewInstance(impl); + // sniff test to make sure we can get an object.. + ConnectorObject obj = facade.GetObject(ObjectClass.ACCOUNT, NewUid(1), null); + Assert.AreEqual(NewUid(1), obj.Uid); + // ok lets add an attribute that doesn't exist.. + String ADDED = "somthing to add to the object"; + String ATTR_NAME = "added"; + ICollection addAttrSet; + addAttrSet = CollectionUtil.NewSet((IEnumerable)obj.GetAttributes()); + addAttrSet.Add(ConnectorAttributeBuilder.Build(ATTR_NAME, ADDED)); + Name name = obj.Name; + addAttrSet.Remove(name); + Uid uid = facade.AddAttributeValues(ObjectClass.ACCOUNT, obj.Uid, ConnectorAttributeUtil.FilterUid(addAttrSet), null); + // get back the object and see if there are the same.. + addAttrSet.Add(name); + ConnectorObject addO = new ConnectorObject(ObjectClass.ACCOUNT, addAttrSet); + obj = facade.GetObject(ObjectClass.ACCOUNT, NewUid(1), null); + Assert.AreEqual(addO, obj); + // attempt to add on to an existing attribute.. + addAttrSet.Remove(name); + uid = facade.AddAttributeValues(ObjectClass.ACCOUNT, obj.Uid, ConnectorAttributeUtil.FilterUid(addAttrSet), null); + // get the object back out and check on it.. + obj = facade.GetObject(ObjectClass.ACCOUNT, uid, null); + expected = ConnectorAttributeBuilder.Build(ATTR_NAME, ADDED, ADDED); + actual = obj.GetAttributeByName(ATTR_NAME); + Assert.AreEqual(expected, actual); + // attempt to delete a value from an attribute.. + ICollection deleteAttrs = CollectionUtil.NewSet((IEnumerable)addO.GetAttributes()); + deleteAttrs.Remove(name); + uid = facade.RemoveAttributeValues(ObjectClass.ACCOUNT, addO.Uid, ConnectorAttributeUtil.FilterUid(deleteAttrs), null); + obj = facade.GetObject(ObjectClass.ACCOUNT, uid, null); + expected = ConnectorAttributeBuilder.Build(ATTR_NAME, ADDED); + actual = obj.GetAttributeByName(ATTR_NAME); + Assert.AreEqual(expected, actual); + // attempt to delete an attribute that doesn't exist.. + ICollection nonExist = new HashSet(); + nonExist.Add(NewUid(1)); + nonExist.Add(ConnectorAttributeBuilder.Build("does not exist", "asdfe")); + uid = facade.RemoveAttributeValues(ObjectClass.ACCOUNT, addO.Uid, ConnectorAttributeUtil.FilterUid(nonExist), null); + obj = facade.GetObject(ObjectClass.ACCOUNT, NewUid(1), null); + Assert.IsTrue(obj.GetAttributeByName("does not exist") == null); + } + + static Uid NewUid(int id) + { + return new Uid(Convert.ToString(id)); + } + + static string GetAndRemoveMethodName(IList calls) + { + string result = calls[0].MethodName; + calls.RemoveAt(0); + return result; + } + } +} diff --git a/FrameworkTests/FrameworkTests.csproj b/FrameworkTests/FrameworkTests.csproj index 58d033e3..01fbdc89 100644 --- a/FrameworkTests/FrameworkTests.csproj +++ b/FrameworkTests/FrameworkTests.csproj @@ -65,8 +65,10 @@ + + Code @@ -159,4 +161,4 @@ - + \ No newline at end of file diff --git a/FrameworkTests/MockConnector.cs b/FrameworkTests/MockConnector.cs new file mode 100755 index 00000000..cddc26a9 --- /dev/null +++ b/FrameworkTests/MockConnector.cs @@ -0,0 +1,333 @@ +/* + * ==================== + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER. + * + * Copyright 2008-2009 Sun Microsystems, Inc. All rights reserved. + * + * The contents of this file are subject to the terms of the Common Development + * and Distribution License("CDDL") (the "License"). You may not use this file + * except in compliance with the License. + * + * You can obtain a copy of the License at + * http://IdentityConnectors.dev.java.net/legal/license.txt + * See the License for the specific language governing permissions and limitations + * under the License. + * + * When distributing the Covered Code, include this CDDL Header Notice in each file + * and include the License file at identityconnectors/legal/license.txt. + * If applicable, add the following below this CDDL Header, with the fields + * enclosed by brackets [] replaced by your own identifying information: + * "Portions Copyrighted [year] [name of copyright owner]" + * ==================== + */ + +using System; +using System.Collections.Generic; + +using NUnit.Framework; +using Org.IdentityConnectors.Common; +using Org.IdentityConnectors.Common.Security; +using Org.IdentityConnectors.Framework.Common.Exceptions; +using Org.IdentityConnectors.Framework.Common.Objects; +using Org.IdentityConnectors.Framework.Common.Objects.Filters; +using Org.IdentityConnectors.Framework.Spi; +using Org.IdentityConnectors.Framework.Spi.Operations; + +namespace FrameworkTests +{ + + public class MockConnector : Connector, SchemaOp + { + + /// + /// Represents a call to a connector method. + /// + public class Call + { + + private readonly string methodName; + private readonly object[] args; + + public Call(string methodName, params object[] args) + { + this.methodName = methodName; + this.args = args; + } + + public string MethodName + { + get + { + return methodName; + } + } + + public object[] Arguments + { + get + { + return this.args; + } + } + } + + // need to keep track of when methods are called an their parameters.. + private static IList callPattern = new List(); + + private Configuration _config; + + public void Dispose() + { + AddCall("Dispose"); + } + + public Schema Schema() + { + AddCall("Schema"); + return null; + } + + public void Init(Configuration cfg) + { + _config = cfg; + AddCall("Init", cfg); + } + + public Configuration getConfiguration() + { + return _config; + } + + /// + /// Clear the call pattern. + /// + public static void Reset() + { + callPattern.Clear(); + } + + /// + /// Get the current call pattern. + /// + public static IList GetCallPattern() + { + return CollectionUtil.NewList(callPattern); + } + + /// + /// Adds the call to the internal call pattern. + /// + public static void AddCall(string methodName, params object[] args) + { + callPattern.Add(new Call(methodName, args)); + } + } + + public class MockAllOpsConnector : MockConnector, CreateOp, + DeleteOp, UpdateOp, SearchOp, UpdateAttributeValuesOp, AuthenticateOp, + TestOp, ScriptOnConnectorOp, ScriptOnResourceOp + { + + public object RunScriptOnConnector(ScriptContext request, + OperationOptions options) + { + Assert.IsNotNull(request); + Assert.IsNotNull(options); + AddCall("RunScriptOnConnector", request, options); + return null; + } + + public object RunScriptOnResource(ScriptContext request, + OperationOptions options) + { + Assert.IsNotNull(request); + Assert.IsNotNull(options); + AddCall("RunScriptOnResource", request, options); + return null; + } + + public Uid Create(ObjectClass oclass, ICollection attrs, + OperationOptions options) + { + Assert.IsNotNull(attrs); + AddCall("Create", attrs); + return null; + } + + public void Delete(ObjectClass objClass, Uid uid, + OperationOptions options) + { + Assert.IsNotNull(uid); + Assert.IsNotNull(objClass); + AddCall("Delete", objClass, uid); + } + + public Uid Update(ObjectClass objclass, Uid uid, ICollection attrs, + OperationOptions options) + { + Assert.IsNotNull(objclass); + Assert.IsNotNull(attrs); + AddCall("Update", objclass, attrs); + return null; + } + + public Uid AddAttributeValues(ObjectClass objclass, Uid uid, + ICollection valuesToAdd, OperationOptions options) + { + AddCall("AddAttributeValues", objclass, valuesToAdd); + return null; + } + + public Uid RemoveAttributeValues(ObjectClass objclass, Uid uid, + ICollection valuesToRemove, OperationOptions options) + { + AddCall("RemoveAttributeValues", objclass, valuesToRemove); + return null; + } + + public FilterTranslator CreateFilterTranslator(ObjectClass oclass, + OperationOptions options) + { + Assert.IsNotNull(oclass); + Assert.IsNotNull(options); + AddCall("CreateFilterTranslator", oclass, options); + // no translation - ok since this is just for tests + return new MockFilterTranslator(); + } + + public void ExecuteQuery(ObjectClass oclass, string query, + ResultsHandler handler, OperationOptions options) + { + Assert.IsNotNull(oclass); + Assert.IsNotNull(handler); + Assert.IsNotNull(options); + AddCall("ExecuteQuery", oclass, query, handler, options); + } + + public Uid Authenticate(ObjectClass objectClass, string username, GuardedString password, + OperationOptions options) + { + Assert.IsNotNull(username); + Assert.IsNotNull(password); + AddCall("Authenticate", username, password); + return null; + } + + public void Test() + { + AddCall("Test"); + } + } + + public class MockUpdateConnector : Connector, UpdateOp, SearchOp + { + + private Configuration _cfg; + + public void Dispose() + { + // nothing to do this is a mock connector.. + } + + public void Init(Configuration cfg) + { + _cfg = cfg; + } + + public Configuration GetConfiguration() + { + return _cfg; + } + + private static IList objects = new List(); + + static MockUpdateConnector() + { + ConnectorObjectBuilder bld = new ConnectorObjectBuilder(); + for (int i = 0; i < 100; i++) + { + bld.SetUid(Convert.ToString(i)); + bld.SetName(Convert.ToString(i)); + objects.Add(bld.Build()); + } + } + + /// + /// This will do a basic replace. + /// + /// + /// + /// + public Uid Update(ObjectClass objclass, Uid uid, ICollection attrs, OperationOptions options) + { + string val = ConnectorAttributeUtil.GetAsStringValue(uid); + int idx = Convert.ToInt32(val); + //.Get out the object.. + ConnectorObject baseObject = objects[idx]; + ConnectorObjectBuilder bld = new ConnectorObjectBuilder(); + bld.Add(baseObject); + bld.AddAttributes(attrs); + ConnectorObject obj = bld.Build(); + objects[idx] = obj; + return obj.Uid; + } + + public FilterTranslator CreateFilterTranslator(ObjectClass oclass, OperationOptions options) + { + //no translation - ok since this is just for tests + return new MockFilterTranslator(); + } + + /// + /// Simply return everything don't bother optimizing. + /// + /// + /// + public void ExecuteQuery(ObjectClass oclass, string query, ResultsHandler handler, OperationOptions options) + { + foreach (ConnectorObject obj in objects) + { + if (!handler(obj)) + { + break; + } + } + } + } + + class MockFilterTranslator : AbstractFilterTranslator + { + } + + public class MockConfiguration : AbstractConfiguration + { + + private readonly bool fail; + + public MockConfiguration() + { + } + + /// + /// Determines if this configuration will fail validation. + /// + public MockConfiguration(bool failvalidation) + { + this.fail = failvalidation; + } + + public bool Fail + { + get; set; + } + + public override void Validate() + { + if (fail) + { + throw new ConfigurationException(); + } + } + + } +} From 4014af9d772e720024c6ea7255990e9b47992d6a Mon Sep 17 00:00:00 2001 From: roman_kitko Date: Tue, 1 Sep 2009 12:39:53 +0000 Subject: [PATCH 241/342] Enable reading of config files as resources from classpath and move config files to proper location as described at #531 --- ActiveDirectoryConnector/build.xml | 3 ++- ExchangeConnector/build.xml | 1 + 2 files changed, 3 insertions(+), 1 deletion(-) diff --git a/ActiveDirectoryConnector/build.xml b/ActiveDirectoryConnector/build.xml index e44f008e..54c99e46 100644 --- a/ActiveDirectoryConnector/build.xml +++ b/ActiveDirectoryConnector/build.xml @@ -23,6 +23,7 @@ - + + diff --git a/ExchangeConnector/build.xml b/ExchangeConnector/build.xml index e28e6851..3c4eba44 100644 --- a/ExchangeConnector/build.xml +++ b/ExchangeConnector/build.xml @@ -24,5 +24,6 @@ + From 7f738ae3cdac4c3d510686f08ea7da59046c64af Mon Sep 17 00:00:00 2001 From: abadea Date: Tue, 1 Sep 2009 16:15:15 +0000 Subject: [PATCH 242/342] Adding Assertions.BlankCheck(), which was missing from the .NET side --- Common/Assertions.cs | 16 ++++++++++++++++ 1 file changed, 16 insertions(+) diff --git a/Common/Assertions.cs b/Common/Assertions.cs index 4510a153..03c0ebb2 100644 --- a/Common/Assertions.cs +++ b/Common/Assertions.cs @@ -47,5 +47,21 @@ public static void NullCheck(Object o, String param) { throw new ArgumentNullException(String.Format(FORMAT, param)); } } + + /** + * Throws {@link IllegalArgumentException} if the parameter o + * is null or blank. + * + * @param o + * value to test for blank. + * @param param + * name of the parameter to check. + */ + public static void BlankCheck(String o, String param) { + String FORMAT = "Parameter '%s' must not be blank."; + if (StringUtil.IsBlank(o)) { + throw new ArgumentException(String.Format(FORMAT, param)); + } + } } } From 17d90503bb02277a95bad3ecbf7619eef4b557cd Mon Sep 17 00:00:00 2001 From: abadea Date: Tue, 1 Sep 2009 16:26:50 +0000 Subject: [PATCH 243/342] .NET is not Java --- Common/Assertions.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Common/Assertions.cs b/Common/Assertions.cs index 03c0ebb2..562676ce 100644 --- a/Common/Assertions.cs +++ b/Common/Assertions.cs @@ -58,7 +58,7 @@ public static void NullCheck(Object o, String param) { * name of the parameter to check. */ public static void BlankCheck(String o, String param) { - String FORMAT = "Parameter '%s' must not be blank."; + String FORMAT = "Parameter '{0}' must not be blank."; if (StringUtil.IsBlank(o)) { throw new ArgumentException(String.Format(FORMAT, param)); } From eb57d9732cc7669bd0ea1446aebacaec03c6d9c9 Mon Sep 17 00:00:00 2001 From: lajos_nagy Date: Mon, 7 Sep 2009 12:52:20 +0000 Subject: [PATCH 244/342] Issue #548: ExceptionUtil has been added to preserve the stack trace of an exception, and all the try..catch blocks that deal with TargetInvocationException have been modified to re-throw the nested exception with the preserved stack trace string. --- FrameworkInternal/Api.cs | 1 + FrameworkInternal/ApiLocalOperations.cs | 1 + FrameworkInternal/ExceptionUtil.cs | 87 ++++++++++++ FrameworkInternal/FrameworkInternal.csproj | 3 +- FrameworkInternal/Server.cs | 4 +- .../ConnectorFacadeExceptionTests.cs | 124 ++++++++++++++++++ FrameworkTests/ExceptionUtilTests.cs | 117 +++++++++++++++++ FrameworkTests/FrameworkTests.csproj | 2 + 8 files changed, 337 insertions(+), 2 deletions(-) create mode 100644 FrameworkInternal/ExceptionUtil.cs create mode 100644 FrameworkTests/ConnectorFacadeExceptionTests.cs create mode 100644 FrameworkTests/ExceptionUtilTests.cs diff --git a/FrameworkInternal/Api.cs b/FrameworkInternal/Api.cs index fbda9413..69f523c7 100644 --- a/FrameworkInternal/Api.cs +++ b/FrameworkInternal/Api.cs @@ -874,6 +874,7 @@ public Object Invoke(Object proxy, MethodInfo method, Object [] args) { return ret; } catch (TargetInvocationException e) { Exception root = e.InnerException; + ExceptionUtil.PreserveStackTrace( root ); throw root; } } diff --git a/FrameworkInternal/ApiLocalOperations.cs b/FrameworkInternal/ApiLocalOperations.cs index 4e1372e8..f676eeaa 100644 --- a/FrameworkInternal/ApiLocalOperations.cs +++ b/FrameworkInternal/ApiLocalOperations.cs @@ -177,6 +177,7 @@ public Object Invoke(Object proxy, MethodInfo method, object[] args) // call out to the operation.. } catch (TargetInvocationException e) { Exception root = e.InnerException; + ExceptionUtil.PreserveStackTrace( root ); throw root; } finally { // make sure dispose of the connector properly diff --git a/FrameworkInternal/ExceptionUtil.cs b/FrameworkInternal/ExceptionUtil.cs new file mode 100644 index 00000000..9ae2a164 --- /dev/null +++ b/FrameworkInternal/ExceptionUtil.cs @@ -0,0 +1,87 @@ +/* + * ==================== + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER. + * + * Copyright 2008-2009 Sun Microsystems, Inc. All rights reserved. + * + * The contents of this file are subject to the terms of the Common Development + * and Distribution License("CDDL") (the "License"). You may not use this file + * except in compliance with the License. + * + * You can obtain a copy of the License at + * http://IdentityConnectors.dev.java.net/legal/license.txt + * See the License for the specific language governing permissions and limitations + * under the License. + * + * When distributing the Covered Code, include this CDDL Header Notice in each file + * and include the License file at identityconnectors/legal/license.txt. + * If applicable, add the following below this CDDL Header, with the fields + * enclosed by brackets [] replaced by your own identifying information: + * "Portions Copyrighted [year] [name of copyright owner]" + * ==================== + */ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Reflection; +using System.Diagnostics; +using Org.IdentityConnectors.Common; +using System.Runtime.CompilerServices; + +namespace Org.IdentityConnectors.Framework.Impl +{ + /// + /// Contains utilities to handle exceptions. + /// + public static class ExceptionUtil + { + private const string PreserveStackTraceMethodName = "InternalPreserveStackTrace"; + + /// + /// Preserves the stack trace of . + /// + /// The exception, the stack trace of which to be preserved. + /// In the .Net Framework the stack trace of an exception starts to get populated when it is thrown, + /// hence if an exception is re-thrown by a method upper in the call chain the stack trace will reflect that the + /// exception occurred at that position where the exception was actually re-thrown and the original stack trace will + /// be lost. + /// + /// + /// try + /// { + /// function_that_throws_an_exception_with_a_nested_one(); + /// } + /// catch( Exception ex ) + /// { + /// throw ex.InnerException; //clears the stack trace of the nested exception + /// } + /// + /// + /// There is no built-in support in .Net to preserve the stack trace, however, an internal method + /// of the class called + /// can be used to achieve this. Since it is an internal method it might be subject to change, therefore if it is + /// not possible to invoke the method by any reason the error cause will be traced, but it will not break the execution. + /// + public static void PreserveStackTrace(Exception exception) + { + Assertions.NullCheck( exception, "exception" ); + + try + { + MethodInfo preserveStackTrace = typeof( Exception ).GetMethod( + PreserveStackTraceMethodName, BindingFlags.Instance | BindingFlags.NonPublic | BindingFlags.InvokeMethod ); + + preserveStackTrace.Invoke( exception, null ); + } + catch( Exception ex ) + { + //it should not ever happen, but we have to make sure that if a next release of .Net Framework does not + //include the invoked method it will not break the execution + TraceUtil.TraceException( string.Format( + @"Could not set an exception to preserve its stack trace. Exception: ""{0}""", exception ), ex ); + return; + } + } + } +} diff --git a/FrameworkInternal/FrameworkInternal.csproj b/FrameworkInternal/FrameworkInternal.csproj index 4ae33963..7ab7282d 100644 --- a/FrameworkInternal/FrameworkInternal.csproj +++ b/FrameworkInternal/FrameworkInternal.csproj @@ -81,6 +81,7 @@ + @@ -105,4 +106,4 @@ TestCommon - + \ No newline at end of file diff --git a/FrameworkInternal/Server.cs b/FrameworkInternal/Server.cs index 4a9125d0..ec6f1520 100644 --- a/FrameworkInternal/Server.cs +++ b/FrameworkInternal/Server.cs @@ -492,7 +492,9 @@ private OperationResponsePart result = method.Invoke(operation, args); } catch (TargetInvocationException e) { - throw e.InnerException; + Exception root = e.InnerException; + ExceptionUtil.PreserveStackTrace( root ); + throw root; } bool anyStreams = argumentsAndStreamHandlers.Count > arguments.Count; diff --git a/FrameworkTests/ConnectorFacadeExceptionTests.cs b/FrameworkTests/ConnectorFacadeExceptionTests.cs new file mode 100644 index 00000000..606e4660 --- /dev/null +++ b/FrameworkTests/ConnectorFacadeExceptionTests.cs @@ -0,0 +1,124 @@ +/* + * ==================== + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER. + * + * Copyright 2008-2009 Sun Microsystems, Inc. All rights reserved. + * + * The contents of this file are subject to the terms of the Common Development + * and Distribution License("CDDL") (the "License"). You may not use this file + * except in compliance with the License. + * + * You can obtain a copy of the License at + * http://IdentityConnectors.dev.java.net/legal/license.txt + * See the License for the specific language governing permissions and limitations + * under the License. + * + * When distributing the Covered Code, include this CDDL Header Notice in each file + * and include the License file at identityconnectors/legal/license.txt. + * If applicable, add the following below this CDDL Header, with the fields + * enclosed by brackets [] replaced by your own identifying information: + * "Portions Copyrighted [year] [name of copyright owner]" + * ==================== + */ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using NUnit.Framework; +using System.Diagnostics; +using Org.IdentityConnectors.Framework.Api; +using Org.IdentityConnectors.Framework.Spi; +using Org.IdentityConnectors.Framework.Spi.Operations; +using Org.IdentityConnectors.Test.Common; +using Org.IdentityConnectors.Common; + +namespace FrameworkTests +{ + [TestFixture] + public class ConnectorFacadeExceptionTests + { + /// + /// The test specific exception that is intended to be distinguished from the exceptions the framework + /// might throw. + /// + private class EUTestException : Exception + { + } + + private class SpyConnector : Connector, TestOp + { + #region Member variables + private static StackTrace _stackTrace; + #endregion + + #region Properties + /// + /// Gets the stack trace of the last call to the method performed on + /// any instance of this class. + /// + public static StackTrace StackTrace + { + get + { + return _stackTrace; + } + } + #endregion + + #region Connector Members + public void Init(Configuration configuration) + { + } + #endregion + + #region IDisposable Members + public void Dispose() + { + } + #endregion + + #region TestOp Members + public void Test() + { + //do not insert file info, as the stack trace of the exception and the dumped stack trace + //will always differ in the line numbers + _stackTrace = new StackTrace( false ); + throw new EUTestException(); + } + #endregion + } + + private class MockConfiguration : AbstractConfiguration + { + public override void Validate() + { + } + } + + /// + /// Tests whether the implementation let the exception - thrown by a connector - + /// propagate to the caller with the call stack representation from the method that throws the exception. + /// + /// The current implementation uses reflection that can hide the original + /// exception. See for more information. + [Test] + public void TestStackTraceOfExceptionThrownByConnectorFacade() + { + ConnectorFacadeFactory factory = ConnectorFacadeFactory.GetInstance(); + Configuration config = new MockConfiguration(); + ConnectorFacade facade = factory.NewInstance( + TestHelpers.CreateTestConfiguration( SafeType.Get(), config ) ); + + try + { + facade.Test(); + + Assert.Fail( "Exception was not thrown" ); + } + catch( EUTestException eutex ) + { + ExceptionUtilTestHelpers.AssertStackTrace( eutex, SpyConnector.StackTrace ); + } + } + } +} diff --git a/FrameworkTests/ExceptionUtilTests.cs b/FrameworkTests/ExceptionUtilTests.cs new file mode 100644 index 00000000..6ba3ddd2 --- /dev/null +++ b/FrameworkTests/ExceptionUtilTests.cs @@ -0,0 +1,117 @@ +/* + * ==================== + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER. + * + * Copyright 2008-2009 Sun Microsystems, Inc. All rights reserved. + * + * The contents of this file are subject to the terms of the Common Development + * and Distribution License("CDDL") (the "License"). You may not use this file + * except in compliance with the License. + * + * You can obtain a copy of the License at + * http://IdentityConnectors.dev.java.net/legal/license.txt + * See the License for the specific language governing permissions and limitations + * under the License. + * + * When distributing the Covered Code, include this CDDL Header Notice in each file + * and include the License file at identityconnectors/legal/license.txt. + * If applicable, add the following below this CDDL Header, with the fields + * enclosed by brackets [] replaced by your own identifying information: + * "Portions Copyrighted [year] [name of copyright owner]" + * ==================== + */ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using NUnit.Framework; +using Org.IdentityConnectors.Test.Common; +using System.Diagnostics; +using Org.IdentityConnectors.Framework.Impl; + +namespace FrameworkTests +{ + /// + /// Contains helper function to verify the output of . + /// + internal class ExceptionUtilTestHelpers + { + /// + /// Tests whether the is originated from the same function as in which the + /// was captured. + /// + /// The exception to test. + /// The captured stack trace to test against. + public static void AssertStackTrace(Exception exception, StackTrace stackTrace) + { + Trace.TraceInformation( "Stack trace from the failing method: {0}", stackTrace.ToString() ); + Trace.TraceInformation( "Exception stack trace: {0}", exception.StackTrace ); + + string fullST = stackTrace.ToString(); + int newLinePos = fullST.IndexOf( Environment.NewLine ); + string failingMethodExpected = fullST.Substring( 0, (newLinePos == -1) ? fullST.Length : newLinePos ); + + newLinePos = exception.StackTrace.IndexOf( Environment.NewLine ); + string failingMethodActual = exception.StackTrace.Substring( 0, (newLinePos == -1) ? fullST.Length : newLinePos ); + + //check if the first line of the stack trace fetched from the failing method is contained in the + //first line of the exception's stack trace, i.e. the method which threw the exception is in the + //stack trace of the exception + Assert.That( failingMethodActual.Contains( failingMethodExpected ) ); + } + } + + [TestFixture] + public class ExceptionUtilTests + { + [Test] + public void TestPreserveStackTrace() + { + StackTrace stackTrace = null; + try + { + try + { + Action bar = () => + { + try + { + //delegate to the failing method + Action foo = () => + { + stackTrace = new StackTrace( false ); + throw new InvalidOperationException(); + }; + + foo(); + + Assert.Fail( "Exception was not thrown - 1" ); + } + catch( InvalidOperationException iopex ) + { + //wrap the exception to make sure that there is an + //inner exception that can be re-thrown later on + throw new ArgumentException( string.Empty, iopex ); + }; + }; + + bar(); + + Assert.Fail( "Exception was not thrown - 2" ); + } + catch( ArgumentException aex ) + { + //preserve the stack trace of the nested exception and re-throw it + ExceptionUtil.PreserveStackTrace( aex.InnerException ); + throw aex.InnerException; + } + + Assert.Fail( "Exception was not thrown - 3" ); + } + catch( InvalidOperationException iopex ) + { + ExceptionUtilTestHelpers.AssertStackTrace( iopex, stackTrace ); + } + } + } +} diff --git a/FrameworkTests/FrameworkTests.csproj b/FrameworkTests/FrameworkTests.csproj index 01fbdc89..3151efc4 100644 --- a/FrameworkTests/FrameworkTests.csproj +++ b/FrameworkTests/FrameworkTests.csproj @@ -69,6 +69,8 @@ + + Code From 7068aa372b502450fd0991f9ebbfaaea7dfbbbed Mon Sep 17 00:00:00 2001 From: roman_kitko Date: Tue, 8 Sep 2009 15:40:09 +0000 Subject: [PATCH 245/342] Remove version.txt from dotnet projects and mark as ignored --- ActiveDirectoryConnector/version.txt | 1 - ActiveDirectoryConnectorTests/version.txt | 1 - BooScriptExecutorFactory/version.txt | 1 - Common/version.txt | 1 - Console/version.txt | 1 - ExchangeConnector/version.txt | 1 - Framework/version.txt | 1 - FrameworkInternal/version.txt | 1 - FrameworkTests/version.txt | 1 - Service/version.txt | 1 - ShellScriptExecutorFactory/version.txt | 1 - TestCommon/version.txt | 1 - 12 files changed, 12 deletions(-) delete mode 100644 ActiveDirectoryConnector/version.txt delete mode 100644 ActiveDirectoryConnectorTests/version.txt delete mode 100644 BooScriptExecutorFactory/version.txt delete mode 100644 Common/version.txt delete mode 100644 Console/version.txt delete mode 100644 ExchangeConnector/version.txt delete mode 100644 Framework/version.txt delete mode 100644 FrameworkInternal/version.txt delete mode 100644 FrameworkTests/version.txt delete mode 100644 Service/version.txt delete mode 100644 ShellScriptExecutorFactory/version.txt delete mode 100644 TestCommon/version.txt diff --git a/ActiveDirectoryConnector/version.txt b/ActiveDirectoryConnector/version.txt deleted file mode 100644 index bd2666ab..00000000 --- a/ActiveDirectoryConnector/version.txt +++ /dev/null @@ -1 +0,0 @@ -1.0.0.0 \ No newline at end of file diff --git a/ActiveDirectoryConnectorTests/version.txt b/ActiveDirectoryConnectorTests/version.txt deleted file mode 100644 index bd2666ab..00000000 --- a/ActiveDirectoryConnectorTests/version.txt +++ /dev/null @@ -1 +0,0 @@ -1.0.0.0 \ No newline at end of file diff --git a/BooScriptExecutorFactory/version.txt b/BooScriptExecutorFactory/version.txt deleted file mode 100644 index bd2666ab..00000000 --- a/BooScriptExecutorFactory/version.txt +++ /dev/null @@ -1 +0,0 @@ -1.0.0.0 \ No newline at end of file diff --git a/Common/version.txt b/Common/version.txt deleted file mode 100644 index bd2666ab..00000000 --- a/Common/version.txt +++ /dev/null @@ -1 +0,0 @@ -1.0.0.0 \ No newline at end of file diff --git a/Console/version.txt b/Console/version.txt deleted file mode 100644 index bd2666ab..00000000 --- a/Console/version.txt +++ /dev/null @@ -1 +0,0 @@ -1.0.0.0 \ No newline at end of file diff --git a/ExchangeConnector/version.txt b/ExchangeConnector/version.txt deleted file mode 100644 index bd2666ab..00000000 --- a/ExchangeConnector/version.txt +++ /dev/null @@ -1 +0,0 @@ -1.0.0.0 \ No newline at end of file diff --git a/Framework/version.txt b/Framework/version.txt deleted file mode 100644 index bd2666ab..00000000 --- a/Framework/version.txt +++ /dev/null @@ -1 +0,0 @@ -1.0.0.0 \ No newline at end of file diff --git a/FrameworkInternal/version.txt b/FrameworkInternal/version.txt deleted file mode 100644 index bd2666ab..00000000 --- a/FrameworkInternal/version.txt +++ /dev/null @@ -1 +0,0 @@ -1.0.0.0 \ No newline at end of file diff --git a/FrameworkTests/version.txt b/FrameworkTests/version.txt deleted file mode 100644 index bd2666ab..00000000 --- a/FrameworkTests/version.txt +++ /dev/null @@ -1 +0,0 @@ -1.0.0.0 \ No newline at end of file diff --git a/Service/version.txt b/Service/version.txt deleted file mode 100644 index bd2666ab..00000000 --- a/Service/version.txt +++ /dev/null @@ -1 +0,0 @@ -1.0.0.0 \ No newline at end of file diff --git a/ShellScriptExecutorFactory/version.txt b/ShellScriptExecutorFactory/version.txt deleted file mode 100644 index bd2666ab..00000000 --- a/ShellScriptExecutorFactory/version.txt +++ /dev/null @@ -1 +0,0 @@ -1.0.0.0 \ No newline at end of file diff --git a/TestCommon/version.txt b/TestCommon/version.txt deleted file mode 100644 index bd2666ab..00000000 --- a/TestCommon/version.txt +++ /dev/null @@ -1 +0,0 @@ -1.0.0.0 \ No newline at end of file From b2bcd95f0ffadc27b362f088af487a65369dedc1 Mon Sep 17 00:00:00 2001 From: roman_kitko Date: Tue, 8 Sep 2009 16:39:19 +0000 Subject: [PATCH 246/342] Revert removal of version.txt, rather split read and write using 2 files --- ActiveDirectoryConnector/version.txt | 1 + ActiveDirectoryConnectorTests/version.txt | 1 + BooScriptExecutorFactory/version.txt | 1 + Common/version.txt | 1 + Console/version.txt | 1 + ExchangeConnector/version.txt | 1 + Framework/version.txt | 1 + FrameworkInternal/version.txt | 1 + FrameworkTests/version.txt | 1 + Service/version.txt | 1 + ShellScriptExecutorFactory/version.txt | 1 + TestCommon/version.txt | 1 + 12 files changed, 12 insertions(+) create mode 100644 ActiveDirectoryConnector/version.txt create mode 100644 ActiveDirectoryConnectorTests/version.txt create mode 100644 BooScriptExecutorFactory/version.txt create mode 100644 Common/version.txt create mode 100644 Console/version.txt create mode 100644 ExchangeConnector/version.txt create mode 100644 Framework/version.txt create mode 100644 FrameworkInternal/version.txt create mode 100644 FrameworkTests/version.txt create mode 100644 Service/version.txt create mode 100644 ShellScriptExecutorFactory/version.txt create mode 100644 TestCommon/version.txt diff --git a/ActiveDirectoryConnector/version.txt b/ActiveDirectoryConnector/version.txt new file mode 100644 index 00000000..bd2666ab --- /dev/null +++ b/ActiveDirectoryConnector/version.txt @@ -0,0 +1 @@ +1.0.0.0 \ No newline at end of file diff --git a/ActiveDirectoryConnectorTests/version.txt b/ActiveDirectoryConnectorTests/version.txt new file mode 100644 index 00000000..bd2666ab --- /dev/null +++ b/ActiveDirectoryConnectorTests/version.txt @@ -0,0 +1 @@ +1.0.0.0 \ No newline at end of file diff --git a/BooScriptExecutorFactory/version.txt b/BooScriptExecutorFactory/version.txt new file mode 100644 index 00000000..bd2666ab --- /dev/null +++ b/BooScriptExecutorFactory/version.txt @@ -0,0 +1 @@ +1.0.0.0 \ No newline at end of file diff --git a/Common/version.txt b/Common/version.txt new file mode 100644 index 00000000..bd2666ab --- /dev/null +++ b/Common/version.txt @@ -0,0 +1 @@ +1.0.0.0 \ No newline at end of file diff --git a/Console/version.txt b/Console/version.txt new file mode 100644 index 00000000..bd2666ab --- /dev/null +++ b/Console/version.txt @@ -0,0 +1 @@ +1.0.0.0 \ No newline at end of file diff --git a/ExchangeConnector/version.txt b/ExchangeConnector/version.txt new file mode 100644 index 00000000..bd2666ab --- /dev/null +++ b/ExchangeConnector/version.txt @@ -0,0 +1 @@ +1.0.0.0 \ No newline at end of file diff --git a/Framework/version.txt b/Framework/version.txt new file mode 100644 index 00000000..bd2666ab --- /dev/null +++ b/Framework/version.txt @@ -0,0 +1 @@ +1.0.0.0 \ No newline at end of file diff --git a/FrameworkInternal/version.txt b/FrameworkInternal/version.txt new file mode 100644 index 00000000..bd2666ab --- /dev/null +++ b/FrameworkInternal/version.txt @@ -0,0 +1 @@ +1.0.0.0 \ No newline at end of file diff --git a/FrameworkTests/version.txt b/FrameworkTests/version.txt new file mode 100644 index 00000000..bd2666ab --- /dev/null +++ b/FrameworkTests/version.txt @@ -0,0 +1 @@ +1.0.0.0 \ No newline at end of file diff --git a/Service/version.txt b/Service/version.txt new file mode 100644 index 00000000..bd2666ab --- /dev/null +++ b/Service/version.txt @@ -0,0 +1 @@ +1.0.0.0 \ No newline at end of file diff --git a/ShellScriptExecutorFactory/version.txt b/ShellScriptExecutorFactory/version.txt new file mode 100644 index 00000000..bd2666ab --- /dev/null +++ b/ShellScriptExecutorFactory/version.txt @@ -0,0 +1 @@ +1.0.0.0 \ No newline at end of file diff --git a/TestCommon/version.txt b/TestCommon/version.txt new file mode 100644 index 00000000..bd2666ab --- /dev/null +++ b/TestCommon/version.txt @@ -0,0 +1 @@ +1.0.0.0 \ No newline at end of file From 1f89b552a719a0d848ead90928b8718369e93474 Mon Sep 17 00:00:00 2001 From: roman_kitko Date: Wed, 9 Sep 2009 09:31:39 +0000 Subject: [PATCH 247/342] Use SvnVersion task to resolve svn revision. Use version.template for reading and version.txt for writing --- .../{version.txt => version.template} | 0 .../{version.txt => version.template} | 0 .../{version.txt => version.template} | 0 Common/{version.txt => version.template} | 0 Console/{version.txt => version.template} | 0 DotNetCommonBuild.Targets | 18 ++++++++++++------ .../{version.txt => version.template} | 0 Framework/{version.txt => version.template} | 0 .../{version.txt => version.template} | 0 .../{version.txt => version.template} | 0 Service/{version.txt => version.template} | 0 .../{version.txt => version.template} | 0 TestCommon/{version.txt => version.template} | 0 13 files changed, 12 insertions(+), 6 deletions(-) rename ActiveDirectoryConnector/{version.txt => version.template} (100%) rename ActiveDirectoryConnectorTests/{version.txt => version.template} (100%) rename BooScriptExecutorFactory/{version.txt => version.template} (100%) rename Common/{version.txt => version.template} (100%) rename Console/{version.txt => version.template} (100%) rename ExchangeConnector/{version.txt => version.template} (100%) rename Framework/{version.txt => version.template} (100%) rename FrameworkInternal/{version.txt => version.template} (100%) rename FrameworkTests/{version.txt => version.template} (100%) rename Service/{version.txt => version.template} (100%) rename ShellScriptExecutorFactory/{version.txt => version.template} (100%) rename TestCommon/{version.txt => version.template} (100%) diff --git a/ActiveDirectoryConnector/version.txt b/ActiveDirectoryConnector/version.template similarity index 100% rename from ActiveDirectoryConnector/version.txt rename to ActiveDirectoryConnector/version.template diff --git a/ActiveDirectoryConnectorTests/version.txt b/ActiveDirectoryConnectorTests/version.template similarity index 100% rename from ActiveDirectoryConnectorTests/version.txt rename to ActiveDirectoryConnectorTests/version.template diff --git a/BooScriptExecutorFactory/version.txt b/BooScriptExecutorFactory/version.template similarity index 100% rename from BooScriptExecutorFactory/version.txt rename to BooScriptExecutorFactory/version.template diff --git a/Common/version.txt b/Common/version.template similarity index 100% rename from Common/version.txt rename to Common/version.template diff --git a/Console/version.txt b/Console/version.template similarity index 100% rename from Console/version.txt rename to Console/version.template diff --git a/DotNetCommonBuild.Targets b/DotNetCommonBuild.Targets index fc7d5b25..5cfd154a 100644 --- a/DotNetCommonBuild.Targets +++ b/DotNetCommonBuild.Targets @@ -23,16 +23,22 @@ - 0 - $(MSBuildProjectDirectory)\version.txt + $(MSBuildProjectDirectory)\version.template + $(MSBuildProjectDirectory)\version.txt Sun Microsystems, Inc. Copyright 2008-2009 Sun Microsystems, Inc. All rights reserved. $(MSBuildProjectDirectory)\..\Build - - + + + + + + + + @@ -40,8 +46,8 @@ - + Date: Mon, 14 Sep 2009 15:10:00 +0000 Subject: [PATCH 248/342] Issue 530: Make framework aware of its version --- Framework/Common.cs | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/Framework/Common.cs b/Framework/Common.cs index 1a2776b2..f2c6cdbd 100644 --- a/Framework/Common.cs +++ b/Framework/Common.cs @@ -285,5 +285,14 @@ public static void CheckOperationOptionValue(Object val) { CheckOperationOptionType(val.GetType()); } } + + /** + * Returns the version of the framework. + * + * @return the framework version; never null. + */ + public static Version GetFrameworkVersion() { + return Assembly.GetExecutingAssembly().GetName().Version; + } } } From fa5eb2fd2a3f1dd9f84b0797a5fa6439f3d4c410 Mon Sep 17 00:00:00 2001 From: abadea Date: Mon, 14 Sep 2009 16:33:20 +0000 Subject: [PATCH 249/342] Issue 516: Provide a way to translate an authentication username to an Uid --- Framework/Api.cs | 4 +-- Framework/ApiOperations.cs | 22 ++++++++++++++ Framework/Common.cs | 4 ++- Framework/SpiOperations.cs | 38 +++++++++++++++++++++++++ FrameworkInternal/Api.cs | 10 +++++++ FrameworkInternal/ApiLocal.cs | 2 ++ FrameworkInternal/ApiLocalOperations.cs | 32 ++++++++++++++++++++- FrameworkInternal/Serializer.cs | 2 ++ FrameworkTests/ConnectorFacadeTests.cs | 16 +++++++++++ FrameworkTests/MockConnector.cs | 9 +++++- 10 files changed, 134 insertions(+), 5 deletions(-) diff --git a/Framework/Api.cs b/Framework/Api.cs index b1b526f6..1575f892 100644 --- a/Framework/Api.cs +++ b/Framework/Api.cs @@ -147,8 +147,8 @@ public interface ConfigurationProperty { * Main interface for which consumers call the Connector API logic. */ public interface ConnectorFacade : CreateApiOp, DeleteApiOp, - SearchApiOp, UpdateApiOp, SchemaApiOp, AuthenticationApiOp, GetApiOp, - ValidateApiOp, TestApiOp, ScriptOnConnectorApiOp, ScriptOnResourceApiOp, + SearchApiOp, UpdateApiOp, SchemaApiOp, AuthenticationApiOp, ResolveUsernameApiOp, + GetApiOp, ValidateApiOp, TestApiOp, ScriptOnConnectorApiOp, ScriptOnResourceApiOp, SyncApiOp { /** diff --git a/Framework/ApiOperations.cs b/Framework/ApiOperations.cs index c4a4a339..f8aedbe1 100644 --- a/Framework/ApiOperations.cs +++ b/Framework/ApiOperations.cs @@ -53,6 +53,28 @@ public interface AuthenticationApiOp : APIOperation { Uid Authenticate(ObjectClass objectClass, string username, GuardedString password, OperationOptions options); } + public interface ResolveUsernameApiOp : APIOperation { + + /** + * Resolve the given {@link AuthenticationApiOp authentication} username + * to the corresponding {@link Uid}. The Uid is the one + * that {@link AuthenticationApiOp#authenticate} would return + * in case of a successful authentication. + * + * @param objectClass The object class to use for authenticate. + * Will typically be an account. Must not be null. + * @param username + * string that represents the account or user id. + * @param options + * additional options that impact the way this operation is run. + * May be null. + * @return Uid The uid of the account that would be used to authenticate. + * @throws RuntimeException + * iff the username could not be resolved. + */ + Uid ResolveUsername(ObjectClass objectClass, string username, OperationOptions options); + } + /// /// Operation to create connector objects based on the attributes provided. /// diff --git a/Framework/Common.cs b/Framework/Common.cs index f2c6cdbd..89b1fd3d 100644 --- a/Framework/Common.cs +++ b/Framework/Common.cs @@ -66,7 +66,9 @@ static FrameworkUtil() { new Dictionary,SafeType>(); temp[SafeType.Get()]= SafeType.Get(); - temp[SafeType.Get()]= + temp[SafeType.Get()] = + SafeType.Get(); + temp[SafeType.Get()] = SafeType.Get(); temp[SafeType.Get()]= SafeType.Get(); diff --git a/Framework/SpiOperations.cs b/Framework/SpiOperations.cs index 12f33c22..07038a10 100644 --- a/Framework/SpiOperations.cs +++ b/Framework/SpiOperations.cs @@ -63,6 +63,44 @@ public interface AuthenticateOp : SPIOperation { Uid Authenticate(ObjectClass objectClass, String username, GuardedString password, OperationOptions options); } + public interface ResolveUsernameOp : SPIOperation { + + /** + * This is a companion to the simple {@link AuthenticateOp authentication} with two parameters + * presumed to be user name and password. The difference is that this + * method does not try to authenticate the credentials; instead, it + * return the {@link Uid} of the username that would be authenticated. + *

+ * If the authentication fails the + * developer should throw a type of {@link RuntimeException} either + * {@link IllegalArgumentException} or if a native exception is available + * and if its of type {@link RuntimeException} simple throw it. If the + * native exception is not a {@link RuntimeException} wrap it in one and + * throw it. This will provide the most detail for logging problem and + * failed attempts. + *

+ * The developer is of course encourage to try and throw the most + * informative exception as possible. In that regards there are several + * exceptions provided in the exceptions package. For instance one of the + * most common is {@link InvalidPasswordException}. + * + * @param objectClass The object class to use for authenticate. + * Will typically be an account. Will not be null. + * @param username + * the username that would be authenticated. Will not be null. + * @param options + * additional options that impact the way this operation is run. + * If the caller passes null, the framework will convert this into + * an empty set of options, so SPI need not worry + * about this ever being null. + * @return Uid The uid of the account that would be authenticated. + * @throws RuntimeException + * iff native authentication fails. If a native exception is + * available attempt to throw it. + */ + Uid ResolveUsername(ObjectClass objectClass, String username, OperationOptions options); + } + /** * The {@link Connector} developer is responsible for taking the attributes * given (which always includes the {@link ObjectClass}) and create an object diff --git a/FrameworkInternal/Api.cs b/FrameworkInternal/Api.cs index 69f523c7..6e815514 100644 --- a/FrameworkInternal/Api.cs +++ b/FrameworkInternal/Api.cs @@ -604,6 +604,16 @@ public Uid Authenticate(ObjectClass objectClass,String username, GuardedString p objectClass,username, password, options); } + /** + * {@inheritDoc} + */ + public Uid ResolveUsername(ObjectClass objectClass, String username, OperationOptions options) + { + return ((ResolveUsernameApiOp)this + .GetOperationCheckSupported(SafeType.Get())).ResolveUsername( + objectClass, username, options); + } + /** * {@inheritDoc} */ diff --git a/FrameworkInternal/ApiLocal.cs b/FrameworkInternal/ApiLocal.cs index 1ada4e51..66ec6531 100644 --- a/FrameworkInternal/ApiLocal.cs +++ b/FrameworkInternal/ApiLocal.cs @@ -584,6 +584,8 @@ static LocalConnectorFacadeImpl() { SafeType.Get()); AddImplementation(SafeType.Get(), SafeType.Get()); + AddImplementation(SafeType.Get(), + SafeType.Get()); AddImplementation(SafeType.Get(), SafeType.Get()); AddImplementation(SafeType.Get(), diff --git a/FrameworkInternal/ApiLocalOperations.cs b/FrameworkInternal/ApiLocalOperations.cs index f676eeaa..dd9b41ae 100644 --- a/FrameworkInternal/ApiLocalOperations.cs +++ b/FrameworkInternal/ApiLocalOperations.cs @@ -275,7 +275,37 @@ public Uid Authenticate(ObjectClass objectClass, String username, GuardedString } } #endregion - + + #region ResolveUsernameImpl + internal class ResolveUsernameImpl : ConnectorAPIOperationRunner, + ResolveUsernameApiOp + { + /** + * Pass the configuration etc to the abstract class. + */ + public ResolveUsernameImpl(ConnectorOperationalContext context, + Connector connector) + : base(context, connector) + { + } + + /** + * Resolve the username to an Uid. + */ + public Uid ResolveUsername(ObjectClass objectClass, String username, OperationOptions options) + { + Assertions.NullCheck(objectClass, "objectClass"); + Assertions.NullCheck(username, "username"); + //convert null into empty + if (options == null) + { + options = new OperationOptionsBuilder().Build(); + } + return ((ResolveUsernameOp)GetConnector()).ResolveUsername(objectClass, username, options); + } + } + #endregion + #region CreateImpl internal class CreateImpl : ConnectorAPIOperationRunner, CreateApiOp { diff --git a/FrameworkInternal/Serializer.cs b/FrameworkInternal/Serializer.cs index 9954fc95..3ebd9ea0 100644 --- a/FrameworkInternal/Serializer.cs +++ b/FrameworkInternal/Serializer.cs @@ -1432,6 +1432,8 @@ internal static class OperationMappings { static OperationMappings() { MAPPINGS.Add(new ObjectTypeMapperImpl(typeof(AuthenticationApiOp), "AuthenticationApiOp")); + MAPPINGS.Add(new ObjectTypeMapperImpl(typeof(ResolveUsernameApiOp), + "ResolveUsernameApiOp")); MAPPINGS.Add(new ObjectTypeMapperImpl(typeof(SearchApiOp), "SearchApiOp")); MAPPINGS.Add(new ObjectTypeMapperImpl(typeof(ValidateApiOp), diff --git a/FrameworkTests/ConnectorFacadeTests.cs b/FrameworkTests/ConnectorFacadeTests.cs index 04f0b68e..219d3c27 100755 --- a/FrameworkTests/ConnectorFacadeTests.cs +++ b/FrameworkTests/ConnectorFacadeTests.cs @@ -181,6 +181,22 @@ public void AuthenticateCallPattern() }); } + [Test] + public void ResolveUsernameCallPattern() + { + TestCallPattern(new TestOperationPattern() + { + MakeCall = facade => + { + facade.ResolveUsername(ObjectClass.ACCOUNT, "dfadf", null); + }, + CheckCalls = calls => + { + Assert.AreEqual("ResolveUsername", GetAndRemoveMethodName(calls)); + } + }); + } + [Test] public void CreateCallPattern() { diff --git a/FrameworkTests/MockConnector.cs b/FrameworkTests/MockConnector.cs index cddc26a9..e7f78967 100755 --- a/FrameworkTests/MockConnector.cs +++ b/FrameworkTests/MockConnector.cs @@ -125,7 +125,7 @@ public static void AddCall(string methodName, params object[] args) public class MockAllOpsConnector : MockConnector, CreateOp, DeleteOp, UpdateOp, SearchOp, UpdateAttributeValuesOp, AuthenticateOp, - TestOp, ScriptOnConnectorOp, ScriptOnResourceOp + ResolveUsernameOp, TestOp, ScriptOnConnectorOp, ScriptOnResourceOp { public object RunScriptOnConnector(ScriptContext request, @@ -213,6 +213,13 @@ public Uid Authenticate(ObjectClass objectClass, string username, GuardedString return null; } + public Uid ResolveUsername(ObjectClass objectClass, string username, OperationOptions options) + { + Assert.IsNotNull(username); + AddCall("ResolveUsername", username); + return null; + } + public void Test() { AddCall("Test"); From 296117176b7fe4b5e1ffbad54800f30567c663a1 Mon Sep 17 00:00:00 2001 From: abadea Date: Tue, 22 Sep 2009 10:58:08 +0000 Subject: [PATCH 250/342] Issue 521: Introduce a Script type usable as a configuration property type --- Framework/Common.cs | 3 +- Framework/CommonObjects.cs | 103 +++++++++++++++++++++ FrameworkInternal/Resources.resx | 12 ++- FrameworkInternal/Serializer.cs | 24 ++++- FrameworkTests/ObjectSerializationTests.cs | 13 ++- FrameworkTests/ScriptTests.cs | 69 ++++++++++++++ 6 files changed, 218 insertions(+), 6 deletions(-) diff --git a/Framework/Common.cs b/Framework/Common.cs index 89b1fd3d..166eb673 100644 --- a/Framework/Common.cs +++ b/Framework/Common.cs @@ -108,7 +108,8 @@ static FrameworkUtil() { typeof(Uri), typeof(FileName), typeof(GuardedByteArray), - typeof(GuardedString) + typeof(GuardedString), + typeof(Script) ); ATTR_SUPPORTED_TYPES = CollectionUtil.NewReadOnlySet ( diff --git a/Framework/CommonObjects.cs b/Framework/CommonObjects.cs index af05247c..1c0c1c94 100644 --- a/Framework/CommonObjects.cs +++ b/Framework/CommonObjects.cs @@ -3194,6 +3194,109 @@ public Schema Build() { } #endregion + #region Script + /** + * Represents a script in a scripting language. + * + * @since 1.1 + */ + public sealed class Script { + + private readonly string scriptLanguage; + private readonly string scriptText; + + internal Script(string scriptLanguage, string scriptText) { + Assertions.BlankCheck(scriptLanguage, "scriptLanguage"); + Assertions.NullCheck(scriptText, "scriptText"); // Allow empty text. + this.scriptLanguage = scriptLanguage; + this.scriptText = scriptText; + } + + /** + * Returns the language of this script. + * + * @return the script language; never null. + */ + public string ScriptLanguage { + get { + return scriptLanguage; + } + } + + /** + * Returns the text of this script. + * + * @return the script text; never null. + */ + public string ScriptText { + get { + return scriptText; + } + } + + public override int GetHashCode() { + return scriptLanguage.GetHashCode() ^ scriptText.GetHashCode(); + } + + public override bool Equals(object obj) { + if (obj is Script) { + Script other = (Script) obj; + if (!scriptLanguage.Equals(other.scriptLanguage)) { + return false; + } + if (!scriptText.Equals(other.scriptText)) { + return false; + } + return true; + } + return false; + } + + public override string ToString() { + // Text can be large, probably should not be included. + return "Script: " + scriptLanguage; + } + } + #endregion + + #region ScriptBuilder + /** + * Builder for {@link Script}. + */ + public class ScriptBuilder { + + /** + * Creates a new ScriptBuilder. + */ + public ScriptBuilder() { + } + + /** + * Gets/sets the language of the script. + */ + public string ScriptLanguage { + get; set; + } + + /** + * Gets/sets the text of the script. + */ + public string ScriptText { + get; set; + } + + /** + * Creates a Script. Prior to calling this method the language + * and the text should have been set. + * + * @return a new script; never null. + */ + public Script Build() { + return new Script(ScriptLanguage, ScriptText); + } + } + #endregion + #region ScriptContext /** * Encapsulates a script and all of its parameters. diff --git a/FrameworkInternal/Resources.resx b/FrameworkInternal/Resources.resx index 9650d361..c9f28759 100644 --- a/FrameworkInternal/Resources.resx +++ b/FrameworkInternal/Resources.resx @@ -169,7 +169,7 @@ ObjectPoolConfiguration | ConfigurationProperty | ConfigurationProperties | APIConfiguration | ConnectorMessages | ConnectorKey | ConnectorInfo | UpdateApiOpType | AttributeInfo | ConnectorObject | ObjectClass | -ObjectClassInfo | Schema | ScriptContext | OperationOptions | +ObjectClassInfo | Schema | Script | ScriptContext | OperationOptions | OperationOptionInfo | SyncDeltaType | SyncToken | SyncDelta | QualifiedUid "> @@ -412,12 +412,18 @@ OperationOptionInfo | SyncDeltaType | SyncToken | SyncDelta | QualifiedUid <!ELEMENT objectClassesByOperation (Map)> <!ELEMENT optionsByOperation (Map)> +<!ELEMENT scriptText (#PCDATA)> +<!ELEMENT scriptArguments (Map)> + +<!ELEMENT Script (scriptText)> +<!ATTLIST Script + scriptLanguage CDATA #REQUIRED +> + <!ELEMENT ScriptContext (scriptArguments,scriptText)> <!ATTLIST ScriptContext scriptLanguage CDATA #REQUIRED > -<!ELEMENT scriptText (#PCDATA)> -<!ELEMENT scriptArguments (Map)> <!ELEMENT OperationOptions (options)> <!ELEMENT options (Map)> diff --git a/FrameworkInternal/Serializer.cs b/FrameworkInternal/Serializer.cs index 3ebd9ea0..b2e8da2b 100644 --- a/FrameworkInternal/Serializer.cs +++ b/FrameworkInternal/Serializer.cs @@ -1492,6 +1492,7 @@ static CommonObjectHandlers() { HANDLERS.Add( new ObjectClassInfoHandler() ); HANDLERS.Add( new SchemaHandler() ); HANDLERS.Add( new UidHandler() ); + HANDLERS.Add( new ScriptHandler()); HANDLERS.Add( new ScriptContextHandler() ); HANDLERS.Add( new OperationOptionsHandler() ); HANDLERS.Add( new OperationOptionInfoHandler() ); @@ -1952,7 +1953,28 @@ public override void Serialize(Object obj, ObjectEncoder encoder) { encoder.WriteStringContents(val.GetUidValue()); } } - private class ScriptContextHandler : AbstractObjectSerializationHandler { + private class ScriptHandler : AbstractObjectSerializationHandler + { + public ScriptHandler() + : base(typeof(Script), "Script") { + + } + public override Object Deserialize(ObjectDecoder decoder) { + ScriptBuilder builder = new ScriptBuilder(); + builder.ScriptLanguage = decoder.ReadStringField("scriptLanguage", null); + builder.ScriptText = (String)decoder.ReadObjectField("scriptText", typeof(string), null); + return builder.Build(); + } + + public override void Serialize(Object obj, ObjectEncoder encoder) + { + Script val = (Script)obj; + encoder.WriteStringField("scriptLanguage", val.ScriptLanguage); + encoder.WriteObjectField("scriptText", val.ScriptText, true); + } + } + private class ScriptContextHandler : AbstractObjectSerializationHandler + { public ScriptContextHandler() : base(typeof(ScriptContext),"ScriptContext") { diff --git a/FrameworkTests/ObjectSerializationTests.cs b/FrameworkTests/ObjectSerializationTests.cs index db950436..b3c1ef0c 100644 --- a/FrameworkTests/ObjectSerializationTests.cs +++ b/FrameworkTests/ObjectSerializationTests.cs @@ -966,7 +966,18 @@ public void TestOperationOptions() { Assert.AreEqual("bar",v2.Options["foo"]); Assert.AreEqual("bar2",v2.Options["foo2"]); } - + + [Test] + public void TestScript() { + ScriptBuilder builder = new ScriptBuilder(); + builder.ScriptLanguage = "language"; + builder.ScriptText = "text"; + Script v1 = builder.Build(); + Script v2 = (Script)CloneObject(v1); + Assert.AreEqual("language", v2.ScriptLanguage); + Assert.AreEqual("text", v2.ScriptText); + } + [Test] public void TestScriptContext() { ScriptContextBuilder builder = new ScriptContextBuilder(); diff --git a/FrameworkTests/ScriptTests.cs b/FrameworkTests/ScriptTests.cs index 62baf918..0f3b3ec8 100644 --- a/FrameworkTests/ScriptTests.cs +++ b/FrameworkTests/ScriptTests.cs @@ -25,6 +25,7 @@ using System.Reflection; using Org.IdentityConnectors.Common; using Org.IdentityConnectors.Common.Script; +using Org.IdentityConnectors.Framework.Common.Objects; using NUnit.Framework; using NUnit.Framework.SyntaxHelpers; @@ -58,5 +59,73 @@ public void testShellScripting() { public void testUnsupported() { ScriptExecutorFactory.NewInstance("fadsflkj"); } + + [Test] + public void testBasic() { + ScriptBuilder builder = new ScriptBuilder(); + builder.ScriptLanguage = "Groovy"; + builder.ScriptText = "print 'foo'"; + Script s1 = builder.Build(); + Assert.AreEqual("Groovy", s1.ScriptLanguage); + Assert.AreEqual("print 'foo'", s1.ScriptText); + builder = new ScriptBuilder(); + builder.ScriptLanguage = "Groovy"; + builder.ScriptText = "print 'foo'"; + Script s2 = builder.Build(); + Assert.AreEqual(s1, s2); + Assert.AreEqual(s1.GetHashCode(), s2.GetHashCode()); + } + + [Test] + public void testLanguageNotBlank() { + try { + ScriptBuilder builder = new ScriptBuilder(); + builder.ScriptText = "print 'foo'"; + builder.Build(); + Assert.Fail(); + } catch (ArgumentException) { + // OK. + } + + try { + ScriptBuilder builder = new ScriptBuilder(); + builder.ScriptText = "print 'foo'"; + builder.ScriptLanguage = ""; + builder.Build(); + Assert.Fail(); + } catch (ArgumentException) { + // OK. + } + + try { + ScriptBuilder builder = new ScriptBuilder(); + builder.ScriptText = "print 'foo'"; + builder.ScriptLanguage = " "; + builder.Build(); + Assert.Fail(); + } + catch (ArgumentException) + { + // OK. + } + } + + [Test] + public void testTextNotNull() { + ScriptBuilder builder = new ScriptBuilder(); + try { + builder.ScriptLanguage = "Groovy"; + builder.Build(); + Assert.Fail(); + } catch (ArgumentNullException) { + // OK. + } + + // The text can be empty. + builder = new ScriptBuilder(); + builder.ScriptLanguage = "Groovy"; + builder.ScriptText = ""; + builder.Build(); + } } } From eec367b7649347077ec05fdfeb6be9b0e39d1985 Mon Sep 17 00:00:00 2001 From: lajos_nagy Date: Thu, 8 Oct 2009 09:54:59 +0000 Subject: [PATCH 251/342] Issue 577: Improve the documentation of the .Net Connector Framework Only the format of the documentation has been changed from JavaDoc to Xml doc, their content might still contain invalid information. Wherever a 1:1 relationship existed between a Java class/interface/etc. and a .Net one, the references have been replaced, however, it is not the case for every reference, hence there might be still invalid refs. too (e.g. IllegalStateException can be resolved to more .Net exceptions). In addition, the files have been modified to adhere to a common formatting convention. --- .../BooScriptExecutorFactory.cs | 36 +- Common/Assertions.cs | 49 +- Common/CollectionUtil.cs | 761 ++- Common/DateTimeUtil.cs | 17 +- Common/FrameworkInternalBridge.cs | 20 +- Common/IOUtil.cs | 5 +- Common/Locale.cs | 164 +- Common/Pair.cs | 30 +- Common/Pooling.cs | 226 +- Common/Proxy.cs | 199 +- Common/ReflectionUtil.cs | 102 +- Common/SafeType.cs | 228 +- Common/Script.cs | 147 +- Common/Security.cs | 815 ++- Common/StringUtil.cs | 79 +- Common/TraceUtil.cs | 9 +- Common/XmlUtil.cs | 219 +- Framework/Api.cs | 665 +- Framework/ApiOperations.cs | 673 +- Framework/Common.cs | 321 +- Framework/CommonExceptions.cs | 447 +- Framework/CommonObjects.cs | 5985 +++++++++-------- Framework/CommonObjectsFilter.cs | 1671 ++--- Framework/CommonSerializer.cs | 416 +- Framework/Spi.cs | 261 +- Framework/SpiOperations.cs | 763 ++- FrameworkInternal/Api.cs | 959 +-- FrameworkInternal/ApiLocal.cs | 1068 +-- FrameworkInternal/ApiLocalOperations.cs | 1478 ++-- FrameworkInternal/ApiRemote.cs | 363 +- FrameworkInternal/ApiRemoteMessages.cs | 247 +- FrameworkInternal/ExceptionUtil.cs | 16 +- FrameworkInternal/Security.cs | 104 +- FrameworkInternal/Serializer.cs | 3082 +++++---- FrameworkInternal/SerializerBinary.cs | 762 ++- FrameworkInternal/SerializerXml.cs | 814 ++- FrameworkInternal/Server.cs | 971 +-- FrameworkInternal/Test.cs | 111 +- FrameworkTests/CollectionUtilTests.cs | 106 +- .../ConnectorFacadeExceptionTests.cs | 10 +- FrameworkTests/ConnectorFacadeTests.cs | 5 +- FrameworkTests/ConnectorInfoManagerTests.cs | 485 +- FrameworkTests/ExceptionUtilTests.cs | 34 +- FrameworkTests/FilterTranslatorTests.cs | 394 +- FrameworkTests/GuardedByteArrayTests.cs | 62 +- FrameworkTests/GuardedStringTests.cs | 59 +- FrameworkTests/LocaleTests.cs | 43 +- FrameworkTests/MockConnector.cs | 45 +- FrameworkTests/ObjectNormalizerFacadeTests.cs | 161 +- FrameworkTests/ObjectPoolTests.cs | 198 +- FrameworkTests/ObjectSerializationTests.cs | 937 +-- FrameworkTests/ProxyTests.cs | 39 +- FrameworkTests/SafeTypeTest.cs | 18 +- FrameworkTests/ScriptTests.cs | 56 +- FrameworkTests/TestHelperTests.cs | 32 +- FrameworkTests/TestUtil.cs | 12 +- FrameworkTests/UpdateImplTests.cs | 121 +- Service/Program.cs | 391 +- .../ShellScriptExecutorFactory.cs | 65 +- TestBundleV1/TestConnector.cs | 188 +- TestBundleV2/TestConnector.cs | 27 +- TestCommon/FrameworkInternalBridge.cs | 22 +- TestCommon/Test.cs | 357 +- TestCommon/TestHelpersSpi.cs | 12 +- 64 files changed, 15706 insertions(+), 12456 deletions(-) diff --git a/BooScriptExecutorFactory/BooScriptExecutorFactory.cs b/BooScriptExecutorFactory/BooScriptExecutorFactory.cs index 72e5664e..3b1a1b8a 100644 --- a/BooScriptExecutorFactory/BooScriptExecutorFactory.cs +++ b/BooScriptExecutorFactory/BooScriptExecutorFactory.cs @@ -35,43 +35,51 @@ public class BooScriptExecutorFactory : ScriptExecutorFactory ///

/// Attempt to trigger an exception if the runtime is not present. /// - public BooScriptExecutorFactory() { + public BooScriptExecutorFactory() + { new BooScriptExecutor(new Assembly[0], "1").Execute(null); } - + /// /// Creates a script executor give the Boo script. /// override - public ScriptExecutor NewScriptExecutor(Assembly [] referencedAssemblies, string script, bool compile) { - return new BooScriptExecutor(referencedAssemblies,script); + public ScriptExecutor NewScriptExecutor(Assembly[] referencedAssemblies, string script, bool compile) + { + return new BooScriptExecutor(referencedAssemblies, script); } - + /// /// Processes the script. /// - class BooScriptExecutor : ScriptExecutor { - private readonly Assembly [] _referencedAssemblies; + class BooScriptExecutor : ScriptExecutor + { + private readonly Assembly[] _referencedAssemblies; private readonly string _script; private readonly InteractiveInterpreter _engine; - - public BooScriptExecutor(Assembly [] referencedAssemblies, string script) { + + public BooScriptExecutor(Assembly[] referencedAssemblies, string script) + { _referencedAssemblies = referencedAssemblies; _script = script; _engine = new InteractiveInterpreter(); _engine.RememberLastValue = true; - foreach (Assembly assembly in referencedAssemblies) { - _engine.References.Add(assembly); + foreach (Assembly assembly in referencedAssemblies) + { + _engine.References.Add(assembly); } } - public object Execute(IDictionary arguments) { + public object Execute(IDictionary arguments) + { // add all the globals IDictionary args = CollectionUtil.NullAsEmpty(arguments); - foreach (KeyValuePair entry in args) { + foreach (KeyValuePair entry in args) + { _engine.SetValue(entry.Key, entry.Value); } CompilerContext context = _engine.Eval(_script); - if ( context.Errors.Count > 0 ) { + if (context.Errors.Count > 0) + { throw context.Errors[0]; } return _engine.LastValue; diff --git a/Common/Assertions.cs b/Common/Assertions.cs index 562676ce..2b933300 100644 --- a/Common/Assertions.cs +++ b/Common/Assertions.cs @@ -29,39 +29,36 @@ namespace Org.IdentityConnectors.Common /// public static class Assertions { - /** - * Throws {@link NullPointerException} if the parameter o - * is null. - * - * @param o - * check if the object is null. - * @param param - * name of the parameter to check for null. - * @throws NullPointerException - * if o is null and constructs a - * message with the name of the parameter. - */ - public static void NullCheck(Object o, String param) { + /// + /// Throws if the parameter o + /// is null. + /// + /// check if the object is null. + /// name of the parameter to check for null. + /// if o is null and constructs a + /// message with the name of the parameter. + public static void NullCheck(Object o, String param) + { String FORMAT = "Parameter '{0}' must not be null."; - if (o == null) { + if (o == null) + { throw new ArgumentNullException(String.Format(FORMAT, param)); } } - /** - * Throws {@link IllegalArgumentException} if the parameter o - * is null or blank. - * - * @param o - * value to test for blank. - * @param param - * name of the parameter to check. - */ - public static void BlankCheck(String o, String param) { + /// + /// Throws if the parameter o + /// is null or blank. + /// + /// value to test for blank. + /// name of the parameter to check. + public static void BlankCheck(String o, String param) + { String FORMAT = "Parameter '{0}' must not be blank."; - if (StringUtil.IsBlank(o)) { + if (StringUtil.IsBlank(o)) + { throw new ArgumentException(String.Format(FORMAT, param)); } } } -} +} \ No newline at end of file diff --git a/Common/CollectionUtil.cs b/Common/CollectionUtil.cs index 93122f14..731ac6b8 100644 --- a/Common/CollectionUtil.cs +++ b/Common/CollectionUtil.cs @@ -27,139 +27,181 @@ namespace Org.IdentityConnectors.Common { - internal class IdentityEqualityComparer : IEqualityComparer where T : class { - public bool Equals(T x, T y) { - return Object.ReferenceEquals(x,y); + internal class IdentityEqualityComparer : IEqualityComparer where T : class + { + public bool Equals(T x, T y) + { + return Object.ReferenceEquals(x, y); } - public int GetHashCode(T o) { + public int GetHashCode(T o) + { return RuntimeHelpers.GetHashCode(o); } } - - internal class ReadOnlyCollection : ICollection { + + internal class ReadOnlyCollection : ICollection + { private readonly ICollection _target; - public ReadOnlyCollection(ICollection target) { + public ReadOnlyCollection(ICollection target) + { _target = target; } - public void Add(T item) { + public void Add(T item) + { throw new NotSupportedException(); } - public void Clear() { + public void Clear() + { throw new NotSupportedException(); } - public bool Contains(T item) { + public bool Contains(T item) + { return _target.Contains(item); } - System.Collections.IEnumerator System.Collections.IEnumerable.GetEnumerator() { + System.Collections.IEnumerator System.Collections.IEnumerable.GetEnumerator() + { return _target.GetEnumerator(); } - public IEnumerator GetEnumerator() { + public IEnumerator GetEnumerator() + { return _target.GetEnumerator(); } - public bool IsReadOnly { - get { + public bool IsReadOnly + { + get + { return true; } } - public int Count { - get { + public int Count + { + get + { return _target.Count; } } - public bool Remove(T item) { + public bool Remove(T item) + { throw new NotSupportedException(); } public void CopyTo(T[] array, - int arrayIndex) { - _target.CopyTo(array,arrayIndex); + int arrayIndex) + { + _target.CopyTo(array, arrayIndex); } - - public ICollection GetTarget() { + + public ICollection GetTarget() + { return _target; } } - - - internal class ReadOnlyList : ReadOnlyCollection, IList { - - public ReadOnlyList(IList target) : base(target) { - - } - public int IndexOf(T item) { + + + internal class ReadOnlyList : ReadOnlyCollection, IList + { + + public ReadOnlyList(IList target) + : base(target) + { + + } + public int IndexOf(T item) + { return GetTarget().IndexOf(item); } - public void Insert(int index, T item) { + public void Insert(int index, T item) + { throw new NotSupportedException(); } - public void RemoveAt(int index) { + public void RemoveAt(int index) + { throw new NotSupportedException(); } - - public T this[int index] { - get { + + public T this[int index] + { + get + { return GetTarget()[index]; } - set { + set + { throw new NotSupportedException(); } } - - protected new IList GetTarget() { + + protected new IList GetTarget() + { return (IList)base.GetTarget(); } } - - internal class ReadOnlyDictionary : + + internal class ReadOnlyDictionary : ReadOnlyCollection>, - IDictionary { - public ReadOnlyDictionary(IDictionary target) : base(target) { - + IDictionary + { + public ReadOnlyDictionary(IDictionary target) + : base(target) + { + } - public void Add(TKey key, TValue val) { + public void Add(TKey key, TValue val) + { throw new NotSupportedException(); } - protected new IDictionary GetTarget() { - return (IDictionary)base.GetTarget(); + protected new IDictionary GetTarget() + { + return (IDictionary)base.GetTarget(); } - public ICollection Keys { - get { + public ICollection Keys + { + get + { return new ReadOnlyCollection(GetTarget().Keys); } } - public ICollection Values { - get { + public ICollection Values + { + get + { return new ReadOnlyCollection(GetTarget().Values); } } - public bool Remove(TKey key) { + public bool Remove(TKey key) + { throw new NotSupportedException(); } - public bool ContainsKey(TKey key) { + public bool ContainsKey(TKey key) + { return GetTarget().ContainsKey(key); } - public bool TryGetValue(TKey key, out TValue value) { - return GetTarget().TryGetValue(key,out value); + public bool TryGetValue(TKey key, out TValue value) + { + return GetTarget().TryGetValue(key, out value); } - public TValue this[TKey key] { - get { + public TValue this[TKey key] + { + get + { return GetTarget()[key]; } - set { + set + { throw new NotSupportedException(); } } } - - - - + + + + /// /// Delegate that returns the Key, given a value /// - public delegate TKey KeyFunction(TValue value); - - + public delegate TKey KeyFunction(TValue value); + + /// /// Description of CollectionUtil. /// @@ -169,71 +211,81 @@ public static class CollectionUtil /// Creates a case-insensitive set /// /// An empty case-insensitive set - public static ICollection NewCaseInsensitiveSet() { + public static ICollection NewCaseInsensitiveSet() + { HashSet rv = new HashSet(StringComparer.OrdinalIgnoreCase); return rv; } - + /// /// Returns true iff the collection is a case-insensitive set /// /// The collection. May be null. /// true iff the collection is a case-insensitive set - public static bool IsCaseInsensitiveSet(ICollection collection) { - if ( collection is ReadOnlyCollection ) { + public static bool IsCaseInsensitiveSet(ICollection collection) + { + if (collection is ReadOnlyCollection) + { ReadOnlyCollection roc = (ReadOnlyCollection)collection; return IsCaseInsensitiveSet(roc.GetTarget()); } - else if ( collection is HashSet ) { + else if (collection is HashSet) + { HashSet set = (HashSet)collection; return StringComparer.OrdinalIgnoreCase.Equals(set.Comparer); } - else { + else + { return false; } - } + } /// /// Creates a case-insensitive map /// /// An empty case-insensitive map - public static IDictionary NewCaseInsensitiveDictionary() { - Dictionary rv = new Dictionary(StringComparer.OrdinalIgnoreCase); + public static IDictionary NewCaseInsensitiveDictionary() + { + Dictionary rv = new Dictionary(StringComparer.OrdinalIgnoreCase); return rv; } - + /// /// Returns true iff the collection is a case-insensitive map /// /// The map. May be null. /// true iff the collection is a case-insensitive map - public static bool IsCaseInsensitiveDictionary(IDictionary map) { - if ( map is ReadOnlyDictionary ) { - ReadOnlyDictionary roc = - (ReadOnlyDictionary)map; - return IsCaseInsensitiveDictionary((IDictionary)roc.GetTarget()); - } - else if ( map is Dictionary ) { - Dictionary d = (Dictionary)map; + public static bool IsCaseInsensitiveDictionary(IDictionary map) + { + if (map is ReadOnlyDictionary) + { + ReadOnlyDictionary roc = + (ReadOnlyDictionary)map; + return IsCaseInsensitiveDictionary((IDictionary)roc.GetTarget()); + } + else if (map is Dictionary) + { + Dictionary d = (Dictionary)map; return StringComparer.OrdinalIgnoreCase.Equals(d.Comparer); } - else { + else + { return false; } - } - + } + /// /// Creates a dictionary where the keys are looked up using /// reference equality /// /// - public static IDictionary NewIdentityDictionary() + public static IDictionary NewIdentityDictionary() where K : class { IdentityEqualityComparer comp = new IdentityEqualityComparer(); - return new Dictionary(comp); + return new Dictionary(comp); } - + /// /// Computes a hashCode over the enumeration. The hashCode is computed /// such that it doesn't matter what order the elements are listed in. Thus, it @@ -241,33 +293,39 @@ public static IDictionary NewIdentityDictionary() /// /// The enumerable /// The hashcode - public static int GetEnumerableHashCode(IEnumerable enum1) { - if ( enum1 == null ) { + public static int GetEnumerableHashCode(IEnumerable enum1) + { + if (enum1 == null) + { return 0; - } + } int rv = 0; - foreach (T val1 in enum1) { - unchecked { - rv+=CollectionUtil.GetHashCode(val1); + foreach (T val1 in enum1) + { + unchecked + { + rv += CollectionUtil.GetHashCode(val1); } } return rv; } - + /// /// Computes a hashCode for a key value pair. /// /// The pair /// The hashcode - public static int GetKeyValuePairHashCode(KeyValuePair pair) { + public static int GetKeyValuePairHashCode(KeyValuePair pair) + { int rv = 0; - unchecked { - rv+=CollectionUtil.GetHashCode(pair.Key); - rv+=CollectionUtil.GetHashCode(pair.Value); + unchecked + { + rv += CollectionUtil.GetHashCode(pair.Key); + rv += CollectionUtil.GetHashCode(pair.Value); } return rv; } - + /// /// Returns true iff the two sets contain the same elements. This is only for /// sets and dictionaries. Does not work for Lists or Arrays. @@ -276,21 +334,26 @@ public static int GetKeyValuePairHashCode(KeyValuePair pair) { /// The second collection /// public static bool SetsEqual(ICollection collection1, - ICollection collection2) { - if ( collection1 == null || collection1 == null ) { + ICollection collection2) + { + if (collection1 == null || collection1 == null) + { return collection1 == null && collection1 == null; } - if ( collection1.Count != collection2.Count ) { + if (collection1.Count != collection2.Count) + { return false; } - foreach (T val1 in collection1) { - if (!collection2.Contains(val1)) { + foreach (T val1 in collection1) + { + if (!collection2.Contains(val1)) + { return false; } } return true; } - + /// /// Gets the given value from the map or default if not exists. Always /// use this rather than map[] since map[] throws an exception if not @@ -300,34 +363,40 @@ public static bool SetsEqual(ICollection collection1, /// The key /// The default value /// - public static TValue GetValue(IDictionary map, + public static TValue GetValue(IDictionary map, TKey key, - TValue def) { + TValue def) + { TValue rv; - bool present = map.TryGetValue(key,out rv); - if (present) { + bool present = map.TryGetValue(key, out rv); + if (present) + { return rv; } - else { + else + { return def; } } - + /// /// Adds all the elements from the given enumerable to the given collection. /// /// The collection to add to /// The enumerable to get from - public static void AddAll(ICollection collection, - IEnumerable enumerable) - where U : T { - if ( enumerable != null ) { - foreach(U obj in enumerable) { + public static void AddAll(ICollection collection, + IEnumerable enumerable) + where U : T + { + if (enumerable != null) + { + foreach (U obj in enumerable) + { collection.Add(obj); } } } - + /// /// Adds all the elements from the given enumerable to the given collection. /// Replace the element value if already stored in the collection. @@ -339,12 +408,15 @@ public static void AddAll(ICollection collection, /// The collection to add to /// The enumerable to get from public static void AddOrReplaceAll(IDictionary collection, - IEnumerable> enumerable) + IEnumerable> enumerable) where UKey : TKey - where UValue : TValue { - if (enumerable != null) { - foreach (KeyValuePair obj in enumerable) { - collection[obj.Key] = obj.Value; + where UValue : TValue + { + if (enumerable != null) + { + foreach (KeyValuePair obj in enumerable) + { + collection[obj.Key] = obj.Value; } } } @@ -354,56 +426,65 @@ public static void AddOrReplaceAll(IDictionary /// The collection to add to /// The enumerable to get from - public static void RemoveAll(ICollection collection, - IEnumerable enumerable) - where U : T { - if ( enumerable != null ) { - foreach(U obj in enumerable) { + public static void RemoveAll(ICollection collection, + IEnumerable enumerable) + where U : T + { + if (enumerable != null) + { + foreach (U obj in enumerable) + { collection.Remove(obj); } } } - + /// /// Adds all the elements from the given enumerable to the given collection. /// /// The collection to add to /// The enumerable to get from - public static void RemovalAll(ICollection collection, - IEnumerable enumerable) - where U : T { - if ( enumerable != null ) { - foreach(U obj in enumerable) { + public static void RemovalAll(ICollection collection, + IEnumerable enumerable) + where U : T + { + if (enumerable != null) + { + foreach (U obj in enumerable) + { collection.Remove(obj); } } } - + /// /// Returns c or an empty collection iff c is null. /// /// The collection /// c or an empty collection iff c is null. - public static ICollection NullAsEmpty(ICollection c) { + public static ICollection NullAsEmpty(ICollection c) + { return c ?? new HashSet(); } - + /// /// Returns c or an empty collection iff c is null. /// /// The collection /// c or an empty collection iff c is null. - public static IDictionary NullAsEmpty(IDictionary c) { - return c ?? new Dictionary(); + public static IDictionary NullAsEmpty(IDictionary c) + { + return c ?? new Dictionary(); } - + /// /// Returns c or an empty collection iff c is null. /// /// The collection /// c or an empty collection iff c is null. - public static IList NullAsEmpty(IList c) { + public static IList NullAsEmpty(IList c) + { return c ?? new List(); } @@ -412,7 +493,8 @@ public static IList NullAsEmpty(IList c) { /// /// The array /// c or an empty collection iff c is null. - public static T[] NullAsEmpty(T [] c) { + public static T[] NullAsEmpty(T[] c) + { return c ?? new T[0]; } @@ -424,12 +506,13 @@ public static T[] NullAsEmpty(T [] c) { /// The dictionay public static IDictionary NewDictionary( TKey k1, - TValue v1) { + TValue v1) + { IDictionary rv = new Dictionary(); rv[k1] = v1; return rv; } - + /// /// Given a collection of values a key function, builds a dictionary /// @@ -438,10 +521,13 @@ public static IDictionary NewDictionary( /// The dictionay public static IDictionary NewDictionary( IEnumerable values, - KeyFunction keyFunction) { + KeyFunction keyFunction) + { IDictionary rv = new Dictionary(); - if ( values != null ) { - foreach (TValue value in values) { + if (values != null) + { + foreach (TValue value in values) + { TKey key = keyFunction(value); //DONT use Add - it throws exceptions if already there rv[key] = value; @@ -449,7 +535,7 @@ public static IDictionary NewDictionary( } return rv; } - + /// /// Given a collection of values a key function, builds a dictionary /// @@ -458,12 +544,13 @@ public static IDictionary NewDictionary( /// The dictionay public static IDictionary NewReadOnlyDictionary( IEnumerable values, - KeyFunction keyFunction) { - IDictionary rv = - NewDictionary(values,keyFunction); + KeyFunction keyFunction) + { + IDictionary rv = + NewDictionary(values, keyFunction); return new ReadOnlyDictionary(rv); } - + /// /// Given a collection of values a key function, builds a dictionary /// @@ -471,28 +558,32 @@ public static IDictionary NewReadOnlyDictionary( /// Key function, mapping from key to value /// The dictionay public static IDictionary NewDictionary( - IDictionary original) { - return NewDictionary(original); + IDictionary original) + { + return NewDictionary(original); } - + /// /// Given a collection of values a key function, builds a dictionary /// /// List of values /// Key function, mapping from key to value /// The dictionay - public static IDictionary NewDictionary( - IDictionary original) { + public static IDictionary NewDictionary( + IDictionary original) + { IDictionary rv = new Dictionary(); - if ( original != null ) { - foreach (KeyValuePair entry in original) { + if (original != null) + { + foreach (KeyValuePair entry in original) + { //DONT use Add - it throws exceptions if already there rv[(TKey2)(object)entry.Key] = (TValue2)(object)entry.Value; } } return rv; } - + /// /// Given a collection of values a key function, builds a dictionary /// @@ -500,55 +591,62 @@ public static IDictionary NewDictionaryKey function, mapping from key to value /// The dictionay public static IDictionary NewReadOnlyDictionary( - IDictionary original) { - return NewReadOnlyDictionary(original); + IDictionary original) + { + return NewReadOnlyDictionary(original); } - + /// /// Given a collection of values a key function, builds a dictionary /// /// List of values /// Key function, mapping from key to value /// The dictionay - public static IDictionary NewReadOnlyDictionary( - IDictionary original) { - IDictionary rv = NewDictionary(original); + public static IDictionary NewReadOnlyDictionary( + IDictionary original) + { + IDictionary rv = NewDictionary(original); return new ReadOnlyDictionary(rv); } - + /// /// Returns a modifiable list, after first copying the collection. /// /// A collection. May be null. /// a modifiable list, after first copying the collection. - public static IList NewList(IEnumerable collection) { - return NewList(collection); - } - + public static IList NewList(IEnumerable collection) + { + return NewList(collection); + } + /// /// Returns a modifiable list, after first copying the collection. /// /// A collection. May be null. /// a modifiable list, after first copying the collection. - public static IList NewList(IEnumerable collection) { + public static IList NewList(IEnumerable collection) + { IList rv = new List(); - if ( collection != null ) { - foreach (T element in collection) { + if (collection != null) + { + foreach (T element in collection) + { rv.Add((U)(object)element); - } + } } return rv; - } + } /// /// Returns a modifiable set, after first copying the collection. /// /// A collection. May be null. /// a modifiable set, after first copying the collection. - public static ICollection NewSet(IEnumerable collection) { - return NewSet(collection); + public static ICollection NewSet(IEnumerable collection) + { + return NewSet(collection); } /// @@ -556,21 +654,25 @@ public static ICollection NewSet(IEnumerable collection) { /// /// An array maybe null. /// a modifiable set, after first copying the array. - public static ICollection NewSet(params T[] items) { + public static ICollection NewSet(params T[] items) + { return NewSet((IEnumerable)items); } - + /// /// Returns a modifiable set, after first copying the collection. /// /// A collection. May be null. /// a modifiable set, after first copying the collection. - public static ICollection NewSet(IEnumerable collection) { + public static ICollection NewSet(IEnumerable collection) + { ICollection rv = new HashSet(); - if ( collection != null ) { - foreach (T element in collection) { + if (collection != null) + { + foreach (T element in collection) + { rv.Add((U)(object)element); - } + } } return rv; } @@ -581,179 +683,204 @@ public static ICollection NewSet(IEnumerable collection) { /// /// A collection. May be null. /// an unmodifiable list, after first copying the collection. - public static IList NewReadOnlyList(ICollection collection) { - return NewReadOnlyList(collection); + public static IList NewReadOnlyList(ICollection collection) + { + return NewReadOnlyList(collection); } - + /// /// Returns an unmodifiable list, after first copying the collection. /// /// A collection. May be null. /// an unmodifiable list, after first copying the collection. - public static IList NewReadOnlyList(ICollection collection) { - IList list = NewList(collection); + public static IList NewReadOnlyList(ICollection collection) + { + IList list = NewList(collection); return new ReadOnlyList(list); - } - + } + /// /// Returns an unmodifiable list, after first copying the collection. /// /// A collection. May be null. /// an unmodifiable list, after first copying the collection. - public static ICollection NewReadOnlySet(ICollection collection) { - return NewReadOnlySet(collection); + public static ICollection NewReadOnlySet(ICollection collection) + { + return NewReadOnlySet(collection); } - + /// /// Returns an unmodifiable list, after first copying the collection. /// /// A collection. May be null. /// an unmodifiable list, after first copying the collection. - private static ICollection NewReadOnlySet(ICollection collection) { - ICollection list = NewSet(collection); + private static ICollection NewReadOnlySet(ICollection collection) + { + ICollection list = NewSet(collection); return new ReadOnlyCollection(list); - } - + } + /// /// Returns an unmodifiable set, backed by the original /// /// A collection. May be null. /// an unmodifiable list, after first copying the collection. - public static ICollection AsReadOnlySet(ICollection collection) { + public static ICollection AsReadOnlySet(ICollection collection) + { ICollection list = NullAsEmpty(collection); return new ReadOnlyCollection(list); - } - + } + /// /// Returns an unmodifiable list, backed by the original /// /// A collection. May be null. /// an unmodifiable list, after first copying the collection. - public static IList AsReadOnlyList(IList collection) { + public static IList AsReadOnlyList(IList collection) + { IList list = NullAsEmpty(collection); return new ReadOnlyList(list); - } - + } + /// /// Returns an unmodifiable list, backed by the original /// /// A collection. May be null. /// an unmodifiable list, after first copying the collection. - public static IDictionary AsReadOnlyDictionary(IDictionary d) { + public static IDictionary AsReadOnlyDictionary(IDictionary d) + { d = NullAsEmpty(d); - return new ReadOnlyDictionary(d); - } - + return new ReadOnlyDictionary(d); + } + /// /// Creates a new read-only list from an array. /// /// /// - public static IList NewReadOnlyList(params T [] args) { - return NewReadOnlyList(args); + public static IList NewReadOnlyList(params T[] args) + { + return NewReadOnlyList(args); } - + /// /// Creates a new read-only list from an array. /// /// /// - private static IList NewReadOnlyList(params T [] args) { - IList list = CollectionUtil.NewList(args); + private static IList NewReadOnlyList(params T[] args) + { + IList list = CollectionUtil.NewList(args); return new ReadOnlyList(list); } - + /// /// Creates a new read-only set from an array. /// /// /// - public static ICollection NewReadOnlySet(params T [] args) { - return NewReadOnlySet(args); + public static ICollection NewReadOnlySet(params T[] args) + { + return NewReadOnlySet(args); } /// /// Creates a new read-only set from an array. /// /// /// - private static ICollection NewReadOnlySet(params T [] args) { - ICollection list = CollectionUtil.NewSet(args); + private static ICollection NewReadOnlySet(params T[] args) + { + ICollection list = CollectionUtil.NewSet(args); return new ReadOnlyCollection(list); } - - public static bool DictionariesEqual(IDictionary m1, - IDictionary m2) + + public static bool DictionariesEqual(IDictionary m1, + IDictionary m2) { - if ( m1.Count != m2.Count ) { + if (m1.Count != m2.Count) + { return false; } - foreach (KeyValuePair entry1 in m1) { + foreach (KeyValuePair entry1 in m1) + { K key1 = entry1.Key; V val1 = entry1.Value; - if (!m2.ContainsKey(key1)) { + if (!m2.ContainsKey(key1)) + { return false; } Object val2 = m2[key1]; - if (!CollectionUtil.Equals(val1,val2)) { + if (!CollectionUtil.Equals(val1, val2)) + { return false; } } return true; } - + public static bool ListsEqual(IList v1, IList v2) { - if ( v1.Count != v2.Count ) { + if (v1.Count != v2.Count) + { return false; } - for ( int i = 0; i < v1.Count; i++ ) { - if (!CollectionUtil.Equals(v1[i],v2[i])) { + for (int i = 0; i < v1.Count; i++) + { + if (!CollectionUtil.Equals(v1[i], v2[i])) + { return false; } } return true; } - - /** - * hashCode function that properly handles arrays, - * collections, maps, collections of arrays, and maps of arrays. - * @param o The object. May be null. - * @return the hashCode - */ - public static int GetHashCode(Object o) { - if ( o == null ) { - return 0; - } - else if ( o is Array ) { + + /// + /// hashCode function that properly handles arrays, + /// collections, maps, collections of arrays, and maps of arrays. + /// + /// The object. May be null. + /// the hashCode + public static int GetHashCode(Object o) + { + if (o == null) + { + return 0; + } + else if (o is Array) + { Array array = (Array)o; int length = array.Length; int rv = 0; - for ( int i = 0; i < length; i++ ) { + for (int i = 0; i < length; i++) + { Object el = array.GetValue(i); - unchecked { + unchecked + { rv += CollectionUtil.GetHashCode(el); } } return rv; } - else if ( ReflectionUtil.IsParentTypeOf(typeof(KeyValuePair<,>),o.GetType()) ) { - Type parent = ReflectionUtil.FindInHierarchyOf(typeof(KeyValuePair<,>),o.GetType()); - Type [] genericArguments = + else if (ReflectionUtil.IsParentTypeOf(typeof(KeyValuePair<,>), o.GetType())) + { + Type parent = ReflectionUtil.FindInHierarchyOf(typeof(KeyValuePair<,>), o.GetType()); + Type[] genericArguments = parent.GetGenericArguments(); Type collectionUtil = typeof(CollectionUtil); MethodInfo info = collectionUtil.GetMethod("GetKeyValuePairHashCode"); info = info.MakeGenericMethod(genericArguments); - - Object rv = info.Invoke(null, new object[]{o}); + + Object rv = info.Invoke(null, new object[] { o }); return (int)rv; } - else if ( ReflectionUtil.IsParentTypeOf(typeof(ICollection<>),o.GetType()) ) { - Type parent = ReflectionUtil.FindInHierarchyOf(typeof(ICollection<>),o.GetType()); - - Type [] genericArguments = + else if (ReflectionUtil.IsParentTypeOf(typeof(ICollection<>), o.GetType())) + { + Type parent = ReflectionUtil.FindInHierarchyOf(typeof(ICollection<>), o.GetType()); + + Type[] genericArguments = parent.GetGenericArguments(); @@ -761,71 +888,86 @@ public static int GetHashCode(Object o) { MethodInfo info = collectionUtil.GetMethod("GetEnumerableHashCode"); info = info.MakeGenericMethod(genericArguments); - - Object rv = info.Invoke(null, new object[]{o}); + + Object rv = info.Invoke(null, new object[] { o }); return (int)rv; } - else { + else + { return o.GetHashCode(); } } - - /** - * Equality function that properly handles arrays, - * lists, maps, lists of arrays, and maps of arrays. - *

- * NOTE: For Sets, this relies on the equals method - * of the Set to do the right thing. This is a reasonable - * assumption since, in order for Sets to behave - * properly as Sets, their values must already have - * a proper implementation of equals. (Or they must - * be specialized Sets that define a custom comparator that - * knows how to do the right thing). The same holds true for Map keys. - * Map values, on the other hand, are compared (so Map values - * can be arrays). - * @param o1 The first object. May be null. - * @param o2 The second object. May be null. - * @return true iff the two objects are equal. - */ - public new static bool Equals(Object o1, Object o2) { - if ( o1 == o2 ) { //same object or both null - return true; - } - else if ( o1 == null ) { + + ///

+ /// Equality function that properly handles arrays, + /// lists, maps, lists of arrays, and maps of arrays. + /// + /// + /// + /// NOTE: For Sets, this relies on the equals method + /// of the Set to do the right thing. This is a reasonable + /// assumption since, in order for Sets to behave + /// properly as Sets, their values must already have + /// a proper implementation of equals. (Or they must + /// be specialized Sets that define a custom comparator that + /// knows how to do the right thing). The same holds true for Map keys. + /// Map values, on the other hand, are compared (so Map values + /// can be arrays). + /// + /// + /// The first object. May be null. + /// The second object. May be null. + /// true iff the two objects are equal. + public new static bool Equals(Object o1, Object o2) + { + if (o1 == o2) + { //same object or both null + return true; + } + else if (o1 == null) + { return false; } - else if ( o2 == null ) { + else if (o2 == null) + { return false; } - else if ( o1 is Array ) { + else if (o1 is Array) + { Type clazz1 = o1.GetType(); Type clazz2 = o2.GetType(); - if ( !clazz1.Equals(clazz2)) { + if (!clazz1.Equals(clazz2)) + { return false; } Array array1 = (Array)o1; Array array2 = (Array)o2; int length1 = array1.Length; int length2 = array2.Length; - if ( length1 != length2 ) { + if (length1 != length2) + { return false; } - for ( int i = 0; i < length1; i++ ) { + for (int i = 0; i < length1; i++) + { Object el1 = array1.GetValue(i); Object el2 = array2.GetValue(i); - if (!CollectionUtil.Equals(el1,el2)) { + if (!CollectionUtil.Equals(el1, el2)) + { return false; } } return true; } - else if ( ReflectionUtil.IsParentTypeOf(typeof(IList<>),o1.GetType()) ) { - Type parent1 = ReflectionUtil.FindInHierarchyOf(typeof(IList<>),o1.GetType()); - Type parent2 = ReflectionUtil.FindInHierarchyOf(typeof(IList<>),o2.GetType()); - if (!parent1.Equals(parent2)) { + else if (ReflectionUtil.IsParentTypeOf(typeof(IList<>), o1.GetType())) + { + Type parent1 = ReflectionUtil.FindInHierarchyOf(typeof(IList<>), o1.GetType()); + Type parent2 = ReflectionUtil.FindInHierarchyOf(typeof(IList<>), o2.GetType()); + if (!parent1.Equals(parent2)) + { return false; } - Type [] genericArguments = + Type[] genericArguments = parent1.GetGenericArguments(); @@ -833,17 +975,19 @@ public static int GetHashCode(Object o) { MethodInfo info = collectionUtil.GetMethod("ListsEqual"); info = info.MakeGenericMethod(genericArguments); - - Object rv = info.Invoke(null, new object[]{o1,o2}); + + Object rv = info.Invoke(null, new object[] { o1, o2 }); return (bool)rv; } - else if ( ReflectionUtil.IsParentTypeOf(typeof(IDictionary<,>),o1.GetType()) ) { - Type parent1 = ReflectionUtil.FindInHierarchyOf(typeof(IDictionary<,>),o1.GetType()); - Type parent2 = ReflectionUtil.FindInHierarchyOf(typeof(IDictionary<,>),o2.GetType()); - if (!parent1.Equals(parent2)) { + else if (ReflectionUtil.IsParentTypeOf(typeof(IDictionary<,>), o1.GetType())) + { + Type parent1 = ReflectionUtil.FindInHierarchyOf(typeof(IDictionary<,>), o1.GetType()); + Type parent2 = ReflectionUtil.FindInHierarchyOf(typeof(IDictionary<,>), o2.GetType()); + if (!parent1.Equals(parent2)) + { return false; } - Type [] genericArguments = + Type[] genericArguments = parent1.GetGenericArguments(); @@ -851,17 +995,19 @@ public static int GetHashCode(Object o) { MethodInfo info = collectionUtil.GetMethod("DictionariesEqual"); info = info.MakeGenericMethod(genericArguments); - - Object rv = info.Invoke(null, new object[]{o1,o2}); + + Object rv = info.Invoke(null, new object[] { o1, o2 }); return (bool)rv; } - else if ( ReflectionUtil.IsParentTypeOf(typeof(ICollection<>),o1.GetType()) ) { - Type parent1 = ReflectionUtil.FindInHierarchyOf(typeof(ICollection<>),o1.GetType()); - Type parent2 = ReflectionUtil.FindInHierarchyOf(typeof(ICollection<>),o2.GetType()); - if (!parent1.Equals(parent2)) { + else if (ReflectionUtil.IsParentTypeOf(typeof(ICollection<>), o1.GetType())) + { + Type parent1 = ReflectionUtil.FindInHierarchyOf(typeof(ICollection<>), o1.GetType()); + Type parent2 = ReflectionUtil.FindInHierarchyOf(typeof(ICollection<>), o2.GetType()); + if (!parent1.Equals(parent2)) + { return false; } - Type [] genericArguments = + Type[] genericArguments = parent1.GetGenericArguments(); @@ -869,15 +1015,16 @@ public static int GetHashCode(Object o) { MethodInfo info = collectionUtil.GetMethod("SetsEqual"); info = info.MakeGenericMethod(genericArguments); - - Object rv = info.Invoke(null, new object[]{o1,o2}); + + Object rv = info.Invoke(null, new object[] { o1, o2 }); return (bool)rv; } - else { + else + { return o1.Equals(o2); } } } - + } diff --git a/Common/DateTimeUtil.cs b/Common/DateTimeUtil.cs index 12fca8e8..46a7c471 100644 --- a/Common/DateTimeUtil.cs +++ b/Common/DateTimeUtil.cs @@ -29,15 +29,18 @@ namespace Org.IdentityConnectors.Common ///
public static class DateTimeUtil { - public static DateTime GetDateTimeFromUtcMillis(long dateTime) { - return DateTime.FromFileTimeUtc(dateTime*10000); + public static DateTime GetDateTimeFromUtcMillis(long dateTime) + { + return DateTime.FromFileTimeUtc(dateTime * 10000); } - - public static long GetUtcTimeMillis(DateTime dateTime) { - return dateTime.ToFileTimeUtc()/10000; + + public static long GetUtcTimeMillis(DateTime dateTime) + { + return dateTime.ToFileTimeUtc() / 10000; } - - public static long GetCurrentUtcTimeMillis() { + + public static long GetCurrentUtcTimeMillis() + { return GetUtcTimeMillis(DateTime.Now); } } diff --git a/Common/FrameworkInternalBridge.cs b/Common/FrameworkInternalBridge.cs index 5610f5d5..9f3e0dab 100644 --- a/Common/FrameworkInternalBridge.cs +++ b/Common/FrameworkInternalBridge.cs @@ -27,7 +27,8 @@ namespace Org.IdentityConnectors.Common /// /// Description of FrameworkInternalBridge. /// - internal static class FrameworkInternalBridge { + internal static class FrameworkInternalBridge + { private static readonly Object LOCK = new Object(); private static Assembly _assembly = null; /// @@ -35,20 +36,23 @@ internal static class FrameworkInternalBridge { /// /// /// - public static Type LoadType(String typeName) { - + public static Type LoadType(String typeName) + { + Assembly assembly; - lock(LOCK) { - if (_assembly == null) { + lock (LOCK) + { + if (_assembly == null) + { AssemblyName assemName = new AssemblyName(); assemName.Name = "FrameworkInternal"; _assembly = Assembly.Load(assemName); } assembly = _assembly; } - - return assembly.GetType(typeName,true); - + + return assembly.GetType(typeName, true); + } } } diff --git a/Common/IOUtil.cs b/Common/IOUtil.cs index 9497fd54..38f747dc 100644 --- a/Common/IOUtil.cs +++ b/Common/IOUtil.cs @@ -37,8 +37,9 @@ public static class IOUtil /// public static IPAddress GetIPAddress(String hostOrIp) { - if ( hostOrIp.Equals("0.0.0.0") || - hostOrIp.Equals("::0") ) { + if (hostOrIp.Equals("0.0.0.0") || + hostOrIp.Equals("::0")) + { return IPAddress.Parse(hostOrIp); } return Dns.GetHostAddresses(hostOrIp)[0]; diff --git a/Common/Locale.cs b/Common/Locale.cs index 293ac539..c0cc45a7 100644 --- a/Common/Locale.cs +++ b/Common/Locale.cs @@ -31,45 +31,46 @@ namespace Org.IdentityConnectors.Common /// public sealed class Locale { - private static readonly IDictionary + private static readonly IDictionary Locale2CultureInfoName = new Dictionary(); - private static readonly IDictionary + private static readonly IDictionary CultureInfoName2Locale = new Dictionary(); - - private static void AddMapping(Locale locale, String name) { + + private static void AddMapping(Locale locale, String name) + { Locale2CultureInfoName[locale] = name; CultureInfoName2Locale[name] = locale; } - + /// /// Special cases /// static Locale() { - AddMapping(new Locale(),""); - AddMapping(new Locale("iw"),"he"); - AddMapping(new Locale("zh"),"zh-CHS"); - AddMapping(new Locale("iw","IL"),"he-IL"); - AddMapping(new Locale("no","NO"),"nb-NO"); - AddMapping(new Locale("no","NO","NY"),"nn-NO"); - } - + AddMapping(new Locale(), ""); + AddMapping(new Locale("iw"), "he"); + AddMapping(new Locale("zh"), "zh-CHS"); + AddMapping(new Locale("iw", "IL"), "he-IL"); + AddMapping(new Locale("no", "NO"), "nb-NO"); + AddMapping(new Locale("no", "NO", "NY"), "nn-NO"); + } + private String _language; private String _country; private String _variant; - + public Locale() : this("") { - + } - + public Locale(String language) - : this(language,"") + : this(language, "") { } public Locale(String language, String country) - : this(language,country,"") + : this(language, country, "") { } public Locale(String language, String country, String variant) @@ -78,52 +79,66 @@ public Locale(String language, String country, String variant) _country = country ?? ""; _variant = variant ?? ""; } - - public string Language { - get { + + public string Language + { + get + { return _language; } } - - public string Country { - get { + + public string Country + { + get + { return _country; } } - - public string Variant { - get { + + public string Variant + { + get + { return _variant; } } - - public override bool Equals(Object o) { + + public override bool Equals(Object o) + { Locale other = o as Locale; - if ( other != null ) { - if (!Object.Equals(Language,other.Language)) { + if (other != null) + { + if (!Object.Equals(Language, other.Language)) + { return false; } - if (!Object.Equals(Country,other.Country)) { + if (!Object.Equals(Country, other.Country)) + { return false; } - if (!Object.Equals(Variant,other.Variant)) { + if (!Object.Equals(Variant, other.Variant)) + { return false; } return true; } return false; } - - public override int GetHashCode() { - unchecked { - return _language.GetHashCode()+_country.GetHashCode(); + + public override int GetHashCode() + { + unchecked + { + return _language.GetHashCode() + _country.GetHashCode(); } } - - public override string ToString() { - return Language+"_"+Country+"_"+Variant; + + public override string ToString() + { + return Language + "_" + Country + "_" + Variant; } - + /// /// Creates the closest CultureInfo that maps to this locale /// @@ -131,57 +146,68 @@ public override string ToString() { public CultureInfo ToCultureInfo() { CultureInfo rv = null; - String code = CollectionUtil.GetValue(Locale2CultureInfoName,this,null); - if (code != null ) { + String code = CollectionUtil.GetValue(Locale2CultureInfoName, this, null); + if (code != null) + { rv = TryCultureCode(code); } - if ( rv == null ) { - if (Country.Length > 0) { - rv = TryCultureCode(Language+"-"+Country); + if (rv == null) + { + if (Country.Length > 0) + { + rv = TryCultureCode(Language + "-" + Country); } } - if ( rv == null ) { + if (rv == null) + { rv = TryCultureCode(Language); } - if ( rv == null ) { + if (rv == null) + { rv = CultureInfo.InvariantCulture; } return rv; } - - public static Locale FindLocale(CultureInfo info) + + public static Locale FindLocale(CultureInfo info) { String code = info.Name; - Locale rv = CollectionUtil.GetValue(CultureInfoName2Locale,code,null); - if (rv == null) { - String [] parts = code.Split(new string[]{"-"}, + Locale rv = CollectionUtil.GetValue(CultureInfoName2Locale, code, null); + if (rv == null) + { + String[] parts = code.Split(new string[] { "-" }, StringSplitOptions.RemoveEmptyEntries); String language = ""; String country = ""; - if ( parts.Length > 0 ) { + if (parts.Length > 0) + { String l = parts[0]; - if (LooksLikeValidLanguageCode(l)) { + if (LooksLikeValidLanguageCode(l)) + { language = l; - if ( parts.Length > 1 ) { + if (parts.Length > 1) + { String c = parts[1]; - if (LooksLikeValidCountryCode(c)) { + if (LooksLikeValidCountryCode(c)) + { country = c; } } } } - rv = new Locale(language,country); + rv = new Locale(language, country); } return rv; } - + /// /// Weeds out some junk /// /// /// - private static bool LooksLikeValidLanguageCode(String l) { - char [] chars = l.ToCharArray(); + private static bool LooksLikeValidLanguageCode(String l) + { + char[] chars = l.ToCharArray(); return (chars.Length == 2 && Char.IsLower(chars[0]) && Char.IsLower(chars[1])); @@ -191,18 +217,22 @@ private static bool LooksLikeValidLanguageCode(String l) { /// /// /// - private static bool LooksLikeValidCountryCode(String l) { - char [] chars = l.ToCharArray(); + private static bool LooksLikeValidCountryCode(String l) + { + char[] chars = l.ToCharArray(); return (chars.Length == 2 && Char.IsUpper(chars[0]) && Char.IsUpper(chars[1])); } - - private static CultureInfo TryCultureCode(String code) { - try { + + private static CultureInfo TryCultureCode(String code) + { + try + { return new CultureInfo(code); } - catch (Exception) { + catch (Exception) + { return null; } } diff --git a/Common/Pair.cs b/Common/Pair.cs index 7faf48d9..15134419 100644 --- a/Common/Pair.cs +++ b/Common/Pair.cs @@ -27,47 +27,49 @@ namespace Org.IdentityConnectors.Common /// /// Represents a Pair of objects /// - public class Pair + public class Pair { public Pair() { } - + public Pair(T1 first, T2 second) { First = first; Second = second; } - + public T1 First { get; set; } public T2 Second { get; set; } - + public override bool Equals(object obj) { - Pair other = obj as Pair; - if ( other != null ) + Pair other = obj as Pair; + if (other != null) { - return Object.Equals(First,other.First) && - Object.Equals(Second,other.Second); + return Object.Equals(First, other.First) && + Object.Equals(Second, other.Second); } return false; } - + public override int GetHashCode() { int rv = 0; - if ( First != null ) { + if (First != null) + { rv ^= First.GetHashCode(); } - if ( Second != null ) { + if (Second != null) + { rv ^= Second.GetHashCode(); } return rv; } - + public override string ToString() { - return "( "+First+", "+Second+" )"; + return "( " + First + ", " + Second + " )"; } } -} +} \ No newline at end of file diff --git a/Common/Pooling.cs b/Common/Pooling.cs index 93cf9981..df00961c 100644 --- a/Common/Pooling.cs +++ b/Common/Pooling.cs @@ -26,156 +26,200 @@ namespace Org.IdentityConnectors.Common.Pooling { - /** - * Configuration for pooling objects - */ - public sealed class ObjectPoolConfiguration { - - /** - * Max objects (idle+active). - */ + /// + /// Configuration for pooling objects + /// + public sealed class ObjectPoolConfiguration + { + + /// + /// Max objects (idle+active). + /// private int _maxObjects = 10; - - /** - * Max idle objects. - */ + + /// + /// Max idle objects. + /// private int _maxIdle = 10; - - /** - * Max time to wait if the pool is waiting for a free object to become - * available before failing. Zero means don't wait - */ + + /// + /// Max time to wait if the pool is waiting for a free object to become + /// available before failing. + /// + /// + /// Zero means don't wait + /// private long _maxWait = 150 * 1000; - - /** - * Minimum time to wait before evicting an idle object. - * Zero means don't wait - */ + + /// + /// Minimum time to wait before evicting an idle object. + /// + /// + /// Zero means don't wait + /// private long _minEvictableIdleTimeMillis = 120 * 1000; - - /** - * Minimum number of idle objects. - */ + + /// + /// Minimum number of idle objects. + /// private int _minIdle = 1; - - - /** - * Get the set number of maximum objects (idle+active) - */ - public int MaxObjects { - get { + + + /// + /// Get the set number of maximum objects (idle+active) + /// + public int MaxObjects + { + get + { return _maxObjects; } - set { + set + { _maxObjects = value; } } - - /** - * Get the maximum number of idle objects. - */ - public int MaxIdle { - get { + + /// + /// Get the maximum number of idle objects. + /// + public int MaxIdle + { + get + { return _maxIdle; } - set { + set + { _maxIdle = value; } } - - /** - * Max time to wait if the pool is waiting for a free object to become - * available before failing. Zero means don't wait - */ - public long MaxWait { - get { + + /// + /// Max time to wait if the pool is waiting for a free object to become + /// available before failing. + /// + /// + /// Zero means don't wait + /// + public long MaxWait + { + get + { return _maxWait; } - set { + set + { _maxWait = value; } } - - /** - * Minimum time to wait before evicting an idle object. - * Zero means don't wait - */ - public long MinEvictableIdleTimeMillis { - get { + + /// + /// Minimum time to wait before evicting an idle object. + /// + /// + /// Zero means don't wait + /// + public long MinEvictableIdleTimeMillis + { + get + { return _minEvictableIdleTimeMillis; } - set { + set + { _minEvictableIdleTimeMillis = value; } } - - /** - * Minimum number of idle objects. - */ - public int MinIdle { - get { + + /// + /// Minimum number of idle objects. + /// + public int MinIdle + { + get + { return _minIdle; } - set { + set + { _minIdle = value; } } - - public void Validate() { - if (_minIdle < 0) { + + public void Validate() + { + if (_minIdle < 0) + { throw new InvalidOperationException("Min idle is less than zero."); } - if (_maxObjects < 0) { + if (_maxObjects < 0) + { throw new InvalidOperationException("Max active is less than zero."); } - if (_maxIdle < 0) { + if (_maxIdle < 0) + { throw new InvalidOperationException("Max idle is less than zero."); } - if (_maxWait < 0) { + if (_maxWait < 0) + { throw new InvalidOperationException("Max wait is less than zero."); } - if (_minEvictableIdleTimeMillis < 0) { + if (_minEvictableIdleTimeMillis < 0) + { throw new InvalidOperationException("Min evictable idle time millis less than zero."); } - if ( _minIdle > _maxIdle ) { - throw new InvalidOperationException("Min idle is greater than max idle."); + if (_minIdle > _maxIdle) + { + throw new InvalidOperationException("Min idle is greater than max idle."); } - if ( _maxIdle > _maxObjects ) { - throw new InvalidOperationException("Max idle is greater than max objects."); + if (_maxIdle > _maxObjects) + { + throw new InvalidOperationException("Max idle is greater than max objects."); } } - - public override int GetHashCode() { - unchecked { - return (int)(MaxObjects+MaxIdle+MaxWait+MinEvictableIdleTimeMillis+MinIdle); + + public override int GetHashCode() + { + unchecked + { + return (int)(MaxObjects + MaxIdle + MaxWait + MinEvictableIdleTimeMillis + MinIdle); } } - - public override bool Equals(Object obj) { - if ( obj is ObjectPoolConfiguration) { + + public override bool Equals(Object obj) + { + if (obj is ObjectPoolConfiguration) + { ObjectPoolConfiguration other = (ObjectPoolConfiguration)obj; - - if (MaxObjects != other.MaxObjects) { + + if (MaxObjects != other.MaxObjects) + { return false; } - if (MaxIdle != other.MaxIdle) { + if (MaxIdle != other.MaxIdle) + { return false; } - if (MaxWait != other.MaxWait) { + if (MaxWait != other.MaxWait) + { return false; } - if (MinEvictableIdleTimeMillis != other.MinEvictableIdleTimeMillis) { + if (MinEvictableIdleTimeMillis != other.MinEvictableIdleTimeMillis) + { return false; } - if (MinIdle != other.MinIdle) { + if (MinIdle != other.MinIdle) + { return false; } return true; } return false; } - - public override String ToString() { + + public override String ToString() + { // poor man's toString() IDictionary bld = new Dictionary(); bld["MaxObjects"] = MaxObjects; @@ -186,4 +230,4 @@ public override String ToString() { return bld.ToString(); } } -} +} \ No newline at end of file diff --git a/Common/Proxy.cs b/Common/Proxy.cs index da7feca2..546fd140 100644 --- a/Common/Proxy.cs +++ b/Common/Proxy.cs @@ -31,11 +31,11 @@ namespace Org.IdentityConnectors.Common.Proxy /// public interface InvocationHandler { - Object - Invoke(Object proxy, MethodInfo method, Object [] args); + Object + Invoke(Object proxy, MethodInfo method, Object[] args); } - - + + /// /// Similar to java.lang.reflect.Proxy /// @@ -48,81 +48,82 @@ public static class Proxy typeof(object[])}); private static readonly MethodInfo GET_METHOD_FROM_HANDLE_METHOD = typeof(MethodBase).GetMethod("GetMethodFromHandle", - new Type[]{typeof(RuntimeMethodHandle)}); - + new Type[] { typeof(RuntimeMethodHandle) }); + private static readonly object LOCK = new Object(); - - private static IDictionary - _implementationMap = new Dictionary(); - + + private static IDictionary + _implementationMap = new Dictionary(); + private static int _count = 0; - + public static Object NewProxyInstance(Type interfaze, InvocationHandler handler) { ConstructorInfo constructor = GetOrCreateConstructorInfo(interfaze); - return constructor.Invoke(new object[]{handler}); + return constructor.Invoke(new object[] { handler }); } - + private static ConstructorInfo GetOrCreateConstructorInfo(Type type) { - lock(LOCK) + lock (LOCK) { ConstructorInfo rv = - CollectionUtil.GetValue(_implementationMap,type,null); - if ( rv == null ) + CollectionUtil.GetValue(_implementationMap, type, null); + if (rv == null) { Type impl = GenerateImplementation(type); - rv = - impl.GetConstructor(new Type[]{typeof(InvocationHandler)}); + rv = + impl.GetConstructor(new Type[] { typeof(InvocationHandler) }); _implementationMap[type] = rv; } return rv; } } - + private static String NextName() { int count; - lock(LOCK) { + lock (LOCK) + { count = _count; count++; } - return "___proxy"+count; + return "___proxy" + count; } - - - - private static Type GenerateImplementation(Type interfaze) + + + + private static Type GenerateImplementation(Type interfaze) { if (!interfaze.IsInterface) { - throw new ArgumentException("Type is not an interface: "+interfaze); + throw new ArgumentException("Type is not an interface: " + interfaze); } - if ( interfaze.IsGenericType ) + if (interfaze.IsGenericType) { - throw new ArgumentException("Type is a generic type: "+interfaze); + throw new ArgumentException("Type is a generic type: " + interfaze); } - + String uniqueName = NextName(); - + AssemblyName assemblyName = new AssemblyName(uniqueName); - AssemblyBuilder assemblyBuilder = + AssemblyBuilder assemblyBuilder = AppDomain.CurrentDomain.DefineDynamicAssembly( - assemblyName, + assemblyName, AssemblyBuilderAccess.RunAndSave); // For a single-module assembly, the module name is usually // the assembly name plus an extension. - ModuleBuilder moduleBuilder = + ModuleBuilder moduleBuilder = assemblyBuilder.DefineDynamicModule(assemblyName.Name, assemblyName.Name + ".dll"); - + TypeBuilder typeBuilder = moduleBuilder.DefineType( - uniqueName, + uniqueName, TypeAttributes.Public); typeBuilder.AddInterfaceImplementation(interfaze); - - MethodInfo [] methods = interfaze.GetMethods(); + + MethodInfo[] methods = interfaze.GetMethods(); ConstructorBuilder classInitializer = typeBuilder.DefineTypeInitializer(); ILGenerator classInitializerCode = @@ -131,21 +132,21 @@ private static Type GenerateImplementation(Type interfaze) FieldBuilder handlerField = typeBuilder.DefineField("_handler", typeof(InvocationHandler), - FieldAttributes.Private|FieldAttributes.InitOnly); + FieldAttributes.Private | FieldAttributes.InitOnly); int count = 0; foreach (MethodInfo method in methods) { - GenerateMethod(typeBuilder,method,classInitializerCode,count, + GenerateMethod(typeBuilder, method, classInitializerCode, count, handlerField); count++; } classInitializerCode.Emit(OpCodes.Ret); - - + + ConstructorBuilder constructorBuilder = typeBuilder.DefineConstructor( - MethodAttributes.Public, - CallingConventions.Standard, - new Type[]{typeof(InvocationHandler)}); + MethodAttributes.Public, + CallingConventions.Standard, + new Type[] { typeof(InvocationHandler) }); ILGenerator constructorCode = constructorBuilder.GetILGenerator(); // For a constructor, argument zero is a reference to the new // instance. Push it on the stack before calling the base @@ -153,115 +154,119 @@ private static Type GenerateImplementation(Type interfaze) // base class (System.Object) by passing an empty array of // types (Type.EmptyTypes) to GetConstructor. constructorCode.Emit(OpCodes.Ldarg_0); - constructorCode.Emit(OpCodes.Call, + constructorCode.Emit(OpCodes.Call, typeof(object).GetConstructor(Type.EmptyTypes)); constructorCode.Emit(OpCodes.Ldarg_0); constructorCode.Emit(OpCodes.Ldarg_1); constructorCode.Emit(OpCodes.Stfld, handlerField); constructorCode.Emit(OpCodes.Ret); - - + + Type t = typeBuilder.CreateType(); - + //assemblyBuilder.Save(assemblyName.Name+".dll"); - + return t; } - + private static void ValidateMethod(MethodInfo info) { - if ( info.GetGenericArguments().Length != 0 ) { + if (info.GetGenericArguments().Length != 0) + { throw new ArgumentException( - "Method not supported since it has generic arguments: "+info); - + "Method not supported since it has generic arguments: " + info); + } - foreach (ParameterInfo parameter in info.GetParameters()) { - if ( parameter.IsOut ) { + foreach (ParameterInfo parameter in info.GetParameters()) + { + if (parameter.IsOut) + { throw new ArgumentException( - "Method not supported since it has output paramaters: "+info); + "Method not supported since it has output paramaters: " + info); } - if ( parameter.IsRetval ) { + if (parameter.IsRetval) + { throw new ArgumentException( - "Method not supported since it has retval paramaters: "+info); + "Method not supported since it has retval paramaters: " + info); } } } - + private static void GenerateMethod(TypeBuilder typeBuilder, - MethodInfo method, + MethodInfo method, ILGenerator classInitializerCode, int methodCount, FieldBuilder handlerField) { ValidateMethod(method); - + FieldBuilder methodField = - typeBuilder.DefineField("METHOD"+methodCount, + typeBuilder.DefineField("METHOD" + methodCount, typeof(MethodInfo), - FieldAttributes.Private|FieldAttributes.InitOnly|FieldAttributes.Static); - - - + FieldAttributes.Private | FieldAttributes.InitOnly | FieldAttributes.Static); + + + classInitializerCode.Emit(OpCodes.Ldtoken, method); classInitializerCode.Emit(OpCodes.Call, GET_METHOD_FROM_HANDLE_METHOD); - classInitializerCode.Emit(OpCodes.Castclass,typeof(MethodInfo)); - classInitializerCode.Emit(OpCodes.Stsfld,methodField); - - - Type [] parameterTypes = ReflectionUtil.GetParameterTypes(method); + classInitializerCode.Emit(OpCodes.Castclass, typeof(MethodInfo)); + classInitializerCode.Emit(OpCodes.Stsfld, methodField); + + + Type[] parameterTypes = ReflectionUtil.GetParameterTypes(method); MethodBuilder methodBuilder = typeBuilder.DefineMethod( - method.Name, - MethodAttributes.Public| - MethodAttributes.HideBySig| - MethodAttributes.NewSlot| - MethodAttributes.Virtual| + method.Name, + MethodAttributes.Public | + MethodAttributes.HideBySig | + MethodAttributes.NewSlot | + MethodAttributes.Virtual | MethodAttributes.Final, method.CallingConvention, - method.ReturnType, + method.ReturnType, parameterTypes); - + ILGenerator methodCode = methodBuilder.GetILGenerator(); methodCode.Emit(OpCodes.Ldarg_0); - methodCode.Emit(OpCodes.Ldfld,handlerField); + methodCode.Emit(OpCodes.Ldfld, handlerField); methodCode.Emit(OpCodes.Ldarg_0); - methodCode.Emit(OpCodes.Ldsfld,methodField); - methodCode.Emit(OpCodes.Ldc_I4,parameterTypes.Length); - methodCode.Emit(OpCodes.Newarr,typeof(object)); - - for (int index = 0; index < parameterTypes.Length; index++) + methodCode.Emit(OpCodes.Ldsfld, methodField); + methodCode.Emit(OpCodes.Ldc_I4, parameterTypes.Length); + methodCode.Emit(OpCodes.Newarr, typeof(object)); + + for (int index = 0; index < parameterTypes.Length; index++) { Type parameterType = parameterTypes[index]; methodCode.Emit(OpCodes.Dup); - methodCode.Emit(OpCodes.Ldc_I4,index); - methodCode.Emit(OpCodes.Ldarg,index+1); + methodCode.Emit(OpCodes.Ldc_I4, index); + methodCode.Emit(OpCodes.Ldarg, index + 1); if (parameterType.IsValueType) { - methodCode.Emit(OpCodes.Box,parameterType); + methodCode.Emit(OpCodes.Box, parameterType); } methodCode.Emit(OpCodes.Stelem_Ref); } - - methodCode.Emit(OpCodes.Callvirt,INVOCATION_HANDLER_METHOD); + + methodCode.Emit(OpCodes.Callvirt, INVOCATION_HANDLER_METHOD); Type returnType = method.ReturnType; - + if (typeof(void).Equals(returnType)) { methodCode.Emit(OpCodes.Pop); } else if (returnType.IsValueType) { - methodCode.Emit(OpCodes.Unbox_Any,returnType); + methodCode.Emit(OpCodes.Unbox_Any, returnType); } - else + else { - methodCode.Emit(OpCodes.Castclass,returnType); + methodCode.Emit(OpCodes.Castclass, returnType); } - + methodCode.Emit(OpCodes.Ret); - - typeBuilder.DefineMethodOverride(methodBuilder,method); + + typeBuilder.DefineMethodOverride(methodBuilder, method); } } -} +} \ No newline at end of file diff --git a/Common/ReflectionUtil.cs b/Common/ReflectionUtil.cs index 48d515b3..7babc343 100644 --- a/Common/ReflectionUtil.cs +++ b/Common/ReflectionUtil.cs @@ -36,32 +36,39 @@ public static class ReflectionUtil /// /// /// - public static Type [] GetAllInterfaces(Type type) { + public static Type[] GetAllInterfaces(Type type) + { HashSet temp = new HashSet(); - GetAllInterfaces2(type,temp); + GetAllInterfaces2(type, temp); return temp.ToArray(); } - - private static void GetAllInterfaces2(Type type, HashSet rv) { - if ( type != null ) { - if ( type.IsInterface ) { + + private static void GetAllInterfaces2(Type type, HashSet rv) + { + if (type != null) + { + if (type.IsInterface) + { rv.Add(type); } - GetAllInterfaces2(type.BaseType,rv); - foreach (Type inter in type.GetInterfaces()) { - GetAllInterfaces2(inter,rv); + GetAllInterfaces2(type.BaseType, rv); + foreach (Type inter in type.GetInterfaces()) + { + GetAllInterfaces2(inter, rv); } } } - + /// /// Returns the generic type definition of the given type /// /// /// - public static Type [] GetTypeErasure(Type [] types) { - Type [] rv = new Type[types.Length]; - for ( int i = 0; i < types.Length; i++ ) { + public static Type[] GetTypeErasure(Type[] types) + { + Type[] rv = new Type[types.Length]; + for (int i = 0; i < types.Length; i++) + { rv[i] = GetTypeErasure(types[i]); } return rv; @@ -71,13 +78,15 @@ public static Type [] GetTypeErasure(Type [] types) { /// /// /// - public static Type GetTypeErasure(Type type) { - if ( type.IsGenericType ) { + public static Type GetTypeErasure(Type type) + { + if (type.IsGenericType) + { type = type.GetGenericTypeDefinition(); } return type; } - + /// /// Returns true iff t1 is a parent type of t2. Unlike /// Type.isAssignableFrom, this handles generic types as @@ -87,14 +96,16 @@ public static Type GetTypeErasure(Type type) { /// /// public static bool IsParentTypeOf(Type t1, - Type t2) { - if (t2 == null) { + Type t2) + { + if (t2 == null) + { return t1 == null; } - Type found = FindInHierarchyOf(t1,t2); + Type found = FindInHierarchyOf(t1, t2); return found != null; - } - + } + /// /// Finds t1 in the hierarchy of t2 or null if not found. The /// returned value will have generic parameters applied to it. @@ -102,48 +113,59 @@ public static bool IsParentTypeOf(Type t1, /// /// /// - public static Type FindInHierarchyOf(Type t1, Type t2) { - if (t2 == null) { + public static Type FindInHierarchyOf(Type t1, Type t2) + { + if (t2 == null) + { return null; } - if ( EqualsIgnoreGeneric(t1,t2) ) { + if (EqualsIgnoreGeneric(t1, t2)) + { return t2; } - Type found1 = FindInHierarchyOf(t1,t2.BaseType); - if (found1 != null) { + Type found1 = FindInHierarchyOf(t1, t2.BaseType); + if (found1 != null) + { return found1; } - foreach (Type inter in t2.GetInterfaces()) { - Type found2 = FindInHierarchyOf(t1,inter); - if (found2 != null) { + foreach (Type inter in t2.GetInterfaces()) + { + Type found2 = FindInHierarchyOf(t1, inter); + if (found2 != null) + { return found2; } } return null; } - + private static bool EqualsIgnoreGeneric(Type t1, - Type t2) { - if ( t1 == null || t2 == null ) { + Type t2) + { + if (t1 == null || t2 == null) + { return t1 == null && t2 == null; } - if ( t1.IsGenericType ) { + if (t1.IsGenericType) + { t1 = t1.GetGenericTypeDefinition(); } - if ( t2.IsGenericType ) { + if (t2.IsGenericType) + { t2 = t2.GetGenericTypeDefinition(); } return t1.Equals(t2); } - - public static Type [] GetParameterTypes(MethodInfo method) + + public static Type[] GetParameterTypes(MethodInfo method) { - ParameterInfo [] parameters = method.GetParameters(); - Type [] rv = new Type[parameters.Length]; - for (int i = 0; i < parameters.Length; i++) { + ParameterInfo[] parameters = method.GetParameters(); + Type[] rv = new Type[parameters.Length]; + for (int i = 0; i < parameters.Length; i++) + { rv[i] = parameters[i].ParameterType; } return rv; } } -} +} \ No newline at end of file diff --git a/Common/SafeType.cs b/Common/SafeType.cs index 4370162e..07bd0267 100644 --- a/Common/SafeType.cs +++ b/Common/SafeType.cs @@ -26,117 +26,119 @@ namespace Org.IdentityConnectors.Common { - /// - /// The equivalent of java's Class<? extends...%gt; syntax. - /// Allows you to restrict a Type to a certain class hierarchy. - /// - public sealed class SafeType - where T : class - { - private readonly Type _rawType; - - /// - /// Make private so no one can create directly - /// - private SafeType(Type rawType) - { - if (!ReflectionUtil.IsParentTypeOf(typeof(T),rawType)) { - throw new ArgumentException("Type: "+rawType+" is not a subclass of"+typeof(T)); - } - _rawType = rawType; - } - - /// - /// Returns the SafeType for a given raw type. - /// NOTE: If possible use Get() instead since it is statically - /// checked at compile time. - /// - /// - /// - public static SafeType ForRawType(Type type) - { - return new SafeType(type); - } - - /// - /// Gets an instance of the safe type in a type-safe fashion. - /// - /// The instance of the safe type - public static SafeType Get() - where U : T - { - return new SafeType(typeof(U)); - } - - /// - /// Gets an instance of the safe type in a type-safe fashion from an object. - /// - /// The instance of the safe type - public static SafeType Get(T obj) - { - return new SafeType(obj.GetType()); - } - - /// - /// Returns the generic type definition corresponding to this type. - /// Will return the same type if this is already a generic type. - /// - /// - public SafeType GetTypeErasure() - { - return SafeType.ForRawType(ReflectionUtil.GetTypeErasure(RawType)); - } - - /// - /// Returns the underlying C# type - /// - public Type RawType { - get { - return _rawType; - } - } - - /// - /// Creates a new instance of the given type - /// - /// The new instance - public T CreateInstance() - { - return (T)Activator.CreateInstance(RawType); - } - - /// - /// Returns true iff these represent the same underlying type - /// and the SafeType has the same parent type. - /// - /// The other - /// true iff these represent the same underylying type - /// and the TypeGroup has the same parent type - public override bool Equals(object o) - { - if ( o is SafeType ) { - SafeType other = (SafeType)o; - return RawType.Equals(other.RawType); - } - return false; - } - /// - /// Returns a hash of the type - /// - /// a hash of the type - public override int GetHashCode() - { - return RawType.GetHashCode(); - } - /// - /// Returns a string representation of the member type - /// - /// a string representation of the member type - public override string ToString() - { - return RawType.ToString(); - } - } + /// + /// The equivalent of java's Class<? extends...%gt; syntax. + /// Allows you to restrict a Type to a certain class hierarchy. + /// + public sealed class SafeType + where T : class + { + private readonly Type _rawType; - -} + /// + /// Make private so no one can create directly + /// + private SafeType(Type rawType) + { + if (!ReflectionUtil.IsParentTypeOf(typeof(T), rawType)) + { + throw new ArgumentException("Type: " + rawType + " is not a subclass of" + typeof(T)); + } + _rawType = rawType; + } + + /// + /// Returns the SafeType for a given raw type. + /// NOTE: If possible use Get() instead since it is statically + /// checked at compile time. + /// + /// + /// + public static SafeType ForRawType(Type type) + { + return new SafeType(type); + } + + /// + /// Gets an instance of the safe type in a type-safe fashion. + /// + /// The instance of the safe type + public static SafeType Get() + where U : T + { + return new SafeType(typeof(U)); + } + + /// + /// Gets an instance of the safe type in a type-safe fashion from an object. + /// + /// The instance of the safe type + public static SafeType Get(T obj) + { + return new SafeType(obj.GetType()); + } + + /// + /// Returns the generic type definition corresponding to this type. + /// Will return the same type if this is already a generic type. + /// + /// + public SafeType GetTypeErasure() + { + return SafeType.ForRawType(ReflectionUtil.GetTypeErasure(RawType)); + } + + /// + /// Returns the underlying C# type + /// + public Type RawType + { + get + { + return _rawType; + } + } + + /// + /// Creates a new instance of the given type + /// + /// The new instance + public T CreateInstance() + { + return (T)Activator.CreateInstance(RawType); + } + + /// + /// Returns true iff these represent the same underlying type + /// and the SafeType has the same parent type. + /// + /// The other + /// true iff these represent the same underylying type + /// and the TypeGroup has the same parent type + public override bool Equals(object o) + { + if (o is SafeType) + { + SafeType other = (SafeType)o; + return RawType.Equals(other.RawType); + } + return false; + } + /// + /// Returns a hash of the type + /// + /// a hash of the type + public override int GetHashCode() + { + return RawType.GetHashCode(); + } + /// + /// Returns a string representation of the member type + /// + /// a string representation of the member type + public override string ToString() + { + return RawType.ToString(); + } + } +} \ No newline at end of file diff --git a/Common/Script.cs b/Common/Script.cs index 0ae4ac77..ea41773e 100644 --- a/Common/Script.cs +++ b/Common/Script.cs @@ -28,44 +28,51 @@ namespace Org.IdentityConnectors.Common.Script { - public interface ScriptExecutor { + public interface ScriptExecutor + { /// /// Executes the script with the given arguments. /// /// key/value set of variables to /// pass to the script. /// - object Execute(IDictionary arguments); + object Execute(IDictionary arguments); } - public abstract class ScriptExecutorFactory { - + public abstract class ScriptExecutorFactory + { + private static readonly object LOCK = new object(); - + /// /// Loaded w/ all supported languages. /// private static IDictionary _supportedLanguages = null; - + /// /// Load all script executor factory assemblies in the same directory /// the 'Common' assembly. /// - private static IDictionary LoadSupportedLanguages() { + private static IDictionary LoadSupportedLanguages() + { // attempt to process all assemblies.. - IDictionary ret = new Dictionary(); + IDictionary ret = new Dictionary(); Assembly assembly = Assembly.GetExecutingAssembly(); FileInfo thisAssemblyFile = new FileInfo(assembly.Location); DirectoryInfo directory = thisAssemblyFile.Directory; // get all *ScriptExecutorFactory assmebly from the current directory - FileInfo [] files = directory.GetFiles("*.ScriptExecutorFactory.dll"); + FileInfo[] files = directory.GetFiles("*.ScriptExecutorFactory.dll"); Type t = typeof(ScriptExecutorFactoryClassAttribute); - foreach (FileInfo file in files) { - try { - Assembly lib = Assembly.LoadFrom(file.ToString()); - foreach (Type type in lib.GetTypes()) { - Object [] attributes = type.GetCustomAttributes(t, false); - if ( attributes.Length > 0 ) { - ScriptExecutorFactoryClassAttribute attribute = + foreach (FileInfo file in files) + { + try + { + Assembly lib = Assembly.LoadFrom(file.ToString()); + foreach (Type type in lib.GetTypes()) + { + Object[] attributes = type.GetCustomAttributes(t, false); + if (attributes.Length > 0) + { + ScriptExecutorFactoryClassAttribute attribute = (ScriptExecutorFactoryClassAttribute)attributes[0]; // attempt to test assembly.. Activator.CreateInstance(type); @@ -74,15 +81,16 @@ private static IDictionary LoadSupportedLanguages() { } } } - catch (Exception e) { - TraceUtil.TraceException("Unable to load assembly: "+ - assembly.FullName+". This is a fatal exception: ",e); + catch (Exception e) + { + TraceUtil.TraceException("Unable to load assembly: " + + assembly.FullName + ". This is a fatal exception: ", e); throw; } } return ret; } - + private static IDictionary GetSupportedLanguages() { lock (LOCK) @@ -92,76 +100,83 @@ private static IDictionary GetSupportedLanguages() _supportedLanguages = LoadSupportedLanguages(); } return _supportedLanguages; - } + } } - - /** - * Returns the set of supported languages. - * @return The set of supported languages. - */ - public static ICollection SupportedLanguages + + /// + /// Returns the set of supported languages. + /// + /// The set of supported languages. + public static ICollection SupportedLanguages { - get + get { IDictionary map = GetSupportedLanguages(); return CollectionUtil.AsReadOnlySet(map.Keys); } } - - /** - * Creates a ScriptExecutorFactory for the given language - * @param language The name of the language - * @return The script executor factory - * @throws IllegalArgumentException If the given language is not - * supported. - */ - public static ScriptExecutorFactory NewInstance(String language) { - if ( language == null ) { + + /// + /// Creates a ScriptExecutorFactory for the given language + /// + /// The name of the language + /// The script executor factory + /// If the given language is not + /// supported. + public static ScriptExecutorFactory NewInstance(String language) + { + if (language == null) + { throw new ArgumentException("Language must be specified"); } - Type type = CollectionUtil.GetValue(GetSupportedLanguages(),language.ToUpper(),null); - if ( type == null ) { - throw new ArgumentException("Language not supported: "+language); + Type type = CollectionUtil.GetValue(GetSupportedLanguages(), language.ToUpper(), null); + if (type == null) + { + throw new ArgumentException("Language not supported: " + language); } - return (ScriptExecutorFactory) Activator.CreateInstance(type); + return (ScriptExecutorFactory)Activator.CreateInstance(type); } - - - /** - * Creates a script executor for the given script. - * @param loader The classloader that contains the java classes - * that the script should have access to. - * @param script The script text. - * @param compile A hint to tell the script executor whether or - * not to compile the given script. This need not be implemented - * by all script executors. If true, the caller is saying that - * they intend to call the script multiple times with different - * arguments, so compile if possible. - * @return A script executor. - */ + + + /// + /// Creates a script executor for the given script. + /// + /// The classloader that contains the java classes + /// that the script should have access to. + /// The script text. + /// A hint to tell the script executor whether or + /// not to compile the given script. This need not be implemented + /// by all script executors. If true, the caller is saying that + /// they intend to call the script multiple times with different + /// arguments, so compile if possible. + /// A script executor. public abstract ScriptExecutor NewScriptExecutor( - Assembly [] referencedAssemblies, + Assembly[] referencedAssemblies, String script, bool compile); } - - [AttributeUsage(AttributeTargets.Class, AllowMultiple=false)] - public class ScriptExecutorFactoryClassAttribute : System.Attribute { + + [AttributeUsage(AttributeTargets.Class, AllowMultiple = false)] + public class ScriptExecutorFactoryClassAttribute : System.Attribute + { private readonly string _lang; - + /// /// Determine the language supported by the factory. /// /// - public ScriptExecutorFactoryClassAttribute(string lang) { + public ScriptExecutorFactoryClassAttribute(string lang) + { _lang = lang; } - - public string Language { - get { + + public string Language + { + get + { return _lang; } } } -} +} \ No newline at end of file diff --git a/Common/Security.cs b/Common/Security.cs index b141aef5..c497e87c 100644 --- a/Common/Security.cs +++ b/Common/Security.cs @@ -36,51 +36,59 @@ namespace Org.IdentityConnectors.Common.Security /// public interface UnmanagedArray : IDisposable { - int Length {get;} - T this[int index] {get;set;} + int Length { get; } + T this[int index] { get; set; } } #endregion - - /** - * Secure byte array implementation that solves the problems associated with - * keeping confidential data as byte[]. That is, anything - * represented as a byte[] is kept in memory in clear - * text and stays in memory at least until it is garbage collected. - *

- * The GuardedByteArray class alleviates this problem by storing the bytes in - * memory in an encrypted form. The encryption key will be a randomly-generated - * key. - *

- * In their serialized form, GuardedByteArray will be encrypted using a known - * default key. This is to provide a minimum level of protection regardless - * of the transport. For communications with the Remote Connector Framework - * it is recommended that deployments enable SSL for true encryption. - *

- * Applications may also wish to persist GuardedByteArrays. In the case of - * Identity Manager, it should convert GuardedByteArrays to EncryptedData so - * that they can be stored and managed using the Manage Encryption features - * of Identity Manager. Other applications may wish to serialize APIConfiguration - * as a whole. These applications are responsible for encrypting the APIConfiguration - * blob for an additional layer of security (beyond the basic default key encryption - * provided by GuardedByteArray). - */ + + ///

+ /// Secure byte array implementation that solves the problems associated with + /// keeping confidential data as byte[]. + /// + /// + /// That is, anything + /// represented as a byte[] is kept in memory in clear + /// text and stays in memory at least until it is garbage collected. + /// + /// The GuardedByteArray class alleviates this problem by storing the bytes in + /// memory in an encrypted form. The encryption key will be a randomly-generated + /// key. + /// + /// + /// In their serialized form, GuardedByteArray will be encrypted using a known + /// default key. This is to provide a minimum level of protection regardless + /// of the transport. For communications with the Remote Connector Framework + /// it is recommended that deployments enable SSL for true encryption. + /// + /// + /// Applications may also wish to persist GuardedByteArrays. In the case of + /// Identity Manager, it should convert GuardedByteArrays to EncryptedData so + /// that they can be stored and managed using the Manage Encryption features + /// of Identity Manager. Other applications may wish to serialize APIConfiguration + /// as a whole. These applications are responsible for encrypting the APIConfiguration + /// blob for an additional layer of security (beyond the basic default key encryption + /// provided by GuardedByteArray). + /// + /// public sealed class GuardedByteArray : IDisposable { - /** - * This method will be called with the clear text of the byte array. - * After the call the clearBytes array will be automatically zeroed - * out, thus keeping the window of potential exposure to a bare-minimum. - * @param clearChars - */ + /// + /// This method will be called with the clear text of the byte array. + /// + /// + /// After the call the clearBytes array will be automatically zeroed + /// out, thus keeping the window of potential exposure to a bare-minimum. + /// + /// public delegate void Accessor(UnmanagedArray clearBytes); private SecureString _target; private String _base64SHA1Hash; - /** - * Creates an empty secure byte array. - */ + /// + /// Creates an empty secure byte array. + /// public GuardedByteArray() { _target = new SecureString(); @@ -100,18 +108,20 @@ private GuardedByteArray(SecureString str) } - /** - * Provides access to the clear-text value of the bytes in a controlled fashion. - * The clear-text bytes will only be available for the duration of the call - * and automatically zeroed out following the call. - * - *

- * NOTE: Callers are encouraged to use {@link #verifyBase64SHA1Hash(String)} - * where possible if the intended use is merely to verify the contents of - * the string match an expected hash value. - * @param accessor Accessor callback. - * @throws IllegalStateException If the byte array has been disposed - */ + ///

+ /// Provides access to the clear-text value of the bytes in a controlled fashion. + /// + /// + /// The clear-text bytes will only be available for the duration of the call + /// and automatically zeroed out following the call. + /// + /// NOTE: Callers are encouraged to use + /// where possible if the intended use is merely to verify the contents of + /// the string match an expected hash value. + /// + /// + /// Accessor callback. + /// If the byte array has been disposed public void Access(Accessor accessor) { using (SecureStringToByteArrayAdapter adapter = new SecureStringToByteArrayAdapter(_target)) @@ -120,14 +130,16 @@ public void Access(Accessor accessor) } } - /** - * Appends a single clear-text byte to the secure byte array. - * The in-memory data will be decrypted, the character will be - * appended, and then it will be re-encrypted. - * @param b The byte to append. - * @throws IllegalStateException If the byte array is read-only - * @throws IllegalStateException If the byte array has been disposed - */ + /// + /// Appends a single clear-text byte to the secure byte array. + /// + /// + /// The in-memory data will be decrypted, the character will be + /// appended, and then it will be re-encrypted. + /// + /// The byte to append. + /// If the byte array is read-only + /// If the byte array has been disposed public void AppendByte(byte b) { _target.AppendChar((char)b); @@ -143,39 +155,42 @@ private void AppendBytes(UnmanagedArray clearBytes) ComputeHash(); } - /** - * Clears the in-memory representation of the byte array. - */ + /// + /// Clears the in-memory representation of the byte array. + /// public void Dispose() { _target.Dispose(); } - /** - * Returns true iff this byte array has been marked read-only - * @return true iff this byte array has been marked read-only - * @throws IllegalStateException If the byte array has been disposed - */ + /// + /// Returns true iff this byte array has been marked read-only + /// + /// true iff this byte array has been marked read-only + /// If the byte array has been disposed public bool IsReadOnly() { return _target.IsReadOnly(); } - /** - * Mark this byte array as read-only. - * @throws IllegalStateException If the byte array has been disposed - */ + /// + /// Mark this byte array as read-only. + /// + /// If the byte array has been disposed public void MakeReadOnly() { _target.MakeReadOnly(); } - /** - * Create a copy of the byte array. If this instance is read-only, - * the copy will not be read-only. - * @return A copy of the byte array. - * @throws IllegalStateException If the byte array has been disposed - */ + /// + /// Create a copy of the byte array. + /// + /// + /// If this instance is read-only, + /// the copy will not be read-only. + /// + /// A copy of the byte array. + /// If the byte array has been disposed public GuardedByteArray Copy() { SecureString t2 = _target.Copy(); @@ -183,13 +198,13 @@ public GuardedByteArray Copy() return rv; } - /** - * Verifies that this base-64 encoded SHA1 hash of this byte array - * matches the given value. - * @param hash The hash to verify against. - * @return True if the hash matches the given parameter. - * @throws IllegalStateException If the byte array has been disposed - */ + /// + /// Verifies that this base-64 encoded SHA1 hash of this byte array + /// matches the given value. + /// + /// The hash to verify against. + /// True if the hash matches the given parameter. + /// If the byte array has been disposed public bool VerifyBase64SHA1Hash(String hash) { CheckNotDisposed(); @@ -242,151 +257,180 @@ private void ComputeHash() } - /** - * Secure string implementation that solves the problems associated with - * keeping passwords as java.lang.String. That is, anything - * represented as a String is kept in memory as a clear - * text password and stays in memory at least until it is garbage collected. - *

- * The GuardedString class alleviates this problem by storing the characters in - * memory in an encrypted form. The encryption key will be a randomly-generated - * key. - *

- * In their serialized form, GuardedString will be encrypted using a known - * default key. This is to provide a minimum level of protection regardless - * of the transport. For communications with the Remote Connector Framework - * it is recommended that deployments enable SSL for true encryption. - *

- * Applications may also wish to persist GuardedStrings. In the case of - * Identity Manager, it should convert GuardedStrings to EncryptedData so - * that they can be stored and managed using the Manage Encryption features - * of Identity Manager. Other applications may wish to serialize APIConfiguration - * as a whole. These applications are responsible for encrypting the APIConfiguration - * blob for an additional layer of security (beyond the basic default key encryption - * provided by GuardedString). - */ - public sealed class GuardedString : IDisposable { - /** - * This method will be called with the clear text of the string. - * After the call the clearChars array will be automatically zeroed - * out, thus keeping the window of potential exposure to a bare-minimum. - * @param clearChars - */ - public delegate void Accessor(UnmanagedArray clearChars); - - + ///

+ /// Secure string implementation that solves the problems associated with + /// keeping passwords as java.lang.String. + /// + /// + /// That is, anything + /// represented as a String is kept in memory as a clear + /// text password and stays in memory at least until it is garbage collected. + /// + /// The GuardedString class alleviates this problem by storing the characters in + /// memory in an encrypted form. The encryption key will be a randomly-generated + /// key. + /// + /// + /// In their serialized form, GuardedString will be encrypted using a known + /// default key. This is to provide a minimum level of protection regardless + /// of the transport. For communications with the Remote Connector Framework + /// it is recommended that deployments enable SSL for true encryption. + /// + /// + /// Applications may also wish to persist GuardedStrings. In the case of + /// Identity Manager, it should convert GuardedStrings to EncryptedData so + /// that they can be stored and managed using the Manage Encryption features + /// of Identity Manager. Other applications may wish to serialize APIConfiguration + /// as a whole. These applications are responsible for encrypting the APIConfiguration + /// blob for an additional layer of security (beyond the basic default key encryption + /// provided by GuardedString). + /// + /// + public sealed class GuardedString : IDisposable + { + /// + /// This method will be called with the clear text of the string. + /// + /// + /// After the call the clearChars array will be automatically zeroed + /// out, thus keeping the window of potential exposure to a bare-minimum. + /// + /// + public delegate void Accessor(UnmanagedArray clearChars); + + private SecureString _target; private String _base64SHA1Hash; - - /** - * Creates an empty secure string - */ - public GuardedString() { + + /// + /// Creates an empty secure string + /// + public GuardedString() + { _target = new SecureString(); ComputeHash(); } - - public GuardedString(SecureString str) { + + public GuardedString(SecureString str) + { _target = str.Copy(); ComputeHash(); } - - - /** - * Provides access to the clear-text value of the string in a controlled fashion. - * The clear-text characters will only be available for the duration of the call - * and automatically zeroed out following the call. - * - *

- * NOTE: Callers are encouraged to use {@link #verifyBase64SHA1Hash(String)} - * where possible if the intended use is merely to verify the contents of - * the string match an expected hash value. - * @param accessor Accessor callback. - * @throws IllegalStateException If the string has been disposed - */ - public void Access(Accessor accessor) { - using (SecureStringAdapter adapter = new SecureStringAdapter(_target)) { + + + ///

+ /// Provides access to the clear-text value of the string in a controlled fashion. + /// + /// + /// The clear-text characters will only be available for the duration of the call + /// and automatically zeroed out following the call. + /// + /// NOTE: Callers are encouraged to use + /// where possible if the intended use is merely to verify the contents of + /// the string match an expected hash value. + /// + /// + /// Accessor callback. + /// If the string has been disposed + public void Access(Accessor accessor) + { + using (SecureStringAdapter adapter = new SecureStringAdapter(_target)) + { accessor(adapter); } - } - - /** - * Appends a single clear-text character to the secure string. - * The in-memory data will be decrypted, the character will be - * appended, and then it will be re-encrypted. - * @param c The character to append. - * @throws IllegalStateException If the string is read-only - * @throws IllegalStateException If the string has been disposed - */ - public void AppendChar(char c) { + } + + /// + /// Appends a single clear-text character to the secure string. + /// + /// + /// The in-memory data will be decrypted, the character will be + /// appended, and then it will be re-encrypted. + /// + /// The character to append. + /// If the string is read-only + /// If the string has been disposed + public void AppendChar(char c) + { _target.AppendChar(c); ComputeHash(); } - - /** - * Clears the in-memory representation of the string. - */ - public void Dispose() { + + /// + /// Clears the in-memory representation of the string. + /// + public void Dispose() + { _target.Dispose(); } - - /** - * Returns true iff this string has been marked read-only - * @return true iff this string has been marked read-only - * @throws IllegalStateException If the string has been disposed - */ - public bool IsReadOnly() { + + /// + /// Returns true iff this string has been marked read-only + /// + /// true iff this string has been marked read-only + /// If the string has been disposed + public bool IsReadOnly() + { return _target.IsReadOnly(); } - - /** - * Mark this string as read-only. - * @throws IllegalStateException If the string has been disposed - */ - public void MakeReadOnly() { + + /// + /// Mark this string as read-only. + /// + /// If the string has been disposed + public void MakeReadOnly() + { _target.MakeReadOnly(); } - - /** - * Create a copy of the string. If this instance is read-only, - * the copy will not be read-only. - * @return A copy of the string. - * @throws IllegalStateException If the string has been disposed - */ - public GuardedString Copy() { + + /// + /// Create a copy of the string. + /// + /// + /// If this instance is read-only, + /// the copy will not be read-only. + /// + /// A copy of the string. + /// If the string has been disposed + public GuardedString Copy() + { SecureString t2 = _target.Copy(); GuardedString rv = new GuardedString(t2); return rv; } - - /** - * Verifies that this base-64 encoded SHA1 hash of this string - * matches the given value. - * @param hash The hash to verify against. - * @return True if the hash matches the given parameter. - * @throws IllegalStateException If the string has been disposed - */ - public bool VerifyBase64SHA1Hash(String hash) { + + /// + /// Verifies that this base-64 encoded SHA1 hash of this string + /// matches the given value. + /// + /// The hash to verify against. + /// True if the hash matches the given parameter. + /// If the string has been disposed + public bool VerifyBase64SHA1Hash(String hash) + { CheckNotDisposed(); return _base64SHA1Hash.Equals(hash); } - - public string GetBase64SHA1Hash() { + + public string GetBase64SHA1Hash() + { CheckNotDisposed(); - return _base64SHA1Hash; + return _base64SHA1Hash; } - - - + + + private void CheckNotDisposed() { //this throws if disposed _target.IsReadOnly(); } - - - public override bool Equals(Object o) { - if ( o is GuardedString ) { + + + public override bool Equals(Object o) + { + if (o is GuardedString) + { GuardedString other = (GuardedString)o; //not the true contract of equals. however, //due to the high mathematical improbability of @@ -398,109 +442,131 @@ public override bool Equals(Object o) { } return false; } - - public override int GetHashCode() { + + public override int GetHashCode() + { return _base64SHA1Hash.GetHashCode(); } - - public SecureString ToSecureString() { + + public SecureString ToSecureString() + { return _target.Copy(); } - + private void ComputeHash() { - Access(array=> { - _base64SHA1Hash = SecurityUtil.ComputeBase64SHA1Hash(array); - }); + Access(array => + { + _base64SHA1Hash = SecurityUtil.ComputeBase64SHA1Hash(array); + }); } - + } - + #region AbstractUnmanagedArray public abstract class AbstractUnmanagedArray : UnmanagedArray { private readonly int _length; private bool _disposed; - public AbstractUnmanagedArray(int length) { - if (length < 0) { - throw new ArgumentException("Invalid length: "+length); + public AbstractUnmanagedArray(int length) + { + if (length < 0) + { + throw new ArgumentException("Invalid length: " + length); } _length = length; } - public int Length { - get { - if (_disposed) { + public int Length + { + get + { + if (_disposed) + { throw new ObjectDisposedException("UnmanagedArray"); } - return _length; + return _length; } } - public T this[int index] { - get { - if (_disposed) { + public T this[int index] + { + get + { + if (_disposed) + { throw new ObjectDisposedException("UnmanagedArray"); } - if ( index < 0 || index >= Length ) { + if (index < 0 || index >= Length) + { throw new IndexOutOfRangeException(); } return GetValue(index); } - set { - if (_disposed) { + set + { + if (_disposed) + { throw new ObjectDisposedException("SecureStringAdapter"); } - if ( index < 0 || index >= Length ) { + if (index < 0 || index >= Length) + { throw new IndexOutOfRangeException(); } - SetValue(index,value); + SetValue(index, value); } } - public void Dispose() { - if (!_disposed) { - for ( int i = 0; i < Length; i++ ) { + public void Dispose() + { + if (!_disposed) + { + for (int i = 0; i < Length; i++) + { this[i] = default(T); } _disposed = true; FreeMemory(); } - } - + } + abstract protected T GetValue(int index); abstract protected void SetValue(int index, T val); abstract protected void FreeMemory(); } #endregion - + #region SecureStringAdapter internal class SecureStringAdapter : AbstractUnmanagedArray { private IntPtr _bstrPtr; - public SecureStringAdapter(SecureString secureString) : base(secureString.Length) { - Assertions.NullCheck(secureString,"secureString"); + public SecureStringAdapter(SecureString secureString) + : base(secureString.Length) + { + Assertions.NullCheck(secureString, "secureString"); _bstrPtr = Marshal.SecureStringToBSTR(secureString); } protected override char GetValue(int index) - { - unsafe { - char * charPtr = (char*)_bstrPtr; - return *(charPtr+index); + { + unsafe + { + char* charPtr = (char*)_bstrPtr; + return *(charPtr + index); } } protected override void SetValue(int index, char c) { - unsafe { - char * charPtr = (char*)_bstrPtr; - *(charPtr+index) = c; - } + unsafe + { + char* charPtr = (char*)_bstrPtr; + *(charPtr + index) = c; + } } protected override void FreeMemory() { - Marshal.ZeroFreeBSTR( _bstrPtr ); + Marshal.ZeroFreeBSTR(_bstrPtr); } } #endregion - + #region SecureStringToByteArrayAdapter internal class SecureStringToByteArrayAdapter : AbstractUnmanagedArray { @@ -538,102 +604,120 @@ protected override void FreeMemory() public class UnmanagedCharArray : AbstractUnmanagedArray { private IntPtr _ptr; - public UnmanagedCharArray(int length) : base(length) { - unsafe { - _ptr = Marshal.AllocHGlobal(length*sizeof(char)); + public UnmanagedCharArray(int length) + : base(length) + { + unsafe + { + _ptr = Marshal.AllocHGlobal(length * sizeof(char)); } } protected override char GetValue(int index) - { - unsafe { - char * charPtr = (char*)_ptr; - return *(charPtr+index); + { + unsafe + { + char* charPtr = (char*)_ptr; + return *(charPtr + index); } } protected override void SetValue(int index, char c) { - unsafe { - char * charPtr = (char*)_ptr; - *(charPtr+index) = c; - } + unsafe + { + char* charPtr = (char*)_ptr; + *(charPtr + index) = c; + } } protected override void FreeMemory() { - Marshal.FreeHGlobal( _ptr ); - } + Marshal.FreeHGlobal(_ptr); + } } #endregion - + #region UnmanagedByteArray public class UnmanagedByteArray : AbstractUnmanagedArray { private IntPtr _ptr; - public UnmanagedByteArray(int length) : base(length) { - unsafe { - _ptr = Marshal.AllocHGlobal(length*sizeof(byte)); + public UnmanagedByteArray(int length) + : base(length) + { + unsafe + { + _ptr = Marshal.AllocHGlobal(length * sizeof(byte)); } } protected override byte GetValue(int index) - { - unsafe { - byte * charPtr = (byte*)_ptr; - return *(charPtr+index); + { + unsafe + { + byte* charPtr = (byte*)_ptr; + return *(charPtr + index); } } protected override void SetValue(int index, byte c) { - unsafe { - byte * charPtr = (byte*)_ptr; - *(charPtr+index) = c; - } + unsafe + { + byte* charPtr = (byte*)_ptr; + *(charPtr + index) = c; + } } protected override void FreeMemory() { - Marshal.FreeHGlobal( _ptr ); - } + Marshal.FreeHGlobal(_ptr); + } } #endregion - + #region SecurityUtil /// /// Description of SecurityUtil. /// public static class SecurityUtil { - - /** - * Converts chars to bytes without using any external functions - * that might allocate additional buffers for the potentially - * sensitive data. This guarantees the caller that they only - * need to cleanup the input and result. - * @param chars The chars - * @return The bytes - */ + + /// + /// Converts chars to bytes without using any external functions + /// that might allocate additional buffers for the potentially + /// sensitive data. + /// + /// + /// This guarantees the caller that they only + /// need to cleanup the input and result. + /// + /// The chars + /// The bytes public static UnmanagedArray CharsToBytes(UnmanagedArray chars) { - UnmanagedByteArray bytes = new UnmanagedByteArray(chars.Length*2); - - for ( int i = 0; i < chars.Length; i++ ) { + UnmanagedByteArray bytes = new UnmanagedByteArray(chars.Length * 2); + + for (int i = 0; i < chars.Length; i++) + { char v = chars[i]; - bytes[i*2] = (byte)(0xff & (v >> 8)); - bytes[i*2+1] = (byte)(0xff & (v)); + bytes[i * 2] = (byte)(0xff & (v >> 8)); + bytes[i * 2 + 1] = (byte)(0xff & (v)); } return bytes; } - - /** - * Converts bytes to chars without using any external functions - * that might allocate additional buffers for the potentially - * sensitive data. This guarantees the caller that they only - * need to cleanup the input and result. - * @param chars The chars - * @return The bytes - */ + + /// + /// Converts bytes to chars without using any external functions + /// that might allocate additional buffers for the potentially + /// sensitive data. + /// + /// + /// This guarantees the caller that they only + /// need to cleanup the input and result. + /// + /// The chars + /// The bytes public static UnmanagedArray BytesToChars(UnmanagedArray bytes) { - UnmanagedCharArray chars = new UnmanagedCharArray(bytes.Length/2); - for ( int i = 0; i < chars.Length; i++ ) { - char v = (char)((bytes[i*2]<<8) | bytes[i*2+1]); + UnmanagedCharArray chars = new UnmanagedCharArray(bytes.Length / 2); + for (int i = 0; i < chars.Length; i++) + { + char v = (char)((bytes[i * 2] << 8) | bytes[i * 2 + 1]); chars[i] = v; } return chars; @@ -647,7 +731,7 @@ public unsafe static string ComputeBase64SHA1Hash(UnmanagedArray input) return ComputeBase64SHA1Hash(bytes); } } - + public unsafe static string ComputeBase64SHA1Hash(UnmanagedArray input) { byte[] managedBytes = new byte[input.Length]; @@ -668,59 +752,61 @@ public unsafe static string ComputeBase64SHA1Hash(UnmanagedArray input) } } } - - /** - * Copies an unmanaged byte array into a managed byte array. - * NOTE: it is imperative for security reasons that this only - * be done in a context where the byte array in question is pinned. - * moreover, the byte array must be cleared prior to leaving the - * pinned block - */ + + /// + /// Copies an unmanaged byte array into a managed byte array. + /// + /// + /// NOTE: it is imperative for security reasons that this only + /// be done in a context where the byte array in question is pinned. + /// moreover, the byte array must be cleared prior to leaving the + /// pinned block + /// public static void UnmanagedBytesToManagedBytes(UnmanagedArray array, - byte [] bytes) + byte[] bytes) { - for ( int i = 0 ; i < array.Length; i++ ) + for (int i = 0; i < array.Length; i++) { bytes[i] = array[i]; } } - - /** - * Clears an array of potentially sensitive bytes - * @param bytes The bytes. May be null. - * NOTE: because this is C#, this alone is not enough. The - * array must be pinned during the interval it is in-use or - * it could be copied out from under you. - */ - public static void Clear(byte [] bytes) - { - if ( bytes != null ) + + /// + /// Clears an array of potentially sensitive bytes + /// + /// The bytes. May be null. + /// NOTE: because this is C#, this alone is not enough. The + /// array must be pinned during the interval it is in-use or + /// it could be copied out from under you. + public static void Clear(byte[] bytes) + { + if (bytes != null) { - for ( int i = 0; i < bytes.Length; i++ ) + for (int i = 0; i < bytes.Length; i++) { bytes[i] = 0; } } } - - /** - * Clears an array of potentially sensitive chars - * @param chars The characters. May be null. - * NOTE: because this is C#, this alone is not enough. The - * array must be pinned during the interval it is in-use or - * it could be copied out from under you. - */ - public static void Clear(char [] chars) - { - if ( chars != null ) + + /// + /// Clears an array of potentially sensitive chars + /// + /// The characters. May be null. + /// NOTE: because this is C#, this alone is not enough. The + /// array must be pinned during the interval it is in-use or + /// it could be copied out from under you. + public static void Clear(char[] chars) + { + if (chars != null) { - for ( int i = 0; i < chars.Length; i++) + for (int i = 0; i < chars.Length; i++) { chars[i] = (char)0; } } } - + public static bool VerifyBase64SHA1Hash(UnmanagedArray input, string hash) { string inputHash = ComputeBase64SHA1Hash(input); @@ -728,58 +814,63 @@ public static bool VerifyBase64SHA1Hash(UnmanagedArray input, string hash) } } #endregion - + #region Encryptor - /** - * Responsible for encrypting/decrypting bytes. Implementations - * are intended to be thread-safe. - */ - public interface Encryptor { - /** - * Decrypts the given byte array - * @param bytes The encrypted bytes - * @return The decrypted bytes - */ - UnmanagedArray Decrypt(byte [] bytes); - - /** - * Encrypts the given byte array - * @param bytes The clear bytes - * @return The ecnrypted bytes - */ - byte [] Encrypt(UnmanagedArray bytes); + /// + /// Responsible for encrypting/decrypting bytes. + /// + /// + /// Implementations + /// are intended to be thread-safe. + /// + public interface Encryptor + { + /// + /// Decrypts the given byte array + /// + /// The encrypted bytes + /// The decrypted bytes + UnmanagedArray Decrypt(byte[] bytes); + + /// + /// Encrypts the given byte array + /// + /// The clear bytes + /// The ecnrypted bytes + byte[] Encrypt(UnmanagedArray bytes); } #endregion #region EncryptorFactory - public abstract class EncryptorFactory { + public abstract class EncryptorFactory + { private static readonly object LOCK = new object(); - + // At some point we might make this pluggable, but for now, hard-code private const String IMPL_NAME = "Org.IdentityConnectors.Common.Security.Impl.EncryptorFactoryImpl"; - + private static EncryptorFactory _instance; - - /** - * Get the singleton instance of the {@link EncryptorFactory}. - */ - public static EncryptorFactory GetInstance() { - lock(LOCK) { - if (_instance == null) { + + /// + /// Get the singleton instance of the . + /// + public static EncryptorFactory GetInstance() + { + lock (LOCK) + { + if (_instance == null) + { Type type = FrameworkInternalBridge.LoadType(IMPL_NAME); _instance = (EncryptorFactory)Activator.CreateInstance(type); } return _instance; } } - - /** - * Default encryptor that encrypts/descrypts using a default key - */ + + /// + /// Default encryptor that encrypts/descrypts using a default key + /// public abstract Encryptor GetDefaultEncryptor(); - - } #endregion - -} +} \ No newline at end of file diff --git a/Common/StringUtil.cs b/Common/StringUtil.cs index ae13619a..daebf339 100644 --- a/Common/StringUtil.cs +++ b/Common/StringUtil.cs @@ -26,39 +26,41 @@ namespace Org.IdentityConnectors.Common { public static class StringUtil { - /** - * Determines if a string is empty. Empty is defined as null or empty - * string. - * - *
-         *  StringUtil.isEmpty(null)               = true
-         *  StringUtil.isEmpty("")       = true
-         *  StringUtil.isEmpty(" ")      = false
-         *  StringUtil.isEmpty("bob")    = false
-         *  StringUtil.isEmpty(" bob ")  = false
-         * 
- * - * @param val - * string to evaluate as empty. - * @return true if the string is empty else false. - */ - public static bool IsEmpty(String val) { + /// + /// Determines if a string is empty. + /// + /// + /// Empty is defined as null or empty + /// string. + ///
+        /// StringUtil.isEmpty(null)               = true
+        /// StringUtil.isEmpty("")       = true
+        /// StringUtil.isEmpty(" ")      = false
+        /// StringUtil.isEmpty("bob")    = false
+        /// StringUtil.isEmpty(" bob ")  = false
+        /// 
+ ///
+ /// string to evaluate as empty. + /// true if the string is empty else false. + public static bool IsEmpty(String val) + { return (val == null) ? true : val.Length == 0; } - - /** - *
-         *      StringUtil.isBlank(null)                = true
-         *      StringUtil.isBlank("")        = true
-         *      StringUtil.isBlank(" ")       = true
-         *      StringUtil.isBlank("bob")     = false
-         *      StringUtil.isBlank("  bob  ") = false
-         * 
- */ - public static bool IsBlank(String val) { + + /// + ///
+        /// StringUtil.isBlank(null)                = true
+        /// StringUtil.isBlank("")        = true
+        /// StringUtil.isBlank(" ")       = true
+        /// StringUtil.isBlank("bob")     = false
+        /// StringUtil.isBlank("  bob  ") = false
+        /// 
+ ///
+ public static bool IsBlank(String val) + { return (val == null) ? true : IsEmpty(val.Trim()); } - + /// /// Constructs a secure string from a char []. The char[] will /// be cleared out when finished. @@ -66,27 +68,30 @@ public static bool IsBlank(String val) { /// The characters to use. Will be cleared /// out. /// A secure string representation - public static GuardedString NewGuardedString(char [] val) + public static GuardedString NewGuardedString(char[] val) { GuardedString rv = new GuardedString(); - for( int i = 0; i < val.Length; i++ ) + for (int i = 0; i < val.Length; i++) { rv.AppendChar(val[i]); val[i] = (char)0; } return rv; } - - - public static bool IsTrue(string val) { - if (!IsBlank(val)) { + + + public static bool IsTrue(string val) + { + if (!IsBlank(val)) + { // clean up the value.. val = val.Trim().ToLower(); - if (val.Equals("1") || val.Equals("on") || val.Equals("true")) { + if (val.Equals("1") || val.Equals("on") || val.Equals("true")) + { return true; } } return false; } } -} +} \ No newline at end of file diff --git a/Common/TraceUtil.cs b/Common/TraceUtil.cs index bb093054..bf5fbc4e 100644 --- a/Common/TraceUtil.cs +++ b/Common/TraceUtil.cs @@ -35,17 +35,18 @@ public static class TraceUtil /// /// An optional error message to display in addition to the exception /// The exception - public static void TraceException(String msg, Exception e) { + public static void TraceException(String msg, Exception e) + { StringBuilder builder = new StringBuilder(); - if ( msg != null ) + if (msg != null) { builder.AppendLine(msg); } - if ( e != null ) + if (e != null) { builder.AppendLine(e.ToString()); } Trace.TraceError(builder.ToString()); } } -} +} \ No newline at end of file diff --git a/Common/XmlUtil.cs b/Common/XmlUtil.cs index 94e31a08..6f4894db 100644 --- a/Common/XmlUtil.cs +++ b/Common/XmlUtil.cs @@ -35,158 +35,191 @@ public static class XmlUtil // DOM Navigation utilities // //////////////////////////////////////////////////////////// - - /** - * Return the value of an attribute on an element.

The DOM getAttribute - * method returns an empty string if the attribute doesn't exist. Here, we - * detect this and return null. - */ - public static String GetAttribute(XmlElement e, String name) { + + ///

+ /// Return the value of an attribute on an element. + /// + /// + ///

The DOM getAttribute + /// method returns an empty string if the attribute doesn't exist. Here, we + /// detect this and return null. + /// + public static String GetAttribute(XmlElement e, String name) + { String value = e.GetAttribute(name); if (value != null && value.Length == 0) value = null; return value; } - - /** - * Find an immediate child of the given name - */ - public static XmlElement FindImmediateChildElement(XmlNode node, String name) { - + + ///

+ /// Find an immediate child of the given name + /// + public static XmlElement FindImmediateChildElement(XmlNode node, String name) + { + XmlElement found = null; - - if (node != null) { - + + if (node != null) + { + for (XmlNode child = node.FirstChild; child != null - && found == null; child = child.NextSibling) { - - if (child.NodeType == XmlNodeType.Element) { - XmlElement tmp = (XmlElement) child; - if ( tmp.LocalName.Equals(name) ) { + && found == null; child = child.NextSibling) + { + + if (child.NodeType == XmlNodeType.Element) + { + XmlElement tmp = (XmlElement)child; + if (tmp.LocalName.Equals(name)) + { return tmp; } } } } - + return found; } - - /** - * Returns the First child element or null if none found - * @param node The node. May be null. - * @return the First child element or null if none found - */ - public static XmlElement GetFirstChildElement(XmlNode node) { - if ( node == null ) { + + /// + /// Returns the First child element or null if none found + /// + /// The node. May be null. + /// the First child element or null if none found + public static XmlElement GetFirstChildElement(XmlNode node) + { + if (node == null) + { return null; } XmlNode child = node.FirstChild; - if ( child != null && child.NodeType == XmlNodeType.Element ) { + if (child != null && child.NodeType == XmlNodeType.Element) + { return (XmlElement)child; } - else { + else + { return GetNextElement(child); } } - - /** - * Get the next right sibling that is an element. - */ - public static XmlElement GetNextElement(XmlNode node) { - + + /// + /// Get the next right sibling that is an element. + /// + public static XmlElement GetNextElement(XmlNode node) + { + XmlElement found = null; - - if (node != null) { - + + if (node != null) + { + for (XmlNode next = node.NextSibling; next != null - && found == null; next = next.NextSibling) { - + && found == null; next = next.NextSibling) + { + if (next.NodeType == XmlNodeType.Element) - found = (XmlElement) next; + found = (XmlElement)next; } } - + return found; } - - /** - * Locate the first text node at any level below the given node. If the - * ignoreEmpty flag is true, we will ignore text nodes that contain only - * whitespace characteres.

Note that if you're trying to extract - * element content, you probably don't want this since parser's can break up - * pcdata into multiple adjacent text nodes. See getContent() for a more - * useful method. - */ - private static XmlText FindText(XmlNode node, bool ignoreEmpty) { - + + ///

+ /// Locate the first text node at any level below the given node. + /// + /// + /// If the + /// ignoreEmpty flag is true, we will ignore text nodes that contain only + /// whitespace characteres.

Note that if you're trying to extract + /// element content, you probably don't want this since parser's can break up + /// pcdata into multiple adjacent text nodes. See getContent() for a more + /// useful method. + /// + private static XmlText FindText(XmlNode node, bool ignoreEmpty) + { + XmlText found = null; - - if (node != null) { - + + if (node != null) + { + if (node.NodeType == XmlNodeType.Text - || node.NodeType == XmlNodeType.CDATA) { - - XmlText t = (XmlText) node; + || node.NodeType == XmlNodeType.CDATA) + { + + XmlText t = (XmlText)node; if (!ignoreEmpty) found = t; - else { + else + { String s = t.Data.Trim(); if (s.Length > 0) found = t; } } - - if (found == null) { - + + if (found == null) + { + for (XmlNode child = node.FirstChild; child != null - && found == null; child = child.NextSibling) { - + && found == null; child = child.NextSibling) + { + found = FindText(child, ignoreEmpty); } } } - + return found; } - - - /** - * Return the content of the given element.

We will descend to an - * arbitrary depth looking for the first text node.

Note that - * the parser may break what was originally a single string of pcdata into - * multiple adjacent text nodes. Xerces appears to do this when it - * encounters a '$' in the text, not sure if there is specified behavior, or - * if its parser specific.

Here, we will congeal adjacent text nodes. - *

We will NOT ignore text nodes that have only whitespace. - */ - public static String GetContent(XmlElement e) { - + + + ///

+ /// Return the content of the given element. + /// + /// + ///

We will descend to an + /// arbitrary depth looking for the first text node.

Note that + /// the parser may break what was originally a single string of pcdata into + /// multiple adjacent text nodes. Xerces appears to do this when it + /// encounters a '$' in the text, not sure if there is specified behavior, or + /// if its parser specific.

Here, we will congeal adjacent text nodes. + ///

We will NOT ignore text nodes that have only whitespace. + /// + public static String GetContent(XmlElement e) + { + String content = null; - - if (e != null) { - + + if (e != null) + { + // find the first inner text node, XmlText t = FindText(e, false); - if (t != null) { + if (t != null) + { // we have at least some text StringBuilder b = new StringBuilder(); - while (t != null) { + while (t != null) + { b.Append(t.Data); XmlNode n = t.NextSibling; - + t = null; if (n != null - && ((n.NodeType == XmlNodeType.Text) || - (n.NodeType == XmlNodeType.CDATA))) { - t = (XmlText) n; + && ((n.NodeType == XmlNodeType.Text) || + (n.NodeType == XmlNodeType.CDATA))) + { + t = (XmlText)n; } } content = b.ToString(); } } - + return content; } } -} +} \ No newline at end of file diff --git a/Framework/Api.cs b/Framework/Api.cs index 1575f892..af22550e 100644 --- a/Framework/Api.cs +++ b/Framework/Api.cs @@ -37,223 +37,230 @@ namespace Org.IdentityConnectors.Framework.Api { - public static class APIConstants { + public static class APIConstants + { public const int NO_TIMEOUT = -1; } - - public interface APIConfiguration { + + public interface APIConfiguration + { ConfigurationProperties ConfigurationProperties { get; } bool IsConnectorPoolingSupported { get; } ObjectPoolConfiguration ConnectorPoolConfiguration { get; } ICollection> SupportedOperations { get; } - + int GetTimeout(SafeType operation); void SetTimeout(SafeType operation, int timeout); - + int ProducerBufferSize { get; set; } } - - /** - * Configuration properties encapsulates the {@link Configuration} and uses - * {@link Reflection} to determine the properties available for manipulation. - */ - public interface ConfigurationProperties { - - /** - * Get the list of properties names for this {@link Configuration}. - * - * @return get the list of properties names. - */ + + ///

+ /// Configuration properties encapsulates the and uses + /// to determine the properties available for manipulation. + /// + public interface ConfigurationProperties + { + /// + /// Get the list of properties names for this . + /// + /// get the list of properties names. IList PropertyNames { get; } - - /** - * Get a particular {@link ConfigurationProperty} by name. - * - * @param name - * the unique name of the property. - * @return a {@link ConfigurationProperty} if it exists otherwise null. - */ + + /// + /// Get a particular by name. + /// + /// the unique name of the property. + /// a if it exists otherwise null. ConfigurationProperty GetProperty(string name); - - /** - * Set the value of the {@link Configuration} property by name. - * - * @param name - * Name of the property to set the value against. - * @param value - * Value to set on the configuration property. - * @throws IllegalArgumentException - * iff the property name does not exist. - */ + + /// + /// Set the value of the property by name. + /// + /// Name of the property to set the value against. + /// Value to set on the configuration property. + /// iff the property name does not exist. void SetPropertyValue(string name, Object value); - + } - /** - * Translation from {@link Configuration} at the SPI layer to the API. - */ - public interface ConfigurationProperty { - + /// + /// Translation from at the SPI layer to the API. + /// + public interface ConfigurationProperty + { int Order { get; } - - /** - * Get the unique name of the configuration property. - */ + + /// + /// Get the unique name of the configuration property. + /// string Name { get; } - - /** - * Get the help message from the message catalog. - */ + + /// + /// Get the help message from the message catalog. + /// string GetHelpMessage(string def); - - /** - * Get the display name for the is configuration - */ + + /// + /// Get the display name for the is configuration + /// string GetDisplayName(string def); - - /** - * Get the value from the property. This should be the default value. - */ + + /// + /// Get the value from the property. + /// + /// + /// This should be the default value. + /// object Value { get; set; } - - /** - * Get the type of the property. - */ + + /// + /// Get the type of the property. + /// Type ValueType { get; } - - /** - * Is this a confidential property whose value should be encrypted by - * the application when persisted? - */ + + /// + /// Is this a confidential property whose value should be encrypted by + /// the application when persisted? + /// bool IsConfidential { get; } - - /** - * Is this a required property - * @return True if the property is required - */ + + /// + /// Is this a required property + /// + /// True if the property is required bool IsRequired { get; } - - /** - * Set of operations for which this property must be specified. - * This is used for the case where a connector may or may not - * implement certain operations depending in the configuration. - * The default value of "empty array" is special in that - * it means that this property is applicable to all operations. - */ + + /// + /// Set of operations for which this property must be specified. + /// + /// + /// This is used for the case where a connector may or may not + /// implement certain operations depending in the configuration. + /// The default value of "empty array" is special in that + /// it means that this property is applicable to all operations. + /// + /// ICollection> Operations { get; } } - - /** - * Main interface for which consumers call the Connector API logic. - */ + + /// + /// Main interface for which consumers call the Connector API logic. + /// public interface ConnectorFacade : CreateApiOp, DeleteApiOp, SearchApiOp, UpdateApiOp, SchemaApiOp, AuthenticationApiOp, ResolveUsernameApiOp, GetApiOp, ValidateApiOp, TestApiOp, ScriptOnConnectorApiOp, ScriptOnResourceApiOp, - SyncApiOp { - - /** - * Get the set of operations that this {@link ConnectorFacade} will support. - */ + SyncApiOp + { + /// + /// Get the set of operations that this will support. + /// ICollection> SupportedOperations { get; } - /** - * Get an instance of an operation that this facade supports. - */ + /// + /// Get an instance of an operation that this facade supports. + /// APIOperation GetOperation(SafeType type); - + } - - /** - * Manages a pool of connectors for use by a provisioner. - * - */ - public abstract class ConnectorFacadeFactory { - + + /// + /// Manages a pool of connectors for use by a provisioner. + /// + public abstract class ConnectorFacadeFactory + { // At some point we might make this pluggable, but for now, hard-code - private const string IMPL_NAME = + private const string IMPL_NAME = "Org.IdentityConnectors.Framework.Impl.Api.ConnectorFacadeFactoryImpl"; private static ConnectorFacadeFactory _instance; private static object LOCK = new Object(); - - /** - * Get the singleton instance of the {@link ConnectorFacadeFactory}. - */ - public static ConnectorFacadeFactory GetInstance() { - lock(LOCK) { - if (_instance == null) { + + /// + /// Get the singleton instance of the . + /// + public static ConnectorFacadeFactory GetInstance() + { + lock (LOCK) + { + if (_instance == null) + { SafeType t = FrameworkInternalBridge.LoadType(IMPL_NAME); _instance = t.CreateInstance(); } } return _instance; } - - /** - * Get a new instance of {@link ConnectorFacade}. - * - * @param config - * all the configuration that the framework, connector, and - * pooling needs. - * @return {@link ConnectorFacade} to call API operations against. - * @throws ClassNotFoundException - */ + + /// + /// Get a new instance of . + /// + /// all the configuration that the framework, connector, and + /// pooling needs. + /// + /// to call API operations against. + /// public abstract ConnectorFacade NewInstance(APIConfiguration config); - - - /** - * Dispose of all connection pools, resources, etc. - */ + + + /// + /// Dispose of all connection pools, resources, etc. + /// public abstract void Dispose(); } - /** - * The connector meta-data for a given connector. - */ - public interface ConnectorInfo { - /** - * Returns a friendly name suitable for display in the UI. - * - * @return The friendly name - */ - string GetConnectorDisplayName(); - + /// + /// The connector meta-data for a given connector. + /// + public interface ConnectorInfo + { + /// + /// Returns a friendly name suitable for display in the UI. + /// + /// The friendly name + string GetConnectorDisplayName(); + ConnectorMessages Messages { get; } - + ConnectorKey ConnectorKey { get; } - /** - * Loads the {@link Connector} and {@link Configuration} class in order to - * determine the proper default configuration parameters. - */ + /// + /// Loads the and class in order to + /// determine the proper default configuration parameters. + /// APIConfiguration CreateDefaultAPIConfiguration(); } - /** - * Class responsible for maintaing a list of ConnectorInfo - * associated with a set of connector bundles. - */ - public interface ConnectorInfoManager { - /** - * Returns the list of ConnectorInfo - * @return the list of ConnectorInfo - */ + /// + /// Class responsible for maintaing a list of ConnectorInfo + /// associated with a set of connector bundles. + /// + public interface ConnectorInfoManager + { + /// + /// Returns the list of ConnectorInfo + /// + /// the list of ConnectorInfo IList ConnectorInfos { get; } - - /** - * Given a connectorName and connectorVersion, returns the - * associated ConnectorInfo. - * @param key The connector key. - * @return The ConnectorInfo or null if it couldn't - * be found. - */ + + /// + /// Given a connectorName and connectorVersion, returns the + /// associated ConnectorInfo. + /// + /// The connector key. + /// The ConnectorInfo or null if it couldn't + /// be found. ConnectorInfo FindConnectorInfo(ConnectorKey key); } - /** - * The main entry point into connectors. This allows you - * to load the connector classes from a set of bundles. - */ - public abstract class ConnectorInfoManagerFactory { + /// + /// The main entry point into connectors. + /// + /// + /// This allows you + /// to load the connector classes from a set of bundles. + /// + public abstract class ConnectorInfoManagerFactory + { //At some point we might make this pluggable, but for now, hard-code - private const string IMPL_NAME = + private const string IMPL_NAME = "Org.IdentityConnectors.Framework.Impl.Api.ConnectorInfoManagerFactoryImpl"; private static ConnectorInfoManagerFactory _instance; private static object LOCK = new Object(); @@ -262,10 +269,13 @@ public abstract class ConnectorInfoManagerFactory { /// ConnectorInfoManagerFactory. ///
/// - public static ConnectorInfoManagerFactory GetInstance() { - lock(LOCK) { - if (_instance == null) { - SafeType t = + public static ConnectorInfoManagerFactory GetInstance() + { + lock (LOCK) + { + if (_instance == null) + { + SafeType t = FrameworkInternalBridge.LoadType(IMPL_NAME); _instance = t.CreateInstance(); } @@ -274,81 +284,104 @@ public static ConnectorInfoManagerFactory GetInstance() { } public abstract ConnectorInfoManager GetLocalManager(); public abstract ConnectorInfoManager GetRemoteManager(RemoteFrameworkConnectionInfo info); - - /** - * Clears the bundle manager cache. Generally intended for unit testing - */ + + /// + /// Clears the bundle manager cache. + /// + /// + /// Generally intended for unit testing + /// public abstract void ClearRemoteCache(); } - - /** - * Uniquely identifies a connector within an installation. - * Consists of the triple (bundleName, bundleVersion, connectorName) - */ - public sealed class ConnectorKey { + + /// + /// Uniquely identifies a connector within an installation. + /// + /// + /// Consists of the triple (bundleName, bundleVersion, connectorName) + /// + public sealed class ConnectorKey + { private readonly string _bundleName; private readonly string _bundleVersion; private readonly string _connectorName; - + public ConnectorKey(String bundleName, String bundleVersion, - String connectorName) { - if (bundleName == null) { + String connectorName) + { + if (bundleName == null) + { throw new ArgumentException("bundleName may not be null"); } - if (bundleVersion == null) { - throw new ArgumentException("bundleVersion may not be null"); + if (bundleVersion == null) + { + throw new ArgumentException("bundleVersion may not be null"); } - if (connectorName == null) { - throw new ArgumentException("connectorName may not be null"); + if (connectorName == null) + { + throw new ArgumentException("connectorName may not be null"); } - _bundleName = bundleName; + _bundleName = bundleName; _bundleVersion = bundleVersion; _connectorName = connectorName; } - - public string BundleName { - get { + + public string BundleName + { + get + { return _bundleName; } } - - public string BundleVersion { - get { + + public string BundleVersion + { + get + { return _bundleVersion; } } - - public string ConnectorName { - get { + + public string ConnectorName + { + get + { return _connectorName; } } - - public override bool Equals(object o) { - if ( o is ConnectorKey ) { + + public override bool Equals(object o) + { + if (o is ConnectorKey) + { ConnectorKey other = (ConnectorKey)o; - if (!_bundleName.Equals(other._bundleName)) { + if (!_bundleName.Equals(other._bundleName)) + { return false; } - if (!_bundleVersion.Equals(other._bundleVersion)) { + if (!_bundleVersion.Equals(other._bundleVersion)) + { return false; } - if (!_connectorName.Equals(other._connectorName)) { + if (!_connectorName.Equals(other._connectorName)) + { return false; } return true; } return false; } - - public override int GetHashCode() { + + public override int GetHashCode() + { int rv = 0; rv ^= _connectorName.GetHashCode(); return rv; } - - public override string ToString() { + + public override string ToString() + { StringBuilder builder = new StringBuilder(); builder.Append("ConnectorKey("); builder.Append(" bundleName=").Append(_bundleName); @@ -360,176 +393,200 @@ public override string ToString() { } - - public sealed class RemoteFrameworkConnectionInfo { + + public sealed class RemoteFrameworkConnectionInfo + { private readonly String _host; private readonly int _port; private readonly GuardedString _key; private readonly bool _useSSL; private readonly RemoteCertificateValidationCallback _certificateValidationCallback; private readonly int _timeout; - - /** - * Creates a new instance of RemoteFrameworkConnectionInfo, using - * a clear (non-ssl) connection and a 60-second timeout. - * @param host The host to connect to - * @param port The port to connect to - */ + + /// + /// Creates a new instance of RemoteFrameworkConnectionInfo, using + /// a clear (non-ssl) connection and a 60-second timeout. + /// + /// The host to connect to + /// The port to connect to public RemoteFrameworkConnectionInfo(String host, int port, GuardedString key) - :this(host,port,key,false,null,60*1000) { + : this(host, port, key, false, null, 60 * 1000) + { } - - /** - * Creates a new instance of RemoteFrameworkConnectionInfo. - * @param host The host to connect to - * @param port The port to connect to - * @param useSSL Set to true if we are to connect via SSL. - * @param certificateValidationCallback to use - * for establising the SSL connection. May be null or empty, - * in which case the default installed providers for the JVM will - * be used. Ignored if 'useSSL' is false. - * @param timeout The timeout to use (in milliseconds). A value of 0 - * means infinite timeout; - */ + + /// + /// Creates a new instance of RemoteFrameworkConnectionInfo. + /// + /// The host to connect to + /// The port to connect to + /// Set to true if we are to connect via SSL. + /// to use + /// for establising the SSL connection. May be null or empty, + /// in which case the default installed providers for the JVM will + /// be used. Ignored if 'useSSL' is false. + /// The timeout to use (in milliseconds). A value of 0 + /// means infinite timeout; public RemoteFrameworkConnectionInfo(String host, int port, GuardedString key, bool useSSL, RemoteCertificateValidationCallback certificateValidationCallback, - int timeout) { - - if ( host == null ) { + int timeout) + { + + if (host == null) + { throw new ArgumentException("Parameter 'host' is null."); } - if ( key == null ) { + if (key == null) + { throw new ArgumentException("Parameter 'key' is null."); } - + _host = host; _port = port; - _key = key; + _key = key; _useSSL = useSSL; _certificateValidationCallback = certificateValidationCallback; _timeout = timeout; } - - /** - * Returns the host to connect to. - * @return The host to connect to. - */ - public String Host { - get { + + /// + /// Returns the host to connect to. + /// + /// The host to connect to. + public String Host + { + get + { return _host; } } - - /** - * Returns the port to connect to - * @return The port to connect to - */ - public int Port { - get { + + /// + /// Returns the port to connect to + /// + /// The port to connect to + public int Port + { + get + { return _port; } } - - public GuardedString Key { - get { + + public GuardedString Key + { + get + { return _key; } } - - /** - * Returns true iff we are to use SSL to connect. - * @return true iff we are to use SSL to connect. - */ - public bool UseSSL { - get { + + /// + /// Returns true iff we are to use SSL to connect. + /// + /// true iff we are to use SSL to connect. + public bool UseSSL + { + get + { return _useSSL; } } - - /** - * Returns the list of {@link TrustManager}'s. to use when establishing - * the connection. - * @return The list of {@link TrustManager}'s. - */ - public RemoteCertificateValidationCallback CertificateValidationCallback { - get { + + /// + /// Returns the list of 's. + /// + /// + /// to use when establishing + /// the connection. + /// + /// The list of 's. + public RemoteCertificateValidationCallback CertificateValidationCallback + { + get + { return _certificateValidationCallback; } } - - /** - * Returns the timeout (in milliseconds) to use for the connection. - * A value of zero means infinite timeout. - * @return the timeout (in milliseconds) to use for the connection. - */ - public int Timeout { - get { + + /// + /// Returns the timeout (in milliseconds) to use for the connection. + /// + /// + /// A value of zero means infinite timeout. + /// + /// the timeout (in milliseconds) to use for the connection. + public int Timeout + { + get + { return _timeout; } } - - /** - * {@inheritDoc} - */ - public override bool Equals(Object o) { - if ( o is RemoteFrameworkConnectionInfo ) { - RemoteFrameworkConnectionInfo other = + + public override bool Equals(Object o) + { + if (o is RemoteFrameworkConnectionInfo) + { + RemoteFrameworkConnectionInfo other = (RemoteFrameworkConnectionInfo)o; - if (!Object.Equals(Host,other.Host)) { + if (!Object.Equals(Host, other.Host)) + { return false; } - if (Port != other.Port) { + if (Port != other.Port) + { return false; } - if (UseSSL != other.UseSSL) { + if (UseSSL != other.UseSSL) + { return false; } if (CertificateValidationCallback == null || - other.CertificateValidationCallback == null) { + other.CertificateValidationCallback == null) + { if (CertificateValidationCallback != null || - other.CertificateValidationCallback != null) { + other.CertificateValidationCallback != null) + { return false; } } - else { + else + { if (!CertificateValidationCallback.Equals - (other.CertificateValidationCallback)) { + (other.CertificateValidationCallback)) + { return false; } } - - if (!Key.Equals(other.Key)) { + + if (!Key.Equals(other.Key)) + { return false; } - - if (Timeout != other.Timeout) { + + if (Timeout != other.Timeout) + { return false; } - + return true; } return false; } - - /** - * {@inheritDoc} - */ - public override int GetHashCode() { + + public override int GetHashCode() + { return _host.GetHashCode() ^ _port; } - - /** - * {@inheritDoc} - */ - public override String ToString() { - return "{host="+_host+", port="+_port+"}"; - } - + public override String ToString() + { + return "{host=" + _host + ", port=" + _port + "}"; + } } } diff --git a/Framework/ApiOperations.cs b/Framework/ApiOperations.cs index f8aedbe1..6b594395 100644 --- a/Framework/ApiOperations.cs +++ b/Framework/ApiOperations.cs @@ -31,64 +31,60 @@ namespace Org.IdentityConnectors.Framework.Api.Operations { - /** - * Base interface for all API operations. - */ - public interface APIOperation { + /// + /// Base interface for all API operations. + /// + public interface APIOperation + { } - public interface AuthenticationApiOp : APIOperation { - - /** - * Most basic authentication available. - * - * @param username - * string that represents the account or user id. - * @param password - * string that represents the password for the account or user. - * @throws RuntimeException - * iff the credentials do not pass authentication otherwise - * nothing. - */ + public interface AuthenticationApiOp : APIOperation + { + /// + /// Most basic authentication available. + /// + /// string that represents the account or user id. + /// string that represents the password for the account or user. + /// iff the credentials do not pass authentication otherwise + /// nothing. Uid Authenticate(ObjectClass objectClass, string username, GuardedString password, OperationOptions options); } - public interface ResolveUsernameApiOp : APIOperation { - - /** - * Resolve the given {@link AuthenticationApiOp authentication} username - * to the corresponding {@link Uid}. The Uid is the one - * that {@link AuthenticationApiOp#authenticate} would return - * in case of a successful authentication. - * - * @param objectClass The object class to use for authenticate. - * Will typically be an account. Must not be null. - * @param username - * string that represents the account or user id. - * @param options - * additional options that impact the way this operation is run. - * May be null. - * @return Uid The uid of the account that would be used to authenticate. - * @throws RuntimeException - * iff the username could not be resolved. - */ + public interface ResolveUsernameApiOp : APIOperation + { + /// + /// Resolve the given username + /// to the corresponding . + /// + /// + /// The Uid is the one + /// that would return + /// in case of a successful authentication. + /// + /// The object class to use for authenticate. + /// Will typically be an account. Must not be null. + /// string that represents the account or user id. + /// additional options that impact the way this operation is run. + /// May be null. + /// Uid The uid of the account that would be used to authenticate. + /// iff the username could not be resolved. Uid ResolveUsername(ObjectClass objectClass, string username, OperationOptions options); } /// /// Operation to create connector objects based on the attributes provided. /// - public interface CreateApiOp : APIOperation { - - /// - /// Creates a user based on the ConnectorAttributes provide. The only + public interface CreateApiOp : APIOperation + { + /// + /// Creates a user based on the ConnectorAttributes provide. The only /// required attribute is the ObjectClass and those required by the /// Connector. The API will validate the existence of the /// ObjectClass attribute and that there are no duplicate name'd /// attributes. - /// - /// ConnectorAttribtes to create the object. - /// Unique id for the created object. + /// + /// ConnectorAttribtes to create the object. + /// Unique id for the created object. Uid Create(ObjectClass oclass, ICollection attrs, OperationOptions options); } @@ -96,7 +92,8 @@ public interface CreateApiOp : APIOperation { /// Deletes an object with the specified Uid and ObjectClass on the /// resource. /// - public interface DeleteApiOp : APIOperation { + public interface DeleteApiOp : APIOperation + { /// /// Delete the object that the specified Uid identifies (if any). /// @@ -106,327 +103,343 @@ public interface DeleteApiOp : APIOperation { void Delete(ObjectClass objectClass, Uid uid, OperationOptions options); } - /** - * Get a particular {@link ConnectorObject} based on the {@link Uid}. - */ - public interface GetApiOp : APIOperation { - - /** - * Get a particular {@link ConnectorObject} based on the {@link Uid}. - * - * @param uid - * the unique id of the object that to get. - * @return {@link ConnectorObject} based on the {@link Uid} provided. - */ + /// + /// Get a particular based on the . + /// + public interface GetApiOp : APIOperation + { + /// + /// Get a particular based on the . + /// + /// the unique id of the object that to get. + /// + /// based on the provided. ConnectorObject GetObject(ObjectClass objClass, Uid uid, OperationOptions options); } - /** - * Get the schema from the {@link Connector}. - */ - public interface SchemaApiOp : APIOperation { - /** - * Retrieve the basic schema of this {@link Connector}. - */ + /// + /// Get the schema from the . + /// + public interface SchemaApiOp : APIOperation + { + /// + /// Retrieve the basic schema of this . + /// Schema Schema(); } - - public interface SearchApiOp : APIOperation { - /** - * Search the resource for all objects that match the filter. - * - * @param filter - * Reduces the number of entries to only those that match the - * {@link Filter} provided. - * @param handler - * class responsible for working with the objects returned from - * the search. - * @throws RuntimeException - * iff there is problem during the processing of the results. - */ + public interface SearchApiOp : APIOperation + { + /// + /// Search the resource for all objects that match the filter. + /// + /// Reduces the number of entries to only those that match the + /// provided. + /// class responsible for working with the objects returned from + /// the search. + /// iff there is problem during the processing of the results. void Search(ObjectClass oclass, Filter filter, ResultsHandler handler, OperationOptions options); } - - /** - * Runs a script in the same JVM or .Net Runtime as the Connector. - * That is, if you are using a local framework, the script will be - * run in your JVM. If you are connected to a remote framework, the - * script will be run in the remote JVM or .Net Runtime. - *

- * This API allows an application to run a script in the context - * of any connector. (A connector need not implement any particular interface - * in order to enable this.) The minimum contract to which each connector - * must adhere is as follows: - *

    - *
  1. Script will run in the same classloader/execution environment - * as the connector, so the script will have access to all the classes - * to which the connector has access. - *
  2. - *
  3. Script will have access to a "connector" variable - * that is equivalent to an initialized instance of a connector. - * Thus, at a minimum the script will be able to access - * {@link Connector#getConfiguration() the configuration of the connector}. - *
  4. - *
  5. Script will have access to any - * {@link ScriptContext#getScriptArguments() script-arguments} - * passed in by the application. - *
  6. - *
- *

- * A connector that implements {@link ScriptOnConnectorOp} - * may provide more variables than what is described above. - * A connector also may perform special processing - * for {@link OperationOptions} specific to that connector. - * Consult the javadoc of each particular connector to find out what - * additional capabilities, if any, that connector exposes for use in scripts. - *

- * NOTE: A caller who wants to execute scripts on a connector - * should assume that a script must not use any method of the connector - * beyond the minimum contract described above, - * unless the connector explicitly documents that method as - * "for use by connector script". The primary function of a connector - * is to implement the SPI in the context of the Connector framework. - * In general, no caller should invoke Connector methods directly - * --whether by a script or by other means. - */ - public interface ScriptOnConnectorApiOp : APIOperation { - - /** - * Runs the script. - * @param request - The script and arguments to run. - * @param options - Additional options that control how the script is - * run. The framework does not currently recognize any options - * but specific connectors might. Consult the documentation - * for each connector to identify supported options. - * @return The result of the script. The return type must be - * a type that the framework supports for serialization. - * @see ObjectSerializerFactory for a list of supported return types. - */ + + ///

+ /// Runs a script in the same JVM or .Net Runtime as the Connector. + /// + /// + /// That is, if you are using a local framework, the script will be + /// run in your JVM. If you are connected to a remote framework, the + /// script will be run in the remote JVM or .Net Runtime. + /// + /// This API allows an application to run a script in the context + /// of any connector. (A connector need not implement any particular interface + /// in order to enable this.) The minimum contract to which each connector + /// must adhere is as follows: + /// + /// + /// Script will run in the same classloader/execution environment + /// as the connector, so the script will have access to all the classes + /// to which the connector has access. + /// + /// + /// + /// Script will have access to a "connector" variable + /// that is equivalent to an initialized instance of a connector. + /// Thus, at a minimum the script will be able to access + /// . + /// + /// + /// + /// Script will have access to any + /// + /// passed in by the application. + /// + /// + /// + /// + /// + /// A connector that implements + /// may provide more variables than what is described above. + /// A connector also may perform special processing + /// for specific to that connector. + /// Consult the javadoc of each particular connector to find out what + /// additional capabilities, if any, that connector exposes for use in scripts. + /// + /// + /// NOTE: A caller who wants to execute scripts on a connector + /// should assume that a script must not use any method of the connector + /// beyond the minimum contract described above, + /// unless the connector explicitly documents that method as + /// "for use by connector script". The primary function of a connector + /// is to implement the SPI in the context of the Connector framework. + /// In general, no caller should invoke Connector methods directly + /// --whether by a script or by other means. + /// + /// + public interface ScriptOnConnectorApiOp : APIOperation + { + /// + /// Runs the script. + /// + /// - The script and arguments to run. + /// - Additional options that control how the script is + /// run. The framework does not currently recognize any options + /// but specific connectors might. Consult the documentation + /// for each connector to identify supported options. + /// The result of the script. The return type must be + /// a type that the framework supports for serialization. + /// Object RunScriptOnConnector(ScriptContext request, OperationOptions options); } - /** - * Runs a script on the target resource that a connector manages. - * This API operation is supported only for a connector that implements - * {@link ScriptOnResourceOp}. - *

- * The contract here at the API level is intentionally very loose. - * Each connector decides what script languages it supports, - * what running a script on a target resource actually means, - * and what script options (if any) that connector supports. - * Refer to the javadoc of each particular connector for more information. - */ - public interface ScriptOnResourceApiOp : APIOperation { - - /** - * Runs a script on a specific target resource. - * @param request The script and arguments to run. - * @param options Additional options which control how the script is - * run. Please refer to the connector documentation for supported - * options. - * @return The result of the script. The return type must be - * a type that the connector framework supports for serialization. - * See {@link ObjectSerializerFactory} for a list of supported return types. - */ + ///

+ /// Runs a script on the target resource that a connector manages. + /// + /// + /// This API operation is supported only for a connector that implements + /// . + /// + /// The contract here at the API level is intentionally very loose. + /// Each connector decides what script languages it supports, + /// what running a script on a target resource actually means, + /// and what script options (if any) that connector supports. + /// Refer to the javadoc of each particular connector for more information. + /// + /// + public interface ScriptOnResourceApiOp : APIOperation + { + /// + /// Runs a script on a specific target resource. + /// + /// The script and arguments to run. + /// Additional options which control how the script is + /// run. Please refer to the connector documentation for supported + /// options. + /// The result of the script. The return type must be + /// a type that the connector framework supports for serialization. + /// See for a list of supported return types. Object RunScriptOnResource(ScriptContext request, OperationOptions options); } - /** - * Receive synchronization events from the resource. This will be supported by - * connectors that implement {@link SyncOp}. - * - * @see SyncOp - */ - public interface SyncApiOp : APIOperation { - /** - * Perform a synchronization. - * - * @param objClass - * The object class to synchronize. Must not be null. - * @param token - * The token representing the last token from the previous sync. - * Should be null if this is the first sync for the given - * resource. - * @param handler - * The result handler Must not be null. - * @param options - * additional options that impact the way this operation is run. - * May be null. - */ + /// + /// Receive synchronization events from the resource. + /// + /// + /// This will be supported by + /// connectors that implement . + /// + /// + public interface SyncApiOp : APIOperation + { + /// + /// Perform a synchronization. + /// + /// The object class to synchronize. Must not be null. + /// The token representing the last token from the previous sync. + /// Should be null if this is the first sync for the given + /// resource. + /// The result handler Must not be null. + /// additional options that impact the way this operation is run. + /// May be null. void Sync(ObjectClass objClass, SyncToken token, SyncResultsHandler handler, OperationOptions options); - /** - * Returns the token corresponding to the latest sync delta. - * This is to support applications that may wish to sync starting - * "now". - * @return The latest token or null if there is no sync data. - */ + /// + /// Returns the token corresponding to the latest sync delta. + /// + /// + /// This is to support applications that may wish to sync starting + /// "now". + /// + /// The latest token or null if there is no sync data. SyncToken GetLatestSyncToken(ObjectClass objectClass); } - - /** - * Updates a {@link ConnectorObject}. This operation - * is supported for those connectors that implement - * either {@link UpdateOp} or the more advanced - * {@link UpdateAttributeValuesOp}. - */ - public interface UpdateApiOp : APIOperation { - - /** - * Update the object specified by the {@link ObjectClass} and {@link Uid}, - * replacing the current values of each attribute with the values - * provided. - *

- * For each input attribute, replace - * all of the current values of that attribute in the target object with - * the values of that attribute. - *

- * If the target object does not currently contain an attribute that the - * input set contains, then add this - * attribute (along with the provided values) to the target object. - *

- * If the value of an attribute in the input set is - * {@code null}, then do one of the following, depending on - * which is most appropriate for the target: - *

    - *
  • If possible, remove that attribute from the target - * object entirely.
  • - *
  • Otherwise, replace all of the current values of that - * attribute in the target object with a single value of - * {@code null}.
  • - *
- * @param objclass - * the type of object to modify. Must not be null. - * @param uid - * the uid of the object to modify. Must not be null. - * @param replaceAttributes - * set of new {@link Attribute}. the values in this set - * represent the new, merged values to be applied to the object. - * This set may also include {@link OperationalAttributes operational attributes}. - * Must not be null. - * @param options - * additional options that impact the way this operation is run. - * May be null. - * @return the {@link Uid} of the updated object in case the update changes - * the formation of the unique identifier. - * @throws UnknownUidException - * iff the {@link Uid} does not exist on the resource. - */ + + /// + /// Updates a . + /// + /// + /// This operation + /// is supported for those connectors that implement + /// either or the more advanced + /// . + /// + public interface UpdateApiOp : APIOperation + { + /// + /// Update the object specified by the and , + /// replacing the current values of each attribute with the values + /// provided. + /// + /// + /// + /// For each input attribute, replace + /// all of the current values of that attribute in the target object with + /// the values of that attribute. + /// + /// + /// If the target object does not currently contain an attribute that the + /// input set contains, then add this + /// attribute (along with the provided values) to the target object. + /// + /// + /// If the value of an attribute in the input set is + /// null, then do one of the following, depending on + /// which is most appropriate for the target: + /// + /// + /// If possible, remove that attribute from the target + /// object entirely. + /// + /// + /// + /// Otherwise, replace all of the current values of that + /// attribute in the target object with a single value of + /// null. + /// + /// + /// + /// + /// + /// the type of object to modify. Must not be null. + /// the uid of the object to modify. Must not be null. + /// set of new . the values in this set + /// represent the new, merged values to be applied to the object. + /// This set may also include . + /// Must not be null. + /// additional options that impact the way this operation is run. + /// May be null. + /// the of the updated object in case the update changes + /// the formation of the unique identifier. + /// iff the does not exist on the resource. Uid Update(ObjectClass objclass, Uid uid, ICollection replaceAttributes, OperationOptions options); - - /** - * Update the object specified by the {@link ObjectClass} and {@link Uid}, - * adding to the current values of each attribute the values provided. - *

- * For each attribute that the input set contains, add to - * the current values of that attribute in the target object all of the - * values of that attribute in the input set. - *

- * NOTE that this does not specify how to handle duplicate values. - * The general assumption for an attribute of a {@code ConnectorObject} - * is that the values for an attribute may contain duplicates. - * Therefore, in general simply append the provided values - * to the current value for each attribute. - *

- * IMPLEMENTATION NOTE: for connectors that merely implement {@link UpdateOp} - * and not {@link UpdateAttributeValuesOp} this method will be simulated by - * fetching, merging, and calling - * {@link UpdateOp#update(ObjectClass, Uid, Set, OperationOptions)}. Therefore, - * connector implementations are encourage to implement {@link UpdateAttributeValuesOp} - * from a performance and atomicity standpoint. - * @param objclass - * the type of object to modify. Must not be null. - * @param uid - * the uid of the object to modify. Must not be null. - * @param valuesToAdd - * set of {@link Attribute} deltas. The values for the attributes - * in this set represent the values to add to attributes in the object. - * merged. This set must not include {@link OperationalAttributes operational attributes}. - * Must not be null. - * @param options - * additional options that impact the way this operation is run. - * May be null. - * @return the {@link Uid} of the updated object in case the update changes - * the formation of the unique identifier. - * @throws UnknownUidException - * iff the {@link Uid} does not exist on the resource. - */ + + ///

+ /// Update the object specified by the and , + /// adding to the current values of each attribute the values provided. + /// + /// + /// + /// For each attribute that the input set contains, add to + /// the current values of that attribute in the target object all of the + /// values of that attribute in the input set. + /// + /// + /// NOTE that this does not specify how to handle duplicate values. + /// The general assumption for an attribute of a ConnectorObject + /// is that the values for an attribute may contain duplicates. + /// Therefore, in general simply append the provided values + /// to the current value for each attribute. + /// + /// + /// IMPLEMENTATION NOTE: for connectors that merely implement + /// and not this method will be simulated by + /// fetching, merging, and calling + /// . Therefore, + /// connector implementations are encourage to implement + /// from a performance and atomicity standpoint. + /// + /// + /// the type of object to modify. Must not be null. + /// the uid of the object to modify. Must not be null. + /// set of deltas. The values for the attributes + /// in this set represent the values to add to attributes in the object. + /// merged. This set must not include . + /// Must not be null. + /// additional options that impact the way this operation is run. + /// May be null. + /// the of the updated object in case the update changes + /// the formation of the unique identifier. + /// iff the does not exist on the resource. Uid AddAttributeValues(ObjectClass objclass, Uid uid, ICollection valuesToAdd, OperationOptions options); - - /** - * Update the object specified by the {@link ObjectClass} and {@link Uid}, - * removing from the current values of each attribute the values provided. - *

- * For each attribute that the input set contains, - * remove from the current values of that attribute in the target object - * any value that matches one of the values of the attribute from the input set. - *

- * NOTE that this does not specify how to handle unmatched values. - * The general assumption for an attribute of a {@code ConnectorObject} - * is that the values for an attribute are merely representational state. - * Therefore, the implementer should simply ignore any provided value - * that does not match a current value of that attribute in the target - * object. Deleting an unmatched value should always succeed. - *

- * IMPLEMENTATION NOTE: for connectors that merely implement {@link UpdateOp} - * and not {@link UpdateAttributeValuesOp} this method will be simulated by - * fetching, merging, and calling - * {@link UpdateOp#update(ObjectClass, Uid, Set, OperationOptions)}. Therefore, - * connector implementations are encourage to implement {@link UpdateAttributeValuesOp} - * from a performance and atomicity standpoint. - * @param objclass - * the type of object to modify. Must not be null. - * @param uid - * the uid of the object to modify. Must not be null. - * @param valuesToRemove - * set of {@link Attribute} deltas. The values for the attributes - * in this set represent the values to remove from attributes in the object. - * merged. This set must not include {@link OperationalAttributes operational attributes}. - * Must not be null. - * @param options - * additional options that impact the way this operation is run. - * May be null. - * @return the {@link Uid} of the updated object in case the update changes - * the formation of the unique identifier. - * @throws UnknownUidException - * iff the {@link Uid} does not exist on the resource. - */ + + ///

+ /// Update the object specified by the and , + /// removing from the current values of each attribute the values provided. + /// + /// + /// + /// For each attribute that the input set contains, + /// remove from the current values of that attribute in the target object + /// any value that matches one of the values of the attribute from the input set. + /// + /// + /// NOTE that this does not specify how to handle unmatched values. + /// The general assumption for an attribute of a ConnectorObject + /// is that the values for an attribute are merely representational state. + /// Therefore, the implementer should simply ignore any provided value + /// that does not match a current value of that attribute in the target + /// object. Deleting an unmatched value should always succeed. + /// + /// + /// IMPLEMENTATION NOTE: for connectors that merely implement + /// and not this method will be simulated by + /// fetching, merging, and calling + /// . Therefore, + /// connector implementations are encourage to implement + /// from a performance and atomicity standpoint. + /// + /// + /// the type of object to modify. Must not be null. + /// the uid of the object to modify. Must not be null. + /// set of deltas. The values for the attributes + /// in this set represent the values to remove from attributes in the object. + /// merged. This set must not include . + /// Must not be null. + /// additional options that impact the way this operation is run. + /// May be null. + /// the of the updated object in case the update changes + /// the formation of the unique identifier. + /// iff the does not exist on the resource. Uid RemoveAttributeValues(ObjectClass objclass, Uid uid, ICollection valuesToRemove, OperationOptions options); - + } - - - public interface ValidateApiOp : APIOperation { - /** - * Tests connectivity and validity of the {@link Configuration}. - * - * @throws RuntimeException - * iff the {@link Configuration} is not valid or a - * {@link Connection} to the resource could not be established. - */ + + public interface ValidateApiOp : APIOperation + { + /// + /// Tests connectivity and validity of the . + /// + /// iff the is not valid or a + /// to the resource could not be established. void Validate(); } - public interface TestApiOp : APIOperation { - /** - * Tests connectivity and validity of the {@link Configuration}. - * - * @throws RuntimeException - * iff the {@link Configuration} is not valid or a - * {@link Connection} to the resource could not be established. - */ + public interface TestApiOp : APIOperation + { + /// + /// Tests connectivity and validity of the . + /// + /// iff the is not valid or a + /// to the resource could not be established. void Test(); } -} +} \ No newline at end of file diff --git a/Framework/Common.cs b/Framework/Common.cs index 166eb673..47d1b6cc 100644 --- a/Framework/Common.cs +++ b/Framework/Common.cs @@ -31,7 +31,8 @@ using Org.IdentityConnectors.Framework.Common.Objects; namespace Org.IdentityConnectors.Framework.Common { - internal static class FrameworkInternalBridge { + internal static class FrameworkInternalBridge + { private static readonly Object LOCK = new Object(); private static Assembly _assembly = null; /// @@ -39,59 +40,64 @@ internal static class FrameworkInternalBridge { /// /// /// - public static SafeType LoadType(String typeName) where T : class { - + public static SafeType LoadType(String typeName) where T : class + { + Assembly assembly; - lock(LOCK) { - if (_assembly == null) { + lock (LOCK) + { + if (_assembly == null) + { AssemblyName assemName = new AssemblyName(); assemName.Name = "FrameworkInternal"; _assembly = Assembly.Load(assemName); } assembly = _assembly; } - - return SafeType.ForRawType(assembly.GetType(typeName,true)); - + + return SafeType.ForRawType(assembly.GetType(typeName, true)); + } } - - public static class FrameworkUtil { - private static readonly IDictionary,SafeType> SPI_TO_API; + + public static class FrameworkUtil + { + private static readonly IDictionary, SafeType> SPI_TO_API; private static readonly ICollection CONFIG_SUPPORTED_TYPES; private static readonly ICollection ATTR_SUPPORTED_TYPES; - static FrameworkUtil() { - IDictionary,SafeType> temp = - new Dictionary,SafeType>(); - temp[SafeType.Get()]= + static FrameworkUtil() + { + IDictionary, SafeType> temp = + new Dictionary, SafeType>(); + temp[SafeType.Get()] = SafeType.Get(); temp[SafeType.Get()] = SafeType.Get(); temp[SafeType.Get()] = SafeType.Get(); - temp[SafeType.Get()]= + temp[SafeType.Get()] = SafeType.Get(); - temp[SafeType.ForRawType(typeof(SearchOp<>))]= + temp[SafeType.ForRawType(typeof(SearchOp<>))] = SafeType.Get(); - temp[SafeType.Get()]= + temp[SafeType.Get()] = SafeType.Get(); - temp[SafeType.Get()]= + temp[SafeType.Get()] = SafeType.Get(); - temp[SafeType.Get()]= + temp[SafeType.Get()] = SafeType.Get(); - temp[SafeType.Get()]= + temp[SafeType.Get()] = SafeType.Get(); - temp[SafeType.Get()]= + temp[SafeType.Get()] = SafeType.Get(); - temp[SafeType.Get()]= + temp[SafeType.Get()] = SafeType.Get(); - temp[SafeType.Get()]= + temp[SafeType.Get()] = SafeType.Get(); SPI_TO_API = CollectionUtil.NewReadOnlyDictionary(temp); - + CONFIG_SUPPORTED_TYPES = CollectionUtil.NewReadOnlySet - ( + ( typeof(string), typeof(long), typeof(long?), @@ -112,7 +118,7 @@ static FrameworkUtil() { typeof(Script) ); ATTR_SUPPORTED_TYPES = CollectionUtil.NewReadOnlySet - ( + ( typeof(string), typeof(long), typeof(long?), @@ -132,65 +138,120 @@ static FrameworkUtil() { typeof(GuardedByteArray), typeof(GuardedString) ); - + } - - - /** - * Determines if the class is a supported attribute type. If not it throws - * an {@link IllegalArgumentException}. - * - *
    - *
  • string
  • - *
  • long
  • - *
  • long?
  • - *
  • char
  • - *
  • char?
  • - *
  • double
  • - *
  • double?
  • - *
  • float
  • - *
  • float?
  • - *
  • int
  • - *
  • int?
  • - *
  • bool
  • - *
  • bool?
  • - *
  • byte[]
  • - *
  • BigDecimal
  • - *
  • BigInteger
  • - *
- * - * @param clazz - * type to check against the support list of types. - * @throws IllegalArgumentException - * iff the type is not on the supported list. - */ - public static void CheckAttributeType(Type type) { - if (!FrameworkUtil.IsSupportedAttributeType(type)) { - String MSG = "Attribute type ''"+type+"'' is not supported."; + + + /// + /// Determines if the class is a supported attribute type. + /// + /// + /// If not it throws + /// an . + /// + /// + /// string + /// + /// + /// + /// long + /// + /// + /// + /// long? + /// + /// + /// + /// char + /// + /// + /// + /// char? + /// + /// + /// + /// double + /// + /// + /// + /// double? + /// + /// + /// + /// float + /// + /// + /// + /// float? + /// + /// + /// + /// int + /// + /// + /// + /// int? + /// + /// + /// + /// bool + /// + /// + /// + /// bool? + /// + /// + /// + /// byte[] + /// + /// + /// + /// BigDecimal + /// + /// + /// + /// BigInteger + /// + /// + /// + /// + /// type to check against the support list of types. + /// iff the type is not on the supported list. + public static void CheckAttributeType(Type type) + { + if (!FrameworkUtil.IsSupportedAttributeType(type)) + { + String MSG = "Attribute type ''" + type + "'' is not supported."; throw new ArgumentException(MSG); } } - public static void CheckAttributeValue(Object value) { - if ( value != null ) { + public static void CheckAttributeValue(Object value) + { + if (value != null) + { CheckAttributeType(value.GetType()); } } - public static ICollection> Spi2Apis(SafeType type) { + public static ICollection> Spi2Apis(SafeType type) + { type = type.GetTypeErasure(); HashSet> set = new HashSet>(); set.Add(SPI_TO_API[type]); // add GetApiOp if search is available.. - - if (type.RawType.Equals(typeof(SearchOp<>))) { + + if (type.RawType.Equals(typeof(SearchOp<>))) + { set.Add(SafeType.Get()); } return set; } - public static ICollection> AllSPIOperations() { + public static ICollection> AllSPIOperations() + { return SPI_TO_API.Keys; } - public static ICollection> AllAPIOperations() { - ICollection> set = + public static ICollection> AllAPIOperations() + { + ICollection> set = new HashSet>(); CollectionUtil.AddAll(set, SPI_TO_API.Values); @@ -199,103 +260,123 @@ public static ICollection> AllAPIOperations() { set.Add(SafeType.Get()); return CollectionUtil.AsReadOnlySet(set); } - public static ICollection> GetDefaultSupportedOperations(SafeType connector) { - ICollection> rv = + public static ICollection> GetDefaultSupportedOperations(SafeType connector) + { + ICollection> rv = new HashSet>(); - ICollection interfaces = + ICollection interfaces = ReflectionUtil.GetTypeErasure(ReflectionUtil.GetAllInterfaces(connector.RawType)); - foreach (SafeType spi in AllSPIOperations()) { - if (interfaces.Contains(spi.RawType)) { - CollectionUtil.AddAll(rv,Spi2Apis(spi)); + foreach (SafeType spi in AllSPIOperations()) + { + if (interfaces.Contains(spi.RawType)) + { + CollectionUtil.AddAll(rv, Spi2Apis(spi)); } } //finally add unconditionally supported ops - CollectionUtil.AddAll(rv,GetUnconditionallySupportedOperations()); + CollectionUtil.AddAll(rv, GetUnconditionallySupportedOperations()); return CollectionUtil.AsReadOnlySet(rv); } - public static ICollection> GetUnconditionallySupportedOperations() { + public static ICollection> GetUnconditionallySupportedOperations() + { HashSet> ret; ret = new HashSet>(); //add validate api op always ret.Add(SafeType.Get()); //add ScriptOnConnectorApiOp always ret.Add(SafeType.Get()); - return ret; + return ret; } - public static ICollection GetAllSupportedConfigTypes() { + public static ICollection GetAllSupportedConfigTypes() + { return CONFIG_SUPPORTED_TYPES; } - public static bool IsSupportedConfigurationType (Type type) { - if ( type.IsArray) { + public static bool IsSupportedConfigurationType(Type type) + { + if (type.IsArray) + { return IsSupportedConfigurationType(type.GetElementType()); } - else { + else + { return CONFIG_SUPPORTED_TYPES.Contains(type); } } - public static ICollection GetAllSupportedAttributeTypes() { + public static ICollection GetAllSupportedAttributeTypes() + { return ATTR_SUPPORTED_TYPES; } - public static bool IsSupportedAttributeType(Type clazz) { + public static bool IsSupportedAttributeType(Type clazz) + { return ATTR_SUPPORTED_TYPES.Contains(clazz); } - - /** - * Determines if the class is a supported type for an OperationOption. If not it throws - * an {@link IllegalArgumentException}. - * - * @param clazz - * type to check against the support list of types. - * @throws IllegalArgumentException - * iff the type is not on the supported list. - */ - public static void CheckOperationOptionType(Type clazz) { + + /// + /// Determines if the class is a supported type for an OperationOption. + /// + /// + /// If not it throws + /// an . + /// + /// type to check against the support list of types. + /// iff the type is not on the supported list. + public static void CheckOperationOptionType(Type clazz) + { //the set of supported operation option types //is the same as that for configuration beans plus Name, //ObjectClass, Uid, and QualifiedUid - - if ( clazz.IsArray ) { + + if (clazz.IsArray) + { CheckOperationOptionType(clazz.GetElementType()); return; } - - if (FrameworkUtil.IsSupportedConfigurationType(clazz)) { + + if (FrameworkUtil.IsSupportedConfigurationType(clazz)) + { return; //ok } - - if (typeof(ObjectClass).IsAssignableFrom(clazz)) { + + if (typeof(ObjectClass).IsAssignableFrom(clazz)) + { return; //ok } - - if (typeof(Uid).IsAssignableFrom(clazz)) { + + if (typeof(Uid).IsAssignableFrom(clazz)) + { return; //ok } - - if (typeof(QualifiedUid).IsAssignableFrom(clazz)) { + + if (typeof(QualifiedUid).IsAssignableFrom(clazz)) + { return; //ok } - String MSG = "ConfigurationOption type '+"+clazz.Name+"+' is not supported."; + String MSG = "ConfigurationOption type '+" + clazz.Name + "+' is not supported."; throw new ArgumentException(MSG); } - /** - * Determines if the class of the object is a supported attribute type. - * If not it throws an {@link IllegalArgumentException}. - * @param value The value to check or null. - */ - public static void CheckOperationOptionValue(Object val) { - if ( val != null ) { + /// + /// Determines if the class of the object is a supported attribute type. + /// + /// + /// If not it throws an . + /// + /// The value to check or null. + public static void CheckOperationOptionValue(Object val) + { + if (val != null) + { CheckOperationOptionType(val.GetType()); } } - /** - * Returns the version of the framework. - * - * @return the framework version; never null. - */ - public static Version GetFrameworkVersion() { + /// + /// Returns the version of the framework. + /// + /// the framework version; never null. + public static Version GetFrameworkVersion() + { return Assembly.GetExecutingAssembly().GetName().Version; } } -} +} \ No newline at end of file diff --git a/Framework/CommonExceptions.cs b/Framework/CommonExceptions.cs index 4b5420d2..911bdf12 100644 --- a/Framework/CommonExceptions.cs +++ b/Framework/CommonExceptions.cs @@ -26,266 +26,367 @@ namespace Org.IdentityConnectors.Framework.Common.Exceptions { #region AlreadyExistsException - public class AlreadyExistsException : ConnectorException { - - public AlreadyExistsException() : base() { + public class AlreadyExistsException : ConnectorException + { + public AlreadyExistsException() + : base() + { } - - public AlreadyExistsException(String message) : base(message) { - + + public AlreadyExistsException(String message) + : base(message) + { + } - - public AlreadyExistsException(Exception ex) : base(ex) { + + public AlreadyExistsException(Exception ex) + : base(ex) + { } - - public AlreadyExistsException(String message, Exception ex) : base(message,ex) { + + public AlreadyExistsException(String message, Exception ex) + : base(message, ex) + { } } #endregion - + #region ConfigurationException - public class ConfigurationException : ConnectorException { - - public ConfigurationException() : base() { + public class ConfigurationException : ConnectorException + { + public ConfigurationException() + : base() + { } - - public ConfigurationException(String message) : base(message) { + + public ConfigurationException(String message) + : base(message) + { } - - public ConfigurationException(Exception ex) : base(ex) { + + public ConfigurationException(Exception ex) + : base(ex) + { } - - public ConfigurationException(String message, Exception ex) : base(message,ex) { + + public ConfigurationException(String message, Exception ex) + : base(message, ex) + { } } #endregion - + #region ConnectionBrokenException - public class ConnectionBrokenException : ConnectorIOException { - - - public ConnectionBrokenException() : base() { + public class ConnectionBrokenException : ConnectorIOException + { + public ConnectionBrokenException() + : base() + { } - - public ConnectionBrokenException(String msg) : base(msg) { + + public ConnectionBrokenException(String msg) + : base(msg) + { + } + + public ConnectionBrokenException(Exception ex) + : base(ex) + { } - - public ConnectionBrokenException(Exception ex) : base(ex) { + + public ConnectionBrokenException(String message, Exception ex) + : base(message, ex) + { } - - public ConnectionBrokenException(String message, Exception ex) : base(message,ex) { - } - } + } #endregion - + #region ConnectionFailedException - public class ConnectionFailedException : ConnectorIOException { - - - public ConnectionFailedException() : base() { + public class ConnectionFailedException : ConnectorIOException + { + public ConnectionFailedException() + : base() + { } - - public ConnectionFailedException(String msg) : base(msg) { + + public ConnectionFailedException(String msg) + : base(msg) + { } - - public ConnectionFailedException(Exception ex) : base(ex) { + + public ConnectionFailedException(Exception ex) + : base(ex) + { } - - public ConnectionFailedException(String message, Exception ex) : base(message,ex) { + + public ConnectionFailedException(String message, Exception ex) + : base(message, ex) + { } - + } #endregion - + #region ConnectorException - public class ConnectorException : ApplicationException { - - public ConnectorException() : base() { - } - - /** - * Sets a message for the {@link Exception}. - * - * @param message - * passed to the {@link RuntimeException} message. - */ - public ConnectorException(String message) :base(message) { - } - - /** - * Sets the stack trace to the original exception, so this exception can - * masquerade as the original only be a {@link RuntimeException}. - * - * @param originalException - * the original exception adapted to {@link RuntimeException}. - */ - public ConnectorException(Exception ex) : base(ex.Message,ex) { - } - - /** - * Sets the stack trace to the original exception, so this exception can - * masquerade as the original only be a {@link RuntimeException}. - * - * @param message - * @param originalException - * the original exception adapted to {@link RuntimeException}. - */ - public ConnectorException(String message, Exception originalException) : base(message,originalException) { - } - + public class ConnectorException : ApplicationException + { + public ConnectorException() + : base() + { + } + + /// + /// Sets a message for the . + /// + /// passed to the message. + public ConnectorException(String message) + : base(message) + { + } + + /// + /// Sets the stack trace to the original exception, so this exception can + /// masquerade as the original only be a . + /// + /// the original exception adapted to . + public ConnectorException(Exception ex) + : base(ex.Message, ex) + { + } + + /// + /// Sets the stack trace to the original exception, so this exception can + /// masquerade as the original only be a . + /// + /// + /// the original exception adapted to . + public ConnectorException(String message, Exception originalException) + : base(message, originalException) + { + } + } #endregion - + #region ConnectorIOException - public class ConnectorIOException : ConnectorException { - - public ConnectorIOException() : base() { + public class ConnectorIOException : ConnectorException + { + public ConnectorIOException() + : base() + { } - - public ConnectorIOException(String msg) : base(msg) { + + public ConnectorIOException(String msg) + : base(msg) + { } - - public ConnectorIOException(Exception ex) : base(ex) { + + public ConnectorIOException(Exception ex) + : base(ex) + { } - - public ConnectorIOException(String message, Exception ex) : base(message,ex) { + + public ConnectorIOException(String message, Exception ex) + : base(message, ex) + { } } #endregion - + #region ConnectorSecurityException - public class ConnectorSecurityException : ConnectorException { - - public ConnectorSecurityException() : base() { + public class ConnectorSecurityException : ConnectorException + { + public ConnectorSecurityException() + : base() + { } - - public ConnectorSecurityException(String message) : base(message) { + + public ConnectorSecurityException(String message) + : base(message) + { } - - public ConnectorSecurityException(Exception ex) : base(ex) { + + public ConnectorSecurityException(Exception ex) + : base(ex) + { } - - public ConnectorSecurityException(String message, Exception ex) : base(message,ex) { + + public ConnectorSecurityException(String message, Exception ex) + : base(message, ex) + { } } #endregion - + #region InvalidCredentialException - public class InvalidCredentialException : ConnectorSecurityException { - - public InvalidCredentialException() : base() { + public class InvalidCredentialException : ConnectorSecurityException + { + public InvalidCredentialException() + : base() + { } - - public InvalidCredentialException(String message) : base(message) { + + public InvalidCredentialException(String message) + : base(message) + { } - - public InvalidCredentialException(Exception ex) : base(ex) { + + public InvalidCredentialException(Exception ex) + : base(ex) + { } - - public InvalidCredentialException(String message, Exception ex) : base(message,ex) { + + public InvalidCredentialException(String message, Exception ex) + : base(message, ex) + { } } - #endregion - + #endregion + #region InvalidPasswordException - public class InvalidPasswordException : InvalidCredentialException { - - public InvalidPasswordException() : base() { + public class InvalidPasswordException : InvalidCredentialException + { + public InvalidPasswordException() + : base() + { } - - public InvalidPasswordException(String message) : base(message) { + + public InvalidPasswordException(String message) + : base(message) + { } - - public InvalidPasswordException(Exception ex) : base(ex) { + + public InvalidPasswordException(Exception ex) + : base(ex) + { } - - public InvalidPasswordException(String message, Exception ex) : base(message,ex) { + + public InvalidPasswordException(String message, Exception ex) + : base(message, ex) + { } } #endregion - + #region OperationTimeoutException - public class OperationTimeoutException : ConnectorException { - - public OperationTimeoutException() : base() { + public class OperationTimeoutException : ConnectorException + { + public OperationTimeoutException() + : base() + { } - - public OperationTimeoutException(String msg) : base(msg) { + + public OperationTimeoutException(String msg) + : base(msg) + { } - - public OperationTimeoutException(Exception e) : base(e) { + + public OperationTimeoutException(Exception e) + : base(e) + { } - - public OperationTimeoutException(String msg, Exception e) : base(msg,e) { + + public OperationTimeoutException(String msg, Exception e) + : base(msg, e) + { } - + } #endregion - + #region PasswordExpiredException - public class PasswordExpiredException : InvalidPasswordException { - + public class PasswordExpiredException : InvalidPasswordException + { private Uid _uid; - - public PasswordExpiredException() : base() { + + public PasswordExpiredException() + : base() + { } - - public PasswordExpiredException(String message) : base(message) { + + public PasswordExpiredException(String message) + : base(message) + { } - - public PasswordExpiredException(Exception ex) : base(ex) { + + public PasswordExpiredException(Exception ex) + : base(ex) + { } - - public PasswordExpiredException(String message, Exception ex) : base(message,ex) { + + public PasswordExpiredException(String message, Exception ex) + : base(message, ex) + { } - - public Uid Uid { - get { + + public Uid Uid + { + get + { return _uid; } - set { + set + { _uid = value; } } } #endregion - + #region PermissionDeniedException - public class PermissionDeniedException : ConnectorSecurityException { - - public PermissionDeniedException() : base() { + public class PermissionDeniedException : ConnectorSecurityException + { + public PermissionDeniedException() + : base() + { } - - public PermissionDeniedException(String message) : base(message) { + + public PermissionDeniedException(String message) + : base(message) + { } - - public PermissionDeniedException(Exception ex) : base(ex) { + + public PermissionDeniedException(Exception ex) + : base(ex) + { } - - public PermissionDeniedException(String message, Exception ex) : base(message,ex) { + + public PermissionDeniedException(String message, Exception ex) + : base(message, ex) + { } } #endregion - + #region UnknownUidException - public class UnknownUidException : InvalidCredentialException { + public class UnknownUidException : InvalidCredentialException + { const string MSG = "Object with Uid '{0}' and ObjectClass '{1}' does not exist!"; - - public UnknownUidException() : base() { + + public UnknownUidException() + : base() + { } - - public UnknownUidException(Uid uid, ObjectClass objclass) : - base(String.Format(MSG, uid, objclass)){ + + public UnknownUidException(Uid uid, ObjectClass objclass) : + base(String.Format(MSG, uid, objclass)) + { } - - public UnknownUidException(String message) : base(message) { + + public UnknownUidException(String message) + : base(message) + { } - - public UnknownUidException(Exception ex) : base(ex) { + + public UnknownUidException(Exception ex) + : base(ex) + { } - - public UnknownUidException(String message, Exception ex) : base(message,ex) { + + public UnknownUidException(String message, Exception ex) + : base(message, ex) + { } } - #endregion -} + #endregion +} \ No newline at end of file diff --git a/Framework/CommonObjects.cs b/Framework/CommonObjects.cs index 1c0c1c94..76075986 100644 --- a/Framework/CommonObjects.cs +++ b/Framework/CommonObjects.cs @@ -38,155 +38,152 @@ namespace Org.IdentityConnectors.Framework.Common.Objects { #region NameUtil - internal static class NameUtil { - - public static bool IsSpecialName(String name) { - return (name.StartsWith("__") && name.EndsWith("__")); + internal static class NameUtil + { + public static bool IsSpecialName(String name) + { + return (name.StartsWith("__") && name.EndsWith("__")); } - public static string CreateSpecialName(string name) { - if (StringUtil.IsBlank(name)) { + public static string CreateSpecialName(string name) + { + if (StringUtil.IsBlank(name)) + { const string ERR = "Name parameter must not be blank!"; throw new ArgumentException(ERR); } return "__" + name + "__"; } - public static bool NamesEqual(string name1, string name2) { + public static bool NamesEqual(string name1, string name2) + { return name1.ToUpper(CultureInfoCache.Instance).Equals( name2.ToUpper(CultureInfoCache.Instance)); } - public static int GetNameHashCode(string name) { + public static int GetNameHashCode(string name) + { return name.ToUpper(CultureInfoCache.Instance).GetHashCode(); } } #endregion #region ConnectorAttributeUtil - public static class ConnectorAttributeUtil { - - /** - * Gets the string value from the single value attribute. - * - * @param attr - * ConnectorAttribute to retrieve the string value from. - * @return null if the value is null otherwise the string value for the - * attribute. - * @throws ClassCastException - * iff the object in the attribute is not an string. - * @throws IllegalArgumentException - * iff the attribute is a multi valued instead of single valued. - */ - public static string GetStringValue(ConnectorAttribute attr) { + public static class ConnectorAttributeUtil + { + /// + /// Gets the string value from the single value attribute. + /// + /// ConnectorAttribute to retrieve the string value from. + /// null if the value is null otherwise the string value for the + /// attribute. + /// iff the object in the attribute is not an string. + /// iff the attribute is a multi valued instead of single valued. + public static string GetStringValue(ConnectorAttribute attr) + { return (string)GetSingleValue(attr); } - - /** - * Gets the string value from the single value attribute. - * - * @param attr - * ConnectorAttribute to retrieve the string value from. - * @return null if the value is null otherwise the string value for the - * attribute. - * @throws IllegalArgumentException - * iff the attribute is a multi valued instead of single valued. - */ - public static string GetAsStringValue(ConnectorAttribute attr) { + + /// + /// Gets the string value from the single value attribute. + /// + /// ConnectorAttribute to retrieve the string value from. + /// null if the value is null otherwise the string value for the + /// attribute. + /// iff the attribute is a multi valued instead of single valued. + public static string GetAsStringValue(ConnectorAttribute attr) + { object obj = GetSingleValue(attr); return obj != null ? obj.ToString() : null; } - public static GuardedString GetGuardedStringValue(ConnectorAttribute attr) { + public static GuardedString GetGuardedStringValue(ConnectorAttribute attr) + { object obj = GetSingleValue(attr); return obj != null ? (GuardedString)obj : null; } - /** - * Gets the integer value from the single value attribute. - * - * @param attr - * ConnectorAttribute to retrieve the integer value from. - * @return null if the value is null otherwise the integer value for the - * attribute. - * @throws ClassCastException - * iff the object in the attribute is not an integer. - * @throws IllegalArgumentException - * iff the attribute is a multi valued instead of single valued. - */ - public static int? GetIntegerValue(ConnectorAttribute attr) { + /// + /// Gets the integer value from the single value attribute. + /// + /// ConnectorAttribute to retrieve the integer value from. + /// null if the value is null otherwise the integer value for the + /// attribute. + /// iff the object in the attribute is not an integer. + /// iff the attribute is a multi valued instead of single valued. + public static int? GetIntegerValue(ConnectorAttribute attr) + { object obj = GetSingleValue(attr); - return obj != null ? (int?) obj : null; - } - - /** - * Gets the long value from the single value attribute. - * - * @param attr - * ConnectorAttribute to retrieve the long value from. - * @return null if the value is null otherwise the long value for the - * attribute. - * @throws ClassCastException - * iff the object in the attribute is not an long. - * @throws IllegalArgumentException - * iff the attribute is a multi valued instead of single valued. - */ - public static long? GetLongValue(ConnectorAttribute attr) { + return obj != null ? (int?)obj : null; + } + + /// + /// Gets the long value from the single value attribute. + /// + /// ConnectorAttribute to retrieve the long value from. + /// null if the value is null otherwise the long value for the + /// attribute. + /// iff the object in the attribute is not an long. + /// iff the attribute is a multi valued instead of single valued. + public static long? GetLongValue(ConnectorAttribute attr) + { Object obj = GetSingleValue(attr); - return obj != null ? (long?) obj : null; - } - - /** - * Gets the date value from the single value attribute. - * - * @param attr - * ConnectorAttribute to retrieve the date value from. - * @return null if the value is null otherwise the date value for the - * attribute. - * @throws ClassCastException - * iff the object in the attribute is not an long. - * @throws IllegalArgumentException - * iff the attribute is a multi valued instead of single valued. - */ - public static DateTime? GetDateTimeValue(ConnectorAttribute attr) { + return obj != null ? (long?)obj : null; + } + + /// + /// Gets the date value from the single value attribute. + /// + /// ConnectorAttribute to retrieve the date value from. + /// null if the value is null otherwise the date value for the + /// attribute. + /// iff the object in the attribute is not an long. + /// iff the attribute is a multi valued instead of single valued. + public static DateTime? GetDateTimeValue(ConnectorAttribute attr) + { long? val = GetLongValue(attr); - if (val != null) { + if (val != null) + { return DateTimeUtil.GetDateTimeFromUtcMillis(val.Value); } return null; } - /** - * Gets the integer value from the single value attribute. - * - * @param attr - * ConnectorAttribute to retrieve the integer value from. - * @return null if the value is null otherwise the integer value for the - * attribute. - * @throws ClassCastException - * iff the object in the attribute is not an integer. - * @throws IllegalArgumentException - * iff the attribute is a multi valued instead of single valued. - */ - public static double? GetDoubleValue(ConnectorAttribute attr) { + /// + /// Gets the integer value from the single value attribute. + /// + /// ConnectorAttribute to retrieve the integer value from. + /// null if the value is null otherwise the integer value for the + /// attribute. + /// iff the object in the attribute is not an integer. + /// iff the attribute is a multi valued instead of single valued. + public static double? GetDoubleValue(ConnectorAttribute attr) + { Object obj = GetSingleValue(attr); - return obj != null ? (double?) obj : null; + return obj != null ? (double?)obj : null; } - - public static bool? GetBooleanValue(ConnectorAttribute attr) { + + public static bool? GetBooleanValue(ConnectorAttribute attr) + { object obj = GetSingleValue(attr); - return obj != null ? (bool?) obj : null; + return obj != null ? (bool?)obj : null; } - /** - * Get the single value from the ConnectorAttribute. Return - * null if the attribute's list of values is null or empty. - */ - public static object GetSingleValue(ConnectorAttribute attr) { + /// + /// Get the single value from the ConnectorAttribute. + /// + /// + /// Return + /// null if the attribute's list of values is null or empty. + /// + public static object GetSingleValue(ConnectorAttribute attr) + { Object ret = null; IList val = attr.Value; - if (val != null && val.Count > 0) { + if (val != null && val.Count > 0) + { // make sure this only called for single value.. - if (val.Count > 1) { + if (val.Count > 1) + { const string MSG = "The method is only for single value attributes."; throw new ArgumentException(MSG); } @@ -194,7 +191,7 @@ public static object GetSingleValue(ConnectorAttribute attr) { } return ret; } - + /// /// Transform a Collection of ConnectorAttribute} instances into a {@link Map}. /// The key to each element in the map is the name of an ConnectorAttribute. @@ -203,150 +200,164 @@ public static object GetSingleValue(ConnectorAttribute attr) { /// /// public static IDictionary ToMap( - ICollection attributes) { - IDictionary ret = + ICollection attributes) + { + IDictionary ret = new Dictionary( StringComparer.OrdinalIgnoreCase); - foreach (ConnectorAttribute attr in attributes) { + foreach (ConnectorAttribute attr in attributes) + { ret[attr.Name] = attr; } return ret; } - - /** - * Get the {@link Uid} from the attribute set. - * - * @param attrs - * set of {@link ConnectorAttribute}s that may contain a {@link Uid}. - * @return null if the set does not contain a {@link Uid} object the first - * one found. - */ - public static Uid GetUidAttribute(ICollection attrs) { + + /// + /// Get the from the attribute set. + /// + /// set of s that may contain a . + /// null if the set does not contain a object the first + /// one found. + public static Uid GetUidAttribute(ICollection attrs) + { return (Uid)Find(Uid.NAME, attrs); } - - /** - * Filters out all special attributes from the set. These special attributes - * include {@link Password}, {@link Uid} etc.. - * - * @param attrs - * set of {@link ConnectorAttribute}s to filter out the operational and - * default attributes. - * @return a set that only contains plain attributes or empty. - */ - public static ICollection GetBasicAttributes(ICollection attrs) { + + /// + /// Filters out all special attributes from the set. + /// + /// + /// These special attributes + /// include , etc.. + /// + /// set of s to filter out the operational and + /// default attributes. + /// a set that only contains plain attributes or empty. + public static ICollection GetBasicAttributes(ICollection attrs) + { ICollection ret = new HashSet(); - foreach (ConnectorAttribute attr in attrs) { + foreach (ConnectorAttribute attr in attrs) + { // note this is dangerous because we need to be consistent // in the naming of special attributes. - if (!IsSpecial(attr)) { + if (!IsSpecial(attr)) + { ret.Add(attr); } } return ret; } - /** - * Filter out any basic attributes from the specified set, leaving only - * special attributes. Special attributes include {@link Name}, {@link Uid}, - * and {@link OperationalAttributes}. - * - * @param attrs - * set of {@link Attribute}s to filter out the basic attributes - * @return a set that only contains special attributes or an empty set if - * there are none. - */ - public static ICollection GetSpecialAttributes(ICollection attrs) { + + /// + /// Filter out any basic attributes from the specified set, leaving only + /// special attributes. + /// + /// + /// Special attributes include , , + /// and . + /// + /// set of s to filter out the basic attributes + /// a set that only contains special attributes or an empty set if + /// there are none. + public static ICollection GetSpecialAttributes(ICollection attrs) + { ICollection ret = new HashSet(); - foreach (ConnectorAttribute attr in attrs) { - if (IsSpecial(attr)) { + foreach (ConnectorAttribute attr in attrs) + { + if (IsSpecial(attr)) + { ret.Add(attr); } } return ret; } - - /** - * Returns a mutable copy of the original set with the uid attribute removed. - * @param attrs The original set. Must not be null. - * @return A mutable copy of the original set with the uid attribute removed. - */ - public static ICollection FilterUid(ICollection attrs) { + + /// + /// Returns a mutable copy of the original set with the uid attribute removed. + /// + /// The original set. Must not be null. + /// A mutable copy of the original set with the uid attribute removed. + public static ICollection FilterUid(ICollection attrs) + { Assertions.NullCheck(attrs, "attrs"); HashSet ret = new HashSet(); - foreach (ConnectorAttribute attr in attrs) { - if (!(attr is Uid)) { + foreach (ConnectorAttribute attr in attrs) + { + if (!(attr is Uid)) + { ret.Add(attr); } } - return ret; + return ret; } - - /** - * Returns a mutable copy of the original set with the uid attribute added. - * @param attrs The original set. Must not be null. - * @param uid The uid. Must not be null. - * @return A mutable copy of the original set with the uid attribute added. - */ - public static ICollection AddUid(ICollection attrs, Uid uid) { + + /// + /// Returns a mutable copy of the original set with the uid attribute added. + /// + /// The original set. Must not be null. + /// The uid. Must not be null. + /// A mutable copy of the original set with the uid attribute added. + public static ICollection AddUid(ICollection attrs, Uid uid) + { Assertions.NullCheck(attrs, "attrs"); Assertions.NullCheck(uid, "uid"); HashSet ret = new HashSet(attrs); ret.Add(uid); return ret; } - - /** - * Determines if this attribute is a special attribute. - * - * @param attr - * {@link ConnectorAttribute} to test for against. - * @return true iff the attribute value is a {@link Uid}, - * {@link ObjectClass}, {@link Password}, or - * {@link OperationalAttributes}. - * @throws NullPointerException - * iff the attribute parameter is null. - */ - public static bool IsSpecial(ConnectorAttribute attr) { + + /// + /// Determines if this attribute is a special attribute. + /// + /// + /// to test for against. + /// true iff the attribute value is a , + /// , , or + /// . + /// iff the attribute parameter is null. + public static bool IsSpecial(ConnectorAttribute attr) + { // note this is dangerous because we need to be consistent // in the naming of special attributes. String name = attr.Name; return IsSpecialName(name); } - - /** - * Determines if this attribute is a special attribute. - * - * @param attr - * {@link ConnectorAttribute} to test for against. - * @return true iff the attribute value is a {@link Uid}, - * {@link ObjectClass}, {@link Password}, or - * {@link OperationalAttributes}. - * @throws NullPointerException - * iff the attribute parameter is null. - */ - public static bool IsSpecial(ConnectorAttributeInfo attr) { + + /// + /// Determines if this attribute is a special attribute. + /// + /// + /// to test for against. + /// true iff the attribute value is a , + /// , , or + /// . + /// iff the attribute parameter is null. + public static bool IsSpecial(ConnectorAttributeInfo attr) + { String name = attr.Name; return IsSpecialName(name); } - + /// /// Determines whether the specified attribute name is special in the /// sense of . /// /// the name of the attribute to test /// true iff the attribute name is special - public static bool IsSpecialName(String name) { + public static bool IsSpecialName(String name) + { return NameUtil.IsSpecialName(name); } - + /// /// Creates the special naming for operational type attributes. /// /// string to make special /// name constructed for use as an operational attribute. - public static string CreateSpecialName(string name) { + public static string CreateSpecialName(string name) + { return NameUtil.CreateSpecialName(name); - } + } /// /// Compares two attribute names for equality. @@ -354,203 +365,222 @@ public static string CreateSpecialName(string name) { /// the first attribute name /// the second attribute name /// true iff the two attribute names are equal - public static bool NamesEqual(string name1, string name2) { + public static bool NamesEqual(string name1, string name2) + { return NameUtil.NamesEqual(name2, name2); } - /// - /// Gets the 'Name' attribute from a set of ConnectorAttributes. - /// - /// set of attributes to search against. - /// the 'Name' attribute it if exsist otherwisenull - public static Name GetNameFromAttributes(ICollection attrs) { - return (Name)Find(Name.NAME, attrs); - } - - - /** - * Find the {@link ConnectorAttribute} of the given name in the {@link Set}. - * - * @param name - * {@link ConnectorAttribute}'s name to search for. - * @param attrs - * {@link Set} of attribute to search. - * @return {@link ConnectorAttribute} with the specified otherwise null. - */ - public static ConnectorAttribute Find(string name, ICollection attrs) { + /// + /// Gets the 'Name' attribute from a set of ConnectorAttributes. + /// + /// set of attributes to search against. + /// the 'Name' attribute it if exsist otherwisenull + public static Name GetNameFromAttributes(ICollection attrs) + { + return (Name)Find(Name.NAME, attrs); + } + + + /// + /// Find the of the given name in the . + /// + /// + /// 's name to search for. + /// + /// of attribute to search. + /// + /// with the specified otherwise null. + public static ConnectorAttribute Find(string name, ICollection attrs) + { Assertions.NullCheck(name, "name"); ICollection attributes = CollectionUtil.NullAsEmpty(attrs); - foreach (ConnectorAttribute attr in attributes) { - if (attr.Is(name)) { + foreach (ConnectorAttribute attr in attributes) + { + if (attr.Is(name)) + { return attr; } } return null; } - /** - * Get the password value from the provided set of {@link ConnectorAttribute}s. - */ - public static GuardedString GetPasswordValue(ICollection attrs) { + /// + /// Get the password value from the provided set of s. + /// + public static GuardedString GetPasswordValue(ICollection attrs) + { ConnectorAttribute pwd = Find(OperationalAttributes.PASSWORD_NAME, attrs); return (pwd == null) ? null : GetGuardedStringValue(pwd); } - - /** - * Get the current password value from the provided set of {@link Attribute}s. - * - * @param attrs - * Set of {@link Attribute}s that may contain the current password - * {@link OperationalAttributes#CURRENT_PASSWORD_NAME} - * {@link Attribute}. - * @return null if it does not exist in the {@link Set} else - * the value. - */ - public static GuardedString GetCurrentPasswordValue(ICollection attrs) { + + /// + /// Get the current password value from the provided set of s. + /// + /// Set of s that may contain the current password + /// + /// . + /// + /// null if it does not exist in the else + /// the value. + public static GuardedString GetCurrentPasswordValue(ICollection attrs) + { ConnectorAttribute pwd = Find(OperationalAttributes.CURRENT_PASSWORD_NAME, attrs); return (pwd == null) ? null : GetGuardedStringValue(pwd); - } - /** - * Determine if the {@link ConnectorObject} is locked out. By getting the - * value of the {@link OperationalAttributes#LOCK_OUT_NAME}. - * - * @param obj - * {@link ConnectorObject} object to inspect. - * @throws NullPointerException - * iff the parameter 'obj' is null. - * @return null if the attribute does not exist otherwise to - * value of the {@link ConnectorAttribute}. - */ - public static bool? IsLockedOut(ConnectorObject obj) { + } + /// + /// Determine if the is locked out. + /// + /// + /// By getting the + /// value of the . + /// + /// + /// object to inspect. + /// iff the parameter 'obj' is null. + /// + /// null if the attribute does not exist otherwise to + /// value of the . + public static bool? IsLockedOut(ConnectorObject obj) + { ConnectorAttribute attr = obj.GetAttributeByName(OperationalAttributes.LOCK_OUT_NAME); - return (attr == null) ? null : GetBooleanValue(attr); - } - - /** - * Determine if the {@link ConnectorObject} is enable. By getting the value - * of the {@link OperationalAttributes#ENABLE_NAME}. - * - * @param obj - * {@link ConnectorObject} object to inspect. - * @throws IllegalStateException - * if the object does not contain attribute in question. - * @throws NullPointerException - * iff the parameter 'obj' is null. - * @return null if the attribute does not exist otherwise to - * value of the {@link ConnectorAttribute}. - */ - public static bool? IsEnabled(ConnectorObject obj) { + return (attr == null) ? null : GetBooleanValue(attr); + } + + /// + /// Determine if the is enable. + /// + /// + /// By getting the value + /// of the . + /// + /// + /// object to inspect. + /// if the object does not contain attribute in question. + /// iff the parameter 'obj' is null. + /// + /// null if the attribute does not exist otherwise to + /// value of the . + public static bool? IsEnabled(ConnectorObject obj) + { ConnectorAttribute attr = obj.GetAttributeByName(OperationalAttributes.ENABLE_NAME); - return (attr == null) ? null : GetBooleanValue(attr); - } - - /** - * Retrieve the password expiration date from the {@link ConnectorObject}. - * - * @param obj - * {@link ConnectorObject} object to inspect. - * @throws IllegalStateException - * if the object does not contain attribute in question. - * @throws NullPointerException - * iff the parameter 'obj' is null. - * @return null if the {@link ConnectorAttribute} does not exist - * otherwise the value of the {@link ConnectorAttribute}. - */ - public static DateTime? GetPasswordExpirationDate(ConnectorObject obj) { + return (attr == null) ? null : GetBooleanValue(attr); + } + + /// + /// Retrieve the password expiration date from the . + /// + /// + /// object to inspect. + /// if the object does not contain attribute in question. + /// iff the parameter 'obj' is null. + /// + /// null if the does not exist + /// otherwise the value of the . + public static DateTime? GetPasswordExpirationDate(ConnectorObject obj) + { DateTime? ret = null; ConnectorAttribute attr = obj.GetAttributeByName(OperationalAttributes.PASSWORD_EXPIRATION_DATE_NAME); - if (attr != null) { + if (attr != null) + { long? date = GetLongValue(attr); - if (date != null) { + if (date != null) + { ret = DateTime.FromFileTimeUtc(date.Value); } } return ret; } - /** - * Get the password expired attribute from a {@link Collection} of - * {@link Attribute}s. - * - * @param attrs - * set of attribute to find the expired password - * {@link Attribute}. - * @return null if the attribute does not exist and the value - * of the {@link Attribute} if it does. - */ - public static bool? GetPasswordExpired(ICollection attrs) { + /// + /// Get the password expired attribute from a of + /// s. + /// + /// set of attribute to find the expired password + /// . + /// + /// null if the attribute does not exist and the value + /// of the if it does. + public static bool? GetPasswordExpired(ICollection attrs) + { ConnectorAttribute pwd = Find(OperationalAttributes.PASSWORD_EXPIRED_NAME, attrs); return (pwd == null) ? null : GetBooleanValue(pwd); } - - /** - * Determine if the password is expired for this object. - * - * @param obj - * {@link ConnectorObject} that should contain a password expired - * attribute. - * @return null if the attribute does not exist and the value - * of the {@link Attribute} if it does. - */ - public static bool? IsPasswordExpired(ConnectorObject obj) { + + /// + /// Determine if the password is expired for this object. + /// + /// + /// that should contain a password expired + /// attribute. + /// + /// null if the attribute does not exist and the value + /// of the if it does. + public static bool? IsPasswordExpired(ConnectorObject obj) + { ConnectorAttribute pwd = obj.GetAttributeByName(OperationalAttributes.PASSWORD_EXPIRED_NAME); return (pwd == null) ? null : GetBooleanValue(pwd); } - /** - * Get the enable date from the set of attributes. - * - * @param attrs - * set of attribute to find the enable date - * {@link Attribute}. - * @return null if the attribute does not exist and the value - * of the {@link Attribute} if it does. - */ - public static DateTime? GetEnableDate(ICollection attrs) { + /// + /// Get the enable date from the set of attributes. + /// + /// set of attribute to find the enable date + /// . + /// + /// null if the attribute does not exist and the value + /// of the if it does. + public static DateTime? GetEnableDate(ICollection attrs) + { ConnectorAttribute attr = Find(OperationalAttributes.ENABLE_DATE_NAME, attrs); return (attr == null) ? null : GetDateTimeValue(attr); } } - #endregion - + #endregion + #region ConnectorAttributeInfoUtil - public static class ConnectorAttributeInfoUtil { - /** - * Transform a Collection of {@link AttributeInfo} instances into - * a {@link Map}. The key to each element in the map is the name of - * an AttributeInfo. The value of each element in the map is the - * AttributeInfo instance with that name. - * - * @param attributes - * set of AttributeInfo to transform to a map. - * @return a map of string and AttributeInfo. - * @throws NullPointerException - * iff the parameter attributes is - * null. - */ + public static class ConnectorAttributeInfoUtil + { + /// + /// Transform a Collection of instances into + /// a . + /// + /// + /// The key to each element in the map is the name of + /// an AttributeInfo. The value of each element in the map is the + /// AttributeInfo instance with that name. + /// + /// set of AttributeInfo to transform to a map. + /// a map of string and AttributeInfo. + /// iff the parameter attributes is + /// null. public static IDictionary ToMap( - ICollection attributes) { - IDictionary + ICollection attributes) + { + IDictionary ret = new Dictionary( StringComparer.OrdinalIgnoreCase); - foreach (ConnectorAttributeInfo attr in attributes) { + foreach (ConnectorAttributeInfo attr in attributes) + { ret[attr.Name] = attr; } return ret; } - - /** - * Find the {@link AttributeInfo} of the given name in the {@link Set}. - * - * @param name - * {@link AttributeInfo}'s name to search for. - * @param attrs - * {@link Set} of AttributeInfo to search. - * @return {@link AttributeInfo} with the specified otherwise null. - */ - public static ConnectorAttributeInfo Find(string name, ICollection attrs) { + + /// + /// Find the of the given name in the . + /// + /// + /// 's name to search for. + /// + /// of AttributeInfo to search. + /// + /// with the specified otherwise null. + public static ConnectorAttributeInfo Find(string name, ICollection attrs) + { Assertions.NullCheck(name, "name"); ICollection attributes = CollectionUtil.NullAsEmpty(attrs); - foreach (ConnectorAttributeInfo attr in attributes) { - if (attr.Is(name)) { + foreach (ConnectorAttributeInfo attr in attributes) + { + if (attr.Is(name)) + { return attr; } } @@ -563,79 +593,98 @@ public static ConnectorAttributeInfo Find(string name, ICollection /// Placeholder since C# doesn't have a BigInteger /// - public sealed class BigDecimal { + public sealed class BigDecimal + { private BigInteger _unscaledVal; private int _scale; public BigDecimal(BigInteger unscaledVal, - int scale) { - if ( unscaledVal == null ) { + int scale) + { + if (unscaledVal == null) + { throw new ArgumentNullException(); } _unscaledVal = unscaledVal; _scale = scale; } - public BigInteger UnscaledValue { - get { + public BigInteger UnscaledValue + { + get + { return _unscaledVal; } } - public int Scale { - get { + public int Scale + { + get + { return _scale; } } - public override bool Equals(object o) { + public override bool Equals(object o) + { BigDecimal other = o as BigDecimal; - if ( other != null ) { + if (other != null) + { return UnscaledValue.Equals(other.UnscaledValue) && Scale == other.Scale; } return false; } - public override int GetHashCode() { + public override int GetHashCode() + { return _unscaledVal.GetHashCode(); } - + public override string ToString() { return UnscaledValue.ToString(); } } #endregion - + #region BigInteger /// /// Placeholder since C# doesn't have a BigInteger /// - public sealed class BigInteger { + public sealed class BigInteger + { private string _value; - public BigInteger(string val) { - if ( val == null ) { + public BigInteger(string val) + { + if (val == null) + { throw new ArgumentNullException(); } _value = val; } - public string Value { - get { + public string Value + { + get + { return _value; } } - public override bool Equals(object o) { + public override bool Equals(object o) + { BigInteger other = o as BigInteger; - if ( other != null ) { + if (other != null) + { return Value.Equals(other.Value); } return false; } - public override int GetHashCode() { + public override int GetHashCode() + { return _value.GetHashCode(); } - public override string ToString() { + public override string ToString() + { return _value; } } - #endregion - + #endregion + #region ConnectorAttribute /// /// Represents a named collection of values within a resource object, @@ -646,78 +695,97 @@ public override string ToString() { /// Connector will use an builder to construct an instance of /// ConnectorAttribute. /// - public class ConnectorAttribute { + public class ConnectorAttribute + { private readonly string _name; private readonly IList _value; - internal ConnectorAttribute(string name, IList val) { - if (StringUtil.IsBlank(name)) { + internal ConnectorAttribute(string name, IList val) + { + if (StringUtil.IsBlank(name)) + { throw new ArgumentException("Name must not be blank!"); } if (OperationalAttributes.PASSWORD_NAME.Equals(name) || - OperationalAttributes.CURRENT_PASSWORD_NAME.Equals(name)) { + OperationalAttributes.CURRENT_PASSWORD_NAME.Equals(name)) + { // check the value.. - if (val == null || val.Count != 1) { + if (val == null || val.Count != 1) + { String MSG = "Must be a single value."; throw new ArgumentException(MSG); } - if (!(val[0] is GuardedString)) { + if (!(val[0] is GuardedString)) + { const string MSG = "Password value must be an instance of GuardedString."; throw new ArgumentException(MSG); } - } + } _name = name; // copy to prevent corruption preserve null _value = (val == null) ? null : CollectionUtil.NewReadOnlyList(val); } - public string Name { - get { + public string Name + { + get + { return _name; } } - public IList Value { - get { + public IList Value + { + get + { return _value; } } - - public bool Is(string name) { + + public bool Is(string name) + { return NameUtil.NamesEqual(_name, name); } - - public sealed override bool Equals(Object obj) { + + public sealed override bool Equals(Object obj) + { // test identity - if (this == obj) { + if (this == obj) + { return true; } // test for null.. - if (obj == null) { + if (obj == null) + { return false; } // test that the exact class matches - if (!(GetType().Equals(obj.GetType()))) { + if (!(GetType().Equals(obj.GetType()))) + { return false; } // test name field.. - ConnectorAttribute other = (ConnectorAttribute) obj; - if (!Is(other._name)) { + ConnectorAttribute other = (ConnectorAttribute)obj; + if (!Is(other._name)) + { return false; } - - if (!CollectionUtil.Equals(_value,other._value)) { + + if (!CollectionUtil.Equals(_value, other._value)) + { return false; } return true; } - - public sealed override int GetHashCode() { + + public sealed override int GetHashCode() + { return NameUtil.GetNameHashCode(_name); } - - - public override string ToString() { + + + public override string ToString() + { // poor man's consistent toString impl.. StringBuilder bld = new StringBuilder(); bld.Append("ConnectorAttribute: "); @@ -726,481 +794,576 @@ public override string ToString() { map["Value"] = Value; bld.Append(map.ToString()); return bld.ToString(); - } + } } #endregion #region ConnectorAttributeBuilder - public sealed class ConnectorAttributeBuilder { + public sealed class ConnectorAttributeBuilder + { private const String NAME_ERROR = "Name must not be blank!"; private string _name; private IList _value; - - public ConnectorAttributeBuilder() { + + public ConnectorAttributeBuilder() + { } - public static ConnectorAttribute Build(String name) { - return new ConnectorAttributeBuilder(){ Name=name }.Build(); + public static ConnectorAttribute Build(String name) + { + return new ConnectorAttributeBuilder() { Name = name }.Build(); } public static ConnectorAttribute Build(String name, - params Object [] args) { + params Object[] args) + { ConnectorAttributeBuilder bld = new ConnectorAttributeBuilder(); bld.Name = name; bld.AddValue(args); return bld.Build(); } public static ConnectorAttribute Build(String name, - ICollection val) { + ICollection val) + { ConnectorAttributeBuilder bld = new ConnectorAttributeBuilder(); bld.Name = name; bld.AddValue(val); return bld.Build(); } - - public string Name { - get { + + public string Name + { + get + { return _name; } - set { - if (StringUtil.IsBlank(value)) { + set + { + if (StringUtil.IsBlank(value)) + { throw new ArgumentException(NAME_ERROR); } _name = value; } } - - public IList Value { - get { + + public IList Value + { + get + { return _value == null ? null : CollectionUtil.AsReadOnlyList(_value); } } - - public ConnectorAttributeBuilder AddValue(params Object [] args) { + + public ConnectorAttributeBuilder AddValue(params Object[] args) + { AddValuesInternal(args); return this; } - public ConnectorAttributeBuilder AddValue(ICollection values) { + public ConnectorAttributeBuilder AddValue(ICollection values) + { AddValuesInternal(values); return this; } - - public ConnectorAttribute Build() { - if (StringUtil.IsBlank(Name)) { + + public ConnectorAttribute Build() + { + if (StringUtil.IsBlank(Name)) + { throw new ArgumentException(NAME_ERROR); } - if (Uid.NAME.Equals(_name)) { + if (Uid.NAME.Equals(_name)) + { return new Uid(GetSingleStringValue()); - } else if (Org.IdentityConnectors.Framework.Common.Objects.Name.NAME.Equals(_name)) { + } + else if (Org.IdentityConnectors.Framework.Common.Objects.Name.NAME.Equals(_name)) + { return new Name(GetSingleStringValue()); - } + } return new ConnectorAttribute(Name, _value); } - private void CheckSingleValue() { - if (_value == null || _value.Count != 1) { + private void CheckSingleValue() + { + if (_value == null || _value.Count != 1) + { const String MSG = "Must be a single value."; throw new ArgumentException(MSG); } } - private String GetSingleStringValue() { + private String GetSingleStringValue() + { CheckSingleValue(); - if (!(_value[0] is String)) { + if (!(_value[0] is String)) + { const String MSG = "Must be single string value."; throw new ArgumentException(MSG); } - return (String) _value[0]; + return (String)_value[0]; } - private void AddValuesInternal(IEnumerable values) { - if (values != null) { + private void AddValuesInternal(IEnumerable values) + { + if (values != null) + { // make sure the list is ready to receive values. - if (_value == null) { + if (_value == null) + { _value = new List(); } // add each value checking to make sure its correct - foreach (Object v in values) { + foreach (Object v in values) + { FrameworkUtil.CheckAttributeValue(v); _value.Add(v); } } } - + // ======================================================================= // Operational Attributes // ======================================================================= - /** - * Builds an password expiration date {@link ConnectorAttribute}. This - * {@link ConnectorAttribute} represents the date/time a password will expire on a - * resource. - * - * @param dateTime - * UTC time in milliseconds. - * @return an {@link ConnectorAttribute} built with the pre-defined name for password - * expiration date. - */ - public static ConnectorAttribute BuildPasswordExpirationDate(DateTime dateTime) { + /// + /// Builds an password expiration date . + /// + /// + /// This + /// represents the date/time a password will expire on a + /// resource. + /// + /// UTC time in milliseconds. + /// an built with the pre-defined name for password + /// expiration date. + public static ConnectorAttribute BuildPasswordExpirationDate(DateTime dateTime) + { return BuildPasswordExpirationDate(DateTimeUtil.GetUtcTimeMillis(dateTime)); } - - /** - * Builds an password expiration date {@link ConnectorAttribute}. This - * {@link ConnectorAttribute} represents the date/time a password will expire on a - * resource. - * - * @param dateTime - * UTC time in milliseconds. - * @return an {@link ConnectorAttribute} built with the pre-defined name for password - * expiration date. - */ - public static ConnectorAttribute BuildPasswordExpirationDate(long dateTime) { + + /// + /// Builds an password expiration date . + /// + /// + /// This + /// represents the date/time a password will expire on a + /// resource. + /// + /// UTC time in milliseconds. + /// an built with the pre-defined name for password + /// expiration date. + public static ConnectorAttribute BuildPasswordExpirationDate(long dateTime) + { return Build(OperationalAttributes.PASSWORD_EXPIRATION_DATE_NAME, dateTime); } - - /** - * Builds the operational attribute password. - * - * @param password - * the string that represents a password. - * @return an attribute that represents a password. - */ - public static ConnectorAttribute BuildPassword(GuardedString password) { + + /// + /// Builds the operational attribute password. + /// + /// the string that represents a password. + /// an attribute that represents a password. + public static ConnectorAttribute BuildPassword(GuardedString password) + { return Build(OperationalAttributes.PASSWORD_NAME, password); } - - /** - * Builds the operational attribute current password. The current password - * indicates this a password change by the account owner and not an - * administrator. The use case is that an administrator password change may - * not keep history or validate against policy. - * - * @param password - * the string that represents a password. - * @return an attribute that represents a password. - */ - public static ConnectorAttribute BuildCurrentPassword(GuardedString password) { + + /// + /// Builds the operational attribute current password. + /// + /// + /// The current password + /// indicates this a password change by the account owner and not an + /// administrator. The use case is that an administrator password change may + /// not keep history or validate against policy. + /// + /// the string that represents a password. + /// an attribute that represents a password. + public static ConnectorAttribute BuildCurrentPassword(GuardedString password) + { return Build(OperationalAttributes.CURRENT_PASSWORD_NAME, password); } - - public static ConnectorAttribute BuildPassword(SecureString password) { + + public static ConnectorAttribute BuildPassword(SecureString password) + { return Build(OperationalAttributes.PASSWORD_NAME, new GuardedString(password)); } - public static ConnectorAttribute BuildCurrentPassword(SecureString password) { + public static ConnectorAttribute BuildCurrentPassword(SecureString password) + { return Build(OperationalAttributes.CURRENT_PASSWORD_NAME, new GuardedString(password)); } - /** - * Builds ant operational attribute that either represents the object is - * enabled or sets in disabled depending on where its used for instance on - * {@link CreateApiOp} it could be used to create a disabled account. In - * {@link SearchApiOp} it would show the object is enabled or disabled. - * - * @param value - * true indicates the object is enabled otherwise false. - * @return {@link ConnectorAttribute} that determines the enable/disable state of an - * object. - */ - public static ConnectorAttribute BuildEnabled(bool val) { + /// + /// Builds ant operational attribute that either represents the object is + /// enabled or sets in disabled depending on where its used for instance on + /// it could be used to create a disabled account. + /// + /// + /// In + /// it would show the object is enabled or disabled. + /// + /// true indicates the object is enabled otherwise false. + /// + /// that determines the enable/disable state of an + /// object. + public static ConnectorAttribute BuildEnabled(bool val) + { return Build(OperationalAttributes.ENABLE_NAME, val); } - - /** - * Builds out an operational {@link ConnectorAttribute} that determines the enable - * date for an object. - * - * @param date - * The date and time to enable a particular object, or the date - * time an object will be enabled. - * @return {@link ConnectorAttribute} - */ - public static ConnectorAttribute BuildEnableDate(DateTime date) { + + /// + /// Builds out an operational that determines the enable + /// date for an object. + /// + /// The date and time to enable a particular object, or the date + /// time an object will be enabled. + /// + /// + /// + public static ConnectorAttribute BuildEnableDate(DateTime date) + { return BuildEnableDate(DateTimeUtil.GetUtcTimeMillis(date)); } - - /** - * Builds out an operational {@link ConnectorAttribute} that determines the enable - * date for an object. The time parameter is UTC in milliseconds. - * - * @param date - * The date and time to enable a particular object, or the date - * time an object will be enabled. - * @return {@link ConnectorAttribute} - */ - public static ConnectorAttribute BuildEnableDate(long date) { + + /// + /// Builds out an operational that determines the enable + /// date for an object. + /// + /// + /// The time parameter is UTC in milliseconds. + /// + /// The date and time to enable a particular object, or the date + /// time an object will be enabled. + /// + /// + /// + public static ConnectorAttribute BuildEnableDate(long date) + { return Build(OperationalAttributes.ENABLE_DATE_NAME, date); } - - /** - * Builds out an operational {@link ConnectorAttribute} that determines the disable - * date for an object. - * - * @param date - * The date and time to enable a particular object, or the date - * time an object will be enabled. - * @return {@link ConnectorAttribute} - */ - public static ConnectorAttribute BuildDisableDate(DateTime date) { + + /// + /// Builds out an operational that determines the disable + /// date for an object. + /// + /// The date and time to enable a particular object, or the date + /// time an object will be enabled. + /// + /// + /// + public static ConnectorAttribute BuildDisableDate(DateTime date) + { return BuildDisableDate(DateTimeUtil.GetUtcTimeMillis(date)); } - - /** - * Builds out an operational {@link ConnectorAttribute} that determines the disable - * date for an object. The time parameter is UTC in milliseconds. - * - * @param date - * The date and time to enable a particular object, or the date - * time an object will be enabled. - * @return {@link ConnectorAttribute} - */ - public static ConnectorAttribute BuildDisableDate(long date) { + + /// + /// Builds out an operational that determines the disable + /// date for an object. + /// + /// + /// The time parameter is UTC in milliseconds. + /// + /// The date and time to enable a particular object, or the date + /// time an object will be enabled. + /// + /// + /// + public static ConnectorAttribute BuildDisableDate(long date) + { return Build(OperationalAttributes.DISABLE_DATE_NAME, date); } - - /** - * Builds the lock attribute that determines if an object is locked out. - * - * @param lock - * true if the object is locked otherwise false. - * @return {@link ConnectorAttribute} that represents the lock state of an object. - */ - public static ConnectorAttribute BuildLockOut(bool lck) { + + /// + /// Builds the lock attribute that determines if an object is locked out. + /// + /// true if the object is locked otherwise false. + /// + /// that represents the lock state of an object. + public static ConnectorAttribute BuildLockOut(bool lck) + { return Build(OperationalAttributes.LOCK_OUT_NAME, lck); } - - /** - * Builds out an operational {@link Attribute} that determines if a password - * is expired or expires a password. - * - * @param value - * from the API true expires and from the SPI its shows its - * either expired or not. - * @return {@link Attribute} - */ - public static ConnectorAttribute BuildPasswordExpired(bool expired) { + + /// + /// Builds out an operational that determines if a password + /// is expired or expires a password. + /// + /// from the API true expires and from the SPI its shows its + /// either expired or not. + /// + /// + /// + public static ConnectorAttribute BuildPasswordExpired(bool expired) + { return Build(OperationalAttributes.PASSWORD_EXPIRED_NAME, expired); } - + // ======================================================================= // Pre-defined Attributes // ======================================================================= - - /** - * Builds out a pre-defined {@link ConnectorAttribute} that determines the last login - * date for an object. - * - * @param date - * The date and time of the last login. - * @return {@link ConnectorAttribute} - */ - public static ConnectorAttribute BuildLastLoginDate(DateTime date) { - return BuildLastLoginDate(DateTimeUtil.GetUtcTimeMillis(date)); - } - - /** - * Builds out a pre-defined {@link ConnectorAttribute} that determines the last login - * date for an object. The time parameter is UTC in milliseconds. - * - * @param date - * The date and time of the last login. - * @return {@link ConnectorAttribute} - */ - public static ConnectorAttribute BuildLastLoginDate(long date) { - return Build(PredefinedAttributes.LAST_LOGIN_DATE_NAME, date); + + /// + /// Builds out a pre-defined that determines the last login + /// date for an object. + /// + /// The date and time of the last login. + /// + /// + /// + public static ConnectorAttribute BuildLastLoginDate(DateTime date) + { + return BuildLastLoginDate(DateTimeUtil.GetUtcTimeMillis(date)); + } + + /// + /// Builds out a pre-defined that determines the last login + /// date for an object. + /// + /// + /// The time parameter is UTC in milliseconds. + /// + /// The date and time of the last login. + /// + /// + /// + public static ConnectorAttribute BuildLastLoginDate(long date) + { + return Build(PredefinedAttributes.LAST_LOGIN_DATE_NAME, date); } - - /** - * Builds out a pre-defined {@link ConnectorAttribute} that determines the last - * password change date for an object. - * - * @param date - * The date and time the password was changed. - * @return {@link ConnectorAttribute} - */ - public static ConnectorAttribute BuildLastPasswordChangeDate(DateTime date) { + + /// + /// Builds out a pre-defined that determines the last + /// password change date for an object. + /// + /// The date and time the password was changed. + /// + /// + /// + public static ConnectorAttribute BuildLastPasswordChangeDate(DateTime date) + { return BuildLastPasswordChangeDate(DateTimeUtil.GetUtcTimeMillis(date)); } - - /** - * Builds out a pre-defined {@link ConnectorAttribute} that determines the last - * password change date for an object. - * - * @param date - * The date and time the password was changed. - * @return {@link ConnectorAttribute} - */ - public static ConnectorAttribute BuildLastPasswordChangeDate(long date) { + + /// + /// Builds out a pre-defined that determines the last + /// password change date for an object. + /// + /// The date and time the password was changed. + /// + /// + /// + public static ConnectorAttribute BuildLastPasswordChangeDate(long date) + { return Build(PredefinedAttributes.LAST_PASSWORD_CHANGE_DATE_NAME, date); - } - - /** - * Common password policy attribute where the password must be changed every - * so often. The value for this attribute is milliseconds since its the - * lowest common denominator. - */ - public static ConnectorAttribute BuildPasswordChangeInterval(long val) { + } + + /// + /// Common password policy attribute where the password must be changed every + /// so often. + /// + /// + /// The value for this attribute is milliseconds since its the + /// lowest common denominator. + /// + public static ConnectorAttribute BuildPasswordChangeInterval(long val) + { return Build(PredefinedAttributes.PASSWORD_CHANGE_INTERVAL_NAME, val); } } #endregion #region ConnectorMessages - /** - * Message catalog for a given connector. - */ - public interface ConnectorMessages { - /** - * Formats the given message key in the current UI culture. - * @param key The message key to format. - * @param dflt The default message if key is not found. If null, defaults - * to key. - * @param args Parameters with which to format the message. - * @return The formatted string. - */ - String Format(String key, String dflt, params object [] args); + /// + /// Message catalog for a given connector. + /// + public interface ConnectorMessages + { + /// + /// Formats the given message key in the current UI culture. + /// + /// The message key to format. + /// The default message if key is not found. If null, defaults + /// to key. + /// Parameters with which to format the message. + /// The formatted string. + String Format(String key, String dflt, params object[] args); } #endregion - + #region ConnectorObject - public sealed class ConnectorObject { + public sealed class ConnectorObject + { private readonly ObjectClass _objectClass; private readonly IDictionary _attrs; - public ConnectorObject(ObjectClass objectClass, ICollection attrs) { - if (objectClass == null) { + public ConnectorObject(ObjectClass objectClass, ICollection attrs) + { + if (objectClass == null) + { throw new ArgumentException("ObjectClass may not be null"); - } - if ( attrs == null || attrs.Count == 0 ) { + } + if (attrs == null || attrs.Count == 0) + { throw new ArgumentException("attrs cannot be empty or null."); } _objectClass = objectClass; - _attrs = + _attrs = CollectionUtil.NewReadOnlyDictionary(attrs, - value => { return value.Name;}); - if (!_attrs.ContainsKey(Uid.NAME)) { + value => { return value.Name; }); + if (!_attrs.ContainsKey(Uid.NAME)) + { const String MSG = "The ConnectorAttribute set must contain a Uid."; throw new ArgumentException(MSG); } - if (!_attrs.ContainsKey(Name.NAME)) { - const string MSG = "The ConnectorAttribute set must contain a Name."; - throw new ArgumentException(MSG); + if (!_attrs.ContainsKey(Name.NAME)) + { + const string MSG = "The ConnectorAttribute set must contain a Name."; + throw new ArgumentException(MSG); } - } - public ICollection GetAttributes() { + } + public ICollection GetAttributes() + { return _attrs.Values; } - public ConnectorAttribute GetAttributeByName(string name) { - return CollectionUtil.GetValue(_attrs,name,null); + public ConnectorAttribute GetAttributeByName(string name) + { + return CollectionUtil.GetValue(_attrs, name, null); } - public Uid Uid { - get { + public Uid Uid + { + get + { return (Uid)GetAttributeByName(Uid.NAME); } } - public Name Name { - get { + public Name Name + { + get + { return (Name)GetAttributeByName(Name.NAME); } } - public ObjectClass ObjectClass { - get { + public ObjectClass ObjectClass + { + get + { return _objectClass; } } - public override int GetHashCode() { + public override int GetHashCode() + { return CollectionUtil.GetHashCode(_attrs); } - public override bool Equals(Object o) { + public override bool Equals(Object o) + { ConnectorObject other = o as ConnectorObject; - if ( other != null ) { - if (!_objectClass.Equals(other.ObjectClass)) { + if (other != null) + { + if (!_objectClass.Equals(other.ObjectClass)) + { return false; } - return CollectionUtil.Equals(_attrs,other._attrs); + return CollectionUtil.Equals(_attrs, other._attrs); } return false; } } #endregion - + #region ConnectorObjectBuilder - public sealed class ConnectorObjectBuilder { + public sealed class ConnectorObjectBuilder + { private IDictionary _attributes; - public ConnectorObjectBuilder() { + public ConnectorObjectBuilder() + { _attributes = new Dictionary(); // default always add the account object class.. ObjectClass = ObjectClass.ACCOUNT; } - - public void SetUid(string uid) { + + public void SetUid(string uid) + { AddAttribute(new Uid(uid)); } - - public void SetUid(Uid uid) { + + public void SetUid(Uid uid) + { AddAttribute(uid); } - - public void SetName(string name) { - AddAttribute(new Name(name)); + + public void SetName(string name) + { + AddAttribute(new Name(name)); } - - public void SetName(Name name) { - AddAttribute(name); + + public void SetName(Name name) + { + AddAttribute(name); } - + public ObjectClass ObjectClass { get; set; } - + // ======================================================================= // Clone basically.. // ======================================================================= - /** - * Takes all the attribute from a {@link ConnectorObject} and add/overwrite - * the current attributes. - */ - public ConnectorObjectBuilder Add(ConnectorObject obj) { + /// + /// Takes all the attribute from a and add/overwrite + /// the current attributes. + /// + public ConnectorObjectBuilder Add(ConnectorObject obj) + { // simply add all the attributes it will include (Uid, ObjectClass..) - foreach (ConnectorAttribute attr in obj.GetAttributes()) { - AddAttribute(attr); + foreach (ConnectorAttribute attr in obj.GetAttributes()) + { + AddAttribute(attr); } ObjectClass = obj.ObjectClass; return this; } - - public ConnectorObjectBuilder AddAttribute(params ConnectorAttribute [] attrs) { + + public ConnectorObjectBuilder AddAttribute(params ConnectorAttribute[] attrs) + { ValidateParameter(attrs, "attrs"); - foreach (ConnectorAttribute a in attrs) { + foreach (ConnectorAttribute a in attrs) + { //DONT use Add - it throws exceptions if already there _attributes[a.Name] = a; } return this; } - public ConnectorObjectBuilder AddAttributes(ICollection attrs) { + public ConnectorObjectBuilder AddAttributes(ICollection attrs) + { ValidateParameter(attrs, "attrs"); - foreach (ConnectorAttribute a in attrs) { + foreach (ConnectorAttribute a in attrs) + { _attributes[a.Name] = a; } return this; } - /** - * Adds values to the attribute. - */ - public ConnectorObjectBuilder AddAttribute(String name, params object [] objs) { + /// + /// Adds values to the attribute. + /// + public ConnectorObjectBuilder AddAttribute(String name, params object[] objs) + { AddAttribute(ConnectorAttributeBuilder.Build(name, objs)); return this; } - /** - * Adds each object in the collection. - */ - public ConnectorObjectBuilder AddAttribute(String name, ICollection obj) { + /// + /// Adds each object in the collection. + /// + public ConnectorObjectBuilder AddAttribute(String name, ICollection obj) + { AddAttribute(ConnectorAttributeBuilder.Build(name, obj)); return this; } - public ConnectorObject Build() { + public ConnectorObject Build() + { // check that there are attributes to return.. - if (_attributes.Count == 0) { + if (_attributes.Count == 0) + { throw new InvalidOperationException("No attributes set!"); } - return new ConnectorObject(ObjectClass,_attributes.Values); + return new ConnectorObject(ObjectClass, _attributes.Values); } - private static void ValidateParameter(Object param, String paramName) { - if (param == null) { - String FORMAT = "Parameter "+param+" must not be null!"; + private static void ValidateParameter(Object param, String paramName) + { + if (param == null) + { + String FORMAT = "Parameter " + param + " must not be null!"; throw new NullReferenceException(FORMAT); } } @@ -1208,43 +1371,69 @@ private static void ValidateParameter(Object param, String paramName) { #endregion #region ConnectorAttributeInfo - public sealed class ConnectorAttributeInfo { + public sealed class ConnectorAttributeInfo + { private readonly string _name; private readonly Type _type; private readonly Flags _flags; - - /** - * Enum of modifier flags to use for attributes. Note that - * this enum is designed for configuration by exception such that - * an empty set of flags are the defaults: - *
    - *
  • updateable
  • - *
  • creatable
  • - *
  • returned by default
  • - *
  • readable
  • - *
  • single-valued
  • - *
  • optional
  • - *
- */ - [FlagsAttribute] - public enum Flags { - NONE = 0, - REQUIRED = 1, - MULTIVALUED = 2, - NOT_CREATABLE = 4, - NOT_UPDATEABLE = 8, - NOT_READABLE = 16, - NOT_RETURNED_BY_DEFAULT = 32 - } + + /// + /// Enum of modifier flags to use for attributes. + /// + /// + /// Note that + /// this enum is designed for configuration by exception such that + /// an empty set of flags are the defaults: + /// + /// + /// updateable + /// + /// + /// + /// creatable + /// + /// + /// + /// returned by default + /// + /// + /// + /// readable + /// + /// + /// + /// single-valued + /// + /// + /// + /// optional + /// + /// + /// + /// + [FlagsAttribute] + public enum Flags + { + NONE = 0, + REQUIRED = 1, + MULTIVALUED = 2, + NOT_CREATABLE = 4, + NOT_UPDATEABLE = 8, + NOT_READABLE = 16, + NOT_RETURNED_BY_DEFAULT = 32 + } internal ConnectorAttributeInfo(string name, Type type, - Flags flags) { - if (StringUtil.IsBlank(name)) { + Flags flags) + { + if (StringUtil.IsBlank(name)) + { throw new ArgumentException("Name must not be blank!"); } if ((OperationalAttributes.PASSWORD_NAME.Equals(name) || OperationalAttributes.CURRENT_PASSWORD_NAME.Equals(name)) && - !typeof(GuardedString).Equals(type)) { + !typeof(GuardedString).Equals(type)) + { String MSG = "Password based attributes must be of type GuardedString."; throw new ArgumentException(MSG); } @@ -1253,448 +1442,549 @@ internal ConnectorAttributeInfo(string name, Type type, _name = name; _type = type; _flags = flags; - if (!IsReadable && IsReturnedByDefault) { - throw new ArgumentException("Attribute "+name+" is flagged as not-readable, so it should also be as not-returned-by-default."); - } - } - - - /** - * The native name of the attribute. - * - * @return the native name of the attribute its describing. - */ - public string Name { - get { + if (!IsReadable && IsReturnedByDefault) + { + throw new ArgumentException("Attribute " + name + " is flagged as not-readable, so it should also be as not-returned-by-default."); + } + } + + + /// + /// The native name of the attribute. + /// + /// the native name of the attribute its describing. + public string Name + { + get + { return _name; } } - /** - * The basic type associated with this attribute. All primitives are - * supported. - * - * @return the native type if uses. - */ - public Type ValueType { - get { + /// + /// The basic type associated with this attribute. + /// + /// + /// All primitives are + /// supported. + /// + /// the native type if uses. + public Type ValueType + { + get + { return _type; } } - - /** - * Returns the set of flags associated with the attribute. - * @return the set of flags associated with the attribute - */ - public Flags InfoFlags { - get { - return _flags; - } - } - - public bool Is(string name) { + /// + /// Returns the set of flags associated with the attribute. + /// + /// the set of flags associated with the attribute + public Flags InfoFlags + { + get + { + return _flags; + } + } + + + public bool Is(string name) + { return NameUtil.NamesEqual(_name, name); } - /** - * Determines if the attribute is readable. - * - * @return true if the attribute is readable else false. - */ - public bool IsReadable { - get { + /// + /// Determines if the attribute is readable. + /// + /// true if the attribute is readable else false. + public bool IsReadable + { + get + { return (_flags & Flags.NOT_READABLE) == 0; } } - /** - * Determines if the attribute is writable on create. - * - * @return true if the attribute is writable on create else false. - */ - public bool IsCreatable { - get { + /// + /// Determines if the attribute is writable on create. + /// + /// true if the attribute is writable on create else false. + public bool IsCreatable + { + get + { return (_flags & Flags.NOT_CREATABLE) == 0; } } - - /** - * Determines if the attribute is writable on update. - * - * @return true if the attribute is writable on update else false. - */ - public bool IsUpdateable { - get { - return (_flags & Flags.NOT_UPDATEABLE) == 0; - } - } - - /** - * Determines whether this attribute is required for creates. - * - * @return true if the attribute is required for an object else false. - */ - public bool IsRequired { - get { + + /// + /// Determines if the attribute is writable on update. + /// + /// true if the attribute is writable on update else false. + public bool IsUpdateable + { + get + { + return (_flags & Flags.NOT_UPDATEABLE) == 0; + } + } + + /// + /// Determines whether this attribute is required for creates. + /// + /// true if the attribute is required for an object else false. + public bool IsRequired + { + get + { return (_flags & Flags.REQUIRED) != 0; } } - /** - * Determines if this attribute can handle multiple values. There is a - * special case with byte[] since in most instances this denotes a single - * object. - * - * @return true if the attribute is multi-value otherwise false. - */ - public bool IsMultiValued { - get { + /// + /// Determines if this attribute can handle multiple values. + /// + /// + /// There is a + /// special case with byte[] since in most instances this denotes a single + /// object. + /// + /// true if the attribute is multi-value otherwise false. + public bool IsMultiValued + { + get + { return (_flags & Flags.MULTIVALUED) != 0; } } - - /** - * Determines if the attribute is returned by default. Indicates if an - * {@link ConnectorAttribute} will be returned during {@link SearchApiOp} or - * {@link GetApiOp} inside a {@link ConnectorObject} by default. The default - * value is true. - * - * @return false iff the attribute should not be returned by default. - */ - public bool IsReturnedByDefault { - get { + + /// + /// Determines if the attribute is returned by default. + /// + /// + /// Indicates if an + /// will be returned during or + /// inside a by default. The default + /// value is true. + /// + /// false iff the attribute should not be returned by default. + public bool IsReturnedByDefault + { + get + { return (_flags & Flags.NOT_RETURNED_BY_DEFAULT) == 0; } } - - public override bool Equals(Object o) { + + public override bool Equals(Object o) + { ConnectorAttributeInfo other = o as ConnectorAttributeInfo; - if ( other != null ) { - if (!Is(other.Name)) { + if (other != null) + { + if (!Is(other.Name)) + { return false; } - if (!ValueType.Equals(other.ValueType)) { + if (!ValueType.Equals(other.ValueType)) + { return false; } - if (_flags != other._flags) { + if (_flags != other._flags) + { return false; } return true; } return false; } - - public override int GetHashCode() { + + public override int GetHashCode() + { return NameUtil.GetNameHashCode(_name); } - - public override string ToString() { - return SerializerUtil.SerializeXmlObject(this,false); + + public override string ToString() + { + return SerializerUtil.SerializeXmlObject(this, false); } } #endregion #region ConnectorAttributeInfoBuilder - /** - * Simplifies the process of building 'AttributeInfo' objects. This class is - * responsible for providing a default implementation of {@link AttributeInfo}. - * - * - * AttributeInfoBuilder bld = new AttributeInfoBuilder("email"); - * bld.setRequired(true); - * AttributeInfo info = bld.build(); - * - * - * @author Will Droste - * @version $Revision: 1.9 $ - * @since 1.0 - */ - public sealed class ConnectorAttributeInfoBuilder { - - private String _name; - private Type _type; - private ConnectorAttributeInfo.Flags _flags; - - /** - * Creates an builder with all the defaults set. The name must be set before - * the 'build' method is called otherwise an {@link IllegalStateException} - * is thrown. - * - *
-         * Name: <not set>
-         * Readable: true
-         * Writeable: true
-         * Required: false
-         * Type: string
-         * MultiValue: false
-         * 
- */ - public ConnectorAttributeInfoBuilder() { - ValueType=(typeof(String)); + /// + /// Simplifies the process of building 'AttributeInfo' objects. + /// + /// + /// This class is + /// responsible for providing a default implementation of . + /// + /// AttributeInfoBuilder bld = new AttributeInfoBuilder("email"); + /// bld.setRequired(true); + /// AttributeInfo info = bld.build(); + /// + /// + /// Will Droste + /// $Revision: 1.9 $ + /// 1.0 + public sealed class ConnectorAttributeInfoBuilder + { + + private String _name; + private Type _type; + private ConnectorAttributeInfo.Flags _flags; + + /// + /// Creates an builder with all the defaults set. + /// + /// + /// The name must be set before + /// the 'build' method is called otherwise an + /// is thrown. + ///
+        /// Name: <not set>
+        /// Readable: true
+        /// Writeable: true
+        /// Required: false
+        /// Type: string
+        /// MultiValue: false
+        /// 
+ ///
+ public ConnectorAttributeInfoBuilder() + { + ValueType = (typeof(String)); _flags = ConnectorAttributeInfo.Flags.NONE; } - - /** - * Creates an builder with all the defaults set. The name must be set before - * the 'build' method is called otherwise an {@link IllegalStateException} - * is thrown. - * - *
-         * Name: <not set>
-         * Readable: true
-         * Writeable: true
-         * Required: false
-         * Type: string
-         * MultiValue: false
-         * 
- */ - public ConnectorAttributeInfoBuilder(String name) : this(name,typeof(String)){ - } - - /** - * Creates an builder with all the defaults set. The name must be set before - * the 'build' method is called otherwise an {@link IllegalStateException} - * is thrown. - * - *
-    	 * Name: <not set>
-    	 * Readable: true
-    	 * Writeable: true
-    	 * Required: false
-    	 * Type: string
-    	 * MultiValue: false
-    	 * 
- */ - public ConnectorAttributeInfoBuilder(String name, Type type) { - Name=(name); - ValueType=(type); - //noneOf means the defaults - _flags = ConnectorAttributeInfo.Flags.NONE; - } - - /** - * Builds an {@link AttributeInfo} object based on the properties set. - * - * @return {@link AttributeInfo} based on the properties set. - */ - public ConnectorAttributeInfo Build() { - return new ConnectorAttributeInfo(_name, _type, _flags); - } - - /** - * Sets the unique name of the {@link AttributeInfo} object. - * - * @param name - * unique name of the {@link AttributeInfo} object. - */ - public String Name { - set { - if (StringUtil.IsBlank(value)) { - throw new ArgumentException("Argument must not be blank."); - } - _name = value; - } - } - - /** - * Please see {@link FrameworkUtil#checkAttributeType(Class)} for the - * definitive list of supported types. - * - * @param value - * type for an {@link Attribute}'s value. - * @throws IllegalArgumentException - * if the Class is not a supported type. - */ - public Type ValueType { - set { - FrameworkUtil.CheckAttributeType(value); - _type = value; - } - } - - /** - * Determines if the attribute is readable. - */ - public bool Readable { - set { - SetFlag(ConnectorAttributeInfo.Flags.NOT_READABLE,!value); - } - } - - /** - * Determines if the attribute is writable. - */ - public bool Creatable { - set { - SetFlag(ConnectorAttributeInfo.Flags.NOT_CREATABLE,!value); - } - } - - /** - * Determines if this attribute is required. - */ - public bool Required { - set { - SetFlag(ConnectorAttributeInfo.Flags.REQUIRED,value); - } - } - - /** - * Determines if this attribute supports multivalue. - */ - public bool MultiValued { - set { - SetFlag(ConnectorAttributeInfo.Flags.MULTIVALUED,value); - } - } - - /** - * Determines if this attribute writable during update. - */ - public bool Updateable { - set { - SetFlag(ConnectorAttributeInfo.Flags.NOT_UPDATEABLE,!value); - } - } - - public bool ReturnedByDefault { - set { - SetFlag(ConnectorAttributeInfo.Flags.NOT_RETURNED_BY_DEFAULT,!value); - } - } - - /** - * Sets all of the flags for this builder. - * @param flags The set of attribute info flags. Null means clear all flags. - *

- * NOTE: EnumSet.noneOf(AttributeInfo.Flags.class) results in - * an attribute with the default behavior: - *

    - *
  • updateable
  • - *
  • creatable
  • - *
  • returned by default
  • - *
  • readable
  • - *
  • single-valued
  • - *
  • optional
  • - *
- */ - public ConnectorAttributeInfo.Flags InfoFlags { - set { - _flags = value; - } - } - - private void SetFlag(ConnectorAttributeInfo.Flags flag, bool value) { - if (value) { + + /// + /// Creates an builder with all the defaults set. + /// + /// + /// The name must be set before + /// the 'build' method is called otherwise an + /// is thrown. + ///
+        /// Name: <not set>
+        /// Readable: true
+        /// Writeable: true
+        /// Required: false
+        /// Type: string
+        /// MultiValue: false
+        /// 
+ ///
+ public ConnectorAttributeInfoBuilder(String name) + : this(name, typeof(String)) + { + } + + /// + /// Creates an builder with all the defaults set. + /// + /// + /// The name must be set before + /// the 'build' method is called otherwise an + /// is thrown. + ///
+        /// Name: <not set>
+        /// Readable: true
+        /// Writeable: true
+        /// Required: false
+        /// Type: string
+        /// MultiValue: false
+        /// 
+ ///
+ public ConnectorAttributeInfoBuilder(String name, Type type) + { + Name = (name); + ValueType = (type); + //noneOf means the defaults + _flags = ConnectorAttributeInfo.Flags.NONE; + } + + /// + /// Builds an object based on the properties set. + /// + /// + /// based on the properties set. + public ConnectorAttributeInfo Build() + { + return new ConnectorAttributeInfo(_name, _type, _flags); + } + + /// + /// Sets the unique name of the object. + /// + /// unique name of the object. + public String Name + { + set + { + if (StringUtil.IsBlank(value)) + { + throw new ArgumentException("Argument must not be blank."); + } + _name = value; + } + } + + /// + /// Please see for the + /// definitive list of supported types. + /// + /// type for an 's value. + /// if the Class is not a supported type. + public Type ValueType + { + set + { + FrameworkUtil.CheckAttributeType(value); + _type = value; + } + } + + /// + /// Determines if the attribute is readable. + /// + public bool Readable + { + set + { + SetFlag(ConnectorAttributeInfo.Flags.NOT_READABLE, !value); + } + } + + /// + /// Determines if the attribute is writable. + /// + public bool Creatable + { + set + { + SetFlag(ConnectorAttributeInfo.Flags.NOT_CREATABLE, !value); + } + } + + /// + /// Determines if this attribute is required. + /// + public bool Required + { + set + { + SetFlag(ConnectorAttributeInfo.Flags.REQUIRED, value); + } + } + + /// + /// Determines if this attribute supports multivalue. + /// + public bool MultiValued + { + set + { + SetFlag(ConnectorAttributeInfo.Flags.MULTIVALUED, value); + } + } + + /// + /// Determines if this attribute writable during update. + /// + public bool Updateable + { + set + { + SetFlag(ConnectorAttributeInfo.Flags.NOT_UPDATEABLE, !value); + } + } + + public bool ReturnedByDefault + { + set + { + SetFlag(ConnectorAttributeInfo.Flags.NOT_RETURNED_BY_DEFAULT, !value); + } + } + + /// + /// Sets all of the flags for this builder. + /// + /// The set of attribute info flags. Null means clear all flags. + /// + /// NOTE: EnumSet.noneOf(AttributeInfo.Flags.class) results in + /// an attribute with the default behavior: + /// + /// + /// updateable + /// + /// + /// + /// creatable + /// + /// + /// + /// returned by default + /// + /// + /// + /// readable + /// + /// + /// + /// single-valued + /// + /// + /// + /// optional + /// + /// + /// + /// + /// + public ConnectorAttributeInfo.Flags InfoFlags + { + set + { + _flags = value; + } + } + + private void SetFlag(ConnectorAttributeInfo.Flags flag, bool value) + { + if (value) + { _flags = _flags | flag; } - else { + else + { _flags = _flags & ~flag; } } - - /** - * Convenience method to create an AttributeInfo. Equivalent to - * - * new AttributeInfoBuilder(name,type).setFlags(flags).build() - * - * @param name The name of the attribute - * @param type The type of the attribute - * @param flags The flags for the attribute. Null means clear all flags - * @return The attribute info - */ - public static ConnectorAttributeInfo Build(String name, Type type, - ConnectorAttributeInfo.Flags flags) { - return new ConnectorAttributeInfoBuilder(name,type){InfoFlags=flags}.Build(); - } - /** - * Convenience method to create an AttributeInfo. Equivalent to - * - * AttributeInfoBuilder.build(name,type,null) - * - * @param name The name of the attribute - * @param type The type of the attribute - * @param flags The flags for the attribute - * @return The attribute info - */ - public static ConnectorAttributeInfo Build(String name, Type type) { - return Build(name,type,ConnectorAttributeInfo.Flags.NONE); - } - - /** - * Convenience method to create an AttributeInfo. Equivalent to - * - * AttributeInfoBuilder.build(name,type) - * - * @param name The name of the attribute - * @return The attribute info - */ - public static ConnectorAttributeInfo Build(String name) { - return Build(name,typeof(String)); + + /// + /// Convenience method to create an AttributeInfo. + /// + /// + /// Equivalent to + /// + /// new AttributeInfoBuilder(name,type).setFlags(flags).build() + /// + /// + /// The name of the attribute + /// The type of the attribute + /// The flags for the attribute. Null means clear all flags + /// The attribute info + public static ConnectorAttributeInfo Build(String name, Type type, + ConnectorAttributeInfo.Flags flags) + { + return new ConnectorAttributeInfoBuilder(name, type) { InfoFlags = flags }.Build(); + } + /// + /// Convenience method to create an AttributeInfo. + /// + /// + /// Equivalent to + /// + /// AttributeInfoBuilder.build(name,type,null) + /// + /// + /// The name of the attribute + /// The type of the attribute + /// The flags for the attribute + /// The attribute info + public static ConnectorAttributeInfo Build(String name, Type type) + { + return Build(name, type, ConnectorAttributeInfo.Flags.NONE); + } + + /// + /// Convenience method to create an AttributeInfo. + /// + /// + /// Equivalent to + /// + /// AttributeInfoBuilder.build(name,type) + /// + /// + /// The name of the attribute + /// The attribute info + public static ConnectorAttributeInfo Build(String name) + { + return Build(name, typeof(String)); } } #endregion - + #region FileName /// /// Placeholder for java.io.File since C#'s /// FileInfo class throws exceptions if the /// file doesn't exist. /// - public sealed class FileName { + public sealed class FileName + { private string _path; - public FileName(string path) { - if ( path == null ) { + public FileName(string path) + { + if (path == null) + { throw new ArgumentNullException(); } _path = path; } - public string Path { - get { + public string Path + { + get + { return _path; } } - public override bool Equals(object o) { + public override bool Equals(object o) + { FileName other = o as FileName; - if ( other != null ) { + if (other != null) + { return Path.Equals(other.Path); } return false; } - public override int GetHashCode() { + public override int GetHashCode() + { return _path.GetHashCode(); } - public override string ToString() { + public override string ToString() + { return _path; } } #endregion - + #region Name - public sealed class Name : ConnectorAttribute { - + public sealed class Name : ConnectorAttribute + { public readonly static string NAME = ConnectorAttributeUtil.CreateSpecialName("NAME"); public readonly static ConnectorAttributeInfo INFO = - new ConnectorAttributeInfoBuilder(NAME) {Required=true}.Build(); - - public Name(String value) : base(NAME, CollectionUtil.NewReadOnlyList(value)) { - } - - /** - * The single value of the attribute that is the unique id of an object. - * - * @return value that identifies an object. - */ - public String GetNameValue() { + new ConnectorAttributeInfoBuilder(NAME) { Required = true }.Build(); + + public Name(String value) + : base(NAME, CollectionUtil.NewReadOnlyList(value)) + { + } + + /// + /// The single value of the attribute that is the unique id of an object. + /// + /// value that identifies an object. + public String GetNameValue() + { return ConnectorAttributeUtil.GetStringValue(this); } } #endregion - - #region ObjectClassUtil - public static class ObjectClassUtil { + #region ObjectClassUtil + public static class ObjectClassUtil + { /// /// Determines if this object class is a special object class. /// Special object classes include the predefined ones, such as @@ -1703,7 +1993,8 @@ public static class ObjectClassUtil { /// the object class to test /// true iff the object class is special /// if the object class parameter is null - public static bool IsSpecial(ObjectClass oclass) { + public static bool IsSpecial(ObjectClass oclass) + { String name = oclass.GetObjectClassValue(); return IsSpecialName(name); } @@ -1714,7 +2005,8 @@ public static bool IsSpecial(ObjectClass oclass) { /// /// the name of the object class to test /// true iff the object class name is special - public static bool IsSpecialName(String name) { + public static bool IsSpecialName(String name) + { return NameUtil.IsSpecialName(name); } @@ -1725,7 +2017,8 @@ public static bool IsSpecialName(String name) { /// /// object class name to make special /// name constructed for use as a special name - public static string CreateSpecialName(string name) { + public static string CreateSpecialName(string name) + { return NameUtil.CreateSpecialName(name); } @@ -1735,165 +2028,184 @@ public static string CreateSpecialName(string name) { /// the first object class name /// the second object class name /// true iff the two object class names are equal - public static bool NamesEqual(string name1, string name2) { + public static bool NamesEqual(string name1, string name2) + { return NameUtil.NamesEqual(name2, name2); } } #endregion #region ObjectClass - public sealed class ObjectClass { + public sealed class ObjectClass + { public static readonly String ACCOUNT_NAME = ObjectClassUtil.CreateSpecialName("ACCOUNT"); public static readonly String GROUP_NAME = ObjectClassUtil.CreateSpecialName("GROUP"); - /** - * Denotes an account based object. - */ - public static readonly ObjectClass ACCOUNT = new ObjectClass(ACCOUNT_NAME); - /** - * Denotes a group based object. - */ - public static readonly ObjectClass GROUP = new ObjectClass(GROUP_NAME); - + /// + /// Denotes an account based object. + /// + public static readonly ObjectClass ACCOUNT = new ObjectClass(ACCOUNT_NAME); + /// + /// Denotes a group based object. + /// + public static readonly ObjectClass GROUP = new ObjectClass(GROUP_NAME); + private readonly String _type; - - public ObjectClass(String type) { - if ( type == null ) { + + public ObjectClass(String type) + { + if (type == null) + { throw new ArgumentException("Type cannot be null."); } _type = type; } - public String GetObjectClassValue() { + public String GetObjectClassValue() + { return _type; } - - /** - * Convenience method to build the display name key for - * an object class. - * - * @return The display name key. - */ - public String GetDisplayNameKey() { - return "MESSAGE_OBJECT_CLASS_"+_type.ToUpper(CultureInfo.GetCultureInfo("en-US")); - } - - /** - * Determines if the 'name' matches this {@link ObjectClass}. - * - * @param name - * case-insensitive string representation of the ObjectClass's - * type. - * @return true if the case-insensitive name is equal to - * that of the one in this {@link ObjectClass}. - */ - public bool Is(String name) { + + /// + /// Convenience method to build the display name key for + /// an object class. + /// + /// The display name key. + public String GetDisplayNameKey() + { + return "MESSAGE_OBJECT_CLASS_" + _type.ToUpper(CultureInfo.GetCultureInfo("en-US")); + } + + /// + /// Determines if the 'name' matches this . + /// + /// case-insensitive string representation of the ObjectClass's + /// type. + /// + /// true if the case-insensitive name is equal to + /// that of the one in this . + public bool Is(String name) + { return NameUtil.NamesEqual(_type, name); - } - - public override int GetHashCode() { + } + + public override int GetHashCode() + { return NameUtil.GetNameHashCode(_type); } - - public override bool Equals(object obj) { + + public override bool Equals(object obj) + { // test identity - if(this == obj) + if (this == obj) { return true; } // test for null.. - if(obj == null) + if (obj == null) { return false; } // test that the exact class matches - if(!(GetType().Equals(obj.GetType()))) + if (!(GetType().Equals(obj.GetType()))) { return false; } - ObjectClass other = (ObjectClass) obj; + ObjectClass other = (ObjectClass)obj; - if(!Is(other._type)) { + if (!Is(other._type)) + { return false; } return true; } - + public override string ToString() { return "ObjectClass: " + _type; } } #endregion - + #region ObjectClassInfo - public sealed class ObjectClassInfo { - + public sealed class ObjectClassInfo + { private readonly String _type; private readonly ICollection _info; private readonly bool _isContainer; - public ObjectClassInfo(String type, + public ObjectClassInfo(String type, ICollection attrInfo, - bool isContainer) { + bool isContainer) + { Assertions.NullCheck(type, "type"); _type = type; _info = CollectionUtil.NewReadOnlySet(attrInfo); _isContainer = isContainer; // check to make sure name exists - IDictionary dict + IDictionary dict = ConnectorAttributeInfoUtil.ToMap(attrInfo); - if (!dict.ContainsKey(Name.NAME)) { + if (!dict.ContainsKey(Name.NAME)) + { const string MSG = "Missing 'Name' connector attribute info."; throw new ArgumentException(MSG); } } - public ICollection ConnectorAttributeInfos { - get { + public ICollection ConnectorAttributeInfos + { + get + { return this._info; } } - - public String ObjectType { - get { + + public String ObjectType + { + get + { return this._type; } } - /** - * Determines if the 'name' matches this {@link ObjectClassInfo}. - * - * @param name - * case-insensitive string representation of the ObjectClassInfo's - * type. - * @return true if the case insensitive type is equal to - * that of the one in this {@link ObjectClassInfo}. - */ - public bool Is(String name) { + /// + /// Determines if the 'name' matches this . + /// + /// case-insensitive string representation of the ObjectClassInfo's + /// type. + /// + /// true if the case insensitive type is equal to + /// that of the one in this . + public bool Is(String name) + { return NameUtil.NamesEqual(_type, name); } - - public bool IsContainer { - get { + + public bool IsContainer + { + get + { return this._isContainer; } } - - public override int GetHashCode() { + + public override int GetHashCode() + { return NameUtil.GetNameHashCode(_type); } - - public override bool Equals(Object obj) { + + public override bool Equals(Object obj) + { // test identity - if (this == obj) { + if (this == obj) + { return true; } // test for null.. - if(obj == null) + if (obj == null) { return false; } @@ -1905,75 +2217,89 @@ public override bool Equals(Object obj) { ObjectClassInfo other = obj as ObjectClassInfo; - if (!Is(other.ObjectType)) { + if (!Is(other.ObjectType)) + { return false; } if (!CollectionUtil.Equals(ConnectorAttributeInfos, - other.ConnectorAttributeInfos)) { + other.ConnectorAttributeInfos)) + { return false; } - if (_isContainer != other._isContainer) { + if (_isContainer != other._isContainer) + { return false; } return true; } - + public override string ToString() { - return SerializerUtil.SerializeXmlObject(this,false); + return SerializerUtil.SerializeXmlObject(this, false); } } #endregion - + #region ObjectClassInfoBuilder - /** - * Used to help facilitate the building of {@link ObjectClassInfo} objects. - */ - public sealed class ObjectClassInfoBuilder { + /// + /// Used to help facilitate the building of objects. + /// + public sealed class ObjectClassInfoBuilder + { private bool _isContainer; private IDictionary _info; - - public ObjectClassInfoBuilder() { + + public ObjectClassInfoBuilder() + { _info = new Dictionary(); - ObjectType = ObjectClass.ACCOUNT_NAME; + ObjectType = ObjectClass.ACCOUNT_NAME; } - + public string ObjectType { get; set; } - - /** - * Add each {@link AttributeInfo} object to the {@link ObjectClassInfo}. - */ - public ObjectClassInfoBuilder AddAttributeInfo(ConnectorAttributeInfo info) { - if (_info.ContainsKey(info.Name)) { + + /// + /// Add each object to the . + /// + public ObjectClassInfoBuilder AddAttributeInfo(ConnectorAttributeInfo info) + { + if (_info.ContainsKey(info.Name)) + { const string MSG = "ConnectorAttributeInfo of name ''{0}'' already exists!"; throw new ArgumentException(String.Format(MSG, info.Name)); } _info[info.Name] = info; return this; } - - public ObjectClassInfoBuilder AddAllAttributeInfo(ICollection info) { - foreach (ConnectorAttributeInfo cainfo in info) { + + public ObjectClassInfoBuilder AddAllAttributeInfo(ICollection info) + { + foreach (ConnectorAttributeInfo cainfo in info) + { AddAttributeInfo(cainfo); } - return this; + return this; } - - public bool IsContainer { - get { + + public bool IsContainer + { + get + { return _isContainer; } - set { + set + { _isContainer = value; } } - - public ObjectClassInfo Build() { + + public ObjectClassInfo Build() + { // determine if name is missing and add it by default - if (!_info.ContainsKey(Name.NAME)) { + if (!_info.ContainsKey(Name.NAME)) + { _info[Name.NAME] = Name.INFO; } return new ObjectClassInfo(ObjectType, _info.Values, _isContainer); @@ -1982,129 +2308,144 @@ public ObjectClassInfo Build() { #endregion #region OperationalAttributeInfos - /** - * {@link AttributeInfo} for each operational attribute. - */ - public static class OperationalAttributeInfos { - /** - * Gets/sets the enable status of an object. - */ - public static readonly ConnectorAttributeInfo ENABLE = + /// + /// for each operational attribute. + /// + public static class OperationalAttributeInfos + { + /// + /// Gets/sets the enable status of an object. + /// + public static readonly ConnectorAttributeInfo ENABLE = ConnectorAttributeInfoBuilder.Build( OperationalAttributes.ENABLE_NAME, typeof(bool)); - - /** - * Gets/sets the enable date for an object. - */ - public static readonly ConnectorAttributeInfo ENABLE_DATE = + + /// + /// Gets/sets the enable date for an object. + /// + public static readonly ConnectorAttributeInfo ENABLE_DATE = ConnectorAttributeInfoBuilder.Build( OperationalAttributes.ENABLE_DATE_NAME, typeof(long)); - - /** - * Gets/sets the disable date for an object. - */ - public static readonly ConnectorAttributeInfo DISABLE_DATE = + + /// + /// Gets/sets the disable date for an object. + /// + public static readonly ConnectorAttributeInfo DISABLE_DATE = ConnectorAttributeInfoBuilder.Build( OperationalAttributes.DISABLE_DATE_NAME, typeof(long)); - - /** - * Gets/sets the lock out attribute for an object. - */ - public static readonly ConnectorAttributeInfo LOCK_OUT = + + /// + /// Gets/sets the lock out attribute for an object. + /// + public static readonly ConnectorAttributeInfo LOCK_OUT = ConnectorAttributeInfoBuilder.Build( OperationalAttributes.LOCK_OUT_NAME, typeof(bool)); - - /** - * Gets/sets the password expiration date for an object. - */ - public static readonly ConnectorAttributeInfo PASSWORD_EXPIRATION_DATE = + + /// + /// Gets/sets the password expiration date for an object. + /// + public static readonly ConnectorAttributeInfo PASSWORD_EXPIRATION_DATE = ConnectorAttributeInfoBuilder.Build( OperationalAttributes.PASSWORD_EXPIRATION_DATE_NAME, typeof(long)); - - /** - * Normally this is a write-only attribute. Sets the password for an object. - */ - public static readonly ConnectorAttributeInfo PASSWORD = + + /// + /// Normally this is a write-only attribute. + /// + /// + /// Sets the password for an object. + /// + public static readonly ConnectorAttributeInfo PASSWORD = ConnectorAttributeInfoBuilder.Build( - OperationalAttributes.PASSWORD_NAME, typeof(GuardedString), + OperationalAttributes.PASSWORD_NAME, typeof(GuardedString), ConnectorAttributeInfo.Flags.NOT_READABLE | ConnectorAttributeInfo.Flags.NOT_RETURNED_BY_DEFAULT); - - /** - * Used in conjunction with password to do an account level password change. - * This is for a non-administrator change of the password and therefore - * requires the current password. - */ - public static readonly ConnectorAttributeInfo CURRENT_PASSWORD = + + /// + /// Used in conjunction with password to do an account level password change. + /// + /// + /// This is for a non-administrator change of the password and therefore + /// requires the current password. + /// + public static readonly ConnectorAttributeInfo CURRENT_PASSWORD = ConnectorAttributeInfoBuilder.Build( - OperationalAttributes.CURRENT_PASSWORD_NAME, typeof(GuardedString), + OperationalAttributes.CURRENT_PASSWORD_NAME, typeof(GuardedString), ConnectorAttributeInfo.Flags.NOT_READABLE | ConnectorAttributeInfo.Flags.NOT_RETURNED_BY_DEFAULT); - - /** - * Used to determine if a password is expired or to expire a password. - */ - public static readonly ConnectorAttributeInfo PASSWORD_EXPIRED = + + /// + /// Used to determine if a password is expired or to expire a password. + /// + public static readonly ConnectorAttributeInfo PASSWORD_EXPIRED = ConnectorAttributeInfoBuilder.Build( OperationalAttributes.PASSWORD_EXPIRED_NAME, typeof(bool)); - + } #endregion - + #region OperationalAttributes - /** - * Operational attributes have special meaning and cannot be represented by pure - * operations. For instance some administrators would like to create an account - * in the disabled state. The do not want this to be a two operation process - * since this can leave the door open to abuse. Therefore special attributes - * that can perform operations were introduced. The - * {@link OperationalAttributes#DISABLED} attribute could be added to the set of - * attribute sent to a Connector for the {@link CreateOp} operation. To tell the - * {@link Connector} to create the account with it in the disabled state whether - * the target resource itself has an attribute or an additional method must be - * called. - */ - public static class OperationalAttributes { - /** - * Gets/sets the enable status of an object. - */ + /// + /// Operational attributes have special meaning and cannot be represented by pure + /// operations. + /// + /// + /// For instance some administrators would like to create an account + /// in the disabled state. The do not want this to be a two operation process + /// since this can leave the door open to abuse. Therefore special attributes + /// that can perform operations were introduced. The + /// attribute could be added to the set of + /// attribute sent to a Connector for the operation. To tell the + /// to create the account with it in the disabled state whether + /// the target resource itself has an attribute or an additional method must be + /// called. + /// + public static class OperationalAttributes + { + /// + /// Gets/sets the enable status of an object. + /// public static readonly string ENABLE_NAME = ConnectorAttributeUtil.CreateSpecialName("ENABLE"); - /** - * Gets/sets the enable date for an object. - */ + /// + /// Gets/sets the enable date for an object. + /// public static readonly string ENABLE_DATE_NAME = ConnectorAttributeUtil.CreateSpecialName("ENABLE_DATE"); - /** - * Gets/sets the disable date for an object. - */ + /// + /// Gets/sets the disable date for an object. + /// public static readonly string DISABLE_DATE_NAME = ConnectorAttributeUtil.CreateSpecialName("DISABLE_DATE"); - /** - * Gets/sets the lock out attribute for an object. - */ + /// + /// Gets/sets the lock out attribute for an object. + /// public static readonly string LOCK_OUT_NAME = ConnectorAttributeUtil.CreateSpecialName("LOCK_OUT"); - /** - * Gets/sets the password expiration date for an object. - */ + /// + /// Gets/sets the password expiration date for an object. + /// public static readonly string PASSWORD_EXPIRATION_DATE_NAME = ConnectorAttributeUtil.CreateSpecialName("PASSWORD_EXPIRATION_DATE"); - /** - * Gets/sets the password expired for an object. - */ + /// + /// Gets/sets the password expired for an object. + /// public static readonly string PASSWORD_EXPIRED_NAME = ConnectorAttributeUtil.CreateSpecialName("PASSWORD_EXPIRED"); - /** - * Normally this is a write-only attribute. Sets the password for an object. - */ + /// + /// Normally this is a write-only attribute. + /// + /// + /// Sets the password for an object. + /// public static readonly string PASSWORD_NAME = ConnectorAttributeUtil.CreateSpecialName("PASSWORD"); - /** - * Used in conjunction with password to do an account level password change. - * This is for a non-administrator change of the password and therefore - * requires the current password. - */ + /// + /// Used in conjunction with password to do an account level password change. + /// + /// + /// This is for a non-administrator change of the password and therefore + /// requires the current password. + /// public static readonly string CURRENT_PASSWORD_NAME = ConnectorAttributeUtil.CreateSpecialName("CURRENT_PASSWORD"); - // ======================================================================= - // Helper Methods.. - // ======================================================================= - public static readonly ICollection OPERATIONAL_ATTRIBUTE_NAMES = - CollectionUtil.NewReadOnlySet ( + // ======================================================================= + // Helper Methods.. + // ======================================================================= + public static readonly ICollection OPERATIONAL_ATTRIBUTE_NAMES = + CollectionUtil.NewReadOnlySet( LOCK_OUT_NAME, ENABLE_NAME, ENABLE_DATE_NAME, @@ -2114,113 +2455,141 @@ public static class OperationalAttributes { CURRENT_PASSWORD_NAME, PASSWORD_EXPIRED_NAME ); - - public static ICollection GetOperationalAttributeNames() { - return CollectionUtil.NewReadOnlySet(OPERATIONAL_ATTRIBUTE_NAMES); - } - public static bool IsOperationalAttribute(ConnectorAttribute attr) { - string name = (attr != null) ? attr.Name : null; - return OPERATIONAL_ATTRIBUTE_NAMES.Contains(name); - } + + public static ICollection GetOperationalAttributeNames() + { + return CollectionUtil.NewReadOnlySet(OPERATIONAL_ATTRIBUTE_NAMES); + } + public static bool IsOperationalAttribute(ConnectorAttribute attr) + { + string name = (attr != null) ? attr.Name : null; + return OPERATIONAL_ATTRIBUTE_NAMES.Contains(name); + } } #endregion #region PredefinedAttributes - /** - * List of well known or pre-defined attributes. Common attributes that most - * resources have that are not operational in nature. - */ - public static class PredefinedAttributes { - /** - * Attribute that should hold a reasonable value to - * display for the value of an object. If this is not present, then the - * application will have to use the NAME to show the value. - */ + /// + /// List of well known or pre-defined attributes. + /// + /// + /// Common attributes that most + /// resources have that are not operational in nature. + /// + public static class PredefinedAttributes + { + /// + /// Attribute that should hold a reasonable value to + /// display for the value of an object. + /// + /// + /// If this is not present, then the + /// application will have to use the NAME to show the value. + /// public static readonly String SHORT_NAME = ConnectorAttributeUtil.CreateSpecialName("SHORT_NAME"); - - /** - * Attribute that should hold the value of the object's description, - * if one is available. - */ + + /// + /// Attribute that should hold the value of the object's description, + /// if one is available. + /// public static readonly String DESCRIPTION = ConnectorAttributeUtil.CreateSpecialName("DESCRIPTION"); - - /** - * Read-only attribute that shows the last date/time the password was - * changed. - */ + + /// + /// Read-only attribute that shows the last date/time the password was + /// changed. + /// public static readonly string LAST_PASSWORD_CHANGE_DATE_NAME = ConnectorAttributeUtil.CreateSpecialName("LAST_PASSWORD_CHANGE_DATE"); - - /** - * Common password policy attribute where the password must be changed every - * so often. The value for this attribute is milliseconds since its the - * lowest common denominator. - */ + + /// + /// Common password policy attribute where the password must be changed every + /// so often. + /// + /// + /// The value for this attribute is milliseconds since its the + /// lowest common denominator. + /// public static readonly string PASSWORD_CHANGE_INTERVAL_NAME = ConnectorAttributeUtil.CreateSpecialName("PASSWORD_CHANGE_INTERVAL"); - - /** - * Last login date for an account. This is usually used to determine inactivity. - */ + + /// + /// Last login date for an account. + /// + /// + /// This is usually used to determine inactivity. + /// public static readonly string LAST_LOGIN_DATE_NAME = ConnectorAttributeUtil.CreateSpecialName("LAST_LOGIN_DATE"); - - /** - * Groups an account object belongs to. - */ + + /// + /// Groups an account object belongs to. + /// public static readonly string GROUPS_NAME = ConnectorAttributeUtil.CreateSpecialName("GROUPS"); } #endregion #region PredefinedAttributeInfos - public static class PredefinedAttributeInfos { - /** - * Attribute that should hold a reasonable value to - * display for the value of an object. If this is not present, then the - * application will have to use the NAME to show the value. - */ - public static readonly ConnectorAttributeInfo SHORT_NAME = + public static class PredefinedAttributeInfos + { + /// + /// Attribute that should hold a reasonable value to + /// display for the value of an object. + /// + /// + /// If this is not present, then the + /// application will have to use the NAME to show the value. + /// + public static readonly ConnectorAttributeInfo SHORT_NAME = ConnectorAttributeInfoBuilder.Build(PredefinedAttributes.SHORT_NAME); - - /** - * Attribute that should hold the value of the object's description, - * if one is available. - */ - public static readonly ConnectorAttributeInfo DESCRIPTION = + + /// + /// Attribute that should hold the value of the object's description, + /// if one is available. + /// + public static readonly ConnectorAttributeInfo DESCRIPTION = ConnectorAttributeInfoBuilder.Build(PredefinedAttributes.DESCRIPTION); - /** - * Read-only attribute that shows the last date/time the password was - * changed. - */ - public static readonly ConnectorAttributeInfo LAST_PASSWORD_CHANGE_DATE = + /// + /// Read-only attribute that shows the last date/time the password was + /// changed. + /// + public static readonly ConnectorAttributeInfo LAST_PASSWORD_CHANGE_DATE = ConnectorAttributeInfoBuilder.Build( - PredefinedAttributes.LAST_PASSWORD_CHANGE_DATE_NAME, - typeof(long), + PredefinedAttributes.LAST_PASSWORD_CHANGE_DATE_NAME, + typeof(long), ConnectorAttributeInfo.Flags.NOT_CREATABLE | ConnectorAttributeInfo.Flags.NOT_UPDATEABLE); - - /** - * Common password policy attribute where the password must be changed every - * so often. The value for this attribute is milliseconds since its the - * lowest common denominator. - */ - public static readonly ConnectorAttributeInfo PASSWORD_CHANGE_INTERVAL = + + /// + /// Common password policy attribute where the password must be changed every + /// so often. + /// + /// + /// The value for this attribute is milliseconds since its the + /// lowest common denominator. + /// + public static readonly ConnectorAttributeInfo PASSWORD_CHANGE_INTERVAL = ConnectorAttributeInfoBuilder.Build( PredefinedAttributes.PASSWORD_CHANGE_INTERVAL_NAME, typeof(long)); - - /** - * Last login date for an account. This is usually used to determine - * inactivity. - */ - public static readonly ConnectorAttributeInfo LAST_LOGIN_DATE = + + /// + /// Last login date for an account. + /// + /// + /// This is usually used to determine + /// inactivity. + /// + public static readonly ConnectorAttributeInfo LAST_LOGIN_DATE = ConnectorAttributeInfoBuilder.Build( - PredefinedAttributes.LAST_LOGIN_DATE_NAME, - typeof(long), + PredefinedAttributes.LAST_LOGIN_DATE_NAME, + typeof(long), ConnectorAttributeInfo.Flags.NOT_CREATABLE | ConnectorAttributeInfo.Flags.NOT_UPDATEABLE); - - /** - * Groups that an account or person belong to. The Attribute values are the - * UID value of each group that an account has membership in. - */ - public static readonly ConnectorAttributeInfo GROUPS = + + /// + /// Groups that an account or person belong to. + /// + /// + /// The Attribute values are the + /// UID value of each group that an account has membership in. + /// + public static readonly ConnectorAttributeInfo GROUPS = ConnectorAttributeInfoBuilder.Build(PredefinedAttributes.GROUPS_NAME, typeof(String), ConnectorAttributeInfo.Flags.MULTIVALUED | @@ -2229,137 +2598,174 @@ public static class PredefinedAttributeInfos { #endregion #region OperationOptions - /** - * Arbitrary options to be passed into various operations. This serves - * as a catch-all for extra options. - */ - public sealed class OperationOptions { - - /** - * An option to use with {@link SearchApiOp} that specified the scope - * under which to perform the search. To be used in conjunction with - * {@link #OP_CONTAINER}. Must be one of the following values - *
    - *
  1. {@link #SCOPE_OBJECT}
  2. - *
  3. {@link #SCOPE_ONE_LEVEL}
  4. - *
  5. {@link #SCOPE_SUBTREE}
  6. - *
- */ + /// + /// Arbitrary options to be passed into various operations. + /// + /// + /// This serves + /// as a catch-all for extra options. + /// + public sealed class OperationOptions + { + /// + /// An option to use with that specified the scope + /// under which to perform the search. + /// + /// + /// To be used in conjunction with + /// . Must be one of the following values + /// + /// + /// + /// + /// + /// + /// + /// + /// + /// + /// + /// + /// + /// + /// public const String OP_SCOPE = "SCOPE"; public const String SCOPE_OBJECT = "object"; public const String SCOPE_ONE_LEVEL = "onelevel"; public const String SCOPE_SUBTREE = "subtree"; - - /** - * An option to use with {@link SearchApiOp} that specified the container - * under which to perform the search. Must be of type {@link QualifiedUid}. - * Should be implemented for those object classes whose {@link ObjectClassInfo#isContainer()} - * returns true. - */ + + /// + /// An option to use with that specified the container + /// under which to perform the search. + /// + /// + /// Must be of type . + /// Should be implemented for those object classes whose + /// returns true. + /// public const String OP_CONTAINER = "CONTAINER"; - - /** - * An option to use with {@link ScriptOnResourceApiOp} and possibly others - * that specifies an account under which to execute the script/operation. - * The specified account will appear to have performed any action that the - * script/operation performs. - *

- * Check the javadoc for a particular connector to see whether that - * connector supports this option. - */ + + ///

+ /// An option to use with and possibly others + /// that specifies an account under which to execute the script/operation. + /// + /// + /// The specified account will appear to have performed any action that the + /// script/operation performs. + /// + /// Check the javadoc for a particular connector to see whether that + /// connector supports this option. + /// + /// public static readonly string OP_RUN_AS_USER = "RUN_AS_USER"; - - /** - * An option to use with {@link ScriptOnResourceApiOp} and possibly others - * that specifies a password under which to execute the script/operation. - */ + + /// + /// An option to use with and possibly others + /// that specifies a password under which to execute the script/operation. + /// public static readonly string OP_RUN_WITH_PASSWORD = "RUN_WITH_PASSWORD"; - - /** - * Determines the attributes to retrieve during {@link SearchApiOp} and - * {@link SyncApiOp}. - */ + + /// + /// Determines the attributes to retrieve during and + /// . + /// public static readonly string OP_ATTRIBUTES_TO_GET = "ATTRS_TO_GET"; - - private readonly IDictionary _operationOptions; - - /** - * Public only for serialization; please use {@link OperationOptionsBuilder}. - * @param operationOptions The options. - */ - public OperationOptions(IDictionary operationOptions) { - foreach (Object val in operationOptions.Values) { + + private readonly IDictionary _operationOptions; + + /// + /// Public only for serialization; please use . + /// + /// The options. + public OperationOptions(IDictionary operationOptions) + { + foreach (Object val in operationOptions.Values) + { FrameworkUtil.CheckOperationOptionValue(val); - } + } //clone options to do a deep copy in case anything //is an array - IDictionary operationOptionsClone = (IDictionary)SerializerUtil.CloneObject(operationOptions); - _operationOptions = CollectionUtil.NewReadOnlyDictionary(operationOptionsClone); - } - - /** - * Returns a map of options. Each value in the map - * must be of a type that the framework can serialize. - * See {@link ObjectSerializerFactory} for a list of supported types. - * - * @return A map of options. - */ - public IDictionary Options { - get { + IDictionary operationOptionsClone = (IDictionary)SerializerUtil.CloneObject(operationOptions); + _operationOptions = CollectionUtil.NewReadOnlyDictionary(operationOptionsClone); + } + + /// + /// Returns a map of options. + /// + /// + /// Each value in the map + /// must be of a type that the framework can serialize. + /// See for a list of supported types. + /// + /// A map of options. + public IDictionary Options + { + get + { return _operationOptions; } } - - /** - * Convenience method that returns {@link #OP_SCOPE}. - * @return The value for {@link #OP_SCOPE}. - */ - public String Scope { - get { - return (String) CollectionUtil.GetValue(_operationOptions,OP_SCOPE,null); - } - } - - /** - * Convenience method that returns {@link #OP_CONTAINER}. - * @return The value for {@link #OP_CONTAINER}. - */ - public QualifiedUid getContainer { - get { - return (QualifiedUid) CollectionUtil.GetValue(_operationOptions,OP_CONTAINER,null); - } - } - - /** - * Get the string array of attribute names to return in the object. - */ - public string[] AttributesToGet { - get { - return (string[]) CollectionUtil.GetValue( + + /// + /// Convenience method that returns . + /// + /// The value for . + public String Scope + { + get + { + return (String)CollectionUtil.GetValue(_operationOptions, OP_SCOPE, null); + } + } + + /// + /// Convenience method that returns . + /// + /// The value for . + public QualifiedUid getContainer + { + get + { + return (QualifiedUid)CollectionUtil.GetValue(_operationOptions, OP_CONTAINER, null); + } + } + + /// + /// Get the string array of attribute names to return in the object. + /// + public string[] AttributesToGet + { + get + { + return (string[])CollectionUtil.GetValue( _operationOptions, OP_ATTRIBUTES_TO_GET, null); } } - - /** - * Get the account to run the operation as.. - */ - public string RunAsUser { - get { - return (string) CollectionUtil.GetValue( + + /// + /// Get the account to run the operation as.. + /// + public string RunAsUser + { + get + { + return (string)CollectionUtil.GetValue( _operationOptions, OP_RUN_AS_USER, null); } } - - /** - * Get the password to run the operation as.. - */ - public GuardedString RunWithPassword { - get { - return (GuardedString) CollectionUtil.GetValue( + + /// + /// Get the password to run the operation as.. + /// + public GuardedString RunWithPassword + { + get + { + return (GuardedString)CollectionUtil.GetValue( _operationOptions, OP_RUN_WITH_PASSWORD, null); } } - + public override string ToString() { StringBuilder bld = new StringBuilder(); @@ -2368,78 +2774,86 @@ public override string ToString() } } #endregion - + #region OperationOptionsBuilder - /** - * Builder for {@link OperationOptions}. - */ - public sealed class OperationOptionsBuilder { - private readonly IDictionary _options; - - /** - * Create a builder with an empty set of options. - */ - public OperationOptionsBuilder() { - _options = new Dictionary(); - } - - /** - * Create a builder from an existing set of options. - * @param options The existing set of options. Must not be null. - */ - public OperationOptionsBuilder(OperationOptions options) { + /// + /// Builder for . + /// + public sealed class OperationOptionsBuilder + { + private readonly IDictionary _options; + + /// + /// Create a builder with an empty set of options. + /// + public OperationOptionsBuilder() + { + _options = new Dictionary(); + } + + /// + /// Create a builder from an existing set of options. + /// + /// The existing set of options. Must not be null. + public OperationOptionsBuilder(OperationOptions options) + { Assertions.NullCheck(options, "options"); // clone options to do a deep copy in case anything // is an array - IDictionary operationOptionsClone = (IDictionary) SerializerUtil + IDictionary operationOptionsClone = (IDictionary)SerializerUtil .CloneObject(options.Options); - _options = CollectionUtil.NewDictionary(operationOptionsClone); - } - - /** - * Sets a given option and a value for that option. - * @param name The name of the option - * @param value The value of the option. Must be one of the types that - * we can serialize. - * See {@link ObjectSerializerFactory} for a list of supported types. - */ - public void SetOption(String name, Object value) { - if ( name == null ) { + _options = CollectionUtil.NewDictionary(operationOptionsClone); + } + + /// + /// Sets a given option and a value for that option. + /// + /// The name of the option + /// The value of the option. Must be one of the types that + /// we can serialize. + /// See for a list of supported types. + public void SetOption(String name, Object value) + { + if (name == null) + { throw new ArgumentException("Argument 'value' cannot be null."); } //don't validate value here - we do that implicitly when //we clone in the constructor of OperationOptions _options[name] = value; } - - /** - * Returns a mutable reference of the options map. - * @return A mutable reference of the options map. - */ - public IDictionary Options { - get { + + /// + /// Returns a mutable reference of the options map. + /// + /// A mutable reference of the options map. + public IDictionary Options + { + get + { //might as well be mutable since it's the builder and //we don't want to deep copy anyway return _options; } } - - /** - * Creates the OperationOptions. - * @return The newly-created OperationOptions - */ - public OperationOptions Build() { + + /// + /// Creates the OperationOptions. + /// + /// The newly-created OperationOptions + public OperationOptions Build() + { return new OperationOptions(_options); } - - /** - * Sets the {@link OperationOptions#OP_ATTRIBUTES_TO_GET} option. - * - * @param attrNames - * list of {@link Attribute} names. - */ - public string[] AttributesToGet { - set { + + /// + /// Sets the option. + /// + /// list of names. + public string[] AttributesToGet + { + set + { Assertions.NullCheck(value, "AttributesToGet"); // don't validate value here - we do that in // the constructor of OperationOptions - that's @@ -2447,45 +2861,53 @@ public string[] AttributesToGet { _options[OperationOptions.OP_ATTRIBUTES_TO_GET] = value; } } - - /** - * Set the run with password option. - */ - public GuardedString RunWithPassword { - set { + + /// + /// Set the run with password option. + /// + public GuardedString RunWithPassword + { + set + { Assertions.NullCheck(value, "RunWithPassword"); _options[OperationOptions.OP_RUN_WITH_PASSWORD] = value; } } - - /** - * Set the run as user option. - */ - public string RunAsUser { - set { + + /// + /// Set the run as user option. + /// + public string RunAsUser + { + set + { Assertions.NullCheck(value, "RunAsUser"); _options[OperationOptions.OP_RUN_AS_USER] = value; } } - /** - * Convenience method to set {@link OperationOptions#OP_SCOPE} - * @param scope The scope. May not be null. - * @return A this reference to allow chaining - */ - public string Scope { - set { + /// + /// Convenience method to set + /// + /// The scope. May not be null. + /// A this reference to allow chaining + public string Scope + { + set + { Assertions.NullCheck(value, "scope"); _options[OperationOptions.OP_SCOPE] = value; } } - - /** - * Convenience method to set {@link OperationOptions#OP_CONTAINER} - * @param container The container. May not be null. - * @return A this reference to allow chaining - */ - public QualifiedUid Container { - set { + + /// + /// Convenience method to set + /// + /// The container. May not be null. + /// A this reference to allow chaining + public QualifiedUid Container + { + set + { Assertions.NullCheck(value, "container"); _options[OperationOptions.OP_CONTAINER] = value; } @@ -2493,52 +2915,63 @@ public QualifiedUid Container { } #endregion - + #region OperationOptionInfo - public sealed class OperationOptionInfo { + public sealed class OperationOptionInfo + { private String _name; private Type _type; - + public OperationOptionInfo(String name, - Type type) { + Type type) + { Assertions.NullCheck(name, "name"); Assertions.NullCheck(type, "type"); FrameworkUtil.CheckOperationOptionType(type); _name = name; _type = type; } - - public String Name { - get { + + public String Name + { + get + { return _name; } } - - public Type OptionType { - get { + + public Type OptionType + { + get + { return _type; } } - - public override bool Equals(Object o) { - if (o is OperationOptionInfo) { + + public override bool Equals(Object o) + { + if (o is OperationOptionInfo) + { OperationOptionInfo other = (OperationOptionInfo)o; - if (!_name.Equals(other._name)) { + if (!_name.Equals(other._name)) + { return false; } - if (!_type.Equals(other._type)) { + if (!_type.Equals(other._type)) + { return false; } return true; } return false; } - - public override int GetHashCode() { + + public override int GetHashCode() + { return _name.GetHashCode(); } - + public override string ToString() { StringBuilder bld = new StringBuilder(); @@ -2548,199 +2981,245 @@ public override string ToString() bld.Append(')'); return bld.ToString(); } - + } #endregion - + #region OperationOptionInfoBuilder - public sealed class OperationOptionInfoBuilder { + public sealed class OperationOptionInfoBuilder + { private String _name; private Type _type; - - public OperationOptionInfoBuilder() { + + public OperationOptionInfoBuilder() + { } - + public OperationOptionInfoBuilder(String name, - Type type) { + Type type) + { _name = name; _type = type; } - - public String Name { - get { + + public String Name + { + get + { return _name; } - set { + set + { _name = value; } } - - public Type OptionType { - get { + + public Type OptionType + { + get + { return _type; } - set { + set + { _type = value; } } - - public OperationOptionInfo Build() { - return new OperationOptionInfo(_name,_type); + + public OperationOptionInfo Build() + { + return new OperationOptionInfo(_name, _type); } - - public static OperationOptionInfo Build(String name, Type type) { - return new OperationOptionInfoBuilder(name,type).Build(); + + public static OperationOptionInfo Build(String name, Type type) + { + return new OperationOptionInfoBuilder(name, type).Build(); } - - public static OperationOptionInfo Build(String name) { + + public static OperationOptionInfo Build(String name) + { return Build(name, typeof(string)); } - - public static OperationOptionInfo BuildAttributesToGet() { + + public static OperationOptionInfo BuildAttributesToGet() + { return Build(OperationOptions.OP_ATTRIBUTES_TO_GET, typeof(string[])); } - - public static OperationOptionInfo BuildRunWithPassword() { + + public static OperationOptionInfo BuildRunWithPassword() + { return Build(OperationOptions.OP_RUN_WITH_PASSWORD); } - - public static OperationOptionInfo BuildRunAsUser() { + + public static OperationOptionInfo BuildRunAsUser() + { return Build(OperationOptions.OP_RUN_AS_USER); } - public static OperationOptionInfo BuildScope() { + public static OperationOptionInfo BuildScope() + { return Build(OperationOptions.OP_SCOPE); } - - public static OperationOptionInfo BuildContainer() { - return Build(OperationOptions.OP_CONTAINER,typeof(QualifiedUid)); + + public static OperationOptionInfo BuildContainer() + { + return Build(OperationOptions.OP_CONTAINER, typeof(QualifiedUid)); } } #endregion #region QualifiedUid - /** - * A fully-qualified uid. That is, a pair of {@link ObjectClass} and - * {@link Uid}. - */ - public sealed class QualifiedUid { + /// + /// A fully-qualified uid. + /// + /// + /// That is, a pair of and + /// . + /// + public sealed class QualifiedUid + { private readonly ObjectClass _objectClass; private readonly Uid _uid; - - /** - * Create a QualifiedUid. - * @param objectClass The object class. May not be null. - * @param uid The uid. May not be null. - */ + + /// + /// Create a QualifiedUid. + /// + /// The object class. May not be null. + /// The uid. May not be null. public QualifiedUid(ObjectClass objectClass, - Uid uid) { - Assertions.NullCheck(objectClass,"objectClass"); - Assertions.NullCheck(uid,"uid"); + Uid uid) + { + Assertions.NullCheck(objectClass, "objectClass"); + Assertions.NullCheck(uid, "uid"); _objectClass = objectClass; _uid = uid; } - - /** - * Returns the object class. - * @return The object class. - */ - public ObjectClass ObjectClass { - get { + + /// + /// Returns the object class. + /// + /// The object class. + public ObjectClass ObjectClass + { + get + { return _objectClass; } } - - /** - * Returns the uid. - * @return The uid. - */ - public Uid Uid { - get { + + /// + /// Returns the uid. + /// + /// The uid. + public Uid Uid + { + get + { return _uid; } } - - /** - * Returns true iff o is a QualifiedUid and the object class and uid match. - */ - public override bool Equals(Object o) { - if ( o is QualifiedUid ) { + + /// + /// Returns true iff o is a QualifiedUid and the object class and uid match. + /// + public override bool Equals(Object o) + { + if (o is QualifiedUid) + { QualifiedUid other = (QualifiedUid)o; - return ( _objectClass.Equals(other._objectClass) && - _uid.Equals(other._uid) ); + return (_objectClass.Equals(other._objectClass) && + _uid.Equals(other._uid)); } return false; } - - /** - * Returns a hash code based on uid - */ - public override int GetHashCode() { + + /// + /// Returns a hash code based on uid + /// + public override int GetHashCode() + { return _uid.GetHashCode(); } - - /** - * Returns a string representation acceptible for debugging. - */ - public override String ToString() { + + /// + /// Returns a string representation acceptible for debugging. + /// + public override String ToString() + { return SerializerUtil.SerializeXmlObject(this, false); } - - } + + } #endregion - + #region ResultsHandler - /** - * Encapsulate the handling of each object returned by the search. - */ - public delegate bool ResultsHandler(ConnectorObject obj); + /// + /// Encapsulate the handling of each object returned by the search. + /// + public delegate bool ResultsHandler(ConnectorObject obj); #endregion - + #region Schema - /** - * Determines the objects supported by a - * {@link com.sun.openconnectors.framework.spi.Connector}. - * The {@link Schema} object is used to represent the basic objects that a - * connector supports. This does not prevent a connector from supporting more. - * Rather, this is informational for the caller of the connector to understand - * a minimum support level. - * The schema defines 4 primary data structures - *
    - *
  1. Declared ObjectClasses ({@link #getObjectClassInfo()}).
  2. - *
  3. Declared OperationOptionInfo ({@link #getOperationOptionInfo()}).
  4. - *
  5. Supported ObjectClasses by operation ({@link #getSupportedObjectClassesByOperation()}).
  6. - *
  7. Supported OperationOptionInfo by operation({@link #getSupportedOptionsByOperation()()}).
  8. - *
- * - * TODO: add more to describe and what is expected from this call and how it is - * used.. based on OperationalAttribute etc.. - */ - public sealed class Schema { + /// + /// Determines the objects supported by a + /// . + /// + /// + /// The object is used to represent the basic objects that a + /// connector supports. This does not prevent a connector from supporting more. + /// Rather, this is informational for the caller of the connector to understand + /// a minimum support level. + /// The schema defines 4 primary data structures + /// + /// + /// Declared ObjectClasses (). + /// + /// + /// + /// Declared OperationOptionInfo (). + /// + /// + /// + /// Supported ObjectClasses by operation (). + /// + /// + /// + /// Supported OperationOptionInfo by operation(). + /// + /// + /// + /// TODO: add more to describe and what is expected from this call and how it is + /// used.. based on OperationalAttribute etc.. + /// + public sealed class Schema + { private readonly ICollection _declaredObjectClasses; private readonly ICollection _declaredOperationOptions; - private readonly IDictionary,ICollection> - _supportedObjectClassesByOperation; - private readonly IDictionary,ICollection> - _supportedOptionsByOperation; - - /** - * Public only for serialization; please use - * SchemaBuilder instead. - * @param info - * @param supportedObjectClassesByOperation - */ + private readonly IDictionary, ICollection> + _supportedObjectClassesByOperation; + private readonly IDictionary, ICollection> + _supportedOptionsByOperation; + + /// + /// Public only for serialization; please use + /// SchemaBuilder instead. + /// + /// + /// public Schema(ICollection info, ICollection options, - IDictionary,ICollection> supportedObjectClassesByOperation, - IDictionary,ICollection> supportedOptionsByOperation) { + IDictionary, ICollection> supportedObjectClassesByOperation, + IDictionary, ICollection> supportedOptionsByOperation) + { _declaredObjectClasses = CollectionUtil.NewReadOnlySet(info); _declaredOperationOptions = CollectionUtil.NewReadOnlySet(options); //make read-only { - IDictionary,ICollection> temp = - new Dictionary,ICollection>(); - foreach (KeyValuePair,ICollection> entry in - supportedObjectClassesByOperation) { + IDictionary, ICollection> temp = + new Dictionary, ICollection>(); + foreach (KeyValuePair, ICollection> entry in + supportedObjectClassesByOperation) + { SafeType op = entry.Key; ICollection resolvedClasses = @@ -2751,10 +3230,11 @@ public Schema(ICollection info, } //make read-only { - IDictionary,ICollection> temp = - new Dictionary,ICollection>(); - foreach (KeyValuePair,ICollection> entry in - supportedOptionsByOperation) { + IDictionary, ICollection> temp = + new Dictionary, ICollection>(); + foreach (KeyValuePair, ICollection> entry in + supportedOptionsByOperation) + { SafeType op = entry.Key; ICollection resolvedClasses = @@ -2764,261 +3244,310 @@ public Schema(ICollection info, _supportedOptionsByOperation = CollectionUtil.AsReadOnlyDictionary(temp); } } - - /** - * Returns the set of object classes that are defined in the schema, regardless - * of which operations support them. - */ - public ICollection ObjectClassInfo { - get { + + /// + /// Returns the set of object classes that are defined in the schema, regardless + /// of which operations support them. + /// + public ICollection ObjectClassInfo + { + get + { return _declaredObjectClasses; } } - - /** - * Returns the ObjectClassInfo for the given type. - * @param type The type to find. - * @return the ObjectClassInfo for the given type or null if not found. - */ - public ObjectClassInfo FindObjectClassInfo(String type) { - foreach (ObjectClassInfo info in _declaredObjectClasses) { - if ( info.Is(type) ) { + + /// + /// Returns the ObjectClassInfo for the given type. + /// + /// The type to find. + /// the ObjectClassInfo for the given type or null if not found. + public ObjectClassInfo FindObjectClassInfo(String type) + { + foreach (ObjectClassInfo info in _declaredObjectClasses) + { + if (info.Is(type)) + { return info; } } return null; } - - /** - * Returns the set of operation options that are defined in the schema, regardless - * of which operations support them. - * @return The options defined in this schema. - */ - public ICollection OperationOptionInfo { - get { + + /// + /// Returns the set of operation options that are defined in the schema, regardless + /// of which operations support them. + /// + /// The options defined in this schema. + public ICollection OperationOptionInfo + { + get + { return _declaredOperationOptions; } } - - /** - * Returns the OperationOptionInfo for the given name. - * @param name The name to find. - * @return the OperationOptionInfo for the given name or null if not found. - */ - public OperationOptionInfo FindOperationOptionInfo(String name) { + + /// + /// Returns the OperationOptionInfo for the given name. + /// + /// The name to find. + /// the OperationOptionInfo for the given name or null if not found. + public OperationOptionInfo FindOperationOptionInfo(String name) + { Assertions.NullCheck(name, "name"); - foreach (OperationOptionInfo info in _declaredOperationOptions) { - if ( info.Name.Equals(name) ) { + foreach (OperationOptionInfo info in _declaredOperationOptions) + { + if (info.Name.Equals(name)) + { return info; } } return null; } - - /** - * Returns the supported object classes for the given operation. - * @param apiop The operation. - * @return the supported object classes for the given operation. - */ - public ICollection GetSupportedObjectClassesByOperation(SafeType apiop) { - ICollection rv = - CollectionUtil.GetValue(_supportedObjectClassesByOperation,apiop,null); - if ( rv == null ) { - ICollection empty = + + /// + /// Returns the supported object classes for the given operation. + /// + /// The operation. + /// the supported object classes for the given operation. + public ICollection GetSupportedObjectClassesByOperation(SafeType apiop) + { + ICollection rv = + CollectionUtil.GetValue(_supportedObjectClassesByOperation, apiop, null); + if (rv == null) + { + ICollection empty = CollectionUtil.NewReadOnlySet(); - + return empty; } - else { + else + { return rv; } } - - /** - * Returns the supported options for the given operation. - * @param apiop The operation. - * @return the supported options for the given operation. - */ - public ICollection GetSupportedOptionsByOperation(SafeType apiop) { - ICollection rv = - CollectionUtil.GetValue(_supportedOptionsByOperation,apiop,null); - if ( rv == null ) { - ICollection empty = + + /// + /// Returns the supported options for the given operation. + /// + /// The operation. + /// the supported options for the given operation. + public ICollection GetSupportedOptionsByOperation(SafeType apiop) + { + ICollection rv = + CollectionUtil.GetValue(_supportedOptionsByOperation, apiop, null); + if (rv == null) + { + ICollection empty = CollectionUtil.NewReadOnlySet(); return empty; } - else { + else + { return rv; } } - - /** - * Returns the set of object classes that apply to a particular operation. - * @return the set of object classes that apply to a particular operation. - */ - public IDictionary,ICollection> SupportedObjectClassesByOperation { - get { + + /// + /// Returns the set of object classes that apply to a particular operation. + /// + /// the set of object classes that apply to a particular operation. + public IDictionary, ICollection> SupportedObjectClassesByOperation + { + get + { return _supportedObjectClassesByOperation; } } - /** - * Returns the set of operation options that apply to a particular operation. - * @return the set of operation options that apply to a particular operation. - */ - public IDictionary,ICollection> SupportedOptionsByOperation { - get { + /// + /// Returns the set of operation options that apply to a particular operation. + /// + /// the set of operation options that apply to a particular operation. + public IDictionary, ICollection> SupportedOptionsByOperation + { + get + { return _supportedOptionsByOperation; } } - - - public override int GetHashCode() { + + + public override int GetHashCode() + { return CollectionUtil.GetHashCode(_declaredObjectClasses); } - - public override bool Equals(object o) { + + public override bool Equals(object o) + { Schema other = o as Schema; - if ( other != null ) { - if (!CollectionUtil.Equals(ObjectClassInfo,other.ObjectClassInfo)) { + if (other != null) + { + if (!CollectionUtil.Equals(ObjectClassInfo, other.ObjectClassInfo)) + { return false; } - if (!CollectionUtil.Equals(OperationOptionInfo,other.OperationOptionInfo)) { + if (!CollectionUtil.Equals(OperationOptionInfo, other.OperationOptionInfo)) + { return false; } if (!CollectionUtil.Equals(_supportedObjectClassesByOperation, - other._supportedObjectClassesByOperation)) { - return false; + other._supportedObjectClassesByOperation)) + { + return false; } if (!CollectionUtil.Equals(_supportedOptionsByOperation, - other._supportedOptionsByOperation)) { - return false; + other._supportedOptionsByOperation)) + { + return false; } return true; } return false; } - + public override string ToString() { return SerializerUtil.SerializeXmlObject(this, false); } } #endregion - + #region SchemaBuilder - /** - * Simple builder class to help facilitate creating a {@link Schema} object. - */ - public sealed class SchemaBuilder { + /// + /// Simple builder class to help facilitate creating a object. + /// + public sealed class SchemaBuilder + { private readonly SafeType _connectorClass; private readonly ICollection _declaredObjectClasses = new HashSet(); private readonly ICollection _declaredOperationOptions = new HashSet(); - - private readonly IDictionary,ICollection> - _supportedObjectClassesByOperation = - new Dictionary,ICollection>(); - private readonly IDictionary,ICollection> - _supportedOptionsByOperation = - new Dictionary,ICollection>(); - - - /** - * - */ - public SchemaBuilder(SafeType connectorClass) { + + private readonly IDictionary, ICollection> + _supportedObjectClassesByOperation = + new Dictionary, ICollection>(); + private readonly IDictionary, ICollection> + _supportedOptionsByOperation = + new Dictionary, ICollection>(); + + + /// + /// + public SchemaBuilder(SafeType connectorClass) + { Assertions.NullCheck(connectorClass, "connectorClass"); _connectorClass = connectorClass; } - - /** - * Adds another ObjectClassInfo to the schema. Also, adds this - * to the set of supported classes for every operation defined by - * the Connector. - * - * @param info - * @throws IllegalStateException If already defined - */ - public void DefineObjectClass(ObjectClassInfo info) { + + /// + /// Adds another ObjectClassInfo to the schema. + /// + /// + /// Also, adds this + /// to the set of supported classes for every operation defined by + /// the Connector. + /// + /// + /// If already defined + public void DefineObjectClass(ObjectClassInfo info) + { Assertions.NullCheck(info, "info"); - if (_declaredObjectClasses.Contains(info)) { - throw new InvalidOperationException("ObjectClass already defined: "+ + if (_declaredObjectClasses.Contains(info)) + { + throw new InvalidOperationException("ObjectClass already defined: " + info.ObjectType); } _declaredObjectClasses.Add(info); - foreach (SafeType op in - FrameworkUtil.GetDefaultSupportedOperations(_connectorClass)) { - ICollection oclasses = - CollectionUtil.GetValue(_supportedObjectClassesByOperation,op,null); - if (oclasses == null) { + foreach (SafeType op in + FrameworkUtil.GetDefaultSupportedOperations(_connectorClass)) + { + ICollection oclasses = + CollectionUtil.GetValue(_supportedObjectClassesByOperation, op, null); + if (oclasses == null) + { oclasses = new HashSet(); _supportedObjectClassesByOperation[op] = oclasses; } oclasses.Add(info); } } - /** - * Adds another OperationOptionInfo to the schema. Also, adds this - * to the set of supported options for every operation defined by - * the Connector. - */ - public void DefineOperationOption(OperationOptionInfo info) { + /// + /// Adds another OperationOptionInfo to the schema. + /// + /// + /// Also, adds this + /// to the set of supported options for every operation defined by + /// the Connector. + /// + public void DefineOperationOption(OperationOptionInfo info) + { Assertions.NullCheck(info, "info"); - if (_declaredOperationOptions.Contains(info)) { - throw new InvalidOperationException("OperationOption already defined: "+ + if (_declaredOperationOptions.Contains(info)) + { + throw new InvalidOperationException("OperationOption already defined: " + info.Name); } _declaredOperationOptions.Add(info); - foreach (SafeType op in - FrameworkUtil.GetDefaultSupportedOperations(_connectorClass)) { - ICollection oclasses = - CollectionUtil.GetValue(_supportedOptionsByOperation,op,null); - if (oclasses == null) { + foreach (SafeType op in + FrameworkUtil.GetDefaultSupportedOperations(_connectorClass)) + { + ICollection oclasses = + CollectionUtil.GetValue(_supportedOptionsByOperation, op, null); + if (oclasses == null) + { oclasses = new HashSet(); _supportedOptionsByOperation[op] = oclasses; } oclasses.Add(info); } } - - /** - * Adds another ObjectClassInfo to the schema. Also, adds this - * to the set of supported classes for every operation defined by - * the Connector. - * @throws IllegalStateException If already defined - */ - public void DefineObjectClass(String type, ICollection attrInfo) { + + /// + /// Adds another ObjectClassInfo to the schema. + /// + /// + /// Also, adds this + /// to the set of supported classes for every operation defined by + /// the Connector. + /// + /// If already defined + public void DefineObjectClass(String type, ICollection attrInfo) + { ObjectClassInfoBuilder bld = new ObjectClassInfoBuilder(); bld.ObjectType = type; bld.AddAllAttributeInfo(attrInfo); ObjectClassInfo obj = bld.Build(); DefineObjectClass(obj); } - - /** - * Adds another OperationOptionInfo to the schema. Also, adds this - * to the set of supported options for every operation defined by - * the Connector. - * @throws IllegalStateException If already defined - */ - public void DefineOperationOption(String optionName, Type type) { + + /// + /// Adds another OperationOptionInfo to the schema. + /// + /// + /// Also, adds this + /// to the set of supported options for every operation defined by + /// the Connector. + /// + /// If already defined + public void DefineOperationOption(String optionName, Type type) + { OperationOptionInfoBuilder bld = new OperationOptionInfoBuilder(); - bld.Name=(optionName); - bld.OptionType=(type); + bld.Name = (optionName); + bld.OptionType = (type); OperationOptionInfo info = bld.Build(); DefineOperationOption(info); } - - /** - * Adds the given ObjectClassInfo as a supported ObjectClass for - * the given operation. - * @param op The SPI operation - * @param def The ObjectClassInfo - * @throws IllegalArgumentException If the given ObjectClassInfo was - * not already defined using {@link #defineObjectClass(ObjectClassInfo)}. - */ + + /// + /// Adds the given ObjectClassInfo as a supported ObjectClass for + /// the given operation. + /// + /// The SPI operation + /// The ObjectClassInfo + /// If the given ObjectClassInfo was + /// not already defined using . public void AddSupportedObjectClass(SafeType op, ObjectClassInfo def) { @@ -3026,33 +3555,37 @@ public void AddSupportedObjectClass(SafeType op, Assertions.NullCheck(def, "def"); ICollection> apis = FrameworkUtil.Spi2Apis(op); - if (!_declaredObjectClasses.Contains(def)) { - throw new ArgumentException("ObjectClass "+def.ObjectType+ + if (!_declaredObjectClasses.Contains(def)) + { + throw new ArgumentException("ObjectClass " + def.ObjectType + " not defined in schema."); } - foreach (SafeType api in apis) { - ICollection infos = - CollectionUtil.GetValue(_supportedObjectClassesByOperation,api,null); - if ( infos == null ) { - throw new ArgumentException("Operation "+op+ - " not implement by connector."); + foreach (SafeType api in apis) + { + ICollection infos = + CollectionUtil.GetValue(_supportedObjectClassesByOperation, api, null); + if (infos == null) + { + throw new ArgumentException("Operation " + op + + " not implement by connector."); } - if ( infos.Contains(def)) { - throw new ArgumentException("ObjectClass "+def.ObjectType+ - " already supported for operation "+op); + if (infos.Contains(def)) + { + throw new ArgumentException("ObjectClass " + def.ObjectType + + " already supported for operation " + op); } infos.Add(def); } } - - /** - * Removes the given ObjectClassInfo as a supported ObjectClass for - * the given operation. - * @param op The SPI operation - * @param def The ObjectClassInfo - * @throws IllegalArgumentException If the given ObjectClassInfo was - * not already defined using {@link #defineObjectClass(ObjectClassInfo)}. - */ + + /// + /// Removes the given ObjectClassInfo as a supported ObjectClass for + /// the given operation. + /// + /// The SPI operation + /// The ObjectClassInfo + /// If the given ObjectClassInfo was + /// not already defined using . public void RemoveSupportedObjectClass(SafeType op, ObjectClassInfo def) { @@ -3060,129 +3593,152 @@ public void RemoveSupportedObjectClass(SafeType op, Assertions.NullCheck(def, "def"); ICollection> apis = FrameworkUtil.Spi2Apis(op); - if (!_declaredObjectClasses.Contains(def)) { - throw new ArgumentException("ObjectClass "+def.ObjectType+ + if (!_declaredObjectClasses.Contains(def)) + { + throw new ArgumentException("ObjectClass " + def.ObjectType + " not defined in schema."); } - foreach (SafeType api in apis) { - ICollection infos = - CollectionUtil.GetValue(_supportedObjectClassesByOperation,api,null); - if ( infos == null ) { - throw new ArgumentException("Operation "+op+ - " not implement by connector."); + foreach (SafeType api in apis) + { + ICollection infos = + CollectionUtil.GetValue(_supportedObjectClassesByOperation, api, null); + if (infos == null) + { + throw new ArgumentException("Operation " + op + + " not implement by connector."); } - if ( !infos.Contains(def)) { - throw new ArgumentException("ObjectClass "+def.ObjectType - +" already removed for operation "+op); + if (!infos.Contains(def)) + { + throw new ArgumentException("ObjectClass " + def.ObjectType + + " already removed for operation " + op); } infos.Remove(def); } } - /** - * Adds the given OperationOptionInfo as a supported option for - * the given operation. - * @param op The SPI operation - * @param def The OperationOptionInfo - * @throws IllegalArgumentException If the given OperationOptionInfo was - * not already defined using {@link #defineOperationOption(OperationOptionInfo)}. - */ + /// + /// Adds the given OperationOptionInfo as a supported option for + /// the given operation. + /// + /// The SPI operation + /// The OperationOptionInfo + /// If the given OperationOptionInfo was + /// not already defined using . public void AddSupportedOperationOption(SafeType op, - OperationOptionInfo def) { + OperationOptionInfo def) + { Assertions.NullCheck(op, "op"); Assertions.NullCheck(def, "def"); ICollection> apis = FrameworkUtil.Spi2Apis(op); - if (!_declaredOperationOptions.Contains(def)) { - throw new ArgumentException("OperationOption "+def.Name+ + if (!_declaredOperationOptions.Contains(def)) + { + throw new ArgumentException("OperationOption " + def.Name + " not defined in schema."); } - foreach (SafeType api in apis) { + foreach (SafeType api in apis) + { ICollection infos = - CollectionUtil.GetValue(_supportedOptionsByOperation,api,null); - if ( infos == null ) { - throw new ArgumentException("Operation "+op+ - " not implement by connector."); + CollectionUtil.GetValue(_supportedOptionsByOperation, api, null); + if (infos == null) + { + throw new ArgumentException("Operation " + op + + " not implement by connector."); } - if ( infos.Contains(def) ) { - throw new ArgumentException("OperationOption "+def.Name+ - " already supported for operation "+op); + if (infos.Contains(def)) + { + throw new ArgumentException("OperationOption " + def.Name + + " already supported for operation " + op); } infos.Add(def); } } - - /** - * Removes the given OperationOptionInfo as a supported option for - * the given operation. - * @param op The SPI operation - * @param def The OperationOptionInfo - * @throws IllegalArgumentException If the given OperationOptionInfo was - * not already defined using {@link #defineOperationOption(OperationOptionInfo)}. - */ + + /// + /// Removes the given OperationOptionInfo as a supported option for + /// the given operation. + /// + /// The SPI operation + /// The OperationOptionInfo + /// If the given OperationOptionInfo was + /// not already defined using . public void RemoveSupportedOperationOption(SafeType op, - OperationOptionInfo def) { + OperationOptionInfo def) + { Assertions.NullCheck(op, "op"); Assertions.NullCheck(def, "def"); ICollection> apis = FrameworkUtil.Spi2Apis(op); - if (!_declaredOperationOptions.Contains(def)) { - throw new ArgumentException("OperationOption "+def.Name+ + if (!_declaredOperationOptions.Contains(def)) + { + throw new ArgumentException("OperationOption " + def.Name + " not defined in schema."); } - foreach (SafeType api in apis) { - ICollection infos = - CollectionUtil.GetValue(_supportedOptionsByOperation,api,null); - if ( infos == null ) { - throw new ArgumentException("Operation "+op+ - " not implement by connector."); + foreach (SafeType api in apis) + { + ICollection infos = + CollectionUtil.GetValue(_supportedOptionsByOperation, api, null); + if (infos == null) + { + throw new ArgumentException("Operation " + op + + " not implement by connector."); } - if ( !infos.Contains(def) ) { - throw new ArgumentException("OperationOption "+def.Name+ - " already removed for operation "+op); + if (!infos.Contains(def)) + { + throw new ArgumentException("OperationOption " + def.Name + + " already removed for operation " + op); } infos.Remove(def); } } - - /** - * Clears the operation-specific supported classes. Normally, when - * you add an ObjectClass, using {@link #defineObjectClass(ObjectClassInfo)}, - * it is added to all operations. You may then remove those that you need - * using {@link #removeSupportedObjectClass(Class, ObjectClassInfo)}. You - * may wish, as an alternative to clear everything out and instead add using - * {@link #addSupportedObjectClass(Class, ObjectClassInfo)}. - */ - public void ClearSupportedObjectClassesByOperation() { - foreach (ICollection values in + + /// + /// Clears the operation-specific supported classes. + /// + /// + /// Normally, when + /// you add an ObjectClass, using , + /// it is added to all operations. You may then remove those that you need + /// using . You + /// may wish, as an alternative to clear everything out and instead add using + /// . + /// + public void ClearSupportedObjectClassesByOperation() + { + foreach (ICollection values in _supportedObjectClassesByOperation.Values) { values.Clear(); } } - /** - * Clears the operation-specific supported options. Normally, when - * you add an OperationOptionInfo, using {@link #defineOperationOption(OperationOptionInfo)(ObjectClassInfo)}, - * it is added to all operations. You may then remove those that you need - * using {@link #removeSupportedOperationOption(Class, OperationOptionInfo)}. You - * may wish, as an alternative to clear everything out and instead add using - * {@link #addSupportedOperationOption(Class, OperationOptionInfo)}. - */ - public void ClearSupportedOptionsByOperation() { - foreach (ICollection values in + /// + /// Clears the operation-specific supported options. + /// + /// + /// Normally, when + /// you add an OperationOptionInfo, using , + /// it is added to all operations. You may then remove those that you need + /// using . You + /// may wish, as an alternative to clear everything out and instead add using + /// . + /// + public void ClearSupportedOptionsByOperation() + { + foreach (ICollection values in _supportedOptionsByOperation.Values) { values.Clear(); } } - - /** - * Builds the {@link Schema} object based on the {@link ObjectClassInfo}s - * added so far. - * - * @return new Schema object based on the info provided. - */ - public Schema Build() { - if (_declaredObjectClasses.Count == 0) { + + /// + /// Builds the object based on the s + /// added so far. + /// + /// new Schema object based on the info provided. + public Schema Build() + { + if (_declaredObjectClasses.Count == 0) + { String ERR = "Must be at least one ObjectClassInfo object!"; throw new InvalidOperationException(ERR); } @@ -3193,66 +3749,75 @@ public Schema Build() { } } #endregion - + #region Script - /** - * Represents a script in a scripting language. - * - * @since 1.1 - */ - public sealed class Script { - + /// + /// Represents a script in a scripting language. + /// + /// 1.1 + public sealed class Script + { + private readonly string scriptLanguage; private readonly string scriptText; - - internal Script(string scriptLanguage, string scriptText) { + + internal Script(string scriptLanguage, string scriptText) + { Assertions.BlankCheck(scriptLanguage, "scriptLanguage"); Assertions.NullCheck(scriptText, "scriptText"); // Allow empty text. this.scriptLanguage = scriptLanguage; this.scriptText = scriptText; } - - /** - * Returns the language of this script. - * - * @return the script language; never null. - */ - public string ScriptLanguage { - get { + + /// + /// Returns the language of this script. + /// + /// the script language; never null. + public string ScriptLanguage + { + get + { return scriptLanguage; } } - - /** - * Returns the text of this script. - * - * @return the script text; never null. - */ - public string ScriptText { - get { + + /// + /// Returns the text of this script. + /// + /// the script text; never null. + public string ScriptText + { + get + { return scriptText; } } - - public override int GetHashCode() { + + public override int GetHashCode() + { return scriptLanguage.GetHashCode() ^ scriptText.GetHashCode(); } - - public override bool Equals(object obj) { - if (obj is Script) { - Script other = (Script) obj; - if (!scriptLanguage.Equals(other.scriptLanguage)) { + + public override bool Equals(object obj) + { + if (obj is Script) + { + Script other = (Script)obj; + if (!scriptLanguage.Equals(other.scriptLanguage)) + { return false; } - if (!scriptText.Equals(other.scriptText)) { + if (!scriptText.Equals(other.scriptText)) + { return false; } return true; } return false; } - - public override string ToString() { + + public override string ToString() + { // Text can be large, probably should not be included. return "Script: " + scriptLanguage; } @@ -3260,114 +3825,133 @@ public override string ToString() { #endregion #region ScriptBuilder - /** - * Builder for {@link Script}. - */ - public class ScriptBuilder { - - /** - * Creates a new ScriptBuilder. - */ - public ScriptBuilder() { - } - - /** - * Gets/sets the language of the script. - */ - public string ScriptLanguage { - get; set; - } - - /** - * Gets/sets the text of the script. - */ - public string ScriptText { - get; set; - } - - /** - * Creates a Script. Prior to calling this method the language - * and the text should have been set. - * - * @return a new script; never null. - */ - public Script Build() { + /// + /// Builder for . + /// + public class ScriptBuilder + { + /// + /// Creates a new ScriptBuilder. + /// + public ScriptBuilder() + { + } + + /// + /// Gets/sets the language of the script. + /// + public string ScriptLanguage + { + get; + set; + } + + /// + /// Gets/sets the text of the script. + /// + public string ScriptText + { + get; + set; + } + + /// + /// Creates a Script. + /// + /// + /// Prior to calling this method the language + /// and the text should have been set. + /// + /// a new script; never null. + public Script Build() + { return new Script(ScriptLanguage, ScriptText); } } #endregion #region ScriptContext - /** - * Encapsulates a script and all of its parameters. - * @see com.sun.openconnectors.framework.api.operations.ScriptOnResourceApiOp - * @see com.sun.openconnectors.framework.api.operations.ScriptOnConnectorApiOp - */ - public sealed class ScriptContext { - + /// + /// Encapsulates a script and all of its parameters. + /// + /// + /// + public sealed class ScriptContext + { private readonly String _scriptLanguage; private readonly String _scriptText; - private readonly IDictionary _scriptArguments; - - /** - * Public only for serialization; please use {@link ScriptContextBuilder}. - * @param scriptLanguage The script language. Must not be null. - * @param scriptText The script text. Must not be null. - * @param scriptArguments The script arguments. May be null. - */ + private readonly IDictionary _scriptArguments; + + /// + /// Public only for serialization; please use . + /// + /// The script language. Must not be null. + /// The script text. Must not be null. + /// The script arguments. May be null. public ScriptContext(String scriptLanguage, String scriptText, - IDictionary scriptArguments) { - - if (scriptLanguage == null) { + IDictionary scriptArguments) + { + + if (scriptLanguage == null) + { throw new ArgumentException("Argument 'scriptLanguage' must be specified"); } - if (scriptText == null) { + if (scriptText == null) + { throw new ArgumentException("Argument 'scriptText' must be specified"); } //clone script arguments and options - this serves two purposes //1)makes sure everthing is serializable //2)does a deep copy - IDictionary scriptArgumentsClone = (IDictionary)SerializerUtil.CloneObject(scriptArguments); + IDictionary scriptArgumentsClone = (IDictionary)SerializerUtil.CloneObject(scriptArguments); _scriptLanguage = scriptLanguage; _scriptText = scriptText; - _scriptArguments = CollectionUtil.NewReadOnlyDictionary(scriptArgumentsClone); - } - - /** - * Identifies the language in which the script is written - * (e.g., bash, csh, - * Perl4 or Python). - * @return The script language. - */ - public String ScriptLanguage { - get { + _scriptArguments = CollectionUtil.NewReadOnlyDictionary(scriptArgumentsClone); + } + + /// + /// Identifies the language in which the script is written + /// (e.g., bash, csh, + /// Perl4 or Python). + /// + /// The script language. + public String ScriptLanguage + { + get + { return _scriptLanguage; } } - - /** - * Returns the text (i.e., actual characters) of the script. - * @return The text of the script. - */ - public String ScriptText { - get { + + /// + /// Returns the text (i.e., actual characters) of the script. + /// + /// The text of the script. + public String ScriptText + { + get + { return _scriptText; } } - - /** - * Returns a map of arguments to be passed to the script. - * Values must be types that the framework can serialize. - * See {@link ObjectSerializerFactory} for a list of supported types. - * @return A map of arguments to be passed to the script. - */ - public IDictionary ScriptArguments { - get { + + /// + /// Returns a map of arguments to be passed to the script. + /// + /// + /// Values must be types that the framework can serialize. + /// See for a list of supported types. + /// + /// A map of arguments to be passed to the script. + public IDictionary ScriptArguments + { + get + { return _scriptArguments; } } - + public override string ToString() { StringBuilder bld = new StringBuilder(); @@ -3380,78 +3964,89 @@ public override string ToString() bld.Append(map.ToString()); return bld.ToString(); } - + } #endregion - + #region ScriptContextBuilder - /** - * Builds an {@link ScriptContext}. - */ - public sealed class ScriptContextBuilder { + /// + /// Builds an . + /// + public sealed class ScriptContextBuilder + { private String _scriptLanguage; private String _scriptText; - private readonly IDictionary _scriptArguments = new - Dictionary(); - - /** - * Creates an empty builder. - */ - public ScriptContextBuilder() { - - } - - /** - * Creates a builder with the required parameters specified. - * @param scriptLanguage a string that identifies the language - * in which the script is written - * (e.g., bash, csh, - * Perl4 or Python). - * @param scriptText The text (i.e., actual characters) of the script. - */ + private readonly IDictionary _scriptArguments = new + Dictionary(); + + /// + /// Creates an empty builder. + /// + public ScriptContextBuilder() + { + + } + + /// + /// Creates a builder with the required parameters specified. + /// + /// a string that identifies the language + /// in which the script is written + /// (e.g., bash, csh, + /// Perl4 or Python). + /// The text (i.e., actual characters) of the script. public ScriptContextBuilder(String scriptLanguage, - String scriptText) { + String scriptText) + { _scriptLanguage = scriptLanguage; _scriptText = scriptText; } - - /** - * Identifies the language in which the script is written - * (e.g., bash, csh, - * Perl4 or Python). - * @return The script language. - */ - public String ScriptLanguage { - get { + + /// + /// Identifies the language in which the script is written + /// (e.g., bash, csh, + /// Perl4 or Python). + /// + /// The script language. + public String ScriptLanguage + { + get + { return _scriptLanguage; } - set { + set + { _scriptLanguage = value; } } - - /** - * Returns the actual characters of the script. - * @return the actual characters of the script. - */ - public String ScriptText { - get { + + /// + /// Returns the actual characters of the script. + /// + /// the actual characters of the script. + public String ScriptText + { + get + { return _scriptText; } - set { + set + { _scriptText = value; } } - - /** - * Adds or sets an argument to pass to the script. - * @param name The name of the argument. Must not be null. - * @param value The value of the argument. Must be one of - * type types that the framework can serialize. - * @see ObjectSerializerFactory for a list of supported types. - */ - public ScriptContextBuilder AddScriptArgument(String name, Object value) { - if ( name == null ) { + + /// + /// Adds or sets an argument to pass to the script. + /// + /// The name of the argument. Must not be null. + /// The value of the argument. Must be one of + /// type types that the framework can serialize. + /// + public ScriptContextBuilder AddScriptArgument(String name, Object value) + { + if (name == null) + { throw new ArgumentException("Argument 'name' cannot be null."); } //don't validate value here - we do that implicitly when @@ -3459,167 +4054,188 @@ public ScriptContextBuilder AddScriptArgument(String name, Object value) { _scriptArguments[name] = value; return this; } - - /** - * Removes the given script argument. - * @param name The name of the argument. Must not be null. - */ - public ScriptContextBuilder RemoveScriptArgument(String name) { - if ( name == null ) { + + /// + /// Removes the given script argument. + /// + /// The name of the argument. Must not be null. + public ScriptContextBuilder RemoveScriptArgument(String name) + { + if (name == null) + { throw new ArgumentException("Argument 'name' cannot be null."); } _scriptArguments.Remove(name); return this; } - - /** - * Returns a mutable reference of the script arguments map. - * @return A mutable reference of the script arguments map. - */ - public IDictionary ScriptArguments { - get { + + /// + /// Returns a mutable reference of the script arguments map. + /// + /// A mutable reference of the script arguments map. + public IDictionary ScriptArguments + { + get + { //might as well be mutable since it's the builder and //we don't want to deep copy anyway return _scriptArguments; } } - - /** - * Creates a ScriptContext. - * The scriptLanguage and scriptText - * must be set prior to calling this. - * @return The ScriptContext. - */ - public ScriptContext Build() { + + /// + /// Creates a ScriptContext. + /// + /// + /// The scriptLanguage and scriptText + /// must be set prior to calling this. + /// + /// The ScriptContext. + public ScriptContext Build() + { return new ScriptContext(_scriptLanguage, _scriptText, _scriptArguments); } } #endregion - + #region SyncDelta - /** - * Represents a change to an object in a resource. - * - * @see SyncApiOp - * @see SyncOp - */ - public sealed class SyncDelta { + /// + /// Represents a change to an object in a resource. + /// + /// + /// + public sealed class SyncDelta + { private readonly SyncToken _token; private readonly SyncDeltaType _deltaType; private readonly Uid _previousUid; private readonly Uid _uid; private readonly ConnectorObject _object; - - /** - * Creates a SyncDelata - * @param token - * The token. Must not be null. - * @param deltaType - * The delta. Must not be null. - * @param uid - * The uid. Must not be null. - * @param object - * The object that has changed. May be null for delete. - */ + + /// + /// Creates a SyncDelata + /// + /// The token. Must not be null. + /// The delta. Must not be null. + /// The uid. Must not be null. + /// The object that has changed. May be null for delete. internal SyncDelta(SyncToken token, SyncDeltaType deltaType, Uid previousUid, Uid uid, - ConnectorObject obj) { + ConnectorObject obj) + { Assertions.NullCheck(token, "token"); Assertions.NullCheck(deltaType, "deltaType"); Assertions.NullCheck(uid, "uid"); - + //do not allow previous Uid for anything else than create or update - if ( previousUid != null && deltaType != SyncDeltaType.CREATE_OR_UPDATE) { + if (previousUid != null && deltaType != SyncDeltaType.CREATE_OR_UPDATE) + { throw new ArgumentException("The previous Uid can only be specified for create or update."); } //only allow null object for delete - if ( obj == null && - deltaType != SyncDeltaType.DELETE) { + if (obj == null && + deltaType != SyncDeltaType.DELETE) + { throw new ArgumentException("ConnectorObject must be specified for anything other than delete."); } - + //if object not null, make sure its Uid //matches - if ( obj != null ) { - if (!uid.Equals(obj.Uid)) { - throw new ArgumentException("Uid does not match that of the object."); + if (obj != null) + { + if (!uid.Equals(obj.Uid)) + { + throw new ArgumentException("Uid does not match that of the object."); } } - + _token = token; _deltaType = deltaType; - _previousUid = previousUid; - _uid = uid; + _previousUid = previousUid; + _uid = uid; _object = obj; - + } - /** - * If the change described by this SyncDelta modified the - * object's Uid, this method returns the Uid before the change. Not - * all resources can determine the previous Uid, so this method can - * return null. - * @return the previous Uid or null if it could not be determined - * or the change did not modify the Uid. - */ + /// + /// If the change described by this SyncDelta modified the + /// object's Uid, this method returns the Uid before the change. + /// + /// + /// Not + /// all resources can determine the previous Uid, so this method can + /// return null. + /// + /// the previous Uid or null if it could not be determined + /// or the change did not modify the Uid. public Uid PreviousUid { - get { + get + { return _previousUid; } } - - /** - * Returns the Uid of the object that changed. - * - * @return the Uid of the object that changed. - */ - public Uid Uid { - get { + + /// + /// Returns the Uid of the object that changed. + /// + /// the Uid of the object that changed. + public Uid Uid + { + get + { return _uid; } } - - /** - * Returns the connector object that changed. This - * may be null in the case of delete. - * @return The object or possibly null if this - * represents a delete. - */ - public ConnectorObject Object { - get { + + /// + /// Returns the connector object that changed. + /// + /// + /// This + /// may be null in the case of delete. + /// + /// The object or possibly null if this + /// represents a delete. + public ConnectorObject Object + { + get + { return _object; } } - - /** - * Returns the SyncToken of the object that changed. - * - * @return the SyncToken of the object that changed. - */ - public SyncToken Token { - get { + + /// + /// Returns the SyncToken of the object that changed. + /// + /// the SyncToken of the object that changed. + public SyncToken Token + { + get + { return _token; } } - - /** - * Returns the type of the change the occured. - * - * @return The type of change that occured. - */ - public SyncDeltaType DeltaType { - get { + + /// + /// Returns the type of the change the occured. + /// + /// The type of change that occured. + public SyncDeltaType DeltaType + { + get + { return _deltaType; } } - - - public override String ToString() { - IDictionary values = new Dictionary(); + + + public override String ToString() + { + IDictionary values = new Dictionary(); values["Token"] = _token; values["DeltaType"] = _deltaType; values["PreviousUid"] = _previousUid; @@ -3627,37 +4243,49 @@ public override String ToString() { values["Object"] = _object; return values.ToString(); } - - public override int GetHashCode() { + + public override int GetHashCode() + { return _uid.GetHashCode(); } - - public override bool Equals(Object o) { - if ( o is SyncDelta ) { + + public override bool Equals(Object o) + { + if (o is SyncDelta) + { SyncDelta other = (SyncDelta)o; - if (!_token.Equals(other._token)) { + if (!_token.Equals(other._token)) + { return false; } - if (!_deltaType.Equals(other._deltaType)) { + if (!_deltaType.Equals(other._deltaType)) + { return false; } - if (_previousUid == null) { - if ( other._previousUid != null ) { + if (_previousUid == null) + { + if (other._previousUid != null) + { return false; } } - else if (!_previousUid.Equals(other._previousUid)) { + else if (!_previousUid.Equals(other._previousUid)) + { return false; } - if (!_uid.Equals(other._uid)) { + if (!_uid.Equals(other._uid)) + { return false; } - if (_object == null) { - if ( other._object != null ) { + if (_object == null) + { + if (other._object != null) + { return false; } } - else if (!_object.Equals(other._object)) { + else if (!_object.Equals(other._object)) + { return false; } return true; @@ -3666,505 +4294,556 @@ public override bool Equals(Object o) { } } #endregion - + #region SyncDeltaBuilder - /** - * Builder for {@link SyncDelta}. - */ - public sealed class SyncDeltaBuilder { + /// + /// Builder for . + /// + public sealed class SyncDeltaBuilder + { private SyncToken _token; private SyncDeltaType _deltaType; private Uid _previousUid; private Uid _uid; private ConnectorObject _object; - - /** - * Create a new SyncDeltaBuilder - */ - public SyncDeltaBuilder() { - - } - - /** - * Creates a new SyncDeltaBuilder whose - * values are initialized to those of the delta. - * @param delta The original delta. - */ - public SyncDeltaBuilder(SyncDelta delta) { + + /// + /// Create a new SyncDeltaBuilder + /// + public SyncDeltaBuilder() + { + + } + + /// + /// Creates a new SyncDeltaBuilder whose + /// values are initialized to those of the delta. + /// + /// The original delta. + public SyncDeltaBuilder(SyncDelta delta) + { _token = delta.Token; _deltaType = delta.DeltaType; _previousUid = delta.PreviousUid; _uid = delta.Uid; _object = delta.Object; } - - /** - * Returns the SyncToken of the object that changed. - * - * @return the SyncToken of the object that changed. - */ - public SyncToken Token { - get { + + /// + /// Returns the SyncToken of the object that changed. + /// + /// the SyncToken of the object that changed. + public SyncToken Token + { + get + { return _token; } - set { + set + { _token = value; } } - - /** - * Returns the type of the change that occurred. - * - * @return The type of change that occurred. - */ - public SyncDeltaType DeltaType { - get { + + /// + /// Returns the type of the change that occurred. + /// + /// The type of change that occurred. + public SyncDeltaType DeltaType + { + get + { return _deltaType; } - set { + set + { _deltaType = value; } } - /** - * Returns the Uid before the change. - * - * @return the Uid before the change. - */ - public Uid PreviousUid { - get { + /// + /// Returns the Uid before the change. + /// + /// the Uid before the change. + public Uid PreviousUid + { + get + { return _previousUid; } - set { + set + { _previousUid = value; } } - - /** - * Returns the Uid of the object that changed. - * Note that this is implicitly set when you call - * {@link #setObject(ConnectorObject)}. - * - * @return the Uid of the object that changed. - */ - public Uid Uid { - get { + + /// + /// Returns the Uid of the object that changed. + /// + /// + /// Note that this is implicitly set when you call + /// . + /// + /// the Uid of the object that changed. + public Uid Uid + { + get + { return _uid; } - set { + set + { _uid = value; } } - - /** - * Returns the object that changed. - * Sets the object that changed and implicitly - * sets Uid if object is not null. - * @return The object that changed. May be null for - * deletes. - */ - public ConnectorObject Object { - get { + + /// + /// Returns the object that changed. + /// + /// + /// Sets the object that changed and implicitly + /// sets Uid if object is not null. + /// + /// The object that changed. May be null for + /// deletes. + public ConnectorObject Object + { + get + { return _object; } - set { + set + { _object = value; - if ( value != null ) { + if (value != null) + { _uid = value.Uid; - } - } - } - - /** - * Creates a SyncDelta. Prior to calling the following must be specified: - *
    - *
  1. {@link #setObject(ConnectorObject) Object} (for anything other than delete)
  2. - *
  3. {@link #setUid(Uid) Uid} (this is implictly set when calling {@link #setObject(ConnectorObject)})
  4. - *
  5. {@link #setToken(SyncToken) Token}
  6. - *
  7. {@link #setDeltaType(SyncDeltaType) DeltaType}
  8. - *
- */ - public SyncDelta Build() { + } + } + } + + /// + /// Creates a SyncDelta. + /// + /// + /// Prior to calling the following must be specified: + /// + /// + /// (for anything other than delete) + /// + /// + /// + /// (this is implictly set when calling ) + /// + /// + /// + /// + /// + /// + /// + /// + /// + /// + /// + /// + public SyncDelta Build() + { return new SyncDelta(_token, _deltaType, _previousUid, _uid, _object); } } #endregion - + #region SyncDeltaType - /** - * The type of change. - */ - public enum SyncDeltaType { - /** - * The change represents either a create or an update in - * the resource. These are combined into a single value because: - *
    - *
  1. Many resources will not be able to distinguish a create from an update. - * Those that have an audit log will be able to. However, many implementations - * will only have the current record and a modification timestamp.
  2. - *
  3. Regardless of whether or not the resource can distinguish the two cases, - * the application needs to distinguish.
  4. - *
- */ - CREATE_OR_UPDATE, - - /** - * The change represents a DELETE in the resource - */ + /// + /// The type of change. + /// + public enum SyncDeltaType + { + /// + /// The change represents either a create or an update in + /// the resource. + /// + /// + /// These are combined into a single value because: + /// + /// + /// Many resources will not be able to distinguish a create from an update. + /// Those that have an audit log will be able to. However, many implementations + /// will only have the current record and a modification timestamp. + /// + /// + /// + /// Regardless of whether or not the resource can distinguish the two cases, + /// the application needs to distinguish. + /// + /// + /// + /// + CREATE_OR_UPDATE, + + /// + /// The change represents a DELETE in the resource + /// DELETE } #endregion #region SyncResultsHandler - /** - * Called to handle a delta in the stream. Will be called multiple times, - * once for each result. Although a callback, this is still invoked - * synchronously. That is, it is guaranteed that following a call to - * {@link SyncApiOp#sync(ObjectClass, SyncToken, SyncResultsHandler)} no - * more invocations to {@link #handle(SyncDelta)} will be performed. - * - * @param delta - * The change - * @return True iff the application wants to continue processing more - * results. - * @throws RuntimeException - * If the application encounters an exception. This will stop - * the interation and the exception will be propogated back to - * the application. - */ + /// + /// Called to handle a delta in the stream. + /// + /// + /// Will be called multiple times, + /// once for each result. Although a callback, this is still invoked + /// synchronously. That is, it is guaranteed that following a call to + /// no + /// more invocations to will be performed. + /// + /// The change + /// True iff the application wants to continue processing more + /// results. + /// If the application encounters an exception. This will stop + /// the interation and the exception will be propogated back to + /// the application. public delegate bool SyncResultsHandler(SyncDelta delta); #endregion - + #region SyncToken - /** - * Abstract "place-holder" for synchronization. The application must not make - * any attempt to interpret the value of the token. From the standpoint of the - * application the token is merely a black-box. The application may only persist - * the value of the token for use on subsequent synchronization requests. - *

- * What this token represents is entirely connector-specific. On some connectors - * this might be a last-modified value. On others, it might be a unique ID of a - * log table entry. On others such as JMS, this might be a dummy value since - * JMS itself keeps track of the state of the sync. - */ - public sealed class SyncToken { - + ///

+ /// Abstract "place-holder" for synchronization. + /// + /// + /// The application must not make + /// any attempt to interpret the value of the token. From the standpoint of the + /// application the token is merely a black-box. The application may only persist + /// the value of the token for use on subsequent synchronization requests. + /// + /// What this token represents is entirely connector-specific. On some connectors + /// this might be a last-modified value. On others, it might be a unique ID of a + /// log table entry. On others such as JMS, this might be a dummy value since + /// JMS itself keeps track of the state of the sync. + /// + /// + public sealed class SyncToken + { + private Object _value; - - /** - * Creates a new - * - * @param value - * May not be null. TODO: define set of allowed value types - * (currently same as set of allowed attribute values). - */ - public SyncToken(Object value) { + + /// + /// Creates a new + /// + /// May not be null. TODO: define set of allowed value types + /// (currently same as set of allowed attribute values). + public SyncToken(Object value) + { Assertions.NullCheck(value, "value"); FrameworkUtil.CheckAttributeValue(value); _value = value; } - - /** - * Returns the value for the token. - * - * @return The value for the token. - */ - public Object Value { - get { + + /// + /// Returns the value for the token. + /// + /// The value for the token. + public Object Value + { + get + { return _value; } } - - public override String ToString() { + + public override String ToString() + { return "SyncToken: " + _value.ToString(); } - - public override int GetHashCode() { + + public override int GetHashCode() + { return CollectionUtil.GetHashCode(_value); } - - public override bool Equals(Object o) { - if ( o is SyncToken ) { + + public override bool Equals(Object o) + { + if (o is SyncToken) + { SyncToken other = (SyncToken)o; return CollectionUtil.Equals(_value, other._value); } return false; } - - + + } #endregion - + #region Uid - public sealed class Uid : ConnectorAttribute { - + public sealed class Uid : ConnectorAttribute + { + public static readonly string NAME = ConnectorAttributeUtil.CreateSpecialName("UID"); - - public Uid(String val) : base(NAME, CollectionUtil.NewReadOnlyList(Check(val))) { + + public Uid(String val) + : base(NAME, CollectionUtil.NewReadOnlyList(Check(val))) + { } - private static String Check(String value) { - if (StringUtil.IsBlank(value)) { + private static String Check(String value) + { + if (StringUtil.IsBlank(value)) + { String ERR = "Uid value must not be blank!"; throw new ArgumentException(ERR); } return value; } - /** - * The single value of the attribute that is the unique id of an object. - * - * @return value that identifies an object. - */ - public String GetUidValue() { + /// + /// The single value of the attribute that is the unique id of an object. + /// + /// value that identifies an object. + public String GetUidValue() + { return ConnectorAttributeUtil.GetStringValue(this); } } #endregion #region ConnectorAttributesAccessor - /** - * Attributes Accessor convenience methods for accessing attributes. - * - * This class wraps a set of attributes to make lookup faster than the - * {@link AttributeUtil#find(String, Set)} method, since that method must - * re-create the map each time. - * - * @author Warren Strange - */ - public class ConnectorAttributesAccessor { - - ICollection _attrs; - IDictionary _attrMap; - - public ConnectorAttributesAccessor(ICollection attrs) { - _attrs = attrs; - _attrMap = ConnectorAttributeUtil.ToMap(attrs); - } - - /** - * Find the named attribute - * - * @param name - - * the attribute name to search for - * @return the Attribute, or null if not found. - */ - public ConnectorAttribute Find(String name) { - return CollectionUtil.GetValue(_attrMap, name, null); - } - - /** - * Get the {@link Name} attribute from the set of attributes. - * - * @return the {@link Name} attribute in the set. - */ - public Name GetName() { - return (Name) Find(Name.NAME); - } - - /** - * Return the enabled status of the account. - * - * If the ENABLE operational attribute is present, it's value takes - * precedence over the current value. If it is missing, the currentlyEnabled - * status is returned instead. - * - * @param dflt - * the default state if enable is not found. - * @return true if the account is enabled, false otherwise - */ - public bool GetEnabled(bool dflt) { - bool e = dflt; - ConnectorAttribute enable = Find(OperationalAttributes.ENABLE_NAME); - if (enable != null) { - e = ConnectorAttributeUtil.GetBooleanValue(enable).Value; - } - return e; - } - - /** - * Get the password as a GuardeString - * - * @return the password as a guarded String - */ - public GuardedString GetPassword() { - ConnectorAttribute a = Find(OperationalAttributes.PASSWORD_NAME); - return a == null ? null : ConnectorAttributeUtil.GetGuardedStringValue(a); - } - - /** - * Return a list of attributes - * - * @param name - - * name of attribute to search for. - * - * @return The List (generic object) iff it exists otherwise null. - */ - public IList FindList(String name) { - ConnectorAttribute a = Find(name); - return (a == null) ? null : a.Value; - } - - /** - * Return the multivalued attribute as a list of strings. This will throw a - * ClassCastException if the underlying attribute list is not of type - * String. - * - * @param name - * the name of the attribute to search for - * @return a List of String values for the attribute - */ - public IList FindStringList(String name) { - IList l = FindList(name); - if (l != null) { - IList ret = new List(l.Count); - foreach (object o in l) { - ret.Add((String) o); - } - return ret; - } - return null; - } - - /** - * Determines if the set as the attribute specified. - * - * @param name - * attribute name - * @return true if the named attribute exists, false otherwise - */ - public bool HasAttribute(String name) { - return Find(name) != null; - } - - /** - * Get the string value from the specified (single-valued) attribute. - * - * @param name - * Attribute from which to retrieve the long value. - * @return null if the value is null otherwise the long value for the - * attribute. - * @throws ClassCastException - * iff the object in the attribute is not an long. - * @throws IllegalArgumentException - * iff the attribute is a multi-valued (rather than - * single-valued). - */ - public String FindString(String name) { - ConnectorAttribute a = Find(name); - return a == null ? null : ConnectorAttributeUtil.GetStringValue(a); - } - - /** - * Get the integer value from the specified (single-valued) attribute. - * - * @param name - * Attribute from which to retrieve the long value. - * @return null if the value is null otherwise the long value for the - * attribute. - * @throws ClassCastException - * iff the object in the attribute is not an long. - * @throws IllegalArgumentException - * iff the attribute is a multi-valued (rather than - * single-valued). - */ - public int? FindInteger(String name) { - ConnectorAttribute a = Find(name); - return (a == null) ? null : ConnectorAttributeUtil.GetIntegerValue(a); - } - - /** - * Get the long value from the specified (single-valued) attribute. - * - * @param name - * Attribute from which to retrieve the long value. - * @return null if the value is null otherwise the long value for the - * attribute. - * @throws ClassCastException - * iff the object in the attribute is not an long. - * @throws IllegalArgumentException - * iff the attribute is a multi-valued (rather than - * single-valued). - */ - public long? FindLong(String name) { - ConnectorAttribute a = Find(name); - return a == null ? null : ConnectorAttributeUtil.GetLongValue(a); - } - - /** - * Get the date value from the specified (single-valued) attribute that - * contains a long. - * - * @param name - * Attribute from which to retrieve the date value. - * @return null if the value is null otherwise the date value for the - * attribute. - * @throws ClassCastException - * iff the object in the attribute is not an long. - * @throws IllegalArgumentException - * iff the attribute is a multi-valued (rather than - * single-valued). - */ - public DateTime? FindDateTime(String name) { - ConnectorAttribute a = Find(name); - return a == null ? null : ConnectorAttributeUtil.GetDateTimeValue(a); - } - - /** - * Get the integer value from the specified (single-valued) attribute. - * - * @param name - * Attribute from which to retrieve the integer value. - * @return null if the value is null otherwise the integer value for the - * attribute. - * @throws ClassCastException - * iff the object in the attribute is not an integer. - * @throws IllegalArgumentException - * iff the attribute is a multi-valued (rather than - * single-valued).. - */ - public double? FindDouble(String name) { - ConnectorAttribute a = Find(name); - return a == null ? null : ConnectorAttributeUtil.GetDoubleValue(a); - } - - /** - * Get the boolean value from the specified (single-valued) attribute. - * - * @param name - * Attribute from which to retrieve the boolean value. - * @return null if the value is null otherwise the boolean value for the - * attribute. - * @throws ClassCastException - * iff the object in the attribute is not an {@link Boolean}. - * @throws IllegalArgumentException - * iff the attribute is a multi-valued (rather than - * single-valued). - */ - public bool? FindBoolean(String name) { - ConnectorAttribute a = Find(name); - return a == null ? null : ConnectorAttributeUtil.GetBooleanValue(a); - } - } - #endregion - - #region CultureInfoCache - internal static class CultureInfoCache { - - private static readonly object LOCK = new Object(); - private static CultureInfo _instance; - - public static CultureInfo Instance { - get { - lock (LOCK) { - if (_instance == null) { - _instance = CultureInfo.CurrentCulture; - if (_instance == null) { - _instance = CultureInfo.InstalledUICulture; - } - } - return _instance; - } - } - } - } - #endregion -} + /// + /// Attributes Accessor convenience methods for accessing attributes. + /// + /// + /// This class wraps a set of attributes to make lookup faster than the + /// method, since that method must + /// re-create the map each time. + /// + /// Warren Strange + public class ConnectorAttributesAccessor + { + + ICollection _attrs; + IDictionary _attrMap; + + public ConnectorAttributesAccessor(ICollection attrs) + { + _attrs = attrs; + _attrMap = ConnectorAttributeUtil.ToMap(attrs); + } + + /// + /// Find the named attribute + /// + /// - + /// the attribute name to search for + /// the Attribute, or null if not found. + public ConnectorAttribute Find(String name) + { + return CollectionUtil.GetValue(_attrMap, name, null); + } + + /// + /// Get the attribute from the set of attributes. + /// + /// the attribute in the set. + public Name GetName() + { + return (Name)Find(Name.NAME); + } + + /// + /// Return the enabled status of the account. + /// + /// + /// If the ENABLE operational attribute is present, it's value takes + /// precedence over the current value. If it is missing, the currentlyEnabled + /// status is returned instead. + /// + /// the default state if enable is not found. + /// true if the account is enabled, false otherwise + public bool GetEnabled(bool dflt) + { + bool e = dflt; + ConnectorAttribute enable = Find(OperationalAttributes.ENABLE_NAME); + if (enable != null) + { + e = ConnectorAttributeUtil.GetBooleanValue(enable).Value; + } + return e; + } + + /// + /// Get the password as a GuardeString + /// + /// the password as a guarded String + public GuardedString GetPassword() + { + ConnectorAttribute a = Find(OperationalAttributes.PASSWORD_NAME); + return a == null ? null : ConnectorAttributeUtil.GetGuardedStringValue(a); + } + + /// + /// Return a list of attributes + /// + /// - + /// name of attribute to search for. + /// The List (generic object) iff it exists otherwise null. + public IList FindList(String name) + { + ConnectorAttribute a = Find(name); + return (a == null) ? null : a.Value; + } + + /// + /// Return the multivalued attribute as a list of strings. + /// + /// + /// This will throw a + /// ClassCastException if the underlying attribute list is not of type + /// String. + /// + /// the name of the attribute to search for + /// a List of String values for the attribute + public IList FindStringList(String name) + { + IList l = FindList(name); + if (l != null) + { + IList ret = new List(l.Count); + foreach (object o in l) + { + ret.Add((String)o); + } + return ret; + } + return null; + } + + /// + /// Determines if the set as the attribute specified. + /// + /// attribute name + /// true if the named attribute exists, false otherwise + public bool HasAttribute(String name) + { + return Find(name) != null; + } + + /// + /// Get the string value from the specified (single-valued) attribute. + /// + /// Attribute from which to retrieve the long value. + /// null if the value is null otherwise the long value for the + /// attribute. + /// iff the object in the attribute is not an long. + /// iff the attribute is a multi-valued (rather than + /// single-valued). + public String FindString(String name) + { + ConnectorAttribute a = Find(name); + return a == null ? null : ConnectorAttributeUtil.GetStringValue(a); + } + + /// + /// Get the integer value from the specified (single-valued) attribute. + /// + /// Attribute from which to retrieve the long value. + /// null if the value is null otherwise the long value for the + /// attribute. + /// iff the object in the attribute is not an long. + /// iff the attribute is a multi-valued (rather than + /// single-valued). + public int? FindInteger(String name) + { + ConnectorAttribute a = Find(name); + return (a == null) ? null : ConnectorAttributeUtil.GetIntegerValue(a); + } + + /// + /// Get the long value from the specified (single-valued) attribute. + /// + /// Attribute from which to retrieve the long value. + /// null if the value is null otherwise the long value for the + /// attribute. + /// iff the object in the attribute is not an long. + /// iff the attribute is a multi-valued (rather than + /// single-valued). + public long? FindLong(String name) + { + ConnectorAttribute a = Find(name); + return a == null ? null : ConnectorAttributeUtil.GetLongValue(a); + } + + /// + /// Get the date value from the specified (single-valued) attribute that + /// contains a long. + /// + /// Attribute from which to retrieve the date value. + /// null if the value is null otherwise the date value for the + /// attribute. + /// iff the object in the attribute is not an long. + /// iff the attribute is a multi-valued (rather than + /// single-valued). + public DateTime? FindDateTime(String name) + { + ConnectorAttribute a = Find(name); + return a == null ? null : ConnectorAttributeUtil.GetDateTimeValue(a); + } + + /// + /// Get the integer value from the specified (single-valued) attribute. + /// + /// Attribute from which to retrieve the integer value. + /// null if the value is null otherwise the integer value for the + /// attribute. + /// iff the object in the attribute is not an integer. + /// iff the attribute is a multi-valued (rather than + /// single-valued).. + public double? FindDouble(String name) + { + ConnectorAttribute a = Find(name); + return a == null ? null : ConnectorAttributeUtil.GetDoubleValue(a); + } + + /// + /// Get the boolean value from the specified (single-valued) attribute. + /// + /// Attribute from which to retrieve the boolean value. + /// null if the value is null otherwise the boolean value for the + /// attribute. + /// iff the object in the attribute is not an . + /// iff the attribute is a multi-valued (rather than + /// single-valued). + public bool? FindBoolean(String name) + { + ConnectorAttribute a = Find(name); + return a == null ? null : ConnectorAttributeUtil.GetBooleanValue(a); + } + } + #endregion + + #region CultureInfoCache + internal static class CultureInfoCache + { + private static readonly object LOCK = new Object(); + private static CultureInfo _instance; + + public static CultureInfo Instance + { + get + { + lock (LOCK) + { + if (_instance == null) + { + _instance = CultureInfo.CurrentCulture; + if (_instance == null) + { + _instance = CultureInfo.InstalledUICulture; + } + } + return _instance; + } + } + } + } + #endregion +} \ No newline at end of file diff --git a/Framework/CommonObjectsFilter.cs b/Framework/CommonObjectsFilter.cs index 3e53c8bc..380102d9 100644 --- a/Framework/CommonObjectsFilter.cs +++ b/Framework/CommonObjectsFilter.cs @@ -28,228 +28,295 @@ namespace Org.IdentityConnectors.Framework.Common.Objects.Filters { #region AbstractFilterTranslator - /** - * Base class to make it easier to implement Search. - * A search filter may contain operators (such as 'contains' or 'in') - * or may contain logical operators (such as 'AND', 'OR' or 'NOT') - * that a connector cannot implement using the native API - * of the target system or application. - * A connector developer should subclass AbstractFilterTranslator - * in order to declare which filter operations the connector does support. - * This allows the FilterTranslator instance to analyze - * a specified search filter and reduce the filter to its most efficient form. - * The default (and worst-case) behavior is to return a null expression, - * which means that the connector should return "everything" - * (that is, should return all values for every requested attribute) - * and rely on the common code in the framework to perform filtering. - * This "fallback" behavior is good (in that it ensures consistency - * of search behavior across connector implementations) but it is - * obviously better for performance and scalability if each connector - * performs as much filtering as the native API of the target can support. - *

- * A subclass should override each of the following methods where possible: - *

    - *
  1. {@link #createAndExpression}
  2. - *
  3. {@link #createOrExpression}
  4. - *
  5. {@link #createContainsExpression(ContainsFilter, boolean)}
  6. - *
  7. {@link #createEndsWithExpression(EndsWithFilter, boolean)}
  8. - *
  9. {@link #createEqualsExpression(EqualsFilter, boolean)}
  10. - *
  11. {@link #createGreaterThanExpression(GreaterThanFilter, boolean)}
  12. - *
  13. {@link #createGreaterThanOrEqualExpression(GreaterThanOrEqualFilter, boolean)}
  14. - *
  15. {@link #createStartsWithExpression(StartsWithFilter, boolean)}
  16. - *
- *

- * Translation can then be performed using {@link #translate(Filter)}. - *

- * @param The result type of the translator. Commonly this will - * be a string, but there are cases where you might need to return - * a more complex data structure. For example if you are building a SQL - * query, you will need not *just* the base WHERE clause but a list - * of tables that need to be joined together. - */ + ///

+ /// Base class to make it easier to implement Search. + /// + /// + /// A search filter may contain operators (such as 'contains' or 'in') + /// or may contain logical operators (such as 'AND', 'OR' or 'NOT') + /// that a connector cannot implement using the native API + /// of the target system or application. + /// A connector developer should subclass AbstractFilterTranslator + /// in order to declare which filter operations the connector does support. + /// This allows the FilterTranslator instance to analyze + /// a specified search filter and reduce the filter to its most efficient form. + /// The default (and worst-case) behavior is to return a null expression, + /// which means that the connector should return "everything" + /// (that is, should return all values for every requested attribute) + /// and rely on the common code in the framework to perform filtering. + /// This "fallback" behavior is good (in that it ensures consistency + /// of search behavior across connector implementations) but it is + /// obviously better for performance and scalability if each connector + /// performs as much filtering as the native API of the target can support. + /// + /// A subclass should override each of the following methods where possible: + /// + /// + /// + /// + /// + /// + /// + /// + /// + /// + /// + /// + /// + /// + /// + /// + /// + /// + /// + /// + /// + /// + /// + /// + /// + /// + /// + /// + /// + /// + /// + /// + /// + /// + /// + /// + /// Translation can then be performed using . + /// + /// + /// + /// + /// The result type of the translator. Commonly this will + /// be a string, but there are cases where you might need to return + /// a more complex data structure. For example if you are building a SQL + /// query, you will need not *just* the base WHERE clause but a list + /// of tables that need to be joined together. abstract public class AbstractFilterTranslator : FilterTranslator where T : class - { - - /** - * Main method to be called to translate a filter - * @param filter The filter to translate. - * @return The list of queries to be performed. The list - * size() may be one of the following: - *
    - *
  1. 0 - This - * signifies fetch everything. This may occur if your filter - * was null or one of your create* methods returned null.
  2. - *
  3. 1 - List contains a single query that will return the results from the filter. - * Note that the results may be a superset of those specified by - * the filter in the case that one of your create* methods returned null. - * That is OK from a behavior standpoint since ConnectorFacade performs - * a second level of filtering. However it is undesirable from a performance standpoint.
  4. - *
  5. >1 - List contains multiple queries that must be performed in order to - * meet the filter that was passed in. Note that this only occurs if your - * {@link #createOrExpression} method can return null. If this happens, it - * is the responsibility of the connector implementor to perform each query - * and combine the results. In order to eliminate duplicates, the connector - * implementation must keep an in-memory HashSet of those UID - * that have been visited thus far. This will not scale well if your - * result sets are large. Therefore it is recommended that if - * at all possible you implement {@link #createOrExpression}
  6. - *
- */ - public IList Translate(Filter filter) { - if ( filter == null ) { + { + /// + /// Main method to be called to translate a filter + /// + /// The filter to translate. + /// The list of queries to be performed. The list + /// size() may be one of the following: + /// + /// + /// 0 - This + /// signifies fetch everything. This may occur if your filter + /// was null or one of your create* methods returned null. + /// + /// + /// + /// 1 - List contains a single query that will return the results from the filter. + /// Note that the results may be a superset of those specified by + /// the filter in the case that one of your create* methods returned null. + /// That is OK from a behavior standpoint since ConnectorFacade performs + /// a second level of filtering. However it is undesirable from a performance standpoint. + /// + /// + /// + /// >1 - List contains multiple queries that must be performed in order to + /// meet the filter that was passed in. Note that this only occurs if your + /// method can return null. If this happens, it + /// is the responsibility of the connector implementor to perform each query + /// and combine the results. In order to eliminate duplicates, the connector + /// implementation must keep an in-memory HashSet of those UID + /// that have been visited thus far. This will not scale well if your + /// result sets are large. Therefore it is recommended that if + /// at all possible you implement + /// + /// + /// + public IList Translate(Filter filter) + { + if (filter == null) + { return new List(); } //this must come first filter = NormalizeNot(filter); filter = SimplifyAndDistribute(filter); //might have simplified it to the everything filter - if ( filter == null ) { + if (filter == null) + { return new List(); } IList result = TranslateInternal(filter); //now "optimize" - we can eliminate exact matches at least HashSet set = new HashSet(); IList optimized = new List(result.Count); - foreach (T obj in result) { - if ( set.Add(obj) ) { + foreach (T obj in result) + { + if (set.Add(obj)) + { optimized.Add(obj); } } return optimized; } - - /** - * Pushes Not's so that they are just before the leaves of the tree - */ - private Filter NormalizeNot(Filter filter) { - if ( filter is AndFilter ) { + + /// + /// Pushes Not's so that they are just before the leaves of the tree + /// + private Filter NormalizeNot(Filter filter) + { + if (filter is AndFilter) + { AndFilter af = (AndFilter)filter; return new AndFilter(NormalizeNot(af.Left), NormalizeNot(af.Right)); } - else if ( filter is OrFilter ) { + else if (filter is OrFilter) + { OrFilter of = (OrFilter)filter; return new OrFilter(NormalizeNot(of.Left), NormalizeNot(of.Right)); } - else if ( filter is NotFilter ) { + else if (filter is NotFilter) + { NotFilter nf = (NotFilter)filter; return Negate(NormalizeNot(nf.Filter)); } - else { + else + { return filter; - } + } } - - /** - * Given a filter, create a filter representing its negative. - * This is used by normalizeNot. - */ + + /// + /// Given a filter, create a filter representing its negative. + /// + /// + /// This is used by normalizeNot. + /// private Filter Negate(Filter filter) { - if ( filter is AndFilter ) { + if (filter is AndFilter) + { AndFilter af = (AndFilter)filter; return new OrFilter(Negate(af.Left), Negate(af.Right)); } - else if ( filter is OrFilter ) { + else if (filter is OrFilter) + { OrFilter of = (OrFilter)filter; return new AndFilter(Negate(of.Left), Negate(of.Right)); } - else if ( filter is NotFilter ) { + else if (filter is NotFilter) + { NotFilter nf = (NotFilter)filter; return nf.Filter; } - else { + else + { return new NotFilter(filter); } } - - /** - * Simultaneously prunes those portions of the - * filter than cannot be implemented and distributes - * Ands over Ors where needed if the resource does not - * implement Or. - * - * @param filter Nots must already be normalized - * @return a simplified filter or null to represent the - * "everything" filter. - */ - private Filter SimplifyAndDistribute(Filter filter) { - if ( filter is AndFilter ) { + + /// + /// Simultaneously prunes those portions of the + /// filter than cannot be implemented and distributes + /// Ands over Ors where needed if the resource does not + /// implement Or. + /// + /// Nots must already be normalized + /// a simplified filter or null to represent the + /// "everything" filter. + private Filter SimplifyAndDistribute(Filter filter) + { + if (filter is AndFilter) + { AndFilter af = (AndFilter)filter; Filter simplifiedLeft = SimplifyAndDistribute(af.Left); Filter simplifiedRight = SimplifyAndDistribute(af.Right); - if ( simplifiedLeft == null ) { + if (simplifiedLeft == null) + { //left is "everything" - just return the right return simplifiedRight; } - else if ( simplifiedRight == null ) { + else if (simplifiedRight == null) + { //right is "everything" - just return the left return simplifiedLeft; } - else { + else + { //simulate translation of the left and right //to see where we end up IList leftExprs = TranslateInternal(simplifiedLeft); IList rightExprs = TranslateInternal(simplifiedRight); - if (leftExprs.Count == 0) { + if (leftExprs.Count == 0) + { //This can happen only when one of the create* methods //is inconsistent from one invocation to the next //(simplifiedLeft should have been null //in the previous 'if' above). - throw new InvalidOperationException("Translation method is inconsistent: "+leftExprs); + throw new InvalidOperationException("Translation method is inconsistent: " + leftExprs); } - if (rightExprs.Count == 0) { + if (rightExprs.Count == 0) + { //This can happen only when one of the create* methods //is inconsistent from one invocation to the next //(simplifiedRight should have been null - //in the previous 'if' above). - throw new InvalidOperationException("Translation method is inconsistent: "+rightExprs); + //in the previous 'if' above). + throw new InvalidOperationException("Translation method is inconsistent: " + rightExprs); } - + //Simulate ANDing each pair(left,right). //If all of them return null (i.e., "everything"), //then the request cannot be filtered. bool anyAndsPossible = false; - foreach ( T leftExpr in leftExprs ) { - foreach ( T rightExpr in rightExprs ) { + foreach (T leftExpr in leftExprs) + { + foreach (T rightExpr in rightExprs) + { T test = CreateAndExpression( leftExpr, rightExpr); - if ( test != null ) { + if (test != null) + { anyAndsPossible = true; break; } } - if ( anyAndsPossible ) { + if (anyAndsPossible) + { break; } } - + //If no AND filtering is possible, //return whichever of left or right //contains the fewest expressions. - if (!anyAndsPossible) { - if ( leftExprs.Count <= rightExprs.Count ) { + if (!anyAndsPossible) + { + if (leftExprs.Count <= rightExprs.Count) + { return simplifiedLeft; } - else { + else + { return simplifiedRight; } } - + //Since AND filtering is possible for at least //one expression, let's distribute. - if ( leftExprs.Count > 1 ) { + if (leftExprs.Count > 1) + { //The left can contain more than one expression - //only if the left-hand side is an unimplemented OR. + //only if the left-hand side is an unimplemented OR. //Distribute our AND to the left. OrFilter left = (OrFilter)simplifiedLeft; OrFilter newFilter = @@ -259,9 +326,10 @@ private Filter SimplifyAndDistribute(Filter filter) { simplifiedRight)); return SimplifyAndDistribute(newFilter); } - else if ( rightExprs.Count > 1 ) { + else if (rightExprs.Count > 1) + { //The right can contain more than one expression - //only if the right-hand side is an unimplemented OR. + //only if the right-hand side is an unimplemented OR. //Distribute our AND to the right. OrFilter right = (OrFilter)simplifiedRight; OrFilter newFilter = @@ -269,21 +337,24 @@ private Filter SimplifyAndDistribute(Filter filter) { right.Left), new AndFilter(simplifiedLeft, right.Right)); - return SimplifyAndDistribute(newFilter); + return SimplifyAndDistribute(newFilter); } - else { + else + { //Each side contains exactly one expression - //and the translator does implement AND + //and the translator does implement AND //(anyAndsPossible must be true //for them to have hit this branch). - if (!anyAndsPossible) { + if (!anyAndsPossible) + { throw new Exception("expected anyAndsPossible"); } - return new AndFilter(simplifiedLeft,simplifiedRight); + return new AndFilter(simplifiedLeft, simplifiedRight); } } } - else if ( filter is OrFilter ) { + else if (filter is OrFilter) + { OrFilter of = (OrFilter)filter; Filter simplifiedLeft = SimplifyAndDistribute(of.Left); @@ -291,360 +362,407 @@ private Filter SimplifyAndDistribute(Filter filter) { SimplifyAndDistribute(of.Right); //If either left or right reduces to "everything", //then simplify the OR to "everything". - if ( simplifiedLeft == null || simplifiedRight == null ) { + if (simplifiedLeft == null || simplifiedRight == null) + { return null; } //otherwise return new OrFilter(simplifiedLeft, simplifiedRight); } - else { + else + { //Otherwise, it's a NOT(LEAF) or a LEAF. //Simulate creating it. T expr = CreateLeafExpression(filter); - if ( expr == null ) { + if (expr == null) + { //If the expression cannot be implemented, - //return the "everything" filter. + //return the "everything" filter. return null; } - else { + else + { //Otherwise, return the filter. return filter; } } } - - /** - * Translates the filter into a list of expressions. - * The filter must have already been transformed - * using normalizeNot followed by a simplifyAndDistribute. - * @param filter A filter (normalized, simplified, and distibuted) - * @return A list of expressions or empty list for everything. - */ - private IList TranslateInternal(Filter filter) { - if ( filter is AndFilter ) { + + /// + /// Translates the filter into a list of expressions. + /// + /// + /// The filter must have already been transformed + /// using normalizeNot followed by a simplifyAndDistribute. + /// + /// A filter (normalized, simplified, and distibuted) + /// A list of expressions or empty list for everything. + private IList TranslateInternal(Filter filter) + { + if (filter is AndFilter) + { T result = TranslateAnd((AndFilter)filter); IList rv = new List(); - if ( result != null ) { + if (result != null) + { rv.Add(result); } return rv; } - else if ( filter is OrFilter ) { + else if (filter is OrFilter) + { return TranslateOr((OrFilter)filter); } - else { + else + { //otherwise it's either a leaf or a NOT (leaf) T expr = CreateLeafExpression(filter); IList exprs = new List(); - if ( expr != null ) { + if (expr != null) + { exprs.Add(expr); } return exprs; } } - - private T TranslateAnd( AndFilter filter ) { + + private T TranslateAnd(AndFilter filter) + { IList leftExprs = TranslateInternal(filter.Left); IList rightExprs = TranslateInternal(filter.Right); - if ( leftExprs.Count != 1 ) { + if (leftExprs.Count != 1) + { //this can happen only if one of the create* methods //is inconsistent from one invocation to the next //(at this point we've already been simplified and //distributed). - throw new InvalidOperationException("Translation method is inconsistent: "+leftExprs); + throw new InvalidOperationException("Translation method is inconsistent: " + leftExprs); } - if ( rightExprs.Count != 1 ) { + if (rightExprs.Count != 1) + { //this can happen only if one of the create* methods //is inconsistent from one invocation to the next //(at this point we've already been simplified and //distributed). - throw new InvalidOperationException("Translation method is inconsistent: "+rightExprs); + throw new InvalidOperationException("Translation method is inconsistent: " + rightExprs); } T rv = CreateAndExpression(leftExprs[0], rightExprs[0]); - if ( rv == null ) { + if (rv == null) + { //This could happen only if we're inconsistent //(since the simplify logic already should have removed //any expression that cannot be filtered). - throw new InvalidOperationException("createAndExpression is inconsistent"); + throw new InvalidOperationException("createAndExpression is inconsistent"); } return rv; } - - private IList TranslateOr( OrFilter filter ) { + + private IList TranslateOr(OrFilter filter) + { IList leftExprs = TranslateInternal(filter.Left); IList rightExprs = TranslateInternal(filter.Right); - if ( leftExprs.Count == 0 ) { + if (leftExprs.Count == 0) + { //This can happen only if one of the create* methods //is inconsistent from one invocation to the next. - throw new InvalidOperationException("Translation method is inconsistent"); + throw new InvalidOperationException("Translation method is inconsistent"); } - if ( rightExprs.Count == 0 ) { + if (rightExprs.Count == 0) + { //This can happen only if one of the create* methods //methods is inconsistent from on invocation to the next. throw new InvalidOperationException("Translation method is inconsistent"); } - if ( leftExprs.Count == 1 && rightExprs.Count == 1 ) { + if (leftExprs.Count == 1 && rightExprs.Count == 1) + { //If each side contains exactly one expression, - //try to create a combined expression. - T val = CreateOrExpression(leftExprs[0], rightExprs[0]); - if ( val != null ) { + //try to create a combined expression. + T val = CreateOrExpression(leftExprs[0], rightExprs[0]); + if (val != null) + { IList rv = new List(); rv.Add(val); return rv; } //Otherwise, fall through } - + //Return a list of queries from the left and from the right - IList rv2 = new List(leftExprs.Count+rightExprs.Count); - CollectionUtil.AddAll(rv2,leftExprs); - CollectionUtil.AddAll(rv2,rightExprs); + IList rv2 = new List(leftExprs.Count + rightExprs.Count); + CollectionUtil.AddAll(rv2, leftExprs); + CollectionUtil.AddAll(rv2, rightExprs); return rv2; } - - /** - * Creates an expression for a LEAF or a NOT(leaf) - * @param filter Must be either a leaf or a NOT(leaf) - * @return The expression - */ - private T CreateLeafExpression(Filter filter) { + + /// + /// Creates an expression for a LEAF or a NOT(leaf) + /// + /// Must be either a leaf or a NOT(leaf) + /// The expression + private T CreateLeafExpression(Filter filter) + { Filter leafFilter; bool not; - if ( filter is NotFilter ) { + if (filter is NotFilter) + { NotFilter nf = (NotFilter)filter; leafFilter = nf.Filter; not = true; } - else { + else + { leafFilter = filter; not = false; } - T expr = CreateLeafExpression(leafFilter,not); + T expr = CreateLeafExpression(leafFilter, not); return expr; } - - /** - * Creates a Leaf expression - * @param filter Must be a leaf expression - * @param not Is ! to be applied to the leaf expression - * @return The expression or null (for everything) - */ - private T CreateLeafExpression(Filter filter, bool not) { - if ( filter is ContainsFilter ) { + + /// + /// Creates a Leaf expression + /// + /// Must be a leaf expression + /// Is ! to be applied to the leaf expression + /// The expression or null (for everything) + private T CreateLeafExpression(Filter filter, bool not) + { + if (filter is ContainsFilter) + { return CreateContainsExpression((ContainsFilter)filter, not); } - else if (filter is EndsWithFilter) { - return CreateEndsWithExpression((EndsWithFilter)filter,not); + else if (filter is EndsWithFilter) + { + return CreateEndsWithExpression((EndsWithFilter)filter, not); } - else if ( filter is EqualsFilter ) { + else if (filter is EqualsFilter) + { return CreateEqualsExpression((EqualsFilter)filter, not); } - else if ( filter is GreaterThanFilter ) { + else if (filter is GreaterThanFilter) + { return CreateGreaterThanExpression((GreaterThanFilter)filter, not); } - else if ( filter is GreaterThanOrEqualFilter ) { + else if (filter is GreaterThanOrEqualFilter) + { return CreateGreaterThanOrEqualExpression((GreaterThanOrEqualFilter)filter, not); } - else if ( filter is LessThanFilter ) { + else if (filter is LessThanFilter) + { return CreateLessThanExpression((LessThanFilter)filter, not); } - else if ( filter is LessThanOrEqualFilter ) { + else if (filter is LessThanOrEqualFilter) + { return CreateLessThanOrEqualExpression((LessThanOrEqualFilter)filter, not); } - else if (filter is StartsWithFilter) { - return CreateStartsWithExpression((StartsWithFilter)filter,not); + else if (filter is StartsWithFilter) + { + return CreateStartsWithExpression((StartsWithFilter)filter, not); } - else if (filter is ContainsAllValuesFilter) { + else if (filter is ContainsAllValuesFilter) + { return CreateContainsAllValuesExpression((ContainsAllValuesFilter)filter, not); } - else { + else + { //unrecognized expression - nothing we can do return null; } } - - /** - * Should be overridden by subclasses to create an AND expression - * if the native resource supports AND. - * @param leftExpression The left expression. Will never be null. - * @param rightExpression The right expression. Will never be null. - * @return The AND expression. A return value of null means - * a native AND query cannot be created for the given expressions. - * In this case, the resulting query will consist of the - * leftExpression only. - */ - protected virtual T CreateAndExpression(T leftExpression, T rightExpression) { + + /// + /// Should be overridden by subclasses to create an AND expression + /// if the native resource supports AND. + /// + /// The left expression. Will never be null. + /// The right expression. Will never be null. + /// The AND expression. A return value of null means + /// a native AND query cannot be created for the given expressions. + /// In this case, the resulting query will consist of the + /// leftExpression only. + protected virtual T CreateAndExpression(T leftExpression, T rightExpression) + { return null; } - - /** - * Should be overridden by subclasses to create an OR expression - * if the native resource supports OR. - * @param leftExpression The left expression. Will never be null. - * @param rightExpression The right expression. Will never be null. - * @return The OR expression. A return value of null means - * a native OR query cannot be created for the given expressions. - * In this case, {@link #translate} may return multiple queries, each - * of which must be run and results combined. - */ - protected virtual T CreateOrExpression(T leftExpression, T rightExpression) { + + /// + /// Should be overridden by subclasses to create an OR expression + /// if the native resource supports OR. + /// + /// The left expression. Will never be null. + /// The right expression. Will never be null. + /// The OR expression. A return value of null means + /// a native OR query cannot be created for the given expressions. + /// In this case, may return multiple queries, each + /// of which must be run and results combined. + protected virtual T CreateOrExpression(T leftExpression, T rightExpression) + { return null; } - - /** - * Should be overridden by subclasses to create a CONTAINS expression - * if the native resource supports CONTAINS. - * @param filter The contains filter. Will never be null. - * @param not True if this should be a NOT CONTAINS - * @return The CONTAINS expression. A return value of null means - * a native CONTAINS query cannot be created for the given filter. - * In this case, {@link #translate} may return an empty query set, meaning - * fetch everything. The filter will be re-applied in memory - * to the resulting object stream. This does not scale well, so - * if possible, you should implement this method. - */ - protected virtual T CreateContainsExpression(ContainsFilter filter, bool not) { + + /// + /// Should be overridden by subclasses to create a CONTAINS expression + /// if the native resource supports CONTAINS. + /// + /// The contains filter. Will never be null. + /// True if this should be a NOT CONTAINS + /// The CONTAINS expression. A return value of null means + /// a native CONTAINS query cannot be created for the given filter. + /// In this case, may return an empty query set, meaning + /// fetch everything. The filter will be re-applied in memory + /// to the resulting object stream. This does not scale well, so + /// if possible, you should implement this method. + protected virtual T CreateContainsExpression(ContainsFilter filter, bool not) + { return null; } - - /** - * Should be overridden by subclasses to create a ENDS-WITH expression - * if the native resource supports ENDS-WITH. - * @param filter The contains filter. Will never be null. - * @param not True if this should be a NOT ENDS-WITH - * @return The ENDS-WITH expression. A return value of null means - * a native ENDS-WITH query cannot be created for the given filter. - * In this case, {@link #translate} may return an empty query set, meaning - * fetch everything. The filter will be re-applied in memory - * to the resulting object stream. This does not scale well, so - * if possible, you should implement this method. - */ - protected virtual T CreateEndsWithExpression(EndsWithFilter filter, bool not) { + + /// + /// Should be overridden by subclasses to create a ENDS-WITH expression + /// if the native resource supports ENDS-WITH. + /// + /// The contains filter. Will never be null. + /// True if this should be a NOT ENDS-WITH + /// The ENDS-WITH expression. A return value of null means + /// a native ENDS-WITH query cannot be created for the given filter. + /// In this case, may return an empty query set, meaning + /// fetch everything. The filter will be re-applied in memory + /// to the resulting object stream. This does not scale well, so + /// if possible, you should implement this method. + protected virtual T CreateEndsWithExpression(EndsWithFilter filter, bool not) + { return null; } - - /** - * Should be overridden by subclasses to create a EQUALS expression - * if the native resource supports EQUALS. - * @param filter The contains filter. Will never be null. - * @param not True if this should be a NOT EQUALS - * @return The EQUALS expression. A return value of null means - * a native EQUALS query cannot be created for the given filter. - * In this case, {@link #translate} may return an empty query set, meaning - * fetch everything. The filter will be re-applied in memory - * to the resulting object stream. This does not scale well, so - * if possible, you should implement this method. - */ - protected virtual T CreateEqualsExpression(EqualsFilter filter, bool not) { + + /// + /// Should be overridden by subclasses to create a EQUALS expression + /// if the native resource supports EQUALS. + /// + /// The contains filter. Will never be null. + /// True if this should be a NOT EQUALS + /// The EQUALS expression. A return value of null means + /// a native EQUALS query cannot be created for the given filter. + /// In this case, may return an empty query set, meaning + /// fetch everything. The filter will be re-applied in memory + /// to the resulting object stream. This does not scale well, so + /// if possible, you should implement this method. + protected virtual T CreateEqualsExpression(EqualsFilter filter, bool not) + { return null; } - - /** - * Should be overridden by subclasses to create a GREATER-THAN expression - * if the native resource supports GREATER-THAN. - * @param filter The contains filter. Will never be null. - * @param not True if this should be a NOT GREATER-THAN - * @return The GREATER-THAN expression. A return value of null means - * a native GREATER-THAN query cannot be created for the given filter. - * In this case, {@link #translate} may return an empty query set, meaning - * fetch everything. The filter will be re-applied in memory - * to the resulting object stream. This does not scale well, so - * if possible, you should implement this method. - */ - protected virtual T CreateGreaterThanExpression(GreaterThanFilter filter, bool not) { + + /// + /// Should be overridden by subclasses to create a GREATER-THAN expression + /// if the native resource supports GREATER-THAN. + /// + /// The contains filter. Will never be null. + /// True if this should be a NOT GREATER-THAN + /// The GREATER-THAN expression. A return value of null means + /// a native GREATER-THAN query cannot be created for the given filter. + /// In this case, may return an empty query set, meaning + /// fetch everything. The filter will be re-applied in memory + /// to the resulting object stream. This does not scale well, so + /// if possible, you should implement this method. + protected virtual T CreateGreaterThanExpression(GreaterThanFilter filter, bool not) + { return null; } - - /** - * Should be overridden by subclasses to create a GREATER-THAN-EQUAL expression - * if the native resource supports GREATER-THAN-EQUAL. - * @param filter The contains filter. Will never be null. - * @param not True if this should be a NOT GREATER-THAN-EQUAL - * @return The GREATER-THAN-EQUAL expression. A return value of null means - * a native GREATER-THAN-EQUAL query cannot be created for the given filter. - * In this case, {@link #translate} may return an empty query set, meaning - * fetch everything. The filter will be re-applied in memory - * to the resulting object stream. This does not scale well, so - * if possible, you should implement this method. - */ - protected virtual T CreateGreaterThanOrEqualExpression(GreaterThanOrEqualFilter filter, bool not) { + + /// + /// Should be overridden by subclasses to create a GREATER-THAN-EQUAL expression + /// if the native resource supports GREATER-THAN-EQUAL. + /// + /// The contains filter. Will never be null. + /// True if this should be a NOT GREATER-THAN-EQUAL + /// The GREATER-THAN-EQUAL expression. A return value of null means + /// a native GREATER-THAN-EQUAL query cannot be created for the given filter. + /// In this case, may return an empty query set, meaning + /// fetch everything. The filter will be re-applied in memory + /// to the resulting object stream. This does not scale well, so + /// if possible, you should implement this method. + protected virtual T CreateGreaterThanOrEqualExpression(GreaterThanOrEqualFilter filter, bool not) + { return null; } - - /** - * Should be overridden by subclasses to create a LESS-THAN expression - * if the native resource supports LESS-THAN. - * @param filter The contains filter. Will never be null. - * @param not True if this should be a NOT LESS-THAN - * @return The LESS-THAN expression. A return value of null means - * a native LESS-THAN query cannot be created for the given filter. - * In this case, {@link #translate} may return an empty query set, meaning - * fetch everything. The filter will be re-applied in memory - * to the resulting object stream. This does not scale well, so - * if possible, you should implement this method. - */ - protected virtual T CreateLessThanExpression(LessThanFilter filter, bool not) { + + /// + /// Should be overridden by subclasses to create a LESS-THAN expression + /// if the native resource supports LESS-THAN. + /// + /// The contains filter. Will never be null. + /// True if this should be a NOT LESS-THAN + /// The LESS-THAN expression. A return value of null means + /// a native LESS-THAN query cannot be created for the given filter. + /// In this case, may return an empty query set, meaning + /// fetch everything. The filter will be re-applied in memory + /// to the resulting object stream. This does not scale well, so + /// if possible, you should implement this method. + protected virtual T CreateLessThanExpression(LessThanFilter filter, bool not) + { return null; } - - /** - * Should be overridden by subclasses to create a LESS-THAN-EQUAL expression - * if the native resource supports LESS-THAN-EQUAL. - * @param filter The contains filter. Will never be null. - * @param not True if this should be a NOT LESS-THAN-EQUAL - * @return The LESS-THAN-EQUAL expression. A return value of null means - * a native LESS-THAN-EQUAL query cannot be created for the given filter. - * In this case, {@link #translate} may return an empty query set, meaning - * fetch everything. The filter will be re-applied in memory - * to the resulting object stream. This does not scale well, so - * if possible, you should implement this method. - */ - protected virtual T CreateLessThanOrEqualExpression(LessThanOrEqualFilter filter, bool not) { + + /// + /// Should be overridden by subclasses to create a LESS-THAN-EQUAL expression + /// if the native resource supports LESS-THAN-EQUAL. + /// + /// The contains filter. Will never be null. + /// True if this should be a NOT LESS-THAN-EQUAL + /// The LESS-THAN-EQUAL expression. A return value of null means + /// a native LESS-THAN-EQUAL query cannot be created for the given filter. + /// In this case, may return an empty query set, meaning + /// fetch everything. The filter will be re-applied in memory + /// to the resulting object stream. This does not scale well, so + /// if possible, you should implement this method. + protected virtual T CreateLessThanOrEqualExpression(LessThanOrEqualFilter filter, bool not) + { return null; } - - /** - * Should be overridden by subclasses to create a STARTS-WITH expression - * if the native resource supports STARTS-WITH. - * @param filter The contains filter. Will never be null. - * @param not True if this should be a NOT STARTS-WITH - * @return The STARTS-WITH expression. A return value of null means - * a native STARTS-WITH query cannot be created for the given filter. - * In this case, {@link #translate} may return an empty query set, meaning - * fetch everything. The filter will be re-applied in memory - * to the resulting object stream. This does not scale well, so - * if possible, you should implement this method. - */ - protected virtual T CreateStartsWithExpression(StartsWithFilter filter, bool not) { + + /// + /// Should be overridden by subclasses to create a STARTS-WITH expression + /// if the native resource supports STARTS-WITH. + /// + /// The contains filter. Will never be null. + /// True if this should be a NOT STARTS-WITH + /// The STARTS-WITH expression. A return value of null means + /// a native STARTS-WITH query cannot be created for the given filter. + /// In this case, may return an empty query set, meaning + /// fetch everything. The filter will be re-applied in memory + /// to the resulting object stream. This does not scale well, so + /// if possible, you should implement this method. + protected virtual T CreateStartsWithExpression(StartsWithFilter filter, bool not) + { return null; } - - protected virtual T CreateContainsAllValuesExpression(ContainsAllValuesFilter filter, bool not) { + + protected virtual T CreateContainsAllValuesExpression(ContainsAllValuesFilter filter, bool not) + { return null; } } #endregion - + #region AndFilter - public sealed class AndFilter : CompositeFilter { - - /** - * And the the left and right filters. - */ - public AndFilter(Filter left, Filter right) - : base(left, right) { - } - - /** - * Ands the left and right filters. - * - * @see Filter#accept(ConnectorObject) - */ - public override bool Accept(ConnectorObject obj) { + public sealed class AndFilter : CompositeFilter + { + /// + /// And the the left and right filters. + /// + public AndFilter(Filter left, Filter right) + : base(left, right) + { + } + + /// + /// Ands the left and right filters. + /// + /// + public override bool Accept(ConnectorObject obj) + { return Left.Accept(obj) && Right.Accept(obj); } - + public override string ToString() { StringBuilder bld = new StringBuilder(); @@ -653,33 +771,38 @@ public override string ToString() } } #endregion - + #region AttributeFilter - public abstract class AttributeFilter : Filter { + public abstract class AttributeFilter : Filter + { private readonly ConnectorAttribute _attribute; - - /** - * Root filter for Attribute testing.. - */ - internal AttributeFilter(ConnectorAttribute attribute) { + + /// + /// Root filter for Attribute testing.. + /// + internal AttributeFilter(ConnectorAttribute attribute) + { _attribute = attribute; - if (attribute == null) { + if (attribute == null) + { throw new ArgumentException("Attribute not be null!"); } } - - /** - * Get the internal attribute. - */ - public ConnectorAttribute GetAttribute() { + + /// + /// Get the internal attribute. + /// + public ConnectorAttribute GetAttribute() + { return _attribute; } - /** - * Determines if the attribute provided is present in the - * {@link ConnectorObject}. - */ - public bool IsPresent(ConnectorObject obj) { + /// + /// Determines if the attribute provided is present in the + /// . + /// + public bool IsPresent(ConnectorObject obj) + { return obj.GetAttributeByName(_attribute.Name) != null; } public abstract bool Accept(ConnectorObject obj); @@ -687,34 +810,42 @@ public bool IsPresent(ConnectorObject obj) { #endregion #region ComparableAttributeFilter - /** - * Filter for an attribute value that is comparable. - */ + /// + /// Filter for an attribute value that is comparable. + /// public abstract class ComparableAttributeFilter : - SingleValueAttributeFilter { - - /** - * Attempt compare attribute values. - */ - internal ComparableAttributeFilter(ConnectorAttribute attr) - : base(attr) { + SingleValueAttributeFilter + { + /// + /// Attempt compare attribute values. + /// + internal ComparableAttributeFilter(ConnectorAttribute attr) + : base(attr) + { // determine if this attribute value is comparable.. - if (!(GetValue() is IComparable)) { + if (!(GetValue() is IComparable)) + { String ERR = "Must be a comparable value!"; throw new ArgumentException(ERR); } } - - /** - * Call compareTo on the attribute values. If the attribute is not present - * in the {@link ConnectorObject} return -1. - */ - public int Compare(ConnectorObject obj) { + + /// + /// Call compareTo on the attribute values. + /// + /// + /// If the attribute is not present + /// in the return -1. + /// + public int Compare(ConnectorObject obj) + { int ret = -1; ConnectorAttribute attr = obj.GetAttributeByName(GetName()); - if (attr != null && attr.Value.Count == 1) { + if (attr != null && attr.Value.Count == 1) + { // it must be a comparable because that's were testing against - if (!(attr.Value[0] is IComparable)) { + if (!(attr.Value[0] is IComparable)) + { String ERR = "Attribute value must be comparable!"; throw new ArgumentException(ERR); } @@ -727,15 +858,16 @@ public int Compare(ConnectorObject obj) { } } #endregion - - #region CompositeFilter - public abstract class CompositeFilter : Filter { + #region CompositeFilter + public abstract class CompositeFilter : Filter + { public Filter Left { get; set; } - + public Filter Right { get; set; } - - internal CompositeFilter(Filter left, Filter right) { + + internal CompositeFilter(Filter left, Filter right) + { Left = left; Right = right; } @@ -744,16 +876,18 @@ internal CompositeFilter(Filter left, Filter right) { #endregion #region ContainsFilter - public sealed class ContainsFilter : StringFilter { - - public ContainsFilter(ConnectorAttribute attr) - : base(attr) { + public sealed class ContainsFilter : StringFilter + { + public ContainsFilter(ConnectorAttribute attr) + : base(attr) + { } - - public override bool Accept(String value) { + + public override bool Accept(String value) + { return value.Contains(GetValue()); } - + public override string ToString() { StringBuilder bld = new StringBuilder(); @@ -762,19 +896,22 @@ public override string ToString() } } #endregion - - #region EndsWithFilter - public sealed class EndsWithFilter : StringFilter { - public EndsWithFilter(ConnectorAttribute attr) - : base(attr) { + #region EndsWithFilter + public sealed class EndsWithFilter : StringFilter + { + public EndsWithFilter(ConnectorAttribute attr) + : base(attr) + { } - - public override bool Accept(String value) { + + public override bool Accept(String value) + { return value.EndsWith(GetValue()); } - - public override string ToString() { + + public override string ToString() + { StringBuilder bld = new StringBuilder(); bld.Append("ENDSWITH: ").Append(GetAttribute()); return bld.ToString(); @@ -783,230 +920,228 @@ public override string ToString() { #endregion #region EqualsFilter - public sealed class EqualsFilter : AttributeFilter { - - /** - * Determines if the attribute inside the {@link ConnectorObject} is equal - * to the {@link Attribute} provided. - */ - public EqualsFilter(ConnectorAttribute attr) - :base(attr) { - } - - /** - * Determines if the attribute exists in the {@link ConnectorObject} and if - * its equal to the one provided. - * - * @see Filter#accept(ConnectorObject) - */ - public override bool Accept(ConnectorObject obj) { + public sealed class EqualsFilter : AttributeFilter + { + /// + /// Determines if the attribute inside the is equal + /// to the provided. + /// + public EqualsFilter(ConnectorAttribute attr) + : base(attr) + { + } + + /// + /// Determines if the attribute exists in the and if + /// its equal to the one provided. + /// + /// + public override bool Accept(ConnectorObject obj) + { bool ret = false; ConnectorAttribute thisAttr = GetAttribute(); ConnectorAttribute attr = obj.GetAttributeByName(thisAttr.Name); - if (attr != null) { + if (attr != null) + { ret = thisAttr.Equals(attr); } return ret; } - - public override string ToString() { + + public override string ToString() + { StringBuilder bld = new StringBuilder(); bld.Append("EQUALS: ").Append(GetAttribute()); return bld.ToString(); } - + } #endregion #region Filter - public interface Filter { + public interface Filter + { bool Accept(ConnectorObject obj); } #endregion - + #region FilterBuilder - /** - * FilterBuilder creates a {@link Filter} object, that can determine if a - * ConnectorObject will be filtered or not. - * - * @author Will Droste - * @version $Revision: 1.7 $ - * @since 1.0 - */ - public static class FilterBuilder { - - /** - * Determine if the {@link ConnectorObject} {@link ConnectorAttribute} value ends - * with the {@link ConnectorAttribute} value provided. - * - * @param attr - * {@link ConnectorAttribute} value to test against the - * {@link ConnectorObject} attribute value. - * @return true if the {@link ConnectorObject} attribute value contains the - * attribute value provided. - */ - public static Filter EndsWith(ConnectorAttribute attr) { + /// + /// FilterBuilder creates a object, that can determine if a + /// ConnectorObject will be filtered or not. + /// + /// Will Droste + /// $Revision: 1.7 $ + /// 1.0 + public static class FilterBuilder + { + /// + /// Determine if the value ends + /// with the value provided. + /// + /// + /// value to test against the + /// attribute value. + /// true if the attribute value contains the + /// attribute value provided. + public static Filter EndsWith(ConnectorAttribute attr) + { return new EndsWithFilter(attr); } - - /** - * Determine if the {@link ConnectorObject} {@link ConnectorAttribute} value starts - * with the {@link ConnectorAttribute} value provided. - * - * @param attr - * {@link ConnectorAttribute} value to test against the - * {@link ConnectorObject} attribute value. - * @return true if the {@link ConnectorObject} attribute value contains the - * attribute value provided. - */ - public static Filter StartsWith(ConnectorAttribute attr) { + + /// + /// Determine if the value starts + /// with the value provided. + /// + /// + /// value to test against the + /// attribute value. + /// true if the attribute value contains the + /// attribute value provided. + public static Filter StartsWith(ConnectorAttribute attr) + { return new StartsWithFilter(attr); } - - public static Filter ContainsAllValues(ConnectorAttribute attr) { + + public static Filter ContainsAllValues(ConnectorAttribute attr) + { return new ContainsAllValuesFilter(attr); } - /** - * Determine if the {@link ConnectorObject} {@link ConnectorAttribute} value contains - * the {@link ConnectorAttribute} value provided. - * - * @param attr - * {@link ConnectorAttribute} value to test against the - * {@link ConnectorObject} attribute value. - * @return true if the {@link ConnectorObject} attribute value contains the - * attribute value provided. - */ - public static Filter Contains(ConnectorAttribute attr) { + /// + /// Determine if the value contains + /// the value provided. + /// + /// + /// value to test against the + /// attribute value. + /// true if the attribute value contains the + /// attribute value provided. + public static Filter Contains(ConnectorAttribute attr) + { return new ContainsFilter(attr); } - - /** - * The {@link ConnectorAttribute} value provided is less than or equal to the - * {@link ConnectorObject} attribute value. - * - * @param attr - * ConnectorAttribute to do the comparison. - * @return true if attribute provided is greater than or equal to the one - * provided by the {@link ConnectorObject}. - */ - public static Filter GreaterThanOrEqualTo(ConnectorAttribute attr) { + + /// + /// The value provided is less than or equal to the + /// attribute value. + /// + /// ConnectorAttribute to do the comparison. + /// true if attribute provided is greater than or equal to the one + /// provided by the . + public static Filter GreaterThanOrEqualTo(ConnectorAttribute attr) + { return new GreaterThanOrEqualFilter(attr); } - - /** - * The {@link ConnectorAttribute} value provided is less than or equal to the - * {@link ConnectorObject} attribute value. - * - * @param attr - * ConnectorAttribute to do the comparison. - * @return true if attribute provided is less than or equal to the one - * provided by the {@link ConnectorObject}. - */ - public static Filter LessThanOrEqualTo(ConnectorAttribute attr) { + + /// + /// The value provided is less than or equal to the + /// attribute value. + /// + /// ConnectorAttribute to do the comparison. + /// true if attribute provided is less than or equal to the one + /// provided by the . + public static Filter LessThanOrEqualTo(ConnectorAttribute attr) + { return new LessThanOrEqualFilter(attr); } - - /** - * The {@link ConnectorAttribute} value provided is less than the - * {@link ConnectorObject} attribute value. - * - * @param attr - * ConnectorAttribute to do the comparison. - * @return true if attribute provided is less than the one provided by the - * {@link ConnectorObject}. - */ - public static Filter LessThan(ConnectorAttribute attr) { + + /// + /// The value provided is less than the + /// attribute value. + /// + /// ConnectorAttribute to do the comparison. + /// true if attribute provided is less than the one provided by the + /// . + public static Filter LessThan(ConnectorAttribute attr) + { return new LessThanFilter(attr); } - - /** - * ConnectorAttribute value is greater than the {@link ConnectorObject} attribute - * value. - * - * @param attr - * ConnectorAttribute to do the comparison. - * @return true if attribute provided is greater than the one provided by - * the {@link ConnectorObject}. - */ - public static Filter GreaterThan(ConnectorAttribute attr) { + + /// + /// ConnectorAttribute value is greater than the attribute + /// value. + /// + /// ConnectorAttribute to do the comparison. + /// true if attribute provided is greater than the one provided by + /// the . + public static Filter GreaterThan(ConnectorAttribute attr) + { return new GreaterThanFilter(attr); } - - /** - * Determines if the {@link ConnectorAttribute} provided exists in the - * {@link ConnectorObject} and is equal. - */ - public static Filter EqualTo(ConnectorAttribute attr) { + + /// + /// Determines if the provided exists in the + /// and is equal. + /// + public static Filter EqualTo(ConnectorAttribute attr) + { return new EqualsFilter(attr); } - - /** - * Ands the two {@link Filter}. - * - * @param leftOperand - * left side operand. - * @param rightOperand - * right side operand. - * @return the result of leftOperand && rightOperand - */ - public static Filter And(Filter leftOperand, Filter rightOperand) { + + /// + /// Ands the two . + /// + /// left side operand. + /// right side operand. + /// the result of leftOperand && rightOperand + public static Filter And(Filter leftOperand, Filter rightOperand) + { return new AndFilter(leftOperand, rightOperand); } - - /** - * ORs the two {@link Filter}. - * - * @param leftOperand - * left side operand. - * @param rightOperand - * right side operand. - * @return the result of leftOperand || rightOperand - */ - public static Filter Or(Filter leftOperand, Filter rightOperand) { + + /// + /// ORs the two . + /// + /// left side operand. + /// right side operand. + /// the result of leftOperand || rightOperand + public static Filter Or(Filter leftOperand, Filter rightOperand) + { return new OrFilter(leftOperand, rightOperand); } - - /** - * NOT the {@link Filter}. - * - * @param filter - * negate the result of {@link Filter}. - * @return the result of not {@link Filter}. - */ - public static Filter Not(Filter filter) { + + /// + /// NOT the . + /// + /// negate the result of . + /// the result of not . + public static Filter Not(Filter filter) + { return new NotFilter(filter); } } #endregion - + #region FilterTranslator - public interface FilterTranslator { - IList Translate(Filter filter); + public interface FilterTranslator + { + IList Translate(Filter filter); } #endregion #region GreaterThanFilter - public sealed class GreaterThanFilter : ComparableAttributeFilter { - - /** - * Determine if the {@link ConnectorObject} {@link Attribute} value is - * greater than the one provided in the filter. - */ - public GreaterThanFilter(ConnectorAttribute attr) - : base (attr) { - } - - /** - * Determine if the {@link ConnectorObject} {@link Attribute} value is - * greater than the one provided in the filter. - * - * @see com.sun.openconnectors.framework.common.objects.Filter#accept(ConnectorObject) - */ - public override bool Accept(ConnectorObject obj) { + public sealed class GreaterThanFilter : ComparableAttributeFilter + { + /// + /// Determine if the value is + /// greater than the one provided in the filter. + /// + public GreaterThanFilter(ConnectorAttribute attr) + : base(attr) + { + } + + /// + /// Determine if the value is + /// greater than the one provided in the filter. + /// + /// + public override bool Accept(ConnectorObject obj) + { return IsPresent(obj) && this.Compare(obj) > 0; } - + public override string ToString() { StringBuilder bld = new StringBuilder(); @@ -1015,28 +1150,29 @@ public override string ToString() } } #endregion - + #region GreaterThanOrEqualFilter - public sealed class GreaterThanOrEqualFilter : ComparableAttributeFilter { - - /** - * Determine if the {@link ConnectorObject} {@link Attribute} value is - * greater than the one provided in the filter. - */ - public GreaterThanOrEqualFilter(ConnectorAttribute attr) - : base (attr) { - } - - /** - * Determine if the {@link ConnectorObject} {@link Attribute} value is - * greater than the one provided in the filter. - * - * @see com.sun.openconnectors.framework.common.objects.Filter#accept(ConnectorObject) - */ - public override bool Accept(ConnectorObject obj) { + public sealed class GreaterThanOrEqualFilter : ComparableAttributeFilter + { + /// + /// Determine if the value is + /// greater than the one provided in the filter. + /// + public GreaterThanOrEqualFilter(ConnectorAttribute attr) + : base(attr) + { + } + + /// + /// Determine if the value is + /// greater than the one provided in the filter. + /// + /// + public override bool Accept(ConnectorObject obj) + { return IsPresent(obj) && this.Compare(obj) >= 0; } - + public override string ToString() { StringBuilder bld = new StringBuilder(); @@ -1047,23 +1183,24 @@ public override string ToString() #endregion #region LessThanFilter - public sealed class LessThanFilter : ComparableAttributeFilter { - - /** - * Determine if the {@link ConnectorObject} {@link Attribute} value is - * greater than the one provided in the filter. - */ - public LessThanFilter(ConnectorAttribute attr) - : base (attr) { - } - - /** - * Determine if the {@link ConnectorObject} {@link Attribute} value is - * greater than the one provided in the filter. - * - * @see com.sun.openconnectors.framework.common.objects.Filter#accept(ConnectorObject) - */ - public override bool Accept(ConnectorObject obj) { + public sealed class LessThanFilter : ComparableAttributeFilter + { + /// + /// Determine if the value is + /// greater than the one provided in the filter. + /// + public LessThanFilter(ConnectorAttribute attr) + : base(attr) + { + } + + /// + /// Determine if the value is + /// greater than the one provided in the filter. + /// + /// + public override bool Accept(ConnectorObject obj) + { return IsPresent(obj) && this.Compare(obj) < 0; } @@ -1077,23 +1214,24 @@ public override string ToString() #endregion #region LessThanOrEqualFilter - public sealed class LessThanOrEqualFilter : ComparableAttributeFilter { - - /** - * Determine if the {@link ConnectorObject} {@link Attribute} value is - * greater than the one provided in the filter. - */ - public LessThanOrEqualFilter(ConnectorAttribute attr) - : base (attr) { - } - - /** - * Determine if the {@link ConnectorObject} {@link Attribute} value is - * greater than the one provided in the filter. - * - * @see com.sun.openconnectors.framework.common.objects.Filter#accept(ConnectorObject) - */ - public override bool Accept(ConnectorObject obj) { + public sealed class LessThanOrEqualFilter : ComparableAttributeFilter + { + /// + /// Determine if the value is + /// greater than the one provided in the filter. + /// + public LessThanOrEqualFilter(ConnectorAttribute attr) + : base(attr) + { + } + + /// + /// Determine if the value is + /// greater than the one provided in the filter. + /// + /// + public override bool Accept(ConnectorObject obj) + { return IsPresent(obj) && this.Compare(obj) <= 0; } @@ -1105,40 +1243,43 @@ public override string ToString() } } #endregion - + #region NotFilter - /** - * Proxy the filter to return the negative of the value. - */ - public sealed class NotFilter : Filter { - + /// + /// Proxy the filter to return the negative of the value. + /// + public sealed class NotFilter : Filter + { private readonly Filter _filter; - - /** - * Take the value returned from the internal filter and NOT it. - */ - public NotFilter(Filter filter) { + + /// + /// Take the value returned from the internal filter and NOT it. + /// + public NotFilter(Filter filter) + { _filter = filter; } - - /** - * Get the internal filter that is being negated. - */ - public Filter Filter { - get { + + /// + /// Get the internal filter that is being negated. + /// + public Filter Filter + { + get + { return _filter; } } - - /** - * Return the opposite the internal filters return value. - * - * @see Filter#accept(ConnectorObject) - */ - public bool Accept(ConnectorObject obj) { + + /// + /// Return the opposite the internal filters return value. + /// + /// + public bool Accept(ConnectorObject obj) + { return !_filter.Accept(obj); } - + public override string ToString() { StringBuilder bld = new StringBuilder(); @@ -1147,83 +1288,92 @@ public override string ToString() } } #endregion - + #region OrFilter - public sealed class OrFilter : CompositeFilter { - - /** - * Or the left and right filters. - */ - public OrFilter(Filter left, Filter right) - : base(left, right) { - } - - /** - * ORs the left and right filters. - * - * @see Filter#accept(ConnectorObject) - */ - public override bool Accept(ConnectorObject obj) { + public sealed class OrFilter : CompositeFilter + { + /// + /// Or the left and right filters. + /// + public OrFilter(Filter left, Filter right) + : base(left, right) + { + } + + /// + /// ORs the left and right filters. + /// + /// + public override bool Accept(ConnectorObject obj) + { return Left.Accept(obj) || Right.Accept(obj); } - public override string ToString() { + public override string ToString() + { StringBuilder bld = new StringBuilder(); bld.Append("OR: ").Append(Left).Append(", ").Append(Right); return bld.ToString(); } } #endregion - + #region SingleValueAttributeFilter - /** - * Get a single value out of the attribute to test w/. - */ - public abstract class SingleValueAttributeFilter : AttributeFilter { - - /** - * Attempt to single out the value for comparison. - */ + /// + /// Get a single value out of the attribute to test w/. + /// + public abstract class SingleValueAttributeFilter : AttributeFilter + { + /// + /// Attempt to single out the value for comparison. + /// internal SingleValueAttributeFilter(ConnectorAttribute attr) : - base(attr) { + base(attr) + { // make sure this is not a Uid.. - if (Uid.NAME.Equals(attr.Name)) { + if (Uid.NAME.Equals(attr.Name)) + { String MSG = "Uid can only be used for equals comparison."; throw new ArgumentException(MSG); } // actual runtime.. - if (attr.Value.Count != 1) { + if (attr.Value.Count != 1) + { String ERR = "Must only be one value!"; throw new ArgumentException(ERR); } } - - /** - * Value to test against. - */ - public Object GetValue() { + + /// + /// Value to test against. + /// + public Object GetValue() + { return GetAttribute().Value[0]; } - /** - * Name of the attribute to find in the {@link ConnectorObject}. - */ - public String GetName() { + /// + /// Name of the attribute to find in the . + /// + public String GetName() + { return GetAttribute().Name; } } #endregion - - #region StartsWithFilter - public sealed class StartsWithFilter : StringFilter { - public StartsWithFilter(ConnectorAttribute attr) - : base(attr) { + #region StartsWithFilter + public sealed class StartsWithFilter : StringFilter + { + public StartsWithFilter(ConnectorAttribute attr) + : base(attr) + { } - - public override bool Accept(String value) { + + public override bool Accept(String value) + { return value.StartsWith(GetValue()); } - + public override string ToString() { StringBuilder bld = new StringBuilder(); @@ -1234,74 +1384,82 @@ public override string ToString() #endregion #region StringFilter - /** - * Filter based on strings. - */ - public abstract class StringFilter : SingleValueAttributeFilter { - - /** - * Attempts to get a string from the attribute. - */ - internal StringFilter(ConnectorAttribute attr) - : base(attr) { + /// + /// Filter based on strings. + /// + public abstract class StringFilter : SingleValueAttributeFilter + { + /// + /// Attempts to get a string from the attribute. + /// + internal StringFilter(ConnectorAttribute attr) + : base(attr) + { Object val = base.GetValue(); - if (!(val is string)) { + if (!(val is string)) + { String MSG = "Value must be a string!"; throw new ArgumentException(MSG); } } - - /** - * Get the string value from the afore mentioned attribute. - * - * @see SingleValueAttributeFilter#getValue() - */ - public new String GetValue() { - return (String) base.GetValue(); - } - - /** - * @throws ClassCastException - * iff the value from the {@link ConnectorObject}'s attribute - * of the same name as provided is not a string. - * @see com.sun.openconnectors.framework.common.objects.Filter#accept(ConnectorObject) - */ - public override bool Accept(ConnectorObject obj) { + + /// + /// Get the string value from the afore mentioned attribute. + /// + /// + public new String GetValue() + { + return (String)base.GetValue(); + } + + /// + /// + /// iff the value from the 's attribute + /// of the same name as provided is not a string. + /// + public override bool Accept(ConnectorObject obj) + { bool ret = false; ConnectorAttribute attr = obj.GetAttributeByName(GetName()); - if (attr != null) { + if (attr != null) + { ret = Accept((string)attr.Value[0]); } return ret; } - + public abstract bool Accept(String value); } #endregion - + #region ContainsAllValues Filter - public class ContainsAllValuesFilter : AttributeFilter { + public class ContainsAllValuesFilter : AttributeFilter + { private readonly string _name; private readonly ICollection _values; - - public ContainsAllValuesFilter(ConnectorAttribute attr) : base(attr) { + + public ContainsAllValuesFilter(ConnectorAttribute attr) + : base(attr) + { _name = attr.Name; _values = attr.Value; } - /** - * Determine if the {@link ConnectorObject} contains an {@link Attribute} - * which contains all the values provided in the {@link Attribute} passed - * into the filter. - * - * {@inheritDoc} - */ - public override bool Accept(ConnectorObject obj) { + /// + /// Determine if the contains an + /// which contains all the values provided in the passed + /// into the filter. + /// + public override bool Accept(ConnectorObject obj) + { bool rv = false; ConnectorAttribute found = obj.GetAttributeByName(_name); - if (found != null) { + if (found != null) + { // TODO: possible optimization using 'Set' - foreach (object o in _values) { - if (!(rv = found.Value.Contains(o))) { + foreach (object o in _values) + { + if (!(rv = found.Value.Contains(o))) + { break; } } @@ -1310,5 +1468,4 @@ public override bool Accept(ConnectorObject obj) { } } #endregion - -} +} \ No newline at end of file diff --git a/Framework/CommonSerializer.cs b/Framework/CommonSerializer.cs index f13e7d53..9524ba1c 100644 --- a/Framework/CommonSerializer.cs +++ b/Framework/CommonSerializer.cs @@ -26,162 +26,172 @@ using Org.IdentityConnectors.Common; namespace Org.IdentityConnectors.Framework.Common.Serializer { - /** - * Interface for reading objects from a stream. - */ - public interface BinaryObjectDeserializer { - /** - * Reads the next object from the stream. Throws - * a wrapped {@link EOFException} if end of stream is reached. - * @return The next object from the stream. - */ + /// + /// Interface for reading objects from a stream. + /// + public interface BinaryObjectDeserializer + { + /// + /// Reads the next object from the stream. + /// + /// + /// Throws + /// a wrapped if end of stream is reached. + /// + /// The next object from the stream. object ReadObject(); - - /** - * Closes the underlying stream - */ + + /// + /// Closes the underlying stream + /// void Close(); } - /** - * Interface for writing objects to a stream. - */ - public interface BinaryObjectSerializer { - /** - * Writes the next object to the stream. - * @param obj The object to write. - * @see ObjectSerializerFactory for a list of supported types. - */ + /// + /// Interface for writing objects to a stream. + /// + public interface BinaryObjectSerializer + { + /// + /// Writes the next object to the stream. + /// + /// The object to write. + /// void WriteObject(object obj); - - /** - * Flushes the underlying stream. - */ + + /// + /// Flushes the underlying stream. + /// void Flush(); - - /** - * Closes the underylying stream after first flushing it. - */ + + /// + /// Closes the underylying stream after first flushing it. + /// void Close(); } - /** - * Serializer factory for serializing connector objects. The list of - * supported types are as follows: - * TODO: list supported types - *
    - *
- * @see SerializerUtil - */ - public abstract class ObjectSerializerFactory { + /// + /// Serializer factory for serializing connector objects. + /// + /// + /// The list of + /// supported types are as follows: + /// TODO: list supported types + /// + /// + /// + /// + public abstract class ObjectSerializerFactory + { // At some point we might make this pluggable, but for now, hard-code private const string IMPL_NAME = "Org.IdentityConnectors.Framework.Impl.Serializer.ObjectSerializerFactoryImpl"; - + private static ObjectSerializerFactory _instance; - + private static readonly Object LOCK = new Object(); - - /** - * Get the singleton instance of the {@link ObjectSerializerFactory}. - */ - public static ObjectSerializerFactory GetInstance() { - lock (LOCK) { - if (_instance == null) { - SafeType t = + + /// + /// Get the singleton instance of the . + /// + public static ObjectSerializerFactory GetInstance() + { + lock (LOCK) + { + if (_instance == null) + { + SafeType t = FrameworkInternalBridge.LoadType(IMPL_NAME); - + _instance = t.CreateInstance(); } return _instance; } } - - /** - * Creates a BinaryObjectSerializer for writing objects to - * the given stream. - * - * NOTE: consider using {@link SerializerUtil#serializeBinaryObject(Object)} - * for convenience serializing a single object. - * - * NOTE2: do not mix and match {@link SerializerUtil#serializeBinaryObject(Object)} - * with {{@link #newBinaryDeserializer(InputStream)}. This is unsafe since there - * is header information and state associated with the stream. Objects written - * using one method must be read using the proper corresponding method. - * - * @param os The stream - * @return The serializer - */ + + /// + /// Creates a BinaryObjectSerializer for writing objects to + /// the given stream. + /// + /// + /// NOTE: consider using + /// for convenience serializing a single object. + /// + /// NOTE2: do not mix and match + /// with {. This is unsafe since there + /// is header information and state associated with the stream. Objects written + /// using one method must be read using the proper corresponding method. + /// + /// The stream + /// The serializer public abstract BinaryObjectSerializer NewBinarySerializer(Stream os); - - /** - * Creates a BinaryObjectDeserializer for reading objects from - * the given stream. - * - * NOTE: consider using {@link SerializerUtil#deserializeBinaryObject(byte[])} - * for convenience deserializing a single object. - * - * NOTE2: do not mix and match {@link SerializerUtil#deserializeBinaryObject(Object)} - * with {{@link #newBinarySerializer(OutputStream)}. This is unsafe since there - * is header information and state associated with the stream. Objects written - * using one method must be read using the proper corresponding method. - * - * @param os The stream - * @return The deserializer - */ + + /// + /// Creates a BinaryObjectDeserializer for reading objects from + /// the given stream. + /// + /// + /// NOTE: consider using + /// for convenience deserializing a single object. + /// NOTE2: do not mix and match + /// with {. This is unsafe since there + /// is header information and state associated with the stream. Objects written + /// using one method must be read using the proper corresponding method. + /// + /// The stream + /// The deserializer public abstract BinaryObjectDeserializer NewBinaryDeserializer(Stream i); - - /** - * Creates a BinaryObjectSerializer for writing objects to - * the given stream. - * - * NOTE: consider using {@link SerializerUtil#serializeXmlObject(Object,boolean)} - * for convenience serializing a single object. - * - * NOTE2: do not mix and match {@link SerializerUtil#serializeXmlObject(Object,boolean)} - * with {{@link #deserializeXmlStream(InputSource, XmlObjectResultsHandler, boolean)}. - * - * @param w The writer - * @param includeHeader True to include the xml header - * @param multiObject Is this to produce a multi-object document. If false, only - * a single object may be written. - * @return The serializer - */ - public abstract XmlObjectSerializer NewXmlSerializer(TextWriter w, + + /// + /// Creates a BinaryObjectSerializer for writing objects to + /// the given stream. + /// + /// + /// NOTE: consider using + /// for convenience serializing a single object. + /// + /// NOTE2: do not mix and match + /// with {. + /// + /// The writer + /// True to include the xml header + /// Is this to produce a multi-object document. If false, only + /// a single object may be written. + /// The serializer + public abstract XmlObjectSerializer NewXmlSerializer(TextWriter w, bool includeHeader, bool multiObject); - - /** - * Deserializes XML objects from a stream - * - * NOTE: Consider using {@link SerializerUtil#deserializeXmlObject(String,boolean)} - * for convenience deserializing a single object. - * - * NOTE2: Do not mix and match {@link SerializerUtil#deserializeXmlObject(Object,boolean)} - * with {{@link #newXmlSerializer(Writer, boolean, boolean)}. - * - * @param is The input source - * @param handler The callback to receive objects from the stream - * @param validate True iff we are to validate - */ - public abstract void DeserializeXmlStream(TextReader reader, + + /// + /// Deserializes XML objects from a stream + /// NOTE: Consider using + /// for convenience deserializing a single object. + /// + /// + /// NOTE2: Do not mix and match + /// with {. + /// + /// The input source + /// The callback to receive objects from the stream + /// True iff we are to validate + public abstract void DeserializeXmlStream(TextReader reader, XmlObjectResultsHandler handler, bool validate); - + } - /** - * Bag of utilities for serialization - */ - public static class SerializerUtil { - - - /** - * Serializes the given object to bytes - * @param object The object to serialize - * @return The bytes - * @see ObjectSerializerFactory for a list of supported types - */ - public static byte [] SerializeBinaryObject(object obj) { + /// + /// Bag of utilities for serialization + /// + public static class SerializerUtil + { + /// + /// Serializes the given object to bytes + /// + /// The object to serialize + /// The bytes + /// + public static byte[] SerializeBinaryObject(object obj) + { ObjectSerializerFactory fact = ObjectSerializerFactory.GetInstance(); MemoryStream mem = new MemoryStream(); BinaryObjectSerializer ser = fact.NewBinarySerializer(mem); @@ -189,28 +199,30 @@ public static byte [] SerializeBinaryObject(object obj) { ser.Close(); return mem.ToArray(); } - - /** - * Deserializes the given object from bytes - * @param bytes The bytes to deserialize - * @return The object - * @see ObjectSerializerFactory for a list of supported types - */ - public static object DeserializeBinaryObject(byte [] bytes) { + + /// + /// Deserializes the given object from bytes + /// + /// The bytes to deserialize + /// The object + /// + public static object DeserializeBinaryObject(byte[] bytes) + { ObjectSerializerFactory fact = ObjectSerializerFactory.GetInstance(); MemoryStream mem = new MemoryStream(bytes); BinaryObjectDeserializer des = fact.NewBinaryDeserializer(mem); return des.ReadObject(); } - - /** - * Serializes the given object to xml - * @param object The object to serialize - * @param includeHeader True if we are to include the xml header. - * @return The xml - * @see ObjectSerializerFactory for a list of supported types - */ - public static String SerializeXmlObject(Object obj, bool includeHeader) { + + /// + /// Serializes the given object to xml + /// + /// The object to serialize + /// True if we are to include the xml header. + /// The xml + /// + public static String SerializeXmlObject(Object obj, bool includeHeader) + { ObjectSerializerFactory fact = ObjectSerializerFactory.GetInstance(); StringWriter w = new StringWriter(); XmlObjectSerializer ser = fact.NewXmlSerializer(w, includeHeader, false); @@ -218,72 +230,76 @@ public static String SerializeXmlObject(Object obj, bool includeHeader) { ser.Close(true); return w.ToString(); } - - /** - * Deserializes the given object from xml - * @param bytes The xml to deserialize - * @param validate True if we are to validate the xml - * @return The object - * @see ObjectSerializerFactory for a list of supported types - */ - public static Object DeserializeXmlObject(String str, bool validate) { + + /// + /// Deserializes the given object from xml + /// + /// The xml to deserialize + /// True if we are to validate the xml + /// The object + /// + public static Object DeserializeXmlObject(String str, bool validate) + { ObjectSerializerFactory fact = ObjectSerializerFactory.GetInstance(); StringReader source = new StringReader(str); IList rv = new List(); - fact.DeserializeXmlStream(source, - obj => { - rv.Add(obj); - return true; - },validate); - if ( rv.Count > 0 ) { + fact.DeserializeXmlStream(source, + obj => + { + rv.Add(obj); + return true; + }, validate); + if (rv.Count > 0) + { return rv[0]; } - else { + else + { return null; } } - - /** - * Clones the given object by serializing it to bytes and then - * deserializing it. - * @param object The object. - * @return A clone of the object - */ - public static object CloneObject(Object obj) { - byte [] bytes = SerializeBinaryObject(obj); + + /// + /// Clones the given object by serializing it to bytes and then + /// deserializing it. + /// + /// The object. + /// A clone of the object + public static object CloneObject(Object obj) + { + byte[] bytes = SerializeBinaryObject(obj); return DeserializeBinaryObject(bytes); } - + } - - /** - * Callback interface to receive xml objects from a stream of objects. - */ - public delegate bool XmlObjectResultsHandler(Object obj); - - /** - * Interface for writing objects to a stream. - */ - public interface XmlObjectSerializer { - /** - * Writes the next object to the stream. - * @param object The object to write. - * @see ObjectSerializerFactory for a list of supported types. - * @throws ConnectorException if there is more than one object - * and this is not configured for multi-object document. - */ + + /// + /// Callback interface to receive xml objects from a stream of objects. + /// + public delegate bool XmlObjectResultsHandler(Object obj); + + /// + /// Interface for writing objects to a stream. + /// + public interface XmlObjectSerializer + { + /// + /// Writes the next object to the stream. + /// + /// The object to write. + /// + /// if there is more than one object + /// and this is not configured for multi-object document. void WriteObject(Object obj); - - /** - * Flushes the underlying stream. - */ + + /// + /// Flushes the underlying stream. + /// void Flush(); - - /** - * Adds document end tag and optinally closes the underlying stream - */ + + /// + /// Adds document end tag and optinally closes the underlying stream + /// void Close(bool closeUnderlyingStream); } - - -} +} \ No newline at end of file diff --git a/Framework/Spi.cs b/Framework/Spi.cs index ee4ebc39..efa80ebf 100644 --- a/Framework/Spi.cs +++ b/Framework/Spi.cs @@ -30,149 +30,198 @@ namespace Org.IdentityConnectors.Framework.Spi { #region AttributeNormalizer - /** - * Interface to be implemented by connectors that need - * to normalize certain attributes. This might, for - * example, be used to normalize whitespace within - * DN's to ensure consistent filtering whether that - * filtering is natively on the resource or by the - * connector framework. For connectors implementing - * this interface, the method {@link #normalizeAttribute(ObjectClass, Attribute)} - * will be applied to each of the following: - *
    - *
  1. The filter passed to {@link SearchOp}.
  2. - *
  3. The results returned from {@link SearchOp}.
  4. - *
  5. The results returned from {@link SyncOp}.
  6. - *
  7. The attributes passed to {@link AdvancedUpdateOp}.
  8. - *
  9. The Uid returned from {@link AdvancedUpdateOp}.
  10. - *
  11. The attributes passed to {@link UpdateOp}.
  12. - *
  13. The Uid returned from {@link UpdateOp}.
  14. - *
  15. The attributes passed to {@link CreateOp}.
  16. - *
  17. The Uid returned from {@link CreateOp}.
  18. - *
  19. The Uid passed to {@link DeleteOp}.
  20. - *
- */ - public interface AttributeNormalizer + /// + /// Interface to be implemented by connectors that need + /// to normalize certain attributes. + /// + /// + /// This might, for + /// example, be used to normalize whitespace within + /// DN's to ensure consistent filtering whether that + /// filtering is natively on the resource or by the + /// connector framework. For connectors implementing + /// this interface, the method + /// will be applied to each of the following: + /// + /// + /// The filter passed to . + /// + /// + /// + /// The results returned from . + /// + /// + /// + /// The results returned from . + /// + /// + /// + /// The attributes passed to . + /// + /// + /// + /// The Uid returned from . + /// + /// + /// + /// The attributes passed to . + /// + /// + /// + /// The Uid returned from . + /// + /// + /// + /// The attributes passed to . + /// + /// + /// + /// The Uid returned from . + /// + /// + /// + /// The Uid passed to . + /// + /// + /// + /// + public interface AttributeNormalizer { ConnectorAttribute NormalizeAttribute(ObjectClass oclass, ConnectorAttribute attribute); } #endregion - + #region Configuration /// /// Configuration information for the Connector. /// - public interface Configuration { + public interface Configuration + { + + ConnectorMessages ConnectorMessages { get; set; } - ConnectorMessages ConnectorMessages{get;set;} - - /** - * Determine if the configuration is valid based on the values set. - */ + /// + /// Determine if the configuration is valid based on the values set. + /// void Validate(); } #endregion - + #region AbstractConfiguration - public abstract class AbstractConfiguration : Configuration { + public abstract class AbstractConfiguration : Configuration + { + + public ConnectorMessages ConnectorMessages { get; set; } - public ConnectorMessages ConnectorMessages{get;set;} - public abstract void Validate(); } #endregion #region ConnectorClassAttribute - [AttributeUsage(AttributeTargets.Class,AllowMultiple=false)] - public class ConnectorClassAttribute : System.Attribute { - + [AttributeUsage(AttributeTargets.Class, AllowMultiple = false)] + public class ConnectorClassAttribute : System.Attribute + { + private readonly String _connectorDisplayNameKey; private readonly SafeType _connectorConfigurationClass; - + public ConnectorClassAttribute(String connectorDisplayNameKey, - Type connectorConfigurationClass) { + Type connectorConfigurationClass) + { _connectorDisplayNameKey = connectorDisplayNameKey; _connectorConfigurationClass = SafeType.ForRawType(connectorConfigurationClass); } - - public string ConnectorDisplayNameKey { - get { + + public string ConnectorDisplayNameKey + { + get + { return _connectorDisplayNameKey; } } - - public SafeType ConnectorConfigurationType { - get { + + public SafeType ConnectorConfigurationType + { + get + { return _connectorConfigurationClass; } } - - public string [] MessageCatalogPaths {get;set;} - + + public string[] MessageCatalogPaths { get; set; } + } #endregion - + #region ConfigurationPropertyAttribute /// - /// The interface is traversed through reflection. This + /// The interface is traversed through reflection. This /// annotation provides a way to override the default configuration operation for /// each property. /// /// /// - /// public class MyClass : IConfiguration { + /// public class MyClass : Configuration { /// [ConfigurationPropertionOptions(Confidential=true)] /// public string MyProperty {get ; set;} /// } /// /// - [AttributeUsage(AttributeTargets.Property)] - public class ConfigurationPropertyAttribute : System.Attribute { - + [AttributeUsage(AttributeTargets.Property)] + public class ConfigurationPropertyAttribute : System.Attribute + { + /// /// Order in which this property is displayed. /// - public int Order {get;set;} + public int Order { get; set; } /// /// Is this a confidential property whose value should be /// encrypted by the application when persisted? /// - public bool Confidential {get;set;} + public bool Confidential { get; set; } /// /// Is this a required property? /// - public bool Required {get;set;} + public bool Required { get; set; } /// /// Change the default help message key. /// - public string HelpMessageKey {get;set;} + public string HelpMessageKey { get; set; } /// /// Change the default display message key. /// - public string DisplayMessageKey {get;set;} - - /** - * List of operations for which this property must be specified. - * This is used for the case where a connector may or may not - * implement certain operations depending in the configuration. - * The default value of "empty array" is special in that - * it means that this property is applicable to all operations. - * MUST be SPI operations - */ - public Type [] OperationTypes {get;set;} - - /** - * List of operations for which this property must be specified. - * This is used for the case where a connector may or may not - * implement certain operations depending in the configuration. - * The default value of "empty array" is special in that - * it means that this property is applicable to all operations. - */ - public SafeType [] Operations { - get { - Type [] types = OperationTypes; - SafeType [] rv = new SafeType[types.Length]; - for ( int i = 0; i < types.Length; i++ ) { + public string DisplayMessageKey { get; set; } + + /// + /// List of operations for which this property must be specified. + /// + /// + /// This is used for the case where a connector may or may not + /// implement certain operations depending in the configuration. + /// The default value of "empty array" is special in that + /// it means that this property is applicable to all operations. + /// MUST be SPI operations + /// + public Type[] OperationTypes { get; set; } + + /// + /// List of operations for which this property must be specified. + /// + /// + /// This is used for the case where a connector may or may not + /// implement certain operations depending in the configuration. + /// The default value of "empty array" is special in that + /// it means that this property is applicable to all operations. + /// + public SafeType[] Operations + { + get + { + Type[] types = OperationTypes; + SafeType[] rv = new SafeType[types.Length]; + for (int i = 0; i < types.Length; i++) + { rv[i] = SafeType.ForRawType(types[i]); } @@ -183,7 +232,8 @@ public SafeType [] Operations { /// /// Default constructor /// - public ConfigurationPropertyAttribute() { + public ConfigurationPropertyAttribute() + { Order = 1; Confidential = false; Required = false; @@ -191,43 +241,44 @@ public ConfigurationPropertyAttribute() { DisplayMessageKey = null; OperationTypes = new Type[0]; } - } + } #endregion - + #region Connector /// /// This is the main interface to declare a connector. Developers must implement - /// this interface. The life-cycle for a is as follows - /// is called then any of the operations implemented - /// in the Connector and finally dispose. The and + /// this interface. The life-cycle for a is as follows + /// is called then any of the operations implemented + /// in the Connector and finally dispose. The and /// allow for block operations. For instance bulk creates or /// deletes and the use of before and after actions. Once is - /// called the object is discarded. + /// called the object is discarded. /// - public interface Connector : IDisposable { - + public interface Connector : IDisposable + { + /// /// Initialize the connector with its configuration. For instance in a JDBC /// Connector this would include the database URL, password, and user. /// - /// instance of the object implemented by - /// the developer and populated with information - /// in order to initialize the . + /// instance of the object implemented by + /// the developer and populated with information + /// in order to initialize the . void Init(Configuration configuration); } #endregion - + #region PoolableConnector - /** - * To be implemented by Connectors that wish to be pooled. - */ - public interface PoolableConnector : Connector { - /** - * Checks to see if the connector is still alive. - * @throws RuntimeException If no longer alive. - */ + /// + /// To be implemented by Connectors that wish to be pooled. + /// + public interface PoolableConnector : Connector + { + /// + /// Checks to see if the connector is still alive. + /// + /// If no longer alive. void CheckAlive(); } #endregion - -} +} \ No newline at end of file diff --git a/Framework/SpiOperations.cs b/Framework/SpiOperations.cs index 07038a10..53d4cd89 100644 --- a/Framework/SpiOperations.cs +++ b/Framework/SpiOperations.cs @@ -29,117 +29,121 @@ namespace Org.IdentityConnectors.Framework.Spi.Operations { - - - /** - * Authenticate an object based on their unique identifier and password. - */ - public interface AuthenticateOp : SPIOperation { - - /** - * Simple authentication with two parameters presumed to be user name and - * password. The {@link Connector} developer is expected to attempt to - * authenticate these credentials natively. If the authentication fails the - * developer should throw a type of {@link RuntimeException} either - * {@link IllegalArgumentException} or if a native exception is available - * and if its of type {@link RuntimeException} simple throw it. If the - * native exception is not a {@link RuntimeException} wrap it in one and - * throw it. This will provide the most detail for logging problem and - * failed attempts. - *

- * The developer is of course encourage to try and throw the most - * informative exception as possible. In that regards there are several - * exceptions provided in the exceptions package. For instance one of the - * most common is {@link InvalidPassword}. - * - * @param username - * the name based credential for authentication. - * @param password - * the password based credential for authentication. - * @throws RuntimeException - * iff native authentication fails. If a native exception if - * available attempt to throw it. - */ + ///

+ /// Authenticate an object based on their unique identifier and password. + /// + public interface AuthenticateOp : SPIOperation + { + /// + /// Simple authentication with two parameters presumed to be user name and + /// password. + /// + /// + /// The developer is expected to attempt to + /// authenticate these credentials natively. If the authentication fails the + /// developer should throw a type of either + /// or if a native exception is available + /// and if its of type simple throw it. If the + /// native exception is not a wrap it in one and + /// throw it. This will provide the most detail for logging problem and + /// failed attempts. + /// + /// The developer is of course encourage to try and throw the most + /// informative exception as possible. In that regards there are several + /// exceptions provided in the exceptions package. For instance one of the + /// most common is . + /// + /// + /// the name based credential for authentication. + /// the password based credential for authentication. + /// iff native authentication fails. If a native exception if + /// available attempt to throw it. Uid Authenticate(ObjectClass objectClass, String username, GuardedString password, OperationOptions options); } - - public interface ResolveUsernameOp : SPIOperation { - - /** - * This is a companion to the simple {@link AuthenticateOp authentication} with two parameters - * presumed to be user name and password. The difference is that this - * method does not try to authenticate the credentials; instead, it - * return the {@link Uid} of the username that would be authenticated. - *

- * If the authentication fails the - * developer should throw a type of {@link RuntimeException} either - * {@link IllegalArgumentException} or if a native exception is available - * and if its of type {@link RuntimeException} simple throw it. If the - * native exception is not a {@link RuntimeException} wrap it in one and - * throw it. This will provide the most detail for logging problem and - * failed attempts. - *

- * The developer is of course encourage to try and throw the most - * informative exception as possible. In that regards there are several - * exceptions provided in the exceptions package. For instance one of the - * most common is {@link InvalidPasswordException}. - * - * @param objectClass The object class to use for authenticate. - * Will typically be an account. Will not be null. - * @param username - * the username that would be authenticated. Will not be null. - * @param options - * additional options that impact the way this operation is run. - * If the caller passes null, the framework will convert this into - * an empty set of options, so SPI need not worry - * about this ever being null. - * @return Uid The uid of the account that would be authenticated. - * @throws RuntimeException - * iff native authentication fails. If a native exception is - * available attempt to throw it. - */ + + public interface ResolveUsernameOp : SPIOperation + { + ///

+ /// This is a companion to the simple with two parameters + /// presumed to be user name and password. + /// + /// + /// The difference is that this + /// method does not try to authenticate the credentials; instead, it + /// return the of the username that would be authenticated. + /// + /// If the authentication fails the + /// developer should throw a type of either + /// or if a native exception is available + /// and if its of type simple throw it. If the + /// native exception is not a wrap it in one and + /// throw it. This will provide the most detail for logging problem and + /// failed attempts. + /// + /// + /// The developer is of course encourage to try and throw the most + /// informative exception as possible. In that regards there are several + /// exceptions provided in the exceptions package. For instance one of the + /// most common is . + /// + /// + /// The object class to use for authenticate. + /// Will typically be an account. Will not be null. + /// the username that would be authenticated. Will not be null. + /// additional options that impact the way this operation is run. + /// If the caller passes null, the framework will convert this into + /// an empty set of options, so SPI need not worry + /// about this ever being null. + /// Uid The uid of the account that would be authenticated. + /// iff native authentication fails. If a native exception is + /// available attempt to throw it. Uid ResolveUsername(ObjectClass objectClass, String username, OperationOptions options); } - /** - * The {@link Connector} developer is responsible for taking the attributes - * given (which always includes the {@link ObjectClass}) and create an object - * and its {@link Uid}. The {@link Connector} developer must return the - * {@link Uid} so that the caller can refer to the created object. - *

- * The {@link Connector} developer should make a best effort to create the - * object otherwise throw an informative {@link RuntimeException} telling the - * caller why the operation could not be completed. It reasonable to use - * defaults for required {@link Attribute}s as long as they are documented. - * - * @author Will Droste - * @version $Revision $ - * @since 1.0 - */ - public interface CreateOp : SPIOperation { - /** - * The {@link Connector} developer is responsible for taking the attributes - * given (which always includes the {@link ObjectClass}) and create an - * object and its {@link Uid}. The {@link Connector} developer must return - * the {@link Uid} so that the caller can refer to the created object. - * - * @param name - * specifies the name of the object to create. - * @param attrs - * includes all the attributes necessary to create the resource - * object including the {@link ObjectClass} attribute. - * @return the unique id for the object that is created. For instance in - * LDAP this would be the 'dn', for a database this would be the - * primary key, and for 'ActiveDirectory' this would be the GUID. - */ + ///

+ /// The developer is responsible for taking the attributes + /// given (which always includes the ) and create an object + /// and its . + /// + /// + /// The developer must return the + /// so that the caller can refer to the created object. + /// + /// The developer should make a best effort to create the + /// object otherwise throw an informative telling the + /// caller why the operation could not be completed. It reasonable to use + /// defaults for required s as long as they are documented. + /// + /// + /// Will Droste + /// $Revision $ + /// 1.0 + public interface CreateOp : SPIOperation + { + /// + /// The developer is responsible for taking the attributes + /// given (which always includes the ) and create an + /// object and its . + /// + /// + /// The developer must return + /// the so that the caller can refer to the created object. + /// + /// specifies the name of the object to create. + /// includes all the attributes necessary to create the resource + /// object including the attribute. + /// the unique id for the object that is created. For instance in + /// LDAP this would be the 'dn', for a database this would be the + /// primary key, and for 'ActiveDirectory' this would be the GUID. Uid Create(ObjectClass oclass, ICollection attrs, OperationOptions options); } - + /// /// Deletes an object with the specified Uid and ObjectClass on the /// resource. /// - public interface DeleteOp : SPIOperation { + public interface DeleteOp : SPIOperation + { /// /// Delete the object that the specified Uid identifies (if any). /// @@ -148,309 +152,326 @@ public interface DeleteOp : SPIOperation { /// Throws UnknowUid if the object does not exist. void Delete(ObjectClass objClass, Uid uid, OperationOptions options); } - - public interface SchemaOp : SPIOperation { - /** - * Determines what types of objects this {@link Connector} supports. This - * method is considered an operation since determining supported objects may - * require configuration information and allows this determination to be - * dynamic. - * - * @return basic schema supported by this {@link Connector}. - */ + public interface SchemaOp : SPIOperation + { + /// + /// Determines what types of objects this supports. + /// + /// + /// This + /// method is considered an operation since determining supported objects may + /// require configuration information and allows this determination to be + /// dynamic. + /// + /// basic schema supported by this . Schema Schema(); } - /** - * Operation that runs a script in the environment of the connector. - * (Compare to {@link ScriptOnResourceOp}, which runs a script - * on the target resource that the connector manages.) - * A connector that intends to provide to scripts - * more than is required by the basic contract - * specified in the javadoc for {@link ScriptOnConnectorApiOp} - * should implement this interface. - *

- * Each connector that implements this interface must support - * at least the behavior specified by {@link ScriptOnConnectorApiOp}. - * A connector also may expose additional variables for use by scripts - * and may respond to specific {@link OperationOptions options}. - * Each connector that implements this interface - * must describe in its javadoc as available "for use by connector scripts" - * any such additional variables or supported options. - */ - public interface ScriptOnConnectorOp : SPIOperation { - - /** - * Runs the script request. - * @param request The script and arguments to run. - * @param options Additional options that control how the script is - * run. - * @return The result of the script. The return type must be - * a type that the framework supports for serialization. - * See {@link ObjectSerializerFactory} for a list of supported types. - */ + ///

+ /// Operation that runs a script in the environment of the connector. + /// + /// + /// (Compare to , which runs a script + /// on the target resource that the connector manages.) + /// A connector that intends to provide to scripts + /// more than is required by the basic contract + /// specified in the javadoc for + /// should implement this interface. + /// + /// Each connector that implements this interface must support + /// at least the behavior specified by . + /// A connector also may expose additional variables for use by scripts + /// and may respond to specific . + /// Each connector that implements this interface + /// must describe in its javadoc as available "for use by connector scripts" + /// any such additional variables or supported options. + /// + /// + public interface ScriptOnConnectorOp : SPIOperation + { + + /// + /// Runs the script request. + /// + /// The script and arguments to run. + /// Additional options that control how the script is + /// run. + /// The result of the script. The return type must be + /// a type that the framework supports for serialization. + /// See for a list of supported types. Object RunScriptOnConnector(ScriptContext request, OperationOptions options); } - /** - * Operation that runs a script directly on a target resource. - * (Compare to {@link ScriptOnConnectorOp}, which runs a script - * in the context of a particular connector.) - *

- * A connector that intends to support - * {@link ScriptOnResourceApiOp} - * should implement this interface. Each connector that implements - * this interface must document which script languages the connector supports, - * as well as any supported {@link OperationOptions}. - */ - public interface ScriptOnResourceOp : SPIOperation { - /** - * Run the specified script on the target resource - * that this connector manages. - * @param request The script and arguments to run. - * @param options Additional options that control - * how the script is run. - * @return The result of the script. The return type must be - * a type that the framework supports for serialization. - * See {@link ObjectSerializerFactory} for a list of supported types. - */ + ///

+ /// Operation that runs a script directly on a target resource. + /// + /// + /// (Compare to , which runs a script + /// in the context of a particular connector.) + /// + /// A connector that intends to support + /// + /// should implement this interface. Each connector that implements + /// this interface must document which script languages the connector supports, + /// as well as any supported . + /// + /// + public interface ScriptOnResourceOp : SPIOperation + { + /// + /// Run the specified script on the target resource + /// that this connector manages. + /// + /// The script and arguments to run. + /// Additional options that control + /// how the script is run. + /// The result of the script. The return type must be + /// a type that the framework supports for serialization. + /// See for a list of supported types. Object RunScriptOnResource(ScriptContext request, OperationOptions options); } - - /** - * Implement this interface to allow the Connector to search for resource - * objects. - */ - public interface SearchOp : SPIOperation where T : class { - /** - * Creates a filter translator that will translate - * a specified filter to the native filter. The - * translated filters will be subsequently passed to - * {@link #search(ObjectClass, Object, ResultsHandler)} - * @param oclass The object class for the search. Will never be null. - * @param options - * additional options that impact the way this operation is run. - * If the caller passes null, the framework will convert this into - * an empty set of options, so SPI need not worry - * about this ever being null. - * @return A filter translator. - */ + + /// + /// Implement this interface to allow the Connector to search for resource + /// objects. + /// + public interface SearchOp : SPIOperation where T : class + { + /// + /// Creates a filter translator that will translate + /// a specified filter to the native filter. + /// + /// + /// The + /// translated filters will be subsequently passed to + /// + /// + /// The object class for the search. Will never be null. + /// additional options that impact the way this operation is run. + /// If the caller passes null, the framework will convert this into + /// an empty set of options, so SPI need not worry + /// about this ever being null. + /// A filter translator. FilterTranslator CreateFilterTranslator(ObjectClass oclass, OperationOptions options); - /** - * This will be called by ConnectorFacade, once for each native query produced - * by the FilterTranslator. If there is more than one query the results will - * automatically be merged together and duplicates eliminated. NOTE - * that this implies an in-memory data structure that holds a set of - * Uids, so memory usage in the event of multiple queries will be O(N) - * where N is the number of results. That is why it is important that - * the FilterTranslator implement OR if possible. - * @param oclass The object class for the search. Will never be null. - * @param query The native query to run. A value of null means 'return everything for the given object class'. - * @param handler - * Results should be returned to this handler - * @param options - * additional options that impact the way this operation is run. - * If the caller passes null, the framework will convert this into - * an empty set of options, so SPI need not worry - * about this ever being null. - */ + /// + /// This will be called by ConnectorFacade, once for each native query produced + /// by the FilterTranslator. + /// + /// + /// If there is more than one query the results will + /// automatically be merged together and duplicates eliminated. NOTE + /// that this implies an in-memory data structure that holds a set of + /// Uids, so memory usage in the event of multiple queries will be O(N) + /// where N is the number of results. That is why it is important that + /// the FilterTranslator implement OR if possible. + /// + /// The object class for the search. Will never be null. + /// The native query to run. A value of null means 'return everything for the given object class'. + /// Results should be returned to this handler + /// additional options that impact the way this operation is run. + /// If the caller passes null, the framework will convert this into + /// an empty set of options, so SPI need not worry + /// about this ever being null. void ExecuteQuery(ObjectClass oclass, T query, ResultsHandler handler, OperationOptions options); } - /** - * Receive synchronization events from the resource. - * - * @see SyncApiOp - */ - public interface SyncOp : SPIOperation { - /** - * Perform a synchronization. - * - * @param objClass - * The object class to synchronize. Must not be null. - * @param token - * The token representing the last token from the previous sync. - * Should be null if this is the first sync for the given - * resource. - * @param handler - * The result handler Must not be null. - * @param options - * additional options that impact the way this operation is run. - * If the caller passes null, the framework will convert this into - * an empty set of options, so SPI need not worry - * about this ever being null. - */ + /// + /// Receive synchronization events from the resource. + /// + /// + public interface SyncOp : SPIOperation + { + /// + /// Perform a synchronization. + /// + /// The object class to synchronize. Must not be null. + /// The token representing the last token from the previous sync. + /// Should be null if this is the first sync for the given + /// resource. + /// The result handler Must not be null. + /// additional options that impact the way this operation is run. + /// If the caller passes null, the framework will convert this into + /// an empty set of options, so SPI need not worry + /// about this ever being null. void Sync(ObjectClass objClass, SyncToken token, SyncResultsHandler handler, OperationOptions options); - /** - * Returns the token corresponding to the latest sync delta. - * This is to support applications that may wish to sync starting - * "now". - * @return The latest token or null if there is no sync data. - */ + /// + /// Returns the token corresponding to the latest sync delta. + /// + /// + /// This is to support applications that may wish to sync starting + /// "now". + /// + /// The latest token or null if there is no sync data. SyncToken GetLatestSyncToken(ObjectClass objectClass); } - - /** - * The developer of a Connector should implement either this interface or the - * {@link UpdateAttributeValuesOp} interface if the Connector will allow an authorized - * caller to update (i.e., modify or replace) objects on the target resource. - *

- * This update method is simpler to implement than {link UpdateAttributeValuesOp}, - * which must handle any of several different types of update that the caller - * may specify. However a true implementation of {link UpdateAttributeValuesOp} - * offers better performance and atomicity semantics. - * - * @author Will Droste - * @version $Revision $ - * @since 1.0 - */ - public interface UpdateOp : SPIOperation { - /** - * Update the object specified by the {@link ObjectClass} and {@link Uid}, - * replacing the current values of each attribute with the values - * provided. - *

- * For each input attribute, replace - * all of the current values of that attribute in the target object with - * the values of that attribute. - *

- * If the target object does not currently contain an attribute that the - * input set contains, then add this - * attribute (along with the provided values) to the target object. - *

- * If the value of an attribute in the input set is - * {@code null}, then do one of the following, depending on - * which is most appropriate for the target: - *

    - *
  • If possible, remove that attribute from the target - * object entirely.
  • - *
  • Otherwise, replace all of the current values of that - * attribute in the target object with a single value of - * {@code null}.
  • - *
- * @param objclass - * the type of object to modify. Will never be null. - * @param uid - * the uid of the object to modify. Will never be null. - * @param replaceAttributes - * set of new {@link Attribute}. the values in this set - * represent the new, merged values to be applied to the object. - * This set may also include {@link OperationalAttributes operational attributes}. - * Will never be null. - * @param options - * additional options that impact the way this operation is run. - * Will never be null. - * @return the {@link Uid} of the updated object in case the update changes - * the formation of the unique identifier. - * @throws UnknownUidException - * iff the {@link Uid} does not exist on the resource. - */ + + /// + /// The developer of a Connector should implement either this interface or the + /// interface if the Connector will allow an authorized + /// caller to update (i.e., modify or replace) objects on the target resource. + /// + /// + /// + /// This update method is simpler to implement than {link UpdateAttributeValuesOp}, + /// which must handle any of several different types of update that the caller + /// may specify. However a true implementation of {link UpdateAttributeValuesOp} + /// offers better performance and atomicity semantics. + /// + /// + /// Will Droste + /// $Revision $ + /// 1.0 + public interface UpdateOp : SPIOperation + { + /// + /// Update the object specified by the and , + /// replacing the current values of each attribute with the values + /// provided. + /// + /// + /// + /// For each input attribute, replace + /// all of the current values of that attribute in the target object with + /// the values of that attribute. + /// + /// + /// If the target object does not currently contain an attribute that the + /// input set contains, then add this + /// attribute (along with the provided values) to the target object. + /// + /// + /// If the value of an attribute in the input set is + /// null, then do one of the following, depending on + /// which is most appropriate for the target: + /// + /// + /// If possible, remove that attribute from the target + /// object entirely. + /// + /// + /// + /// Otherwise, replace all of the current values of that + /// attribute in the target object with a single value of + /// null. + /// + /// + /// + /// + /// + /// the type of object to modify. Will never be null. + /// the uid of the object to modify. Will never be null. + /// set of new . the values in this set + /// represent the new, merged values to be applied to the object. + /// This set may also include . + /// Will never be null. + /// additional options that impact the way this operation is run. + /// Will never be null. + /// the of the updated object in case the update changes + /// the formation of the unique identifier. + /// iff the does not exist on the resource. Uid Update(ObjectClass objclass, Uid uid, ICollection replaceAttributes, OperationOptions options); } - - /** - * More advanced implementation of {@link UpdateOp} to be implemented by - * connectors that wish to offer better performance and atomicity semantics - * for the methods {@link UpdateApiOp#addAttributeValues(ObjectClass, Uid, Set, OperationOptions)} - * and {@link UpdateApiOp#removeAttributeValues(ObjectClass, Uid, Set, OperationOptions)}. - */ - public interface UpdateAttributeValuesOp : UpdateOp { - - /** - * Update the object specified by the {@link ObjectClass} and {@link Uid}, - * adding to the current values of each attribute the values provided. - *

- * For each attribute that the input set contains, add to - * the current values of that attribute in the target object all of the - * values of that attribute in the input set. - *

- * NOTE that this does not specify how to handle duplicate values. - * The general assumption for an attribute of a {@code ConnectorObject} - * is that the values for an attribute may contain duplicates. - * Therefore, in general simply append the provided values - * to the current value for each attribute. - *

- * @param objclass - * the type of object to modify. Will never be null. - * @param uid - * the uid of the object to modify. Will never be null. - * @param valuesToAdd - * set of {@link Attribute} deltas. The values for the attributes - * in this set represent the values to add to attributes in the object. - * merged. This set will never include {@link OperationalAttributes operational attributes}. - * Will never be null. - * @param options - * additional options that impact the way this operation is run. - * Will never be null. - * @return the {@link Uid} of the updated object in case the update changes - * the formation of the unique identifier. - * @throws UnknownUidException - * iff the {@link Uid} does not exist on the resource. - */ + + ///

+ /// More advanced implementation of to be implemented by + /// connectors that wish to offer better performance and atomicity semantics + /// for the methods + /// and . + /// + public interface UpdateAttributeValuesOp : UpdateOp + { + + /// + /// Update the object specified by the and , + /// adding to the current values of each attribute the values provided. + /// + /// + /// + /// For each attribute that the input set contains, add to + /// the current values of that attribute in the target object all of the + /// values of that attribute in the input set. + /// + /// + /// NOTE that this does not specify how to handle duplicate values. + /// The general assumption for an attribute of a ConnectorObject + /// is that the values for an attribute may contain duplicates. + /// Therefore, in general simply append the provided values + /// to the current value for each attribute. + /// + /// + /// + /// + /// the type of object to modify. Will never be null. + /// the uid of the object to modify. Will never be null. + /// set of deltas. The values for the attributes + /// in this set represent the values to add to attributes in the object. + /// merged. This set will never include . + /// Will never be null. + /// additional options that impact the way this operation is run. + /// Will never be null. + /// the of the updated object in case the update changes + /// the formation of the unique identifier. + /// iff the does not exist on the resource. Uid AddAttributeValues(ObjectClass objclass, Uid uid, ICollection valuesToAdd, OperationOptions options); - - /** - * Update the object specified by the {@link ObjectClass} and {@link Uid}, - * removing from the current values of each attribute the values provided. - *

- * For each attribute that the input set contains, - * remove from the current values of that attribute in the target object - * any value that matches one of the values of the attribute from the input set. - *

- * NOTE that this does not specify how to handle unmatched values. - * The general assumption for an attribute of a {@code ConnectorObject} - * is that the values for an attribute are merely representational state. - * Therefore, the implementer should simply ignore any provided value - * that does not match a current value of that attribute in the target - * object. Deleting an unmatched value should always succeed. - * @param objclass - * the type of object to modify. Will never be null. - * @param uid - * the uid of the object to modify. Will never be null. - * @param valuesToRemove - * set of {@link Attribute} deltas. The values for the attributes - * in this set represent the values to remove from attributes in the object. - * merged. This set will never include {@link OperationalAttributes operational attributes}. - * Will never be null. - * @param options - * additional options that impact the way this operation is run. - * Will never be null.. - * @return the {@link Uid} of the updated object in case the update changes - * the formation of the unique identifier. - * @throws UnknownUidException - * iff the {@link Uid} does not exist on the resource. - */ + + ///

+ /// Update the object specified by the and , + /// removing from the current values of each attribute the values provided. + /// + /// + /// + /// For each attribute that the input set contains, + /// remove from the current values of that attribute in the target object + /// any value that matches one of the values of the attribute from the input set. + /// + /// + /// NOTE that this does not specify how to handle unmatched values. + /// The general assumption for an attribute of a ConnectorObject + /// is that the values for an attribute are merely representational state. + /// Therefore, the implementer should simply ignore any provided value + /// that does not match a current value of that attribute in the target + /// object. Deleting an unmatched value should always succeed. + /// + /// + /// the type of object to modify. Will never be null. + /// the uid of the object to modify. Will never be null. + /// set of deltas. The values for the attributes + /// in this set represent the values to remove from attributes in the object. + /// merged. This set will never include . + /// Will never be null. + /// additional options that impact the way this operation is run. + /// Will never be null.. + /// the of the updated object in case the update changes + /// the formation of the unique identifier. + /// iff the does not exist on the resource. Uid RemoveAttributeValues(ObjectClass objclass, Uid uid, ICollection valuesToRemove, OperationOptions options); - } - public interface TestOp : SPIOperation { - - /** - * Tests connectivity and validity of the {@link Configuration}. - * - * @throws RuntimeException - * iff the {@link Configuration} is not valid or a - * {@link Connection} to the resource could not be established. - */ + public interface TestOp : SPIOperation + { + /// + /// Tests connectivity and validity of the . + /// + /// iff the is not valid or a + /// to the resource could not be established. void Test(); } - - /** - * Tagging interface for the {@link Connector} SPI. - */ - public interface SPIOperation { - + + /// + /// Tagging interface for the SPI. + /// + public interface SPIOperation + { } -} +} \ No newline at end of file diff --git a/FrameworkInternal/Api.cs b/FrameworkInternal/Api.cs index 6e815514..8b66f069 100644 --- a/FrameworkInternal/Api.cs +++ b/FrameworkInternal/Api.cs @@ -50,121 +50,143 @@ namespace Org.IdentityConnectors.Framework.Impl.Api /// /// Internal class, public only for unit tests /// - public class ConfigurationPropertyImpl : ConfigurationProperty { - + public class ConfigurationPropertyImpl : ConfigurationProperty + { private ICollection> _operations; - public ICollection> Operations { - get { + public ICollection> Operations + { + get + { return _operations; } - set { + set + { _operations = CollectionUtil.NewReadOnlySet(value); } } - + public ConfigurationPropertiesImpl Parent { get; set; } - + public int Order { get; set; } - + public string Name { get; set; } - + public string HelpMessageKey { get; set; } - + public string DisplayMessageKey { get; set; } - - public string GetHelpMessage(string def) { - return FormatMessage(HelpMessageKey,def); + + public string GetHelpMessage(string def) + { + return FormatMessage(HelpMessageKey, def); } - - public string GetDisplayName(string def) { - return FormatMessage(DisplayMessageKey,def); + + public string GetDisplayName(string def) + { + return FormatMessage(DisplayMessageKey, def); } - + public object Value { get; set; } - + public Type ValueType { get; set; } - + public bool IsConfidential { get; set; } - + public bool IsRequired { get; set; } public override int GetHashCode() { return Name.GetHashCode(); } - - public override bool Equals(Object o) { + + public override bool Equals(Object o) + { ConfigurationPropertyImpl other = o as ConfigurationPropertyImpl; - if ( other != null ) { - if (!Name.Equals(other.Name)) { + if (other != null) + { + if (!Name.Equals(other.Name)) + { return false; } - if (!CollectionUtil.Equals(Value,other.Value)) { + if (!CollectionUtil.Equals(Value, other.Value)) + { return false; } - if (Order != other.Order) { + if (Order != other.Order) + { return false; } - if (!CollectionUtil.Equals(HelpMessageKey,other.HelpMessageKey)) { + if (!CollectionUtil.Equals(HelpMessageKey, other.HelpMessageKey)) + { return false; } - if (!CollectionUtil.Equals(DisplayMessageKey,other.DisplayMessageKey)) { + if (!CollectionUtil.Equals(DisplayMessageKey, other.DisplayMessageKey)) + { return false; } - if (IsConfidential != other.IsConfidential) { + if (IsConfidential != other.IsConfidential) + { return false; } - if (IsRequired != other.IsRequired) { + if (IsRequired != other.IsRequired) + { return false; } - if (!CollectionUtil.Equals(ValueType,other.ValueType)) { + if (!CollectionUtil.Equals(ValueType, other.ValueType)) + { return false; } - if (!CollectionUtil.Equals(_operations,other._operations)) { + if (!CollectionUtil.Equals(_operations, other._operations)) + { return false; } - + return true; } return false; } - private String FormatMessage(String key, String dflt, params object [] args) { + private String FormatMessage(String key, String dflt, params object[] args) + { APIConfigurationImpl apiConfig = Parent.Parent; - ConnectorMessages messages = + ConnectorMessages messages = apiConfig.ConnectorInfo.Messages; return messages.Format(key, dflt, args); } } #endregion - + #region ConfigurationPropertiesImpl /// /// Internal class, public only for unit tests /// - public class ConfigurationPropertiesImpl : ConfigurationProperties { + public class ConfigurationPropertiesImpl : ConfigurationProperties + { //properties, sorted by "order" private IList _properties; //property names, sorted by "order private IList _propertyNames; private IDictionary _propertiesByName; - - public IList Properties { - get { + + public IList Properties + { + get + { return _properties; } - set { - ConfigurationPropertyImpl [] arr = + set + { + ConfigurationPropertyImpl[] arr = value == null ? new ConfigurationPropertyImpl[0] : value.ToArray(); - + Array.Sort(arr, - (p1,p2)=> {return p1.Order.CompareTo(p2.Order);}); - _properties = + (p1, p2) => { return p1.Order.CompareTo(p2.Order); }); + _properties = CollectionUtil.NewReadOnlyList(arr); IList propertyNames = new List(); IDictionary propertiesByName = new Dictionary(); - foreach (ConfigurationPropertyImpl property in _properties) { + foreach (ConfigurationPropertyImpl property in _properties) + { propertyNames.Add(property.Name); propertiesByName[property.Name] = property; property.Parent = this; @@ -173,32 +195,39 @@ public IList Properties { _propertiesByName = CollectionUtil.AsReadOnlyDictionary(propertiesByName); } } - public ConfigurationProperty GetProperty(String name) { - return CollectionUtil.GetValue(_propertiesByName,name,null); + public ConfigurationProperty GetProperty(String name) + { + return CollectionUtil.GetValue(_propertiesByName, name, null); } - public IList PropertyNames { - get { + public IList PropertyNames + { + get + { return _propertyNames; } } public APIConfigurationImpl Parent { get; set; } - public void SetPropertyValue(String name, object val) { + public void SetPropertyValue(String name, object val) + { ConfigurationProperty property = GetProperty(name); - if ( property == null ) { - String MSG = "Property '"+name+"' does not exist."; + if (property == null) + { + String MSG = "Property '" + name + "' does not exist."; throw new ArgumentException(MSG); } property.Value = val; } - + public override int GetHashCode() { return CollectionUtil.GetHashCode(_properties); } - - public override bool Equals(object o) { + + public override bool Equals(object o) + { ConfigurationPropertiesImpl other = o as ConfigurationPropertiesImpl; - if ( other != null ) { + if (other != null) + { return CollectionUtil.SetsEqual(_properties, other._properties); } @@ -206,117 +235,140 @@ public override bool Equals(object o) { } } #endregion - + #region APIConfigurationImpl /// /// Internal class, public only for unit tests /// - public class APIConfigurationImpl : APIConfiguration { - + public class APIConfigurationImpl : APIConfiguration + { private ObjectPoolConfiguration _connectorPooling; - + private ConfigurationPropertiesImpl _configurationProperties; private ICollection> _supportedOperations = CollectionUtil.NewReadOnlySet>(new SafeType[0]); - + private IDictionary, int> _timeoutMap = new Dictionary, int>(); - - public ConfigurationProperties ConfigurationProperties { - get { + + public ConfigurationProperties ConfigurationProperties + { + get + { return _configurationProperties; } - set { - if (_configurationProperties != null) { + set + { + if (_configurationProperties != null) + { _configurationProperties.Parent = null; } _configurationProperties = (ConfigurationPropertiesImpl)value; - if (_configurationProperties != null) { + if (_configurationProperties != null) + { _configurationProperties.Parent = this; - } + } } } - public IDictionary, int> TimeoutMap { - get { + public IDictionary, int> TimeoutMap + { + get + { return _timeoutMap; } - set { + set + { _timeoutMap = value; } } - public bool IsConnectorPoolingSupported { get; set;} - public ObjectPoolConfiguration ConnectorPoolConfiguration - { - get { - if (_connectorPooling == null) { + public bool IsConnectorPoolingSupported { get; set; } + public ObjectPoolConfiguration ConnectorPoolConfiguration + { + get + { + if (_connectorPooling == null) + { _connectorPooling = new ObjectPoolConfiguration(); } return _connectorPooling; } - set { + set + { _connectorPooling = value; } } - public ICollection> SupportedOperations { - get { + public ICollection> SupportedOperations + { + get + { return _supportedOperations; } - set { + set + { _supportedOperations = CollectionUtil.NewReadOnlySet>(value); } } - - public int GetTimeout(SafeType operation) { - return CollectionUtil.GetValue(_timeoutMap,operation, + + public int GetTimeout(SafeType operation) + { + return CollectionUtil.GetValue(_timeoutMap, operation, APIConstants.NO_TIMEOUT); } - public void SetTimeout(SafeType operation, int timeout) { + public void SetTimeout(SafeType operation, int timeout) + { _timeoutMap[operation] = timeout; } - + public AbstractConnectorInfo ConnectorInfo { get; set; } - + public int ProducerBufferSize { get; set; } - public APIConfigurationImpl() { + public APIConfigurationImpl() + { ProducerBufferSize = 100; } } #endregion - + #region AbstractConnectorInfo /// /// internal class, public only for unit tests /// - public class AbstractConnectorInfo : ConnectorInfo { - + public class AbstractConnectorInfo : ConnectorInfo + { private APIConfigurationImpl _defaultAPIConfiguration; - - - public string GetConnectorDisplayName() { + + + public string GetConnectorDisplayName() + { return Messages.Format(ConnectorDisplayNameKey, - ConnectorKey.ConnectorName); + ConnectorKey.ConnectorName); } - + public ConnectorKey ConnectorKey { get; set; } - + public string ConnectorDisplayNameKey { get; set; } - + public ConnectorMessages Messages { get; set; } - - public APIConfigurationImpl DefaultAPIConfiguration { - get { + + public APIConfigurationImpl DefaultAPIConfiguration + { + get + { return _defaultAPIConfiguration; } - set { - if ( value != null ) { + set + { + if (value != null) + { value.ConnectorInfo = this; } _defaultAPIConfiguration = value; } } - public APIConfiguration CreateDefaultAPIConfiguration() { - APIConfigurationImpl rv = + public APIConfiguration CreateDefaultAPIConfiguration() + { + APIConfigurationImpl rv = (APIConfigurationImpl) SerializerUtil.CloneObject(_defaultAPIConfiguration); rv.ConnectorInfo = this; @@ -329,108 +381,131 @@ public APIConfiguration CreateDefaultAPIConfiguration() { /// /// internal class, public only for unit tests /// - public class ConnectorMessagesImpl : ConnectorMessages { - - private IDictionary> - _catalogs = new Dictionary>(); - - public String Format(String key, String dflt, params object [] args) { - if ( key == null ) { + public class ConnectorMessagesImpl : ConnectorMessages + { + private IDictionary> + _catalogs = new Dictionary>(); + + public String Format(String key, String dflt, params object[] args) + { + if (key == null) + { return dflt; } CultureInfo locale = CultureInfo.CurrentUICulture; - if ( locale == null ) { + if (locale == null) + { locale = CultureInfo.CurrentCulture; } - if ( dflt == null ) { + if (dflt == null) + { dflt = key; } CultureInfo foundCulture = locale; IDictionary - catalog = CollectionUtil.GetValue(_catalogs,foundCulture,null); + catalog = CollectionUtil.GetValue(_catalogs, foundCulture, null); //check neutral culture - if ( catalog == null ) { + if (catalog == null) + { foundCulture = foundCulture.Parent; - catalog = CollectionUtil.GetValue(_catalogs,foundCulture,null); + catalog = CollectionUtil.GetValue(_catalogs, foundCulture, null); } //check invariant culture - if ( catalog == null ) { + if (catalog == null) + { foundCulture = foundCulture.Parent; - catalog = CollectionUtil.GetValue(_catalogs,foundCulture,null); + catalog = CollectionUtil.GetValue(_catalogs, foundCulture, null); } String message = null; - if ( catalog != null ) { - message = CollectionUtil.GetValue(catalog,key,null); + if (catalog != null) + { + message = CollectionUtil.GetValue(catalog, key, null); } - if ( message == null ) { - message = GetFrameworkMessage(locale,key); + if (message == null) + { + message = GetFrameworkMessage(locale, key); } - if ( message == null ) { + if (message == null) + { return dflt; } - else { + else + { //TODO: think more about this since the formatting //is slightly different than Java - return String.Format(foundCulture,message,args); + return String.Format(foundCulture, message, args); } } - + private String GetFrameworkMessage(CultureInfo culture, String key) { ResourceManager manager = new ResourceManager("Org.IdentityConnectors.Resources", typeof(ConnectorMessagesImpl).Assembly); - String contents = (String)manager.GetObject(key,culture); + String contents = (String)manager.GetObject(key, culture); return contents; } - - public IDictionary> Catalogs { - get { + + public IDictionary> Catalogs + { + get + { return _catalogs; } - set { - if ( value == null ) { - _catalogs = - new Dictionary>(); + set + { + if (value == null) + { + _catalogs = + new Dictionary>(); } - else { + else + { _catalogs = value; } } } } #endregion - + #region ConnectorInfoManagerFactoryImpl public sealed class ConnectorInfoManagerFactoryImpl : - ConnectorInfoManagerFactory { - private class RemoteManagerKey { + ConnectorInfoManagerFactory + { + private class RemoteManagerKey + { private readonly String _host; private readonly int _port; - - public RemoteManagerKey(RemoteFrameworkConnectionInfo info) { + + public RemoteManagerKey(RemoteFrameworkConnectionInfo info) + { _host = info.Host; _port = info.Port; } - - public override bool Equals(Object o) { - if ( o is RemoteManagerKey ) { + + public override bool Equals(Object o) + { + if (o is RemoteManagerKey) + { RemoteManagerKey other = (RemoteManagerKey)o; - if (!_host.Equals(other._host)) { + if (!_host.Equals(other._host)) + { return false; } - if (_port != other._port) { + if (_port != other._port) + { return false; } return true; } return false; } - - public override int GetHashCode() { - return _host.GetHashCode()^_port; + + public override int GetHashCode() + { + return _host.GetHashCode() ^ _port; } - + } private Object LOCAL_LOCK = new Object(); @@ -438,62 +513,66 @@ public override int GetHashCode() { private ConnectorInfoManager _localManagerCache; - - private IDictionary - _remoteManagerCache = new Dictionary(); - - public ConnectorInfoManagerFactoryImpl() { - - } - - - - public override void ClearRemoteCache() { - lock (REMOTE_LOCK) { + + private IDictionary + _remoteManagerCache = new Dictionary(); + + public ConnectorInfoManagerFactoryImpl() + { + + } + + public override void ClearRemoteCache() + { + lock (REMOTE_LOCK) + { _remoteManagerCache.Clear(); } } - - + public override ConnectorInfoManager GetLocalManager() { - lock (LOCAL_LOCK) { + lock (LOCAL_LOCK) + { ConnectorInfoManager rv = _localManagerCache; - if ( rv == null ) { + if (rv == null) + { rv = new LocalConnectorInfoManagerImpl(); } _localManagerCache = rv; - return rv; + return rv; } } - + public override ConnectorInfoManager GetRemoteManager(RemoteFrameworkConnectionInfo info) { RemoteManagerKey key = new RemoteManagerKey(info); - lock (REMOTE_LOCK) { - RemoteConnectorInfoManagerImpl rv = CollectionUtil.GetValue(_remoteManagerCache,key,null); - if ( rv == null ) { + lock (REMOTE_LOCK) + { + RemoteConnectorInfoManagerImpl rv = CollectionUtil.GetValue(_remoteManagerCache, key, null); + if (rv == null) + { rv = new RemoteConnectorInfoManagerImpl(info); } _remoteManagerCache[key] = rv; return rv.Derive(info); } } - + } #endregion #region AbstractConnectorFacade - internal abstract class AbstractConnectorFacade : ConnectorFacade { - + internal abstract class AbstractConnectorFacade : ConnectorFacade + { private readonly APIConfigurationImpl _configuration; - - - /** - * Builds up the maps of supported operations and calls. - */ - public AbstractConnectorFacade(APIConfigurationImpl configuration) { - Assertions.NullCheck(configuration,"configuration"); + + /// + /// Builds up the maps of supported operations and calls. + /// + public AbstractConnectorFacade(APIConfigurationImpl configuration) + { + Assertions.NullCheck(configuration, "configuration"); //clone in case application tries to modify //after the fact. this is necessary to //ensure thread-safety of a ConnectorFacade @@ -501,112 +580,94 @@ public AbstractConnectorFacade(APIConfigurationImpl configuration) { //pool, so it is important that it not be modified. _configuration = (APIConfigurationImpl)SerializerUtil.CloneObject(configuration); //parent ref not included in the clone - _configuration.ConnectorInfo=(configuration.ConnectorInfo); - } - - /** - * Return an instance of an API operation. - * - * @return null if the operation is not support otherwise - * return an instance of the operation. - * @see com.sun.openconnectors.framework.api.ConnectorFacade#getOperation(java.lang.Class) - */ - public APIOperation GetOperation(SafeType api) { - if (!SupportedOperations.Contains(api)) { + _configuration.ConnectorInfo = (configuration.ConnectorInfo); + } + + /// + /// Return an instance of an API operation. + /// + /// + /// null if the operation is not support otherwise + /// return an instance of the operation. + /// + public APIOperation GetOperation(SafeType api) + { + if (!SupportedOperations.Contains(api)) + { return null; } return GetOperationImplementation(api); } - - /** - * {@inheritDoc} - */ - public ICollection> SupportedOperations { - get { + + public ICollection> SupportedOperations + { + get + { return _configuration.SupportedOperations; } } - + // ======================================================================= // Operation API Methods // ======================================================================= - /** - * {@inheritDoc} - */ - public Schema Schema() { - return ((SchemaApiOp) this.GetOperationCheckSupported(SafeType.Get())) + public Schema Schema() + { + return ((SchemaApiOp)this.GetOperationCheckSupported(SafeType.Get())) .Schema(); } - - /** - * {@inheritDoc} - */ - public Uid Create(ObjectClass oclass, ICollection attrs, OperationOptions options) { - CreateApiOp op = ((CreateApiOp) GetOperationCheckSupported(SafeType.Get())); - return op.Create(oclass,attrs,options); - } - - /** - * {@inheritDoc} - */ - public void Delete(ObjectClass objClass, Uid uid, OperationOptions options) { - ((DeleteApiOp) + + public Uid Create(ObjectClass oclass, ICollection attrs, OperationOptions options) + { + CreateApiOp op = ((CreateApiOp)GetOperationCheckSupported(SafeType.Get())); + return op.Create(oclass, attrs, options); + } + + public void Delete(ObjectClass objClass, Uid uid, OperationOptions options) + { + ((DeleteApiOp) this.GetOperationCheckSupported(SafeType.Get())) .Delete(objClass, uid, options); } - - /** - * {@inheritDoc} - */ - public void Search(ObjectClass oclass,Filter filter, ResultsHandler handler, OperationOptions options) { - ((SearchApiOp) this.GetOperationCheckSupported(SafeType.Get())).Search( - oclass,filter, handler, options); - } - - /** - * {@inheritDoc} - */ - public Uid Update(ObjectClass objclass, Uid uid, ICollection attrs, OperationOptions options) { - return ((UpdateApiOp) this.GetOperationCheckSupported(SafeType.Get())) + + public void Search(ObjectClass oclass, Filter filter, ResultsHandler handler, OperationOptions options) + { + ((SearchApiOp)this.GetOperationCheckSupported(SafeType.Get())).Search( + oclass, filter, handler, options); + } + + public Uid Update(ObjectClass objclass, Uid uid, ICollection attrs, OperationOptions options) + { + return ((UpdateApiOp)this.GetOperationCheckSupported(SafeType.Get())) .Update(objclass, uid, attrs, options); } - - /** - * {@inheritDoc} - */ + public Uid AddAttributeValues( ObjectClass objclass, Uid uid, ICollection attrs, - OperationOptions options) { - return ((UpdateApiOp) this.GetOperationCheckSupported(SafeType.Get())) + OperationOptions options) + { + return ((UpdateApiOp)this.GetOperationCheckSupported(SafeType.Get())) .AddAttributeValues(objclass, uid, attrs, options); } - - /** - * {@inheritDoc} - */ + public Uid RemoveAttributeValues( ObjectClass objclass, Uid uid, ICollection attrs, - OperationOptions options) { - return ((UpdateApiOp) this.GetOperationCheckSupported(SafeType.Get())) + OperationOptions options) + { + return ((UpdateApiOp)this.GetOperationCheckSupported(SafeType.Get())) .RemoveAttributeValues(objclass, uid, attrs, options); } - - /** - * {@inheritDoc} - */ - public Uid Authenticate(ObjectClass objectClass,String username, GuardedString password, OperationOptions options) { - return ((AuthenticationApiOp) this + + public Uid Authenticate(ObjectClass objectClass, String username, GuardedString password, OperationOptions options) + { + return ((AuthenticationApiOp)this .GetOperationCheckSupported(SafeType.Get())).Authenticate( - objectClass,username, password, options); + objectClass, username, password, options); } - /** - * {@inheritDoc} - */ public Uid ResolveUsername(ObjectClass objectClass, String username, OperationOptions options) { return ((ResolveUsernameApiOp)this @@ -614,257 +675,280 @@ public Uid ResolveUsername(ObjectClass objectClass, String username, OperationOp objectClass, username, options); } - /** - * {@inheritDoc} - */ - public ConnectorObject GetObject(ObjectClass objClass, Uid uid, OperationOptions options) { - return ((GetApiOp) this.GetOperationCheckSupported(SafeType.Get())) + public ConnectorObject GetObject(ObjectClass objClass, Uid uid, OperationOptions options) + { + return ((GetApiOp)this.GetOperationCheckSupported(SafeType.Get())) .GetObject(objClass, uid, options); } - /** - * {@inheritDoc} - */ + public Object RunScriptOnConnector(ScriptContext request, - OperationOptions options) { - return ((ScriptOnConnectorApiOp) this + OperationOptions options) + { + return ((ScriptOnConnectorApiOp)this .GetOperationCheckSupported(SafeType.Get())) - .RunScriptOnConnector(request, options); + .RunScriptOnConnector(request, options); } - - /** - * {@inheritDoc} - */ + public Object RunScriptOnResource(ScriptContext request, - OperationOptions options) { - return ((ScriptOnResourceApiOp) this + OperationOptions options) + { + return ((ScriptOnResourceApiOp)this .GetOperationCheckSupported(SafeType.Get())) - .RunScriptOnResource(request, options); - } - - /** - * {@inheritDoc} - */ - public void Test() { - ((TestApiOp) this.GetOperationCheckSupported(SafeType.Get())).Test(); - } - - /** - * {@inheritDoc} - */ - public void Validate() { - ((ValidateApiOp) this.GetOperationCheckSupported(SafeType.Get())).Validate(); - } - + .RunScriptOnResource(request, options); + } + + public void Test() + { + ((TestApiOp)this.GetOperationCheckSupported(SafeType.Get())).Test(); + } + + public void Validate() + { + ((ValidateApiOp)this.GetOperationCheckSupported(SafeType.Get())).Validate(); + } + public void Sync(ObjectClass objClass, SyncToken token, SyncResultsHandler handler, - OperationOptions options) { + OperationOptions options) + { ((SyncApiOp)this.GetOperationCheckSupported(SafeType.Get())) .Sync(objClass, token, handler, options); } - - public SyncToken GetLatestSyncToken(ObjectClass objectClass) { + + public SyncToken GetLatestSyncToken(ObjectClass objectClass) + { return ((SyncApiOp)this.GetOperationCheckSupported(SafeType.Get())) .GetLatestSyncToken(objectClass); } - - private APIOperation GetOperationCheckSupported(SafeType api) { + + private APIOperation GetOperationCheckSupported(SafeType api) + { // check if this operation is supported. - if (!SupportedOperations.Contains(api)) { + if (!SupportedOperations.Contains(api)) + { String MSG = "Operation ''{0}'' not supported."; String str = String.Format(MSG, api); throw new InvalidOperationException(str); } return GetOperationImplementation(api); } - - /** - * Gets the implementation of the given operation - * @param api The operation to implement. - * @return The implementation - */ + + /// + /// Gets the implementation of the given operation + /// + /// The operation to implement. + /// The implementation protected abstract APIOperation GetOperationImplementation(SafeType api); - - protected APIConfigurationImpl GetAPIConfiguration() { + + protected APIConfigurationImpl GetAPIConfiguration() + { return _configuration; } - - /** - * Creates a new {@link APIOperation} proxy given a handler. - */ - protected APIOperation NewAPIOperationProxy(SafeType api, InvocationHandler handler) { - return (APIOperation) Proxy.NewProxyInstance(api.RawType, handler); + + /// + /// Creates a new proxy given a handler. + /// + protected APIOperation NewAPIOperationProxy(SafeType api, InvocationHandler handler) + { + return (APIOperation)Proxy.NewProxyInstance(api.RawType, handler); } private static bool LOGGINGPROXY_ENABLED; - static AbstractConnectorFacade() { + static AbstractConnectorFacade() + { string enabled = System.Configuration. ConfigurationManager.AppSettings.Get("logging.proxy"); - LOGGINGPROXY_ENABLED = StringUtil.IsTrue(enabled); + LOGGINGPROXY_ENABLED = StringUtil.IsTrue(enabled); } - - protected APIOperation CreateLoggingProxy(SafeType api, APIOperation target) { + + protected APIOperation CreateLoggingProxy(SafeType api, APIOperation target) + { APIOperation ret = target; - if (LOGGINGPROXY_ENABLED) { - LoggingProxy logging = new LoggingProxy(api, target); + if (LOGGINGPROXY_ENABLED) + { + LoggingProxy logging = new LoggingProxy(api, target); ret = NewAPIOperationProxy(api, logging); } return ret; } } #endregion - + #region ConnectorFacadeFactoryImpl - public class ConnectorFacadeFactoryImpl : ConnectorFacadeFactory { - - - - - /** - * {@inheritDoc} - */ - public override ConnectorFacade NewInstance(APIConfiguration config) { + public class ConnectorFacadeFactoryImpl : ConnectorFacadeFactory + { + public override ConnectorFacade NewInstance(APIConfiguration config) + { ConnectorFacade ret = null; - APIConfigurationImpl impl = (APIConfigurationImpl) config; + APIConfigurationImpl impl = (APIConfigurationImpl)config; AbstractConnectorInfo connectorInfo = impl.ConnectorInfo; - if ( connectorInfo is LocalConnectorInfoImpl ) { + if (connectorInfo is LocalConnectorInfoImpl) + { LocalConnectorInfoImpl localInfo = (LocalConnectorInfoImpl)connectorInfo; // create a new Provisioner.. - ret = new LocalConnectorFacadeImpl(localInfo,impl); + ret = new LocalConnectorFacadeImpl(localInfo, impl); } - else { + else + { ret = new RemoteConnectorFacadeImpl(impl); } return ret; } - - - /** - * Dispose of all object pools and other resources associated with this - * class. - */ - public override void Dispose() { + + + /// + /// Dispose of all object pools and other resources associated with this + /// class. + /// + public override void Dispose() + { ConnectorPoolManager.Dispose(); } - + } #endregion #region ObjectStreamHandler - internal interface ObjectStreamHandler { + internal interface ObjectStreamHandler + { bool Handle(Object obj); } #endregion - + #region StreamHandlerUtil - internal static class StreamHandlerUtil { - - /** - * Adapts from a ObjectStreamHandler to a ResultsHandler - */ - private class ResultsHandlerAdapter { - private readonly ObjectStreamHandler _target; - public ResultsHandlerAdapter(ObjectStreamHandler target) { - _target = target; - } - public bool Handle(ConnectorObject obj) { - return _target.Handle(obj); - } - } - - /** - * Adapts from a ObjectStreamHandler to a SyncResultsHandler - */ - private class SyncResultsHandlerAdapter { - private readonly ObjectStreamHandler _target; - public SyncResultsHandlerAdapter(ObjectStreamHandler target) { - _target = target; - } - public bool Handle(SyncDelta obj) { - return _target.Handle(obj); - } - } - - /** - * Adapts from a ObjectStreamHandler to a SyncResultsHandler - */ - private class ObjectStreamHandlerAdapter : ObjectStreamHandler { - private readonly Type _targetInterface; - private readonly Object _target; - public ObjectStreamHandlerAdapter(Type targetInterface, Object target) { - Assertions.NullCheck(targetInterface, "targetInterface"); - Assertions.NullCheck(target, "target"); - if (!targetInterface.IsAssignableFrom(target.GetType())) { - throw new ArgumentException("Target" +targetInterface+" "+target); - } - if (!IsAdaptableToObjectStreamHandler(targetInterface)) { - throw new ArgumentException("Target interface not supported: "+targetInterface); - } - _targetInterface = targetInterface; - _target = target; - } - public bool Handle(Object obj) { - if (_targetInterface.Equals( typeof(ResultsHandler) )) { - return ((ResultsHandler)_target)((ConnectorObject)obj); - } - else if (_targetInterface.Equals(typeof(SyncResultsHandler))) { - return ((SyncResultsHandler)_target)((SyncDelta)obj); - } - else { - throw new InvalidOperationException("Unhandled case: "+_targetInterface); - } - } - } - - public static bool IsAdaptableToObjectStreamHandler(Type clazz) { - return ( typeof(ResultsHandler).IsAssignableFrom(clazz) || - typeof(SyncResultsHandler).IsAssignableFrom(clazz)); - } - - public static ObjectStreamHandler AdaptToObjectStreamHandler(Type interfaceType, - Object target) { - return new ObjectStreamHandlerAdapter(interfaceType,target); - } - public static Object AdaptFromObjectStreamHandler(Type interfaceType, - ObjectStreamHandler target) { - if (interfaceType.Equals( typeof(ResultsHandler) ) ){ - return new ResultsHandler(new ResultsHandlerAdapter(target).Handle); - } - else if (interfaceType.Equals( typeof(SyncResultsHandler) ) ) { - return new SyncResultsHandler(new SyncResultsHandlerAdapter(target).Handle); - } - else { - throw new InvalidOperationException("Unhandled case: "+interfaceType); - } - } - + internal static class StreamHandlerUtil + { + /// + /// Adapts from a ObjectStreamHandler to a ResultsHandler + /// + private class ResultsHandlerAdapter + { + private readonly ObjectStreamHandler _target; + public ResultsHandlerAdapter(ObjectStreamHandler target) + { + _target = target; + } + public bool Handle(ConnectorObject obj) + { + return _target.Handle(obj); + } + } + + /// + /// Adapts from a ObjectStreamHandler to a SyncResultsHandler + /// + private class SyncResultsHandlerAdapter + { + private readonly ObjectStreamHandler _target; + public SyncResultsHandlerAdapter(ObjectStreamHandler target) + { + _target = target; + } + public bool Handle(SyncDelta obj) + { + return _target.Handle(obj); + } + } + + /// + /// Adapts from a ObjectStreamHandler to a SyncResultsHandler + /// + private class ObjectStreamHandlerAdapter : ObjectStreamHandler + { + private readonly Type _targetInterface; + private readonly Object _target; + public ObjectStreamHandlerAdapter(Type targetInterface, Object target) + { + Assertions.NullCheck(targetInterface, "targetInterface"); + Assertions.NullCheck(target, "target"); + if (!targetInterface.IsAssignableFrom(target.GetType())) + { + throw new ArgumentException("Target" + targetInterface + " " + target); + } + if (!IsAdaptableToObjectStreamHandler(targetInterface)) + { + throw new ArgumentException("Target interface not supported: " + targetInterface); + } + _targetInterface = targetInterface; + _target = target; + } + public bool Handle(Object obj) + { + if (_targetInterface.Equals(typeof(ResultsHandler))) + { + return ((ResultsHandler)_target)((ConnectorObject)obj); + } + else if (_targetInterface.Equals(typeof(SyncResultsHandler))) + { + return ((SyncResultsHandler)_target)((SyncDelta)obj); + } + else + { + throw new InvalidOperationException("Unhandled case: " + _targetInterface); + } + } + } + + public static bool IsAdaptableToObjectStreamHandler(Type clazz) + { + return (typeof(ResultsHandler).IsAssignableFrom(clazz) || + typeof(SyncResultsHandler).IsAssignableFrom(clazz)); + } + + public static ObjectStreamHandler AdaptToObjectStreamHandler(Type interfaceType, + Object target) + { + return new ObjectStreamHandlerAdapter(interfaceType, target); + } + public static Object AdaptFromObjectStreamHandler(Type interfaceType, + ObjectStreamHandler target) + { + if (interfaceType.Equals(typeof(ResultsHandler))) + { + return new ResultsHandler(new ResultsHandlerAdapter(target).Handle); + } + else if (interfaceType.Equals(typeof(SyncResultsHandler))) + { + return new SyncResultsHandler(new SyncResultsHandlerAdapter(target).Handle); + } + else + { + throw new InvalidOperationException("Unhandled case: " + interfaceType); + } + } } #endregion - + #region LoggingProxy - public class LoggingProxy : InvocationHandler { - + public class LoggingProxy : InvocationHandler + { private readonly SafeType _op; private readonly object _target; - - public LoggingProxy(SafeType api, object target) { + + public LoggingProxy(SafeType api, object target) + { _op = api; _target = target; } /// /// Log all operations. /// - public Object Invoke(Object proxy, MethodInfo method, Object [] args) { + public Object Invoke(Object proxy, MethodInfo method, Object[] args) + { //do not proxy equals, hashCode, toString - if (method.DeclaringType.Equals(typeof(object))) { + if (method.DeclaringType.Equals(typeof(object))) + { return method.Invoke(this, args); } StringBuilder bld = new StringBuilder(); bld.Append("Enter: "); AddMethodName(bld, method); bld.Append('('); - for (int i=0; args != null && i < args.Length; i++) { - if (i != 0) { + for (int i = 0; args != null && i < args.Length; i++) + { + if (i != 0) + { bld.Append(", "); } bld.Append('{').Append(i).Append('}'); @@ -873,7 +957,8 @@ public Object Invoke(Object proxy, MethodInfo method, Object [] args) { // write out trace header Trace.TraceInformation(bld.ToString(), args); // invoke the method - try { + try + { object ret = method.Invoke(_target, args); // clear out buffer. bld.Length = 0; @@ -882,19 +967,21 @@ public Object Invoke(Object proxy, MethodInfo method, Object [] args) { bld.Append("({0})"); Trace.TraceInformation(bld.ToString(), ret); return ret; - } catch (TargetInvocationException e) { + } + catch (TargetInvocationException e) + { Exception root = e.InnerException; - ExceptionUtil.PreserveStackTrace( root ); + ExceptionUtil.PreserveStackTrace(root); throw root; } } - - private void AddMethodName(StringBuilder bld, MethodInfo method) { + + private void AddMethodName(StringBuilder bld, MethodInfo method) + { bld.Append(_op.RawType.Name); bld.Append('.'); bld.Append(method.Name); } } #endregion - -} +} \ No newline at end of file diff --git a/FrameworkInternal/ApiLocal.cs b/FrameworkInternal/ApiLocal.cs index 66ec6531..0acc3c53 100644 --- a/FrameworkInternal/ApiLocal.cs +++ b/FrameworkInternal/ApiLocal.cs @@ -45,33 +45,40 @@ namespace Org.IdentityConnectors.Framework.Impl.Api.Local { #region ConnectorPoolManager - public class ConnectorPoolManager { - - - private class ConnectorPoolKey { + public class ConnectorPoolManager + { + private class ConnectorPoolKey + { private readonly ConnectorKey _connectorKey; private readonly ConfigurationPropertiesImpl _configProperties; private readonly ObjectPoolConfiguration _poolingConfig; public ConnectorPoolKey(ConnectorKey connectorKey, ConfigurationPropertiesImpl configProperties, - ObjectPoolConfiguration poolingConfig) { + ObjectPoolConfiguration poolingConfig) + { _connectorKey = connectorKey; _configProperties = configProperties; _poolingConfig = poolingConfig; } - public override int GetHashCode() { + public override int GetHashCode() + { return _connectorKey.GetHashCode(); } - public override bool Equals(Object o) { - if ( o is ConnectorPoolKey ) { + public override bool Equals(Object o) + { + if (o is ConnectorPoolKey) + { ConnectorPoolKey other = (ConnectorPoolKey)o; - if (!_connectorKey.Equals(other._connectorKey)) { + if (!_connectorKey.Equals(other._connectorKey)) + { return false; } - if (!_configProperties.Equals(other._configProperties)) { + if (!_configProperties.Equals(other._configProperties)) + { return false; } - if (!_poolingConfig.Equals(other._poolingConfig)) { + if (!_poolingConfig.Equals(other._poolingConfig)) + { return false; } return true; @@ -79,16 +86,19 @@ public override bool Equals(Object o) { return false; } } - - private class ConnectorPoolHandler : ObjectPoolHandler { + + private class ConnectorPoolHandler : ObjectPoolHandler + { private readonly APIConfigurationImpl _apiConfiguration; private readonly LocalConnectorInfoImpl _localInfo; public ConnectorPoolHandler(APIConfigurationImpl apiConfiguration, - LocalConnectorInfoImpl localInfo) { + LocalConnectorInfoImpl localInfo) + { _apiConfiguration = apiConfiguration; - _localInfo = localInfo; + _localInfo = localInfo; } - public PoolableConnector NewObject() { + public PoolableConnector NewObject() + { Configuration config = CSharpClassProperties.CreateBean((ConfigurationPropertiesImpl)_apiConfiguration.ConfigurationProperties, _localInfo.ConnectorConfigurationClass); @@ -97,45 +107,51 @@ public PoolableConnector NewObject() { connector.Init(config); return connector; } - public void TestObject(PoolableConnector obj) { + public void TestObject(PoolableConnector obj) + { obj.CheckAlive(); } - public void DisposeObject(PoolableConnector obj) { + public void DisposeObject(PoolableConnector obj) + { obj.Dispose(); } } - - /** - * Cache of the various _pools.. - */ + + /// + /// Cache of the various _pools.. + /// private static readonly IDictionary> _pools = new Dictionary>(); - - - /** - * Get a object pool for this connector if it supports connector pooling. - */ - public static ObjectPool GetPool(APIConfigurationImpl impl, - LocalConnectorInfoImpl localInfo) { + + + /// + /// Get a object pool for this connector if it supports connector pooling. + /// + public static ObjectPool GetPool(APIConfigurationImpl impl, + LocalConnectorInfoImpl localInfo) + { ObjectPool pool = null; // determine if this connector wants generic connector pooling.. - if (impl.IsConnectorPoolingSupported) { + if (impl.IsConnectorPoolingSupported) + { ConnectorPoolKey key = new ConnectorPoolKey( impl.ConnectorInfo.ConnectorKey, (ConfigurationPropertiesImpl)impl.ConfigurationProperties, impl.ConnectorPoolConfiguration); - - lock (_pools) { + + lock (_pools) + { // get the pool associated.. - pool = CollectionUtil.GetValue(_pools,key,null); + pool = CollectionUtil.GetValue(_pools, key, null); // create a new pool if it doesn't exist.. - if (pool == null) { - Trace.TraceInformation("Creating new pool: "+ + if (pool == null) + { + Trace.TraceInformation("Creating new pool: " + impl.ConnectorInfo.ConnectorKey); // this instance is strictly used for the pool.. pool = new ObjectPool( - new ConnectorPoolHandler(impl,localInfo), + new ConnectorPoolHandler(impl, localInfo), impl.ConnectorPoolConfiguration); // add back to the map of _pools.. _pools[key] = pool; @@ -144,14 +160,20 @@ public static ObjectPool GetPool(APIConfigurationImpl impl, } return pool; } - - public static void Dispose() { - lock (_pools) { + + public static void Dispose() + { + lock (_pools) + { // close each pool.. - foreach (ObjectPool pool in _pools.Values) { - try { + foreach (ObjectPool pool in _pools.Values) + { + try + { pool.Shutdown(); - } catch (Exception e) { + } + catch (Exception e) + { TraceUtil.TraceException("Failed to close pool", e); } } @@ -159,29 +181,29 @@ public static void Dispose() { _pools.Clear(); } } - } #endregion - + #region CSharpClassProperties internal static class CSharpClassProperties { - public static ConfigurationPropertiesImpl - CreateConfigurationProperties(Configuration defaultObject) + public static ConfigurationPropertiesImpl + CreateConfigurationProperties(Configuration defaultObject) { SafeType config = SafeType.Get(defaultObject); - ConfigurationPropertiesImpl properties = + ConfigurationPropertiesImpl properties = new ConfigurationPropertiesImpl(); - IList temp = + IList temp = new List(); - IDictionary descs = GetFilteredProperties(config); - - foreach (PropertyInfo desc in descs.Values) { - + IDictionary descs = GetFilteredProperties(config); + + foreach (PropertyInfo desc in descs.Values) + { + String name = desc.Name; - + // get the configuration options.. - ConfigurationPropertyAttribute options = + ConfigurationPropertyAttribute options = GetPropertyOptions(desc); // use the options to set internal properties.. int order = 0; @@ -189,12 +211,15 @@ public static ConfigurationPropertiesImpl String displKey = name + ".display"; bool confidential = false; bool required = false; - if (options != null) { + if (options != null) + { // determine the display and help keys.. - if (!StringUtil.IsBlank(options.HelpMessageKey)) { + if (!StringUtil.IsBlank(options.HelpMessageKey)) + { helpKey = options.HelpMessageKey; } - if (!StringUtil.IsBlank(options.DisplayMessageKey)) { + if (!StringUtil.IsBlank(options.DisplayMessageKey)) + { displKey = options.DisplayMessageKey; } // determine the order.. @@ -203,53 +228,57 @@ public static ConfigurationPropertiesImpl confidential = options.Confidential; } Type type = desc.PropertyType; - if (!FrameworkUtil.IsSupportedConfigurationType(type)) { - const String MSG = "Property type ''{0}'' is not supported."; - throw new ArgumentException(String.Format(MSG, type)); - } - - Object value = desc.GetValue(defaultObject,null); - + if (!FrameworkUtil.IsSupportedConfigurationType(type)) + { + const String MSG = "Property type ''{0}'' is not supported."; + throw new ArgumentException(String.Format(MSG, type)); + } + + Object value = desc.GetValue(defaultObject, null); + ConfigurationPropertyImpl prop = new ConfigurationPropertyImpl(); - prop.IsConfidential=confidential; - prop.IsRequired=required; - prop.DisplayMessageKey=displKey; - prop.HelpMessageKey=helpKey; - prop.Name=name; - prop.Order=order; - prop.Value=value; - prop.ValueType=type; + prop.IsConfidential = confidential; + prop.IsRequired = required; + prop.DisplayMessageKey = displKey; + prop.HelpMessageKey = helpKey; + prop.Name = name; + prop.Order = order; + prop.Value = value; + prop.ValueType = type; prop.Operations = options == null ? null : TranslateOperations(options.Operations); - + temp.Add(prop); - + } - properties.Properties=(temp); + properties.Properties = (temp); return properties; } - - private static ICollection> TranslateOperations(SafeType [] ops) + + private static ICollection> TranslateOperations(SafeType[] ops) { ICollection> set = new HashSet>(); - foreach (SafeType spi in ops) { - CollectionUtil.AddAll(set,FrameworkUtil.Spi2Apis(spi)); + foreach (SafeType spi in ops) + { + CollectionUtil.AddAll(set, FrameworkUtil.Spi2Apis(spi)); } return set; } - - public static Configuration + + public static Configuration CreateBean(ConfigurationPropertiesImpl properties, - SafeType configType) { + SafeType configType) + { Configuration rv = configType.CreateInstance(); - rv.ConnectorMessages=properties.Parent.ConnectorInfo.Messages; + rv.ConnectorMessages = properties.Parent.ConnectorInfo.Messages; MergeIntoBean(properties, rv); return rv; } public static void MergeIntoBean(ConfigurationPropertiesImpl properties, - Configuration config) { + Configuration config) + { SafeType configType = SafeType.Get(config); IDictionary descriptors = @@ -276,59 +305,64 @@ public static void desc.SetValue(config, val, null); } } - - private static IDictionary + + private static IDictionary GetFilteredProperties(SafeType config) { - IDictionary rv = - new Dictionary(); - PropertyInfo [] descriptors = config.RawType.GetProperties(); - foreach (PropertyInfo descriptor in descriptors) { + IDictionary rv = + new Dictionary(); + PropertyInfo[] descriptors = config.RawType.GetProperties(); + foreach (PropertyInfo descriptor in descriptors) + { String propName = descriptor.Name; - if ( !descriptor.CanWrite ) { + if (!descriptor.CanWrite) + { //if there's no setter, ignore it continue; } - if ("ConnectorMessages".Equals(propName)) { + if ("ConnectorMessages".Equals(propName)) + { continue; } - if ( !descriptor.CanRead ) { - const String FMT = + if (!descriptor.CanRead) + { + const String FMT = "Found setter ''{0}'' but not the corresponding getter."; - String MSG = String.Format(FMT,propName); + String MSG = String.Format(FMT, propName); throw new ArgumentException(MSG); } rv[propName] = descriptor; } return rv; } - - - - /** - * Get the option from the property. - */ + + /// + /// Get the option from the property. + /// private static ConfigurationPropertyAttribute GetPropertyOptions( - PropertyInfo propertyInfo) { - Object [] objs = + PropertyInfo propertyInfo) + { + Object[] objs = propertyInfo.GetCustomAttributes( - typeof(ConfigurationPropertyAttribute),true); - if ( objs.Length == 0 ) { + typeof(ConfigurationPropertyAttribute), true); + if (objs.Length == 0) + { return null; } - else { + else + { return (ConfigurationPropertyAttribute)objs[0]; } } } #endregion - + #region LocalConnectorInfoManagerImpl internal class LocalConnectorInfoManagerImpl : ConnectorInfoManager { private IList _connectorInfo; - + public LocalConnectorInfoManagerImpl() { _connectorInfo = new List(); @@ -337,139 +371,159 @@ public LocalConnectorInfoManagerImpl() new FileInfo(assembly.Location); DirectoryInfo directory = thisAssemblyFile.Directory; - FileInfo [] files = + FileInfo[] files = directory.GetFiles("*.Connector.dll"); - foreach (FileInfo file in files) { - Assembly lib = - Assembly.LoadFrom(file.ToString()); - CollectionUtil.AddAll(_connectorInfo, - ProcessAssembly(lib)); - } + foreach (FileInfo file in files) + { + Assembly lib = + Assembly.LoadFrom(file.ToString()); + CollectionUtil.AddAll(_connectorInfo, + ProcessAssembly(lib)); + } // also handle connector DLL file names with a version FileInfo[] versionedFiles = directory.GetFiles("*.Connector-*.dll"); - foreach (FileInfo versionedFile in versionedFiles) { - Assembly lib = - Assembly.LoadFrom(versionedFile.ToString()); - CollectionUtil.AddAll(_connectorInfo, - ProcessAssembly(lib)); - } + foreach (FileInfo versionedFile in versionedFiles) + { + Assembly lib = + Assembly.LoadFrom(versionedFile.ToString()); + CollectionUtil.AddAll(_connectorInfo, + ProcessAssembly(lib)); + } } - - private IList ProcessAssembly(Assembly assembly) { + + private IList ProcessAssembly(Assembly assembly) + { IList rv = new List(); - - Type [] types = null; - try { + + Type[] types = null; + try + { types = assembly.GetTypes(); } - catch (Exception e) { - TraceUtil.TraceException("Unable to load assembly: "+assembly.FullName+". Assembly will be ignored.",e); + catch (Exception e) + { + TraceUtil.TraceException("Unable to load assembly: " + assembly.FullName + ". Assembly will be ignored.", e); } - - foreach (Type type in types) { - Object [] attributes = type.GetCustomAttributes( + + foreach (Type type in types) + { + Object[] attributes = type.GetCustomAttributes( typeof(ConnectorClassAttribute), false); - if ( attributes.Length > 0 ) { - ConnectorClassAttribute attribute = + if (attributes.Length > 0) + { + ConnectorClassAttribute attribute = (ConnectorClassAttribute)attributes[0]; LocalConnectorInfoImpl info = - CreateConnectorInfo(assembly,type,attribute); + CreateConnectorInfo(assembly, type, attribute); rv.Add(info); } } return rv; } - - private LocalConnectorInfoImpl CreateConnectorInfo(Assembly assembly, - Type rawConnectorClass, - ConnectorClassAttribute attribute) { + + private LocalConnectorInfoImpl CreateConnectorInfo(Assembly assembly, + Type rawConnectorClass, + ConnectorClassAttribute attribute) + { String fileName = assembly.Location; - if (!typeof(Connector).IsAssignableFrom(rawConnectorClass)) { - String MSG = ( "File "+fileName+ - " declares a connector "+rawConnectorClass+ + if (!typeof(Connector).IsAssignableFrom(rawConnectorClass)) + { + String MSG = ("File " + fileName + + " declares a connector " + rawConnectorClass + " that does not implement Connector."); - throw new ConfigurationException(MSG); + throw new ConfigurationException(MSG); } SafeType connectorClass = SafeType.ForRawType(rawConnectorClass); SafeType connectorConfigurationClass = attribute.ConnectorConfigurationType; - if ( connectorConfigurationClass == null ) { - String MSG = ( "File "+fileName+ - " contains a ConnectorInfo attribute "+ + if (connectorConfigurationClass == null) + { + String MSG = ("File " + fileName + + " contains a ConnectorInfo attribute " + "with no connector configuration class."); - throw new ConfigurationException(MSG); + throw new ConfigurationException(MSG); } - String connectorDisplayNameKey = + String connectorDisplayNameKey = attribute.ConnectorDisplayNameKey; - if ( connectorDisplayNameKey == null ) { - String MSG = ( "File "+fileName+ - " contains a ConnectorInfo attribute "+ + if (connectorDisplayNameKey == null) + { + String MSG = ("File " + fileName + + " contains a ConnectorInfo attribute " + "with no connector display name."); throw new ConfigurationException(MSG); } - ConnectorKey key = + ConnectorKey key = new ConnectorKey(assembly.GetName().Name, assembly.GetName().Version.ToString(), - connectorClass.RawType.Namespace+"."+connectorClass.RawType.Name); + connectorClass.RawType.Namespace + "." + connectorClass.RawType.Name); LocalConnectorInfoImpl rv = new LocalConnectorInfoImpl(); rv.ConnectorClass = connectorClass; rv.ConnectorConfigurationClass = connectorConfigurationClass; rv.ConnectorDisplayNameKey = connectorDisplayNameKey; rv.ConnectorKey = key; rv.DefaultAPIConfiguration = CreateDefaultAPIConfiguration(rv); - rv.Messages = LoadMessages(assembly,rv,attribute.MessageCatalogPaths); + rv.Messages = LoadMessages(assembly, rv, attribute.MessageCatalogPaths); return rv; } - - private APIConfigurationImpl - CreateDefaultAPIConfiguration(LocalConnectorInfoImpl localInfo) { + + private APIConfigurationImpl + CreateDefaultAPIConfiguration(LocalConnectorInfoImpl localInfo) + { SafeType connectorClass = localInfo.ConnectorClass; APIConfigurationImpl rv = new APIConfigurationImpl(); - Configuration config = + Configuration config = localInfo.ConnectorConfigurationClass.CreateInstance(); bool pooling = IsPoolingSupported(connectorClass); - rv.IsConnectorPoolingSupported=pooling; - rv.ConfigurationProperties=(CSharpClassProperties.CreateConfigurationProperties(config)); - rv.ConnectorInfo=(localInfo); - rv.SupportedOperations=(FrameworkUtil.GetDefaultSupportedOperations(connectorClass)); + rv.IsConnectorPoolingSupported = pooling; + rv.ConfigurationProperties = (CSharpClassProperties.CreateConfigurationProperties(config)); + rv.ConnectorInfo = (localInfo); + rv.SupportedOperations = (FrameworkUtil.GetDefaultSupportedOperations(connectorClass)); return rv; } - private static bool IsPoolingSupported(SafeType clazz) { - return ReflectionUtil.IsParentTypeOf(typeof(PoolableConnector),clazz.RawType); + private static bool IsPoolingSupported(SafeType clazz) + { + return ReflectionUtil.IsParentTypeOf(typeof(PoolableConnector), clazz.RawType); } + /// /// Given an assembly, returns the list of cultures that /// it is localized for /// /// /// - private CultureInfo [] GetLocalizedCultures(Assembly assembly) { + private CultureInfo[] GetLocalizedCultures(Assembly assembly) + { FileInfo assemblyFile = new FileInfo(assembly.Location); DirectoryInfo directory = assemblyFile.Directory; IList temp = new List(); - DirectoryInfo [] subdirs = directory.GetDirectories(); - foreach (DirectoryInfo subdir in subdirs) { + DirectoryInfo[] subdirs = directory.GetDirectories(); + foreach (DirectoryInfo subdir in subdirs) + { String name = subdir.Name; CultureInfo cultureInfo; //get the culture if the directory is the name of the //culture - try { + try + { cultureInfo = new CultureInfo(name); } - catch (ArgumentException) { + catch (ArgumentException) + { //invalid culture continue; } //see if there's a satellite assembly for this - try { + try + { assembly.GetSatelliteAssembly(cultureInfo); } - catch (Exception) { + catch (Exception) + { //invalid assembly continue; } @@ -478,109 +532,125 @@ private CultureInfo [] GetLocalizedCultures(Assembly assembly) { temp.Add(CultureInfo.InvariantCulture); return temp.ToArray(); } - + private ConnectorMessagesImpl LoadMessages(Assembly assembly, LocalConnectorInfoImpl info, - String [] nameBases) { - if ( nameBases == null || nameBases.Length == 0 ) { + String[] nameBases) + { + if (nameBases == null || nameBases.Length == 0) + { String pkage = info.ConnectorClass.RawType.Namespace; - nameBases = new String[]{pkage+".Messages"}; + nameBases = new String[] { pkage + ".Messages" }; } ConnectorMessagesImpl rv = new ConnectorMessagesImpl(); - CultureInfo [] cultures = GetLocalizedCultures(assembly); - for ( int i = nameBases.Length - 1; i >= 0; i-- ) { + CultureInfo[] cultures = GetLocalizedCultures(assembly); + for (int i = nameBases.Length - 1; i >= 0; i--) + { String nameBase = nameBases[i]; - ResourceManager manager = new ResourceManager(nameBase,assembly); - foreach (CultureInfo culture in cultures) { - ResourceSet resourceSet = manager.GetResourceSet(culture,true,false); - if ( resourceSet != null ) { - IDictionary temp = - CollectionUtil.GetValue(rv.Catalogs,culture,null); - if ( temp == null ) { + ResourceManager manager = new ResourceManager(nameBase, assembly); + foreach (CultureInfo culture in cultures) + { + ResourceSet resourceSet = manager.GetResourceSet(culture, true, false); + if (resourceSet != null) + { + IDictionary temp = + CollectionUtil.GetValue(rv.Catalogs, culture, null); + if (temp == null) + { temp = new Dictionary(); rv.Catalogs[culture] = temp; } - foreach (System.Collections.DictionaryEntry entry in resourceSet) { - String key = ""+entry.Key; - String val = ""+entry.Value; + foreach (System.Collections.DictionaryEntry entry in resourceSet) + { + String key = "" + entry.Key; + String val = "" + entry.Value; temp[key] = val; } - } + } } } - + return rv; } - - public ConnectorInfo FindConnectorInfo(ConnectorKey key) { - foreach (ConnectorInfo info in _connectorInfo) { - if ( info.ConnectorKey.Equals(key) ) { + + public ConnectorInfo FindConnectorInfo(ConnectorKey key) + { + foreach (ConnectorInfo info in _connectorInfo) + { + if (info.ConnectorKey.Equals(key)) + { return info; } } return null; } - public IList ConnectorInfos { - get { + public IList ConnectorInfos + { + get + { return CollectionUtil.AsReadOnlyList(_connectorInfo); } } } #endregion - + #region LocalConnectorInfoImpl /// /// Internal class, public only for unit tests /// public class LocalConnectorInfoImpl : AbstractConnectorInfo { - public RemoteConnectorInfoImpl ToRemote() { + public RemoteConnectorInfoImpl ToRemote() + { RemoteConnectorInfoImpl rv = new RemoteConnectorInfoImpl(); - rv.ConnectorDisplayNameKey=ConnectorDisplayNameKey; - rv.ConnectorKey=ConnectorKey; - rv.DefaultAPIConfiguration=DefaultAPIConfiguration; - rv.Messages=Messages; + rv.ConnectorDisplayNameKey = ConnectorDisplayNameKey; + rv.ConnectorKey = ConnectorKey; + rv.DefaultAPIConfiguration = DefaultAPIConfiguration; + rv.Messages = Messages; return rv; } - public SafeType ConnectorClass {get;set;} - public SafeType ConnectorConfigurationClass {get;set;} + public SafeType ConnectorClass { get; set; } + public SafeType ConnectorConfigurationClass { get; set; } } #endregion - - #region LocalConnectorFacadeImpl - internal class LocalConnectorFacadeImpl : AbstractConnectorFacade { + #region LocalConnectorFacadeImpl + internal class LocalConnectorFacadeImpl : AbstractConnectorFacade + { // ======================================================================= // Constants // ======================================================================= - /** - * Map the API interfaces to their implementation counterparts. - */ - private static readonly IDictionary,ConstructorInfo> API_TO_IMPL= - new Dictionary,ConstructorInfo>(); - + /// + /// Map the API interfaces to their implementation counterparts. + /// + private static readonly IDictionary, ConstructorInfo> API_TO_IMPL = + new Dictionary, ConstructorInfo>(); + private static void AddImplementation(SafeType inter, - SafeType impl) { + SafeType impl) + { ConstructorInfo info = impl.RawType.GetConstructor(new Type[]{typeof(ConnectorOperationalContext), typeof(Connector)}); - if ( info == null ) { - throw new ArgumentException(impl+" does not define the proper constructor"); + if (info == null) + { + throw new ArgumentException(impl + " does not define the proper constructor"); } - API_TO_IMPL[inter]= info; + API_TO_IMPL[inter] = info; } - - static LocalConnectorFacadeImpl() { + + static LocalConnectorFacadeImpl() + { AddImplementation(SafeType.Get(), SafeType.Get()); - AddImplementation(SafeType.Get(), + AddImplementation(SafeType.Get(), SafeType.Get()); - AddImplementation(SafeType.Get(), + AddImplementation(SafeType.Get(), SafeType.Get()); - AddImplementation(SafeType.Get(), + AddImplementation(SafeType.Get(), SafeType.Get()); - AddImplementation(SafeType.Get(), + AddImplementation(SafeType.Get(), SafeType.Get()); AddImplementation(SafeType.Get(), SafeType.Get()); @@ -588,382 +658,431 @@ static LocalConnectorFacadeImpl() { SafeType.Get()); AddImplementation(SafeType.Get(), SafeType.Get()); - AddImplementation(SafeType.Get(), + AddImplementation(SafeType.Get(), SafeType.Get()); AddImplementation(SafeType.Get(), SafeType.Get()); AddImplementation(SafeType.Get(), SafeType.Get()); } - - - + // ======================================================================= // Fields // ======================================================================= - /** - * Pool used to acquire connection from to use during operations. - */ - - /** - * The connector info - */ + /// + /// Pool used to acquire connection from to use during operations. + /// + + /// + /// The connector info + /// private readonly LocalConnectorInfoImpl connectorInfo; - - /** - * Builds up the maps of supported operations and calls. - */ + + /// + /// Builds up the maps of supported operations and calls. + /// public LocalConnectorFacadeImpl(LocalConnectorInfoImpl connectorInfo, - APIConfigurationImpl apiConfiguration) - :base(apiConfiguration) { + APIConfigurationImpl apiConfiguration) + : base(apiConfiguration) + { this.connectorInfo = connectorInfo; } - + // ======================================================================= // ConnectorFacade Interface // ======================================================================= - - protected override APIOperation GetOperationImplementation(SafeType api) { + + protected override APIOperation GetOperationImplementation(SafeType api) + { APIOperation proxy; //first create the inner proxy - this is the proxy that obtaining //a connection from the pool, etc //NOTE: we want to skip this part of the proxy for //validate op, but we will want the timeout proxy - if ( api.RawType.Equals(typeof(ValidateApiOp))) { + if (api.RawType.Equals(typeof(ValidateApiOp))) + { OperationalContext context = - new OperationalContext(connectorInfo,GetAPIConfiguration()); + new OperationalContext(connectorInfo, GetAPIConfiguration()); proxy = new ValidateImpl(context); } - else if ( api.RawType.Equals( typeof(GetApiOp) ) ) { + else if (api.RawType.Equals(typeof(GetApiOp))) + { ConstructorInfo constructor = API_TO_IMPL[SafeType.Get()]; ConnectorOperationalContext context = new ConnectorOperationalContext(connectorInfo, GetAPIConfiguration(), GetPool()); - + ConnectorAPIOperationRunnerProxy handler = - new ConnectorAPIOperationRunnerProxy(context,constructor); + new ConnectorAPIOperationRunnerProxy(context, constructor); proxy = - new GetImpl((SearchApiOp)NewAPIOperationProxy(SafeType.Get(),handler)); + new GetImpl((SearchApiOp)NewAPIOperationProxy(SafeType.Get(), handler)); } - else { + else + { ConstructorInfo constructor = API_TO_IMPL[api]; ConnectorOperationalContext context = new ConnectorOperationalContext(connectorInfo, GetAPIConfiguration(), GetPool()); - + ConnectorAPIOperationRunnerProxy handler = - new ConnectorAPIOperationRunnerProxy(context,constructor); + new ConnectorAPIOperationRunnerProxy(context, constructor); proxy = - NewAPIOperationProxy(api,handler); + NewAPIOperationProxy(api, handler); } - + //TODO: timeout - + // add logging proxy.. proxy = CreateLoggingProxy(api, proxy); return proxy; } private ObjectPool GetPool() { - return ConnectorPoolManager.GetPool(GetAPIConfiguration(),connectorInfo); + return ConnectorPoolManager.GetPool(GetAPIConfiguration(), connectorInfo); } } #endregion - + #region ObjectPool - public class ObjectPool where T : class { - - /** - * Statistics bean - */ - public sealed class Statistics { + public class ObjectPool where T : class + { + /// + /// Statistics bean + /// + public sealed class Statistics + { private readonly int _numIdle; private readonly int _numActive; - - internal Statistics(int numIdle, int numActive) { + + internal Statistics(int numIdle, int numActive) + { _numIdle = numIdle; _numActive = numActive; } - - /** - * Returns the number of idle objects - */ - public int NumIdle { - get { + + /// + /// Returns the number of idle objects + /// + public int NumIdle + { + get + { return _numIdle; } } - - /** - * Returns the number of active objects - */ - public int NumActive { - get { + + /// + /// Returns the number of active objects + /// + public int NumActive + { + get + { return _numActive; } } } - - /** - * An object plus additional book-keeping - * information about the object - */ - private class PooledObject where T2 : class { - /** - * The underlying object - */ + + /// + /// An object plus additional book-keeping + /// information about the object + /// + private class PooledObject where T2 : class + { + /// + /// The underlying object + /// private readonly T2 _object; - - /** - * True if this is currently active, false if - * it is idle - */ + + /// + /// True if this is currently active, false if + /// it is idle + /// private bool _isActive; - - /** - * Last state change (change from active to - * idle or vice-versa) - */ + + /// + /// Last state change (change from active to + /// idle or vice-versa) + /// private long _lastStateChangeTimestamp; - - /** - * Is this a freshly created object (never been pooled)? - */ + + /// + /// Is this a freshly created object (never been pooled)? + /// private bool _isNew; - - public PooledObject(T2 obj) { + + public PooledObject(T2 obj) + { _object = obj; _isNew = true; Touch(); } - - public T2 Object { - get { + + public T2 Object + { + get + { return _object; } } - - public bool IsActive { - get { + + public bool IsActive + { + get + { return _isActive; } - set { - if (_isActive != value) { + set + { + if (_isActive != value) + { Touch(); _isActive = value; - } + } } } - - public bool IsNew { - get { + + public bool IsNew + { + get + { return _isNew; } - set { + set + { _isNew = value; } } - - - private void Touch() { + + + private void Touch() + { _lastStateChangeTimestamp = DateTimeUtil.GetCurrentUtcTimeMillis(); } - - public long LastStateChangeTimestamp { - get { + + public long LastStateChangeTimestamp + { + get + { return _lastStateChangeTimestamp; } } } - - /** - * The lock object we use for everything - */ + + /// + /// The lock object we use for everything + /// private readonly Object LOCK = new Object(); - - /** - * Map from the object to the - * PooledObject (use IdentityHashMap so it's - * always object equality) - */ - private readonly IDictionary> + + /// + /// Map from the object to the + /// PooledObject (use IdentityHashMap so it's + /// always object equality) + /// + private readonly IDictionary> _activeObjects = CollectionUtil.NewIdentityDictionary>(); - - /** - * Queue of idle objects. The one that has - * been idle for the longest comes first in the queue - */ + + /// + /// Queue of idle objects. + /// + /// + /// The one that has + /// been idle for the longest comes first in the queue + /// private readonly LinkedList> _idleObjects = new LinkedList>(); - - /** - * ObjectPoolHandler we use for managing object lifecycle - */ + + /// + /// ObjectPoolHandler we use for managing object lifecycle + /// private readonly ObjectPoolHandler _handler; - - /** - * Configuration for this pool. - */ + + /// + /// Configuration for this pool. + /// private readonly ObjectPoolConfiguration _config; - - /** - * Is the pool shutdown - */ + + /// + /// Is the pool shutdown + /// private bool _isShutdown; - - /** - * Create a new ObjectPool - * @param handler Handler for objects - * @param config Configuration for the pool - */ + + /// + /// Create a new ObjectPool + /// + /// Handler for objects + /// Configuration for the pool public ObjectPool(ObjectPoolHandler handler, - ObjectPoolConfiguration config) { - + ObjectPoolConfiguration config) + { + Assertions.NullCheck(handler, "handler"); Assertions.NullCheck(config, "config"); - + _handler = handler; //clone it - _config = + _config = (ObjectPoolConfiguration)SerializerUtil.CloneObject(config); //validate it _config.Validate(); - + } - - /** - * Return an object to the pool - * @param object - */ - public void ReturnObject(T obj) { + + /// + /// Return an object to the pool + /// + /// + public void ReturnObject(T obj) + { Assertions.NullCheck(obj, "object"); - lock (LOCK) { + lock (LOCK) + { //remove it from the active list PooledObject pooled = - CollectionUtil.GetValue(_activeObjects,obj,null); - + CollectionUtil.GetValue(_activeObjects, obj, null); + //they are attempting to return something //we haven't allocated (or that they've //already returned) - if ( pooled == null ) { - throw new InvalidOperationException("Attempt to return an object not in the pool: "+obj); + if (pooled == null) + { + throw new InvalidOperationException("Attempt to return an object not in the pool: " + obj); } _activeObjects.Remove(obj); - + //set it to idle and add to idle list //(this might get evicted right away //by evictIdleObjects if we're over the //limit or if we're shutdown) - pooled.IsActive=(false); - pooled.IsNew=(false); + pooled.IsActive = (false); + pooled.IsNew = (false); _idleObjects.AddLast(pooled); - + //finally evict idle objects EvictIdleObjects(); - + //wake anyone up who was waiting on a object Monitor.PulseAll(LOCK); } } - - /** - * Borrow an object from the pool. - * @return An object - */ - public T BorrowObject() { - while ( true ) { + + /// + /// Borrow an object from the pool. + /// + /// An object + public T BorrowObject() + { + while (true) + { PooledObject rv = BorrowObjectNoTest(); - try { + try + { //make sure we are testing it outside //of synchronization. otherwise this //can create an IO bottleneck _handler.TestObject(rv.Object); return rv.Object; } - catch (Exception e) { + catch (Exception e) + { //it's bad - remove from active objects - lock (LOCK) { + lock (LOCK) + { _activeObjects.Remove(rv.Object); } DisposeNoException(rv.Object); //if it's a new object, break out of the loop //immediately - if ( rv.IsNew ) { + if (rv.IsNew) + { throw e; } } } } - - /** - * Borrow an object from the pool, but don't test - * it (it gets tested by the caller *outside* of - * synchronization) - * @return the object - */ - private PooledObject BorrowObjectNoTest() { + + /// + /// Borrow an object from the pool, but don't test + /// it (it gets tested by the caller *outside* of + /// synchronization) + /// + /// the object + private PooledObject BorrowObjectNoTest() + { //time when the call began long startTime = DateTimeUtil.GetCurrentUtcTimeMillis(); - - lock (LOCK) { + + lock (LOCK) + { EvictIdleObjects(); - while ( true ) { - if (_isShutdown) { + while (true) + { + if (_isShutdown) + { throw new InvalidOperationException("Object pool already shutdown"); } - + PooledObject pooledConn = null; - + //first try to recycle an idle object - if (_idleObjects.Count > 0) { + if (_idleObjects.Count > 0) + { pooledConn = _idleObjects.First(); _idleObjects.RemoveFirst(); } //otherwise, allocate a new object if //below the limit - else if (_activeObjects.Count < _config.MaxObjects) { + else if (_activeObjects.Count < _config.MaxObjects) + { pooledConn = new PooledObject(_handler.NewObject()); } - + //if there's an object available, return it //and break out of the loop - if ( pooledConn != null ) { - pooledConn.IsActive=(true); + if (pooledConn != null) + { + pooledConn.IsActive = (true); _activeObjects[pooledConn.Object] = pooledConn; return pooledConn; } - + //see if we haven't timed-out yet long elapsed = DateTimeUtil.GetCurrentUtcTimeMillis() - startTime; long remaining = _config.MaxWait - elapsed; - + //wait if we haven't timed out - if (remaining > 0) { - Monitor.Wait(LOCK,(int)remaining); + if (remaining > 0) + { + Monitor.Wait(LOCK, (int)remaining); } - else { + else + { //otherwise throw throw new ConnectorException("Max objects exceeded"); } } } } - - /** - * Closes any idle objects in the pool. - * Existing active objects will remain alive and - * be allowed to shutdown gracefully, but no more - * objects will be allocated. - */ - public void Shutdown() { - lock(LOCK) { + + /// + /// Closes any idle objects in the pool. + /// + /// + /// Existing active objects will remain alive and + /// be allowed to shutdown gracefully, but no more + /// objects will be allocated. + /// + public void Shutdown() + { + lock (LOCK) + { _isShutdown = true; //just evict idle objects //if there are any active objects still @@ -974,81 +1093,102 @@ public void Shutdown() { Monitor.PulseAll(LOCK); } } - - /** - * Gets a snapshot of the pool's stats at a point in time. - * @return The statistics - */ - public Statistics GetStatistics() { - lock(LOCK) { + + /// + /// Gets a snapshot of the pool's stats at a point in time. + /// + /// The statistics + public Statistics GetStatistics() + { + lock (LOCK) + { return new Statistics(_idleObjects.Count, _activeObjects.Count); } } - - /** - * Evicts idle objects as needed (evicts - * all idle objects if we're shutdown) - */ - private void EvictIdleObjects() { - while (TooManyIdleObjects()) { + + /// + /// Evicts idle objects as needed (evicts + /// all idle objects if we're shutdown) + /// + private void EvictIdleObjects() + { + while (TooManyIdleObjects()) + { PooledObject conn = _idleObjects.First(); _idleObjects.RemoveFirst(); DisposeNoException(conn.Object); } } - - /** - * Returns true if any of the following are true: - *
    - *
  1. We're shutdown and there are idle objects
  2. - *
  3. Max idle objects exceeded
  4. - *
  5. Min idle objects exceeded and there are old objects
  6. - *
- */ - private bool TooManyIdleObjects() { - - if (_isShutdown && _idleObjects.Count > 0) { + + /// + /// Returns true if any of the following are true: + /// + /// + /// We're shutdown and there are idle objects + /// + /// + /// + /// Max idle objects exceeded + /// + /// + /// + /// Min idle objects exceeded and there are old objects + /// + /// + /// + /// + private bool TooManyIdleObjects() + { + + if (_isShutdown && _idleObjects.Count > 0) + { return true; } - - if (_config.MaxIdle < _idleObjects.Count) { + + if (_config.MaxIdle < _idleObjects.Count) + { return true; } - if (_config.MinIdle >= _idleObjects.Count) { + if (_config.MinIdle >= _idleObjects.Count) + { return false; } - + PooledObject oldest = _idleObjects.First(); - - long age = - ( DateTimeUtil.GetCurrentUtcTimeMillis()-oldest.LastStateChangeTimestamp ); - - + + long age = + (DateTimeUtil.GetCurrentUtcTimeMillis() - oldest.LastStateChangeTimestamp); + + return age > _config.MinEvictableIdleTimeMillis; } - - /** - * Dispose of an object, but don't throw any exceptions - * @param object - */ - private void DisposeNoException(T obj) { - try { + + /// + /// Dispose of an object, but don't throw any exceptions + /// + /// + private void DisposeNoException(T obj) + { + try + { _handler.DisposeObject(obj); } - catch (Exception e) { - TraceUtil.TraceException("disposeObject() is not supposed to throw",e); + catch (Exception e) + { + TraceUtil.TraceException("disposeObject() is not supposed to throw", e); } } } #endregion #region ObjectPoolHandler - public interface ObjectPoolHandler where T : class { + public interface ObjectPoolHandler where T : class + { T NewObject(); void TestObject(T obj); void DisposeObject(T obj); } #endregion -} +} \ No newline at end of file diff --git a/FrameworkInternal/ApiLocalOperations.cs b/FrameworkInternal/ApiLocalOperations.cs index dd9b41ae..f5c14742 100644 --- a/FrameworkInternal/ApiLocalOperations.cs +++ b/FrameworkInternal/ApiLocalOperations.cs @@ -39,118 +39,130 @@ using System.Linq; namespace Org.IdentityConnectors.Framework.Impl.Api.Local.Operations -{ +{ #region APIOperationRunner - /** - * NOTE: internal class, public only for unit tests - * Base class for API operation runners. - */ - public abstract class APIOperationRunner { - - /** - * Context that has all the information required to execute an operation. - */ + /// + /// NOTE: internal class, public only for unit tests + /// Base class for API operation runners. + /// + public abstract class APIOperationRunner + { + /// + /// Context that has all the information required to execute an operation. + /// private readonly OperationalContext _context; - - - /** - * Creates the API operation so it can called multiple times. - */ - public APIOperationRunner(OperationalContext context) { + + /// + /// Creates the API operation so it can called multiple times. + /// + public APIOperationRunner(OperationalContext context) + { _context = context; //TODO: verify this // get the APIOperation that this class implements.. //List> apiOps = getInterfaces(this - //.getClass(), APIOperation.class); + //.getClass(), APIOperation.class); // there should be only one.. //if (apiOps.size() > 1) { // final String MSG = "Must only implement one operation."; // throw new IllegalStateException(MSG); //} } - - /** - * Get the current operational context. - */ - public OperationalContext GetOperationalContext() { + + /// + /// Get the current operational context. + /// + public OperationalContext GetOperationalContext() + { return _context; } - + } #endregion - + #region ConnectorAPIOperationRunner - /** - * NOTE: internal class, public only for unit tests - * Subclass of APIOperationRunner for operations that require a connector. - */ - public abstract class ConnectorAPIOperationRunner : APIOperationRunner { - - /** - * The connector instance - */ + /// + /// NOTE: internal class, public only for unit tests + /// Subclass of APIOperationRunner for operations that require a connector. + /// + public abstract class ConnectorAPIOperationRunner : APIOperationRunner + { + /// + /// The connector instance + /// private readonly Connector _connector; - - /** - * Creates the API operation so it can called multiple times. - */ + + /// + /// Creates the API operation so it can called multiple times. + /// public ConnectorAPIOperationRunner(ConnectorOperationalContext context, - Connector connector) : base(context) { + Connector connector) + : base(context) + { _connector = connector; } - - public Connector GetConnector() { + + public Connector GetConnector() + { return _connector; } - - public ObjectNormalizerFacade GetNormalizer(ObjectClass oclass) { + + public ObjectNormalizerFacade GetNormalizer(ObjectClass oclass) + { AttributeNormalizer norm = null; Connector connector = GetConnector(); - if ( connector is AttributeNormalizer ) { + if (connector is AttributeNormalizer) + { norm = (AttributeNormalizer)connector; } - return new ObjectNormalizerFacade(oclass,norm); + return new ObjectNormalizerFacade(oclass, norm); } } #endregion #region ConnectorAPIOperationRunnerProxy - /** - * Proxy for APIOperationRunner that takes care of setting up underlying - * connector and creating the implementation of APIOperationRunner. - * The implementation of APIOperationRunner gets created whenever the - * actual method is invoked. - */ - internal class ConnectorAPIOperationRunnerProxy : InvocationHandler { - - - /** - * The operational context - */ + /// + /// Proxy for APIOperationRunner that takes care of setting up underlying + /// connector and creating the implementation of APIOperationRunner. + /// + /// + /// The implementation of APIOperationRunner gets created whenever the + /// actual method is invoked. + /// + internal class ConnectorAPIOperationRunnerProxy : InvocationHandler + { + /// + /// The operational context + /// private readonly ConnectorOperationalContext _context; - - /** - * The implementation constructor. The instance is lazily created upon - * invocation - */ + + /// + /// The implementation constructor. + /// + /// + /// The instance is lazily created upon + /// invocation + /// private readonly ConstructorInfo _runnerImplConstructor; - - /** - * Create an APIOperationRunnerProxy - * @param context The operational context - * @param runnerImplConstructor The implementation constructor. Implementation - * must define a two-argument constructor(OperationalContext,Connector) - */ + + /// + /// Create an APIOperationRunnerProxy + /// + /// The operational context + /// The implementation constructor. Implementation + /// must define a two-argument constructor(OperationalContext,Connector) public ConnectorAPIOperationRunnerProxy(ConnectorOperationalContext context, - ConstructorInfo runnerImplConstructor) { + ConstructorInfo runnerImplConstructor) + { _context = context; _runnerImplConstructor = runnerImplConstructor; } - + public Object Invoke(Object proxy, MethodInfo method, object[] args) { //do not proxy equals, hashCode, toString - if (method.DeclaringType.Equals(typeof(object))) { + if (method.DeclaringType.Equals(typeof(object))) + { return method.Invoke(this, args); } object ret = null; @@ -158,33 +170,43 @@ public Object Invoke(Object proxy, MethodInfo method, object[] args) ObjectPool pool = _context.GetPool(); // get the connector class.. SafeType connectorClazz = _context.GetConnectorClass(); - try { + try + { // pooling is implemented get one.. - if (pool != null) { + if (pool != null) + { connector = pool.BorrowObject(); } - else { + else + { // get a new instance of the connector.. connector = connectorClazz.CreateInstance(); // initialize the connector.. connector.Init(_context.GetConfiguration()); } - APIOperationRunner runner = + APIOperationRunner runner = (APIOperationRunner)_runnerImplConstructor.Invoke(new object[]{ _context, connector}); ret = method.Invoke(runner, args); // call out to the operation.. - } catch (TargetInvocationException e) { + } + catch (TargetInvocationException e) + { Exception root = e.InnerException; - ExceptionUtil.PreserveStackTrace( root ); + ExceptionUtil.PreserveStackTrace(root); throw root; - } finally { + } + finally + { // make sure dispose of the connector properly - if (connector != null) { + if (connector != null) + { // determine if there was a pool.. - if (pool != null) { - try { + if (pool != null) + { + try + { //try to return it to the pool even though an //exception may have happened that leaves it in //a bad state. The contract of checkAlive @@ -192,86 +214,98 @@ public Object Invoke(Object proxy, MethodInfo method, object[] args) //still valid and so we leave it up to the pool //and connector to work it out. pool.ReturnObject((PoolableConnector)connector); - } catch (Exception e) { + } + catch (Exception e) + { //don't let pool exceptions propogate or mask //other exceptions. do log it though. - TraceUtil.TraceException(null,e); + TraceUtil.TraceException(null, e); } } //not pooled - just dispose - else { + else + { //dispose it not supposed to throw, but just in case, //catch the exception and log it so we know about it //but don't let the exception prevent additional //cleanup that needs to happen - try { + try + { connector.Dispose(); - } catch (Exception e) { + } + catch (Exception e) + { //log this though - TraceUtil.TraceException(null,e); - } + TraceUtil.TraceException(null, e); + } } - } + } } return ret; - } + } } #endregion #region ConnectorOperationalContext - /** - * NOTE: internal class, public only for unit tests - * Simple structure to pass more variables through the constructor of - * {@link APIOperationRunner}. - */ - public class ConnectorOperationalContext : OperationalContext { - + /// + /// NOTE: internal class, public only for unit tests + /// Simple structure to pass more variables through the constructor of + /// . + /// + public class ConnectorOperationalContext : OperationalContext + { private readonly ObjectPool _pool; - + public ConnectorOperationalContext(LocalConnectorInfoImpl connectorInfo, APIConfigurationImpl apiConfiguration, ObjectPool pool) - :base(connectorInfo,apiConfiguration) { + : base(connectorInfo, apiConfiguration) + { _pool = pool; } - - public ObjectPool GetPool() { + + public ObjectPool GetPool() + { return _pool; } - - - public SafeType GetConnectorClass() { + + + public SafeType GetConnectorClass() + { return GetConnectorInfo().ConnectorClass; } - + } #endregion - + #region AuthenticationImpl internal class AuthenticationImpl : ConnectorAPIOperationRunner, - AuthenticationApiOp { - /** - * Pass the configuration etc to the abstract class. - */ + AuthenticationApiOp + { + /// + /// Pass the configuration etc to the abstract class. + /// public AuthenticationImpl(ConnectorOperationalContext context, - Connector connector) - :base(context,connector) { - } - - /** - * Authenticate using the basic credentials. - * - * @see Authentication#authenticate(String, String) - */ - public Uid Authenticate(ObjectClass objectClass, String username, GuardedString password, OperationOptions options) { - Assertions.NullCheck(objectClass,"objectClass"); + Connector connector) + : base(context, connector) + { + } + + /// + /// Authenticate using the basic credentials. + /// + /// + public Uid Authenticate(ObjectClass objectClass, String username, GuardedString password, OperationOptions options) + { + Assertions.NullCheck(objectClass, "objectClass"); Assertions.NullCheck(username, "username"); Assertions.NullCheck(password, "password"); //convert null into empty - if ( options == null ) { + if (options == null) + { options = new OperationOptionsBuilder().Build(); } - return ((AuthenticateOp) GetConnector()).Authenticate(objectClass,username, password,options); + return ((AuthenticateOp)GetConnector()).Authenticate(objectClass, username, password, options); } } #endregion @@ -280,18 +314,18 @@ public Uid Authenticate(ObjectClass objectClass, String username, GuardedString internal class ResolveUsernameImpl : ConnectorAPIOperationRunner, ResolveUsernameApiOp { - /** - * Pass the configuration etc to the abstract class. - */ + /// + /// Pass the configuration etc to the abstract class. + /// public ResolveUsernameImpl(ConnectorOperationalContext context, Connector connector) : base(context, connector) { } - /** - * Resolve the username to an Uid. - */ + /// + /// Resolve the username to an Uid. + /// public Uid ResolveUsername(ObjectClass objectClass, String username, OperationOptions options) { Assertions.NullCheck(objectClass, "objectClass"); @@ -308,123 +342,134 @@ public Uid ResolveUsername(ObjectClass objectClass, String username, OperationOp #region CreateImpl internal class CreateImpl : ConnectorAPIOperationRunner, - CreateApiOp { - - /** - * Initializes the operation works. - */ + CreateApiOp + { + /// + /// Initializes the operation works. + /// public CreateImpl(ConnectorOperationalContext context, - Connector connector) - :base(context,connector) { - } - - /** - * Calls the create method on the Connector side. - * - * @see CreateApiOp#create(Set) - */ - public Uid Create(ObjectClass oclass, ICollection attributes, OperationOptions options) { + Connector connector) + : base(context, connector) + { + } + + /// + /// Calls the create method on the Connector side. + /// + /// + public Uid Create(ObjectClass oclass, ICollection attributes, OperationOptions options) + { Assertions.NullCheck(oclass, "oclass"); Assertions.NullCheck(attributes, "attributes"); //convert null into empty - if ( options == null ) { + if (options == null) + { options = new OperationOptionsBuilder().Build(); } - HashSet dups = new HashSet(); - foreach (ConnectorAttribute attr in attributes) { - if (dups.Contains(attr.Name)) { - throw new ArgumentException("Duplicate attribute name exists: " + attr.Name); - } - dups.Add(attr.Name); - } - if (oclass == null) { - throw new ArgumentException("Required attribute ObjectClass not found!"); - } + HashSet dups = new HashSet(); + foreach (ConnectorAttribute attr in attributes) + { + if (dups.Contains(attr.Name)) + { + throw new ArgumentException("Duplicate attribute name exists: " + attr.Name); + } + dups.Add(attr.Name); + } + if (oclass == null) + { + throw new ArgumentException("Required attribute ObjectClass not found!"); + } Connector connector = GetConnector(); ObjectNormalizerFacade normalizer = GetNormalizer(oclass); ICollection normalizedAttributes = normalizer.NormalizeAttributes(attributes); // create the object.. - Uid ret = ((CreateOp) connector).Create(oclass,attributes,options); + Uid ret = ((CreateOp)connector).Create(oclass, attributes, options); return (Uid)normalizer.NormalizeAttribute(ret); } } #endregion - + #region DeleteImpl - internal class DeleteImpl : ConnectorAPIOperationRunner , - DeleteApiOp { - - /** - * Initializes the operation works. - */ + internal class DeleteImpl : ConnectorAPIOperationRunner, + DeleteApiOp + { + /// + /// Initializes the operation works. + /// public DeleteImpl(ConnectorOperationalContext context, - Connector connector) - :base(context,connector) { - } - /** - * Calls the delete method on the Connector side. - * - * @see com.sun.openconnectors.framework.api.operations.CreateApiOp#create(java.util.Set) - */ - public void Delete(ObjectClass objClass, Uid uid, OperationOptions options) { + Connector connector) + : base(context, connector) + { + } + /// + /// Calls the delete method on the Connector side. + /// + /// + public void Delete(ObjectClass objClass, Uid uid, OperationOptions options) + { Assertions.NullCheck(objClass, "objClass"); Assertions.NullCheck(uid, "uid"); //convert null into empty - if ( options == null ) { + if (options == null) + { options = new OperationOptionsBuilder().Build(); } Connector connector = GetConnector(); ObjectNormalizerFacade normalizer = GetNormalizer(objClass); // delete the object.. - ((DeleteOp) connector).Delete(objClass, - (Uid)normalizer.NormalizeAttribute(uid), + ((DeleteOp)connector).Delete(objClass, + (Uid)normalizer.NormalizeAttribute(uid), options); } } #endregion - + #region AttributesToGetResultsHandler - public abstract class AttributesToGetResultsHandler { + public abstract class AttributesToGetResultsHandler + { // ======================================================================= // Fields // ======================================================================= - private readonly string[] _attrsToGet; + private readonly string[] _attrsToGet; // ======================================================================= // Constructors // ======================================================================= - /** - * Keep the attribute to get.. - */ - public AttributesToGetResultsHandler(string[] attrsToGet) { + /// + /// Keep the attribute to get.. + /// + public AttributesToGetResultsHandler(string[] attrsToGet) + { Assertions.NullCheck(attrsToGet, "attrsToGet"); _attrsToGet = attrsToGet; } - - /** - * Simple method that clones the object and remove the attribute thats are - * not in the {@link OperationOptions#OP_ATTRIBUTES_TO_GET} set. - * - * @param attrsToGet - * case insensitive set of attribute names. - */ + + /// + /// Simple method that clones the object and remove the attribute thats are + /// not in the set. + /// + /// case insensitive set of attribute names. public ICollection ReduceToAttrsToGet( - ICollection attrs) { + ICollection attrs) + { ICollection ret = new HashSet(); IDictionary map = ConnectorAttributeUtil.ToMap(attrs); - foreach (string attrName in _attrsToGet) { + foreach (string attrName in _attrsToGet) + { ConnectorAttribute attr = CollectionUtil.GetValue(map, attrName, null); // TODO: Should we throw if the attribute is not yet it was // requested?? Or do we ignore because the API maybe asking // for what the resource doesn't have?? - if (attr != null) { + if (attr != null) + { ret.Add(attr); } } return ret; } - public ConnectorObject ReduceToAttrsToGet(ConnectorObject obj) { + public ConnectorObject ReduceToAttrsToGet(ConnectorObject obj) + { // clone the object and reduce the attributes only the set of // attributes. ConnectorObjectBuilder bld = new ConnectorObjectBuilder(); @@ -435,61 +480,68 @@ public ConnectorObject ReduceToAttrsToGet(ConnectorObject obj) { ICollection attrs = ReduceToAttrsToGet(objAttrs); bld.AddAttributes(attrs); return bld.Build(); - } + } } #endregion - + #region SearchAttributesToGetResultsHandler - public sealed class SearchAttributesToGetResultsHandler : - AttributesToGetResultsHandler { - + public sealed class SearchAttributesToGetResultsHandler : + AttributesToGetResultsHandler + { // ======================================================================= // Fields // ======================================================================= private readonly ResultsHandler _handler; - + // ======================================================================= // Constructors // ======================================================================= public SearchAttributesToGetResultsHandler( - ResultsHandler handler, string[] attrsToGet) : base(attrsToGet) { + ResultsHandler handler, string[] attrsToGet) + : base(attrsToGet) + { Assertions.NullCheck(handler, "handler"); this._handler = handler; } - - public bool Handle(ConnectorObject obj) { + + public bool Handle(ConnectorObject obj) + { // clone the object and reduce the attributes only the set of // attributes. return _handler(ReduceToAttrsToGet(obj)); - } + } } #endregion - + #region SearchAttributesToGetResultsHandler - public sealed class SyncAttributesToGetResultsHandler : - AttributesToGetResultsHandler { - + public sealed class SyncAttributesToGetResultsHandler : + AttributesToGetResultsHandler + { // ======================================================================= // Fields // ======================================================================= private readonly SyncResultsHandler _handler; - + // ======================================================================= // Constructors // ======================================================================= public SyncAttributesToGetResultsHandler( - SyncResultsHandler handler, string[] attrsToGet) : base(attrsToGet) { + SyncResultsHandler handler, string[] attrsToGet) + : base(attrsToGet) + { Assertions.NullCheck(handler, "handler"); this._handler = handler; } - - public bool Handle(SyncDelta delta) { + + public bool Handle(SyncDelta delta) + { SyncDeltaBuilder bld = new SyncDeltaBuilder(); bld.Uid = delta.Uid; bld.Token = delta.Token; bld.DeltaType = delta.DeltaType; - if ( delta.Object != null ) { - bld.Object=ReduceToAttrsToGet(delta.Object); + if (delta.Object != null) + { + bld.Object = ReduceToAttrsToGet(delta.Object); } return _handler(bld.Build()); } @@ -497,38 +549,39 @@ public bool Handle(SyncDelta delta) { #endregion #region DuplicateFilteringResultsHandler - public sealed class DuplicateFilteringResultsHandler { - + public sealed class DuplicateFilteringResultsHandler + { // ======================================================================= // Fields // ======================================================================= private readonly ResultsHandler _handler; private readonly HashSet _visitedUIDs = new HashSet(); - + private bool _stillHandling; - + // ======================================================================= // Constructors // ======================================================================= - /** - * Filter chain for producers. - * - * @param producer - * Producer to filter. - * - */ - public DuplicateFilteringResultsHandler(ResultsHandler handler) { + /// + /// Filter chain for producers. + /// + /// Producer to filter. + public DuplicateFilteringResultsHandler(ResultsHandler handler) + { // there must be a producer.. - if (handler == null) { + if (handler == null) + { throw new ArgumentException("Handler must not be null!"); } this._handler = handler; } - - public bool Handle(ConnectorObject obj) { + + public bool Handle(ConnectorObject obj) + { String uid = obj.Uid.GetUidValue(); - if (!_visitedUIDs.Add(uid)) { + if (!_visitedUIDs.Add(uid)) + { //we've already seen this - don't pass it //throw return true; @@ -536,698 +589,780 @@ public bool Handle(ConnectorObject obj) { _stillHandling = _handler(obj); return _stillHandling; } - - public bool IsStillHandling { - get { + + public bool IsStillHandling + { + get + { return _stillHandling; } } - } #endregion - + #region FilteredResultsHandler - public sealed class FilteredResultsHandler { - + public sealed class FilteredResultsHandler + { // ======================================================================= // Fields // ======================================================================= readonly ResultsHandler handler; readonly Filter filter; - + // ======================================================================= // Constructors // ======================================================================= - /** - * Filter chain for producers. - * - * @param producer - * Producer to filter. - * @param filter - * Filter to use to accept objects. - */ - public FilteredResultsHandler(ResultsHandler handler, Filter filter) { + /// + /// Filter chain for producers. + /// + /// Producer to filter. + /// Filter to use to accept objects. + public FilteredResultsHandler(ResultsHandler handler, Filter filter) + { // there must be a producer.. - if (handler == null) { + if (handler == null) + { throw new ArgumentException("Producer must not be null!"); } this.handler = handler; // use a default pass through filter.. this.filter = filter == null ? new PassThruFilter() : filter; } - - public bool Handle(ConnectorObject obj) { - if ( filter.Accept(obj) ) { + + public bool Handle(ConnectorObject obj) + { + if (filter.Accept(obj)) + { return handler(obj); } - else { + else + { return true; } } - - /** - * Use a pass thru filter to use if a null filter is provided. - */ - class PassThruFilter : Filter { - public bool Accept(ConnectorObject obj) { + + /// + /// Use a pass thru filter to use if a null filter is provided. + /// + class PassThruFilter : Filter + { + public bool Accept(ConnectorObject obj) + { return true; } } - } #endregion - + #region GetImpl - /** - * Uses {@link SearchOp} to find the object that is referenced by the - * {@link Uid} provided. - */ - public class GetImpl : GetApiOp { - + /// + /// Uses to find the object that is referenced by the + /// provided. + /// + public class GetImpl : GetApiOp + { readonly SearchApiOp op; - - private class ResultAdapter { + + private class ResultAdapter + { private IList _list = new List(); - public bool Handle(ConnectorObject obj) { + public bool Handle(ConnectorObject obj) + { _list.Add(obj); return false; } - public ConnectorObject GetResult() { + public ConnectorObject GetResult() + { return _list.Count == 0 ? null : _list[0]; } } - - public GetImpl(SearchApiOp search) { + + public GetImpl(SearchApiOp search) + { this.op = search; } - - public ConnectorObject GetObject(ObjectClass objClass, Uid uid, OperationOptions options) { + + public ConnectorObject GetObject(ObjectClass objClass, Uid uid, OperationOptions options) + { Assertions.NullCheck(objClass, "objClass"); Assertions.NullCheck(uid, "uid"); //convert null into empty - if ( options == null ) { + if (options == null) + { options = new OperationOptionsBuilder().Build(); } Filter filter = FilterBuilder.EqualTo(uid); ResultAdapter adapter = new ResultAdapter(); - op.Search(objClass,filter,new ResultsHandler(adapter.Handle),options); + op.Search(objClass, filter, new ResultsHandler(adapter.Handle), options); return adapter.GetResult(); } } #endregion - + #region OperationalContext - /** - * NOTE: internal class, public only for unit tests - * OperationalContext - base class for operations that do not - * require a connection. - */ - public class OperationalContext { - - /** - * ConnectorInfo - */ + /// + /// NOTE: internal class, public only for unit tests + /// OperationalContext - base class for operations that do not + /// require a connection. + /// + public class OperationalContext + { + /// + /// ConnectorInfo + /// private readonly LocalConnectorInfoImpl connectorInfo; - - /** - * Contains the {@link Connector} {@link Configuration}. - */ + + /// + /// Contains the . + /// private readonly APIConfigurationImpl apiConfiguration; - - + + public OperationalContext(LocalConnectorInfoImpl connectorInfo, - APIConfigurationImpl apiConfiguration) { + APIConfigurationImpl apiConfiguration) + { this.connectorInfo = connectorInfo; this.apiConfiguration = apiConfiguration; } - - public Configuration GetConfiguration() { + + public Configuration GetConfiguration() + { return CSharpClassProperties.CreateBean((ConfigurationPropertiesImpl)this.apiConfiguration.ConfigurationProperties, connectorInfo.ConnectorConfigurationClass); } - - protected LocalConnectorInfoImpl GetConnectorInfo() { + + protected LocalConnectorInfoImpl GetConnectorInfo() + { return connectorInfo; } } #endregion - + #region NormalizingResultsHandler - public class NormalizingResultsHandler { - + public class NormalizingResultsHandler + { private readonly ResultsHandler _target; private readonly ObjectNormalizerFacade _normalizer; - + public NormalizingResultsHandler(ResultsHandler target, - ObjectNormalizerFacade normalizer) { + ObjectNormalizerFacade normalizer) + { Assertions.NullCheck(target, "target"); Assertions.NullCheck(normalizer, "normalizer"); _target = target; _normalizer = normalizer; } - - - public bool Handle(ConnectorObject obj) { + + + public bool Handle(ConnectorObject obj) + { ConnectorObject normalized = _normalizer.NormalizeObject(obj); return _target(normalized); } - + } - #endregion - + #endregion + #region NormalizingSyncResultsHandler - public class NormalizingSyncResultsHandler { - + public class NormalizingSyncResultsHandler + { private readonly SyncResultsHandler _target; private readonly ObjectNormalizerFacade _normalizer; - + public NormalizingSyncResultsHandler(SyncResultsHandler target, - ObjectNormalizerFacade normalizer) { + ObjectNormalizerFacade normalizer) + { Assertions.NullCheck(target, "target"); Assertions.NullCheck(normalizer, "normalizer"); _target = target; _normalizer = normalizer; } - - - public bool Handle(SyncDelta delta) { + + + public bool Handle(SyncDelta delta) + { SyncDelta normalized = _normalizer.NormalizeSyncDelta(delta); return _target(normalized); } - } #endregion - + #region ObjectNormalizerFacade - public sealed class ObjectNormalizerFacade { - /** - * The (non-null) object class - */ + public sealed class ObjectNormalizerFacade + { + /// + /// The (non-null) object class + /// private readonly ObjectClass _objectClass; - /** - * The (possibly null) attribute normalizer - */ + /// + /// The (possibly null) attribute normalizer + /// private readonly AttributeNormalizer _normalizer; - - /** - * Create a new ObjectNormalizer - * @param objectClass The object class - * @param normalizer The normalizer. May be null. - */ + + /// + /// Create a new ObjectNormalizer + /// + /// The object class + /// The normalizer. May be null. public ObjectNormalizerFacade(ObjectClass objectClass, - AttributeNormalizer normalizer) { + AttributeNormalizer normalizer) + { Assertions.NullCheck(objectClass, "objectClass"); _objectClass = objectClass; - _normalizer = normalizer; - } - - /** - * Returns the normalized value of the attribute. - * If no normalizer is specified, returns the original - * attribute. - * @param attribute The attribute to normalize. - * @return The normalized attribute - */ - public ConnectorAttribute NormalizeAttribute(ConnectorAttribute attribute) { - if ( attribute == null ) { + _normalizer = normalizer; + } + + /// + /// Returns the normalized value of the attribute. + /// + /// + /// If no normalizer is specified, returns the original + /// attribute. + /// + /// The attribute to normalize. + /// The normalized attribute + public ConnectorAttribute NormalizeAttribute(ConnectorAttribute attribute) + { + if (attribute == null) + { return null; } - else if (_normalizer != null) { + else if (_normalizer != null) + { return _normalizer.NormalizeAttribute(_objectClass, attribute); } - else { + else + { return attribute; } } - - /** - * Returns the normalized set of attributes or null - * if the original set is null. - * @param attributes The original attributes. - * @return The normalized attributes or null if - * the original set is null. - */ - public ICollection NormalizeAttributes(ICollection attributes) { - if ( attributes == null ) { + + /// + /// Returns the normalized set of attributes or null + /// if the original set is null. + /// + /// The original attributes. + /// The normalized attributes or null if + /// the original set is null. + public ICollection NormalizeAttributes(ICollection attributes) + { + if (attributes == null) + { return null; } ICollection temp = new HashSet(); - foreach (ConnectorAttribute attribute in attributes ) { + foreach (ConnectorAttribute attribute in attributes) + { temp.Add(NormalizeAttribute(attribute)); } return CollectionUtil.AsReadOnlySet(temp); } - - /** - * Returns the normalized object. - * @param orig The original object - * @return The normalized object. - */ - public ConnectorObject NormalizeObject(ConnectorObject orig) { + + /// + /// Returns the normalized object. + /// + /// The original object + /// The normalized object. + public ConnectorObject NormalizeObject(ConnectorObject orig) + { return new ConnectorObject(orig.ObjectClass, NormalizeAttributes(orig.GetAttributes())); } - - /** - * Returns the normalized sync delta. - * @param delta The original delta. - * @return The normalized delta. - */ - public SyncDelta NormalizeSyncDelta(SyncDelta delta) { + + /// + /// Returns the normalized sync delta. + /// + /// The original delta. + /// The normalized delta. + public SyncDelta NormalizeSyncDelta(SyncDelta delta) + { SyncDeltaBuilder builder = new SyncDeltaBuilder(delta); - if ( delta.Object != null ) { - builder.Object=NormalizeObject(delta.Object); + if (delta.Object != null) + { + builder.Object = NormalizeObject(delta.Object); } return builder.Build(); } - - /** - * Returns a filter consisting of the original with - * all attributes normalized. - * @param filter The original. - * @return The normalized filter. - */ - public Filter NormalizeFilter(Filter filter) { - if ( filter is ContainsFilter ) { + + /// + /// Returns a filter consisting of the original with + /// all attributes normalized. + /// + /// The original. + /// The normalized filter. + public Filter NormalizeFilter(Filter filter) + { + if (filter is ContainsFilter) + { AttributeFilter afilter = (AttributeFilter)filter; return new ContainsFilter(NormalizeAttribute(afilter.GetAttribute())); } - else if (filter is EndsWithFilter) { + else if (filter is EndsWithFilter) + { AttributeFilter afilter = (AttributeFilter)filter; return new EndsWithFilter(NormalizeAttribute(afilter.GetAttribute())); } - else if ( filter is EqualsFilter ) { + else if (filter is EqualsFilter) + { AttributeFilter afilter = (AttributeFilter)filter; return new EqualsFilter(NormalizeAttribute(afilter.GetAttribute())); } - else if ( filter is GreaterThanFilter ) { + else if (filter is GreaterThanFilter) + { AttributeFilter afilter = (AttributeFilter)filter; return new GreaterThanFilter(NormalizeAttribute(afilter.GetAttribute())); } - else if ( filter is GreaterThanOrEqualFilter ) { + else if (filter is GreaterThanOrEqualFilter) + { AttributeFilter afilter = (AttributeFilter)filter; return new GreaterThanOrEqualFilter(NormalizeAttribute(afilter.GetAttribute())); } - else if ( filter is LessThanFilter ) { + else if (filter is LessThanFilter) + { AttributeFilter afilter = (AttributeFilter)filter; return new LessThanFilter(NormalizeAttribute(afilter.GetAttribute())); } - else if ( filter is LessThanOrEqualFilter ) { + else if (filter is LessThanOrEqualFilter) + { AttributeFilter afilter = (AttributeFilter)filter; return new LessThanOrEqualFilter(NormalizeAttribute(afilter.GetAttribute())); } - else if (filter is StartsWithFilter) { + else if (filter is StartsWithFilter) + { AttributeFilter afilter = (AttributeFilter)filter; return new StartsWithFilter(NormalizeAttribute(afilter.GetAttribute())); } - else if (filter is ContainsAllValuesFilter) { + else if (filter is ContainsAllValuesFilter) + { AttributeFilter afilter = (AttributeFilter)filter; return new ContainsAllValuesFilter(NormalizeAttribute(afilter.GetAttribute())); } - else if ( filter is NotFilter ) { + else if (filter is NotFilter) + { NotFilter notFilter = (NotFilter)filter; return new NotFilter(NormalizeFilter(notFilter.Filter)); } - else if ( filter is AndFilter ) { + else if (filter is AndFilter) + { AndFilter andFilter = (AndFilter)filter; return new AndFilter(NormalizeFilter(andFilter.Left), NormalizeFilter(andFilter.Right)); } - else if ( filter is OrFilter ) { + else if (filter is OrFilter) + { OrFilter orFilter = (OrFilter)filter; return new OrFilter(NormalizeFilter(orFilter.Left), NormalizeFilter(orFilter.Right)); } - else { + else + { return filter; } } - - - } + } #endregion - + #region SchemaImpl - internal class SchemaImpl : ConnectorAPIOperationRunner , SchemaApiOp { - /** - * Initializes the operation works. - */ + internal class SchemaImpl : ConnectorAPIOperationRunner, SchemaApiOp + { + /// + /// Initializes the operation works. + /// public SchemaImpl(ConnectorOperationalContext context, - Connector connector) - :base(context,connector) { - } - - /** - * Retrieve the schema from the {@link Connector}. - * - * @see com.sun.openconnectors.framework.api.operations.SchemaApiOp#schema() - */ - public Schema Schema() { + Connector connector) + : base(context, connector) + { + } + + /// + /// Retrieve the schema from the . + /// + /// + public Schema Schema() + { return ((SchemaOp)GetConnector()).Schema(); } } #endregion - + #region ScriptOnConnectorImpl public class ScriptOnConnectorImpl : ConnectorAPIOperationRunner, - ScriptOnConnectorApiOp { - + ScriptOnConnectorApiOp + { public ScriptOnConnectorImpl(ConnectorOperationalContext context, - Connector connector) : - base(context,connector) { + Connector connector) : + base(context, connector) + { } - + public Object RunScriptOnConnector(ScriptContext request, - OperationOptions options) { + OperationOptions options) + { Assertions.NullCheck(request, "request"); //convert null into empty - if ( options == null ) { + if (options == null) + { options = new OperationOptionsBuilder().Build(); } Object rv; - if ( GetConnector() is ScriptOnConnectorOp ) { + if (GetConnector() is ScriptOnConnectorOp) + { rv = ((ScriptOnConnectorOp)GetConnector()).RunScriptOnConnector(request, options); } - else { + else + { String language = request.ScriptLanguage; Assembly assembly = GetConnector().GetType().Assembly; - - ScriptExecutor executor = + + ScriptExecutor executor = ScriptExecutorFactory.NewInstance(language).NewScriptExecutor( BuildReferenceList(assembly), request.ScriptText, false); - IDictionary scriptArgs = - new Dictionary(request.ScriptArguments); - scriptArgs["connector"]=GetConnector(); //add the connector instance itself - rv = executor.Execute(scriptArgs); + IDictionary scriptArgs = + new Dictionary(request.ScriptArguments); + scriptArgs["connector"] = GetConnector(); //add the connector instance itself + rv = executor.Execute(scriptArgs); } return SerializerUtil.CloneObject(rv); } - - private Assembly [] BuildReferenceList(Assembly assembly) + + private Assembly[] BuildReferenceList(Assembly assembly) { List list = new List(); - BuildReferenceList2(assembly,list,new HashSet()); + BuildReferenceList2(assembly, list, new HashSet()); return list.ToArray(); } - + private void BuildReferenceList2(Assembly assembly, List list, - HashSet visited) { + HashSet visited) + { bool notThere = visited.Add(assembly.GetName().FullName); if (notThere) { list.Add(assembly); - foreach (AssemblyName referenced in assembly.GetReferencedAssemblies()) { + foreach (AssemblyName referenced in assembly.GetReferencedAssemblies()) + { Assembly assembly2 = Assembly.Load(referenced); - BuildReferenceList2(assembly2,list,visited); + BuildReferenceList2(assembly2, list, visited); } } } - } #endregion - + #region ScriptOnResourceImpl public class ScriptOnResourceImpl : ConnectorAPIOperationRunner, - ScriptOnResourceApiOp { - + ScriptOnResourceApiOp + { public ScriptOnResourceImpl(ConnectorOperationalContext context, Connector connector) : - base(context,connector) { + base(context, connector) + { } - + public Object RunScriptOnResource(ScriptContext request, - OperationOptions options) { + OperationOptions options) + { Assertions.NullCheck(request, "request"); //convert null into empty - if ( options == null ) { + if (options == null) + { options = new OperationOptionsBuilder().Build(); } Object rv = ((ScriptOnResourceOp)GetConnector()).RunScriptOnResource(request, options); return SerializerUtil.CloneObject(rv); } - + } #endregion - + #region SearchImpl - internal class SearchImpl : ConnectorAPIOperationRunner , SearchApiOp { - - /** - * Initializes the operation works. - */ + internal class SearchImpl : ConnectorAPIOperationRunner, SearchApiOp + { + /// + /// Initializes the operation works. + /// public SearchImpl(ConnectorOperationalContext context, - Connector connector) - :base(context,connector) { - } - - /** - * Call the SPI search routines to return the results to the - * {@link ResultsHandler}. - * - * @see SearchApiOp#search(Filter, long, long, SearchApiOp.ResultsHandler) - */ - public void Search(ObjectClass oclass, Filter originalFilter, ResultsHandler handler, OperationOptions options) { + Connector connector) + : base(context, connector) + { + } + + /// + /// Call the SPI search routines to return the results to the + /// . + /// + /// + public void Search(ObjectClass oclass, Filter originalFilter, ResultsHandler handler, OperationOptions options) + { Assertions.NullCheck(oclass, "oclass"); Assertions.NullCheck(handler, "handler"); //convert null into empty - if ( options == null ) { + if (options == null) + { options = new OperationOptionsBuilder().Build(); } ObjectNormalizerFacade normalizer = GetNormalizer(oclass); //chain a normalizing handler (must come before //filter handler) - handler = - new NormalizingResultsHandler(handler,normalizer).Handle; + handler = + new NormalizingResultsHandler(handler, normalizer).Handle; Filter normalizedFilter = normalizer.NormalizeFilter(originalFilter); - + //get the IList interface that this type implements Type interfaceType = ReflectionUtil.FindInHierarchyOf - (typeof(SearchOp<>),GetConnector().GetType()); - Type [] val = interfaceType.GetGenericArguments(); - if (val.Length != 1) { - throw new Exception("Unexpected type: "+interfaceType); + (typeof(SearchOp<>), GetConnector().GetType()); + Type[] val = interfaceType.GetGenericArguments(); + if (val.Length != 1) + { + throw new Exception("Unexpected type: " + interfaceType); } Type queryType = val[0]; Type searcherRawType = typeof(RawSearcherImpl<>); - Type searcherType = + Type searcherType = searcherRawType.MakeGenericType(queryType); RawSearcher searcher = (RawSearcher)Activator.CreateInstance(searcherType); // add filtering handler handler = new FilteredResultsHandler(handler, normalizedFilter).Handle; // add attributes to get handler string[] attrsToGet = options.AttributesToGet; - if (attrsToGet != null && attrsToGet.Length > 0) { + if (attrsToGet != null && attrsToGet.Length > 0) + { handler = new SearchAttributesToGetResultsHandler( handler, attrsToGet).Handle; } - searcher.RawSearch(GetConnector(),oclass,normalizedFilter,handler,options); + searcher.RawSearch(GetConnector(), oclass, normalizedFilter, handler, options); } } #endregion - + #region RawSearcher - internal interface RawSearcher { - /** - * Public because it is used by TestHelpers. Raw, - * SPI-level search. - * @param search The underlying implementation of search - * (generally the connector itself) - * @param oclass The object class - * @param filter The filter - * @param handler The handler - * @param options The options - */ + internal interface RawSearcher + { + /// + /// Public because it is used by TestHelpers. + /// + /// + /// Raw, + /// SPI-level search. + /// + /// The underlying implementation of search + /// (generally the connector itself) + /// The object class + /// The filter + /// The handler + /// The options void RawSearch(Object search, - ObjectClass oclass, - Filter filter, + ObjectClass oclass, + Filter filter, ResultsHandler handler, - OperationOptions options); + OperationOptions options); } #endregion - + #region RawSearcherImpl - internal class RawSearcherImpl : RawSearcher where T : class { + internal class RawSearcherImpl : RawSearcher where T : class + { public void RawSearch(Object search, - ObjectClass oclass, - Filter filter, + ObjectClass oclass, + Filter filter, ResultsHandler handler, - OperationOptions options) { - RawSearch((SearchOp)search,oclass,filter,handler,options); - } - - /** - * Public because it is used by TestHelpers. Raw, - * SPI-level search. - * @param search The underlying implementation of search - * (generally the connector itself) - * @param oclass The object class - * @param filter The filter - * @param handler The handler - * @param options The options - */ + OperationOptions options) + { + RawSearch((SearchOp)search, oclass, filter, handler, options); + } + + /// + /// Public because it is used by TestHelpers. + /// + /// + /// Raw, + /// SPI-level search. + /// + /// The underlying implementation of search + /// (generally the connector itself) + /// The object class + /// The filter + /// The handler + /// The options public static void RawSearch(SearchOp search, - ObjectClass oclass, - Filter filter, + ObjectClass oclass, + Filter filter, ResultsHandler handler, - OperationOptions options) { - + OperationOptions options) + { + FilterTranslator translator = search.CreateFilterTranslator(oclass, options); IList queries = (IList)translator.Translate(filter); - if ( queries.Count == 0) { - search.ExecuteQuery(oclass, + if (queries.Count == 0) + { + search.ExecuteQuery(oclass, null, handler, options); } - else { + else + { //eliminate dups if more than one bool eliminateDups = queries.Count > 1; DuplicateFilteringResultsHandler dups = null; - if (eliminateDups) { + if (eliminateDups) + { dups = new DuplicateFilteringResultsHandler(handler); handler = dups.Handle; } - foreach( T query in queries ) { - search.ExecuteQuery(oclass, + foreach (T query in queries) + { + search.ExecuteQuery(oclass, query, handler, options); //don't run any more queries if the consumer //has stopped - if (dups != null ) { - if (!dups.IsStillHandling) { + if (dups != null) + { + if (!dups.IsStillHandling) + { break; } } } - } + } } - + } #endregion #region SyncImpl public class SyncImpl : ConnectorAPIOperationRunner, - SyncApiOp { - + SyncApiOp + { public SyncImpl(ConnectorOperationalContext context, - Connector connector) - : base(context,connector) { + Connector connector) + : base(context, connector) + { } - + public void Sync(ObjectClass objClass, SyncToken token, SyncResultsHandler handler, - OperationOptions options) { + OperationOptions options) + { //token is allowed to be null, objClass and handler must not be null Assertions.NullCheck(objClass, "objClass"); Assertions.NullCheck(handler, "handler"); //convert null into empty - if ( options == null ) { + if (options == null) + { options = new OperationOptionsBuilder().Build(); } // add a handler in the chain to remove attributes string[] attrsToGet = options.AttributesToGet; - if (attrsToGet != null && attrsToGet.Length > 0) { + if (attrsToGet != null && attrsToGet.Length > 0) + { handler = new SyncAttributesToGetResultsHandler( handler, attrsToGet).Handle; } //chain a normalizing results handler - ObjectNormalizerFacade normalizer = + ObjectNormalizerFacade normalizer = GetNormalizer(objClass); - handler = new NormalizingSyncResultsHandler(handler,normalizer).Handle; + handler = new NormalizingSyncResultsHandler(handler, normalizer).Handle; ((SyncOp)GetConnector()).Sync(objClass, token, handler, options); } - public SyncToken GetLatestSyncToken(ObjectClass objectClass) + public SyncToken GetLatestSyncToken(ObjectClass objectClass) { - return ((SyncOp) GetConnector()).GetLatestSyncToken(objectClass); - } + return ((SyncOp)GetConnector()).GetLatestSyncToken(objectClass); + } } #endregion - + #region TestImpl - /** - * Provides a method for the API to call the SPI's test method on the - * connector. The test method is intended to determine if the {@link Connector} - * is ready to perform the various operations it supports. - * - * @author Will Droste - * - */ - internal class TestImpl : ConnectorAPIOperationRunner , TestApiOp { - - public TestImpl(ConnectorOperationalContext context, Connector connector) - :base(context,connector) { - } - - /** - * {@inheritDoc} - */ - public void Test() { - ((TestOp) GetConnector()).Test(); - } - + /// + /// Provides a method for the API to call the SPI's test method on the + /// connector. + /// + /// + /// The test method is intended to determine if the + /// is ready to perform the various operations it supports. + /// + /// Will Droste + internal class TestImpl : ConnectorAPIOperationRunner, TestApiOp + { + public TestImpl(ConnectorOperationalContext context, Connector connector) + : base(context, connector) + { + } + + public void Test() + { + ((TestOp)GetConnector()).Test(); + } + } #endregion - + #region UpdateImpl - /** - * NOTE: internal class, public only for unit tests - * Handles both version of update this include simple replace and the advance - * update. - */ - public class UpdateImpl : ConnectorAPIOperationRunner , UpdateApiOp { - /** - * All the operational attributes that can not be added or deleted. - */ + /// + /// NOTE: internal class, public only for unit tests + /// Handles both version of update this include simple replace and the advance + /// update. + /// + public class UpdateImpl : ConnectorAPIOperationRunner, UpdateApiOp + { + /// + /// All the operational attributes that can not be added or deleted. + /// static readonly HashSet OPERATIONAL_ATTRIBUTE_NAMES = new HashSet(); - - const String OPERATIONAL_ATTRIBUTE_ERR = + + const String OPERATIONAL_ATTRIBUTE_ERR = "Operational attribute '{0}' can not be added or deleted only replaced."; - - static UpdateImpl() { + + static UpdateImpl() + { OPERATIONAL_ATTRIBUTE_NAMES.Add(Name.NAME); CollectionUtil.AddAll(OPERATIONAL_ATTRIBUTE_NAMES, OperationalAttributes.OPERATIONAL_ATTRIBUTE_NAMES); } - - /** - * Determines which type of update a connector supports and then uses that - * handler. - */ + + /// + /// Determines which type of update a connector supports and then uses that + /// handler. + /// public UpdateImpl(ConnectorOperationalContext context, - Connector connector) - :base(context,connector){ + Connector connector) + : base(context, connector) + { } - + public Uid Update(ObjectClass objclass, Uid uid, ICollection replaceAttributes, - OperationOptions options) { + OperationOptions options) + { // validate all the parameters.. - ValidateInput(objclass,uid,replaceAttributes,false); + ValidateInput(objclass, uid, replaceAttributes, false); //cast null as empty - if ( options == null ) { + if (options == null) + { options = new OperationOptionsBuilder().Build(); } - + ObjectNormalizerFacade normalizer = GetNormalizer(objclass); uid = (Uid)normalizer.NormalizeAttribute(uid); @@ -1237,18 +1372,20 @@ public Uid Update(ObjectClass objclass, Uid ret = op.Update(objclass, uid, replaceAttributes, options); return (Uid)normalizer.NormalizeAttribute(ret); } - + public Uid AddAttributeValues(ObjectClass objclass, Uid uid, ICollection valuesToAdd, - OperationOptions options) { + OperationOptions options) + { // validate all the parameters.. - ValidateInput(objclass,uid,valuesToAdd,true); + ValidateInput(objclass, uid, valuesToAdd, true); //cast null as empty - if ( options == null ) { + if (options == null) + { options = new OperationOptionsBuilder().Build(); } - + ObjectNormalizerFacade normalizer = GetNormalizer(objclass); uid = (Uid)normalizer.NormalizeAttribute(uid); @@ -1256,30 +1393,34 @@ public Uid AddAttributeValues(ObjectClass objclass, normalizer.NormalizeAttributes(valuesToAdd); UpdateOp op = (UpdateOp)GetConnector(); Uid ret; - if ( op is UpdateAttributeValuesOp ) { + if (op is UpdateAttributeValuesOp) + { UpdateAttributeValuesOp valueOp = (UpdateAttributeValuesOp)op; ret = valueOp.AddAttributeValues(objclass, uid, valuesToAdd, options); } - else { + else + { ICollection replaceAttributes = - FetchAndMerge(objclass,uid,valuesToAdd,true,options); + FetchAndMerge(objclass, uid, valuesToAdd, true, options); ret = op.Update(objclass, uid, replaceAttributes, options); } return (Uid)normalizer.NormalizeAttribute(ret); } - + public Uid RemoveAttributeValues(ObjectClass objclass, Uid uid, ICollection valuesToRemove, - OperationOptions options) { + OperationOptions options) + { // validate all the parameters.. - ValidateInput(objclass,uid,valuesToRemove,true); + ValidateInput(objclass, uid, valuesToRemove, true); //cast null as empty - if ( options == null ) { + if (options == null) + { options = new OperationOptionsBuilder().Build(); } - + ObjectNormalizerFacade normalizer = GetNormalizer(objclass); uid = (Uid)normalizer.NormalizeAttribute(uid); @@ -1287,161 +1428,186 @@ public Uid RemoveAttributeValues(ObjectClass objclass, normalizer.NormalizeAttributes(valuesToRemove); UpdateOp op = (UpdateOp)GetConnector(); Uid ret; - if ( op is UpdateAttributeValuesOp ) { + if (op is UpdateAttributeValuesOp) + { UpdateAttributeValuesOp valueOp = (UpdateAttributeValuesOp)op; ret = valueOp.RemoveAttributeValues(objclass, uid, valuesToRemove, options); } - else { + else + { ICollection replaceAttributes = - FetchAndMerge(objclass,uid,valuesToRemove,false,options); + FetchAndMerge(objclass, uid, valuesToRemove, false, options); ret = op.Update(objclass, uid, replaceAttributes, options); } return (Uid)normalizer.NormalizeAttribute(ret); } - - private ICollection FetchAndMerge(ObjectClass objclass, Uid uid, - ICollection valuesToChange, + + private ICollection FetchAndMerge(ObjectClass objclass, Uid uid, + ICollection valuesToChange, bool add, OperationOptions options) { // check that this connector supports Search.. - if (ReflectionUtil.FindInHierarchyOf(typeof(SearchOp<>),GetConnector().GetType()) == null) { + if (ReflectionUtil.FindInHierarchyOf(typeof(SearchOp<>), GetConnector().GetType()) == null) + { String MSG = "Connector must support search"; throw new InvalidOperationException(MSG); } - + //add attrs to get to operation options, so that the //object we fetch has exactly the set of attributes we require //(there may be ones that are not in the default set) OperationOptionsBuilder builder = new OperationOptionsBuilder(options); ICollection attrNames = new HashSet(); - foreach (ConnectorAttribute attribute in valuesToChange) { + foreach (ConnectorAttribute attribute in valuesToChange) + { attrNames.Add(attribute.Name); } - builder.AttributesToGet=(attrNames.ToArray()); + builder.AttributesToGet = (attrNames.ToArray()); options = builder.Build(); - + // get the connector object from the resource... ConnectorObject o = GetConnectorObject(objclass, uid, options); - if (o == null) { + if (o == null) + { throw new UnknownUidException(uid, objclass); } // merge the update data.. - ICollection mergeAttrs = Merge(valuesToChange, o.GetAttributes(),add); + ICollection mergeAttrs = Merge(valuesToChange, o.GetAttributes(), add); return mergeAttrs; } - - /** - * Merges two connector objects into a single updated object. - */ + + /// + /// Merges two connector objects into a single updated object. + /// public ICollection Merge(ICollection updateAttrs, - ICollection baseAttrs, bool add) { + ICollection baseAttrs, bool add) + { // return the merged attributes ICollection ret = new HashSet(); // create map that can be modified to get the subset of changes IDictionary baseAttrMap = ConnectorAttributeUtil.ToMap(baseAttrs); // run through attributes of the current object.. - foreach (ConnectorAttribute updateAttr in updateAttrs) { + foreach (ConnectorAttribute updateAttr in updateAttrs) + { // get the name of the update attributes String name = updateAttr.Name; // remove each attribute that is an update attribute.. - ConnectorAttribute baseAttr = CollectionUtil.GetValue(baseAttrMap,name,null); + ConnectorAttribute baseAttr = CollectionUtil.GetValue(baseAttrMap, name, null); IList values; - ConnectorAttribute modifiedAttr; - if (add) { - if (baseAttr == null) { + ConnectorAttribute modifiedAttr; + if (add) + { + if (baseAttr == null) + { modifiedAttr = updateAttr; - } else { + } + else + { // create a new list with the base attribute to add to.. values = CollectionUtil.NewList(baseAttr.Value); - CollectionUtil.AddAll(values,updateAttr.Value); + CollectionUtil.AddAll(values, updateAttr.Value); modifiedAttr = ConnectorAttributeBuilder.Build(name, values); } - } - else { - if (baseAttr == null) { + } + else + { + if (baseAttr == null) + { // nothing to actually do the attribute do not exist - continue; - } else { + continue; + } + else + { // create a list with the base attribute to remove from.. values = CollectionUtil.NewList(baseAttr.Value); - foreach (Object val in updateAttr.Value) { + foreach (Object val in updateAttr.Value) + { values.Remove(val); } // if the values are empty send a null to the connector.. - if (values.Count == 0) { + if (values.Count == 0) + { modifiedAttr = ConnectorAttributeBuilder.Build(name); - } else { + } + else + { modifiedAttr = ConnectorAttributeBuilder.Build(name, values); } } - } + } ret.Add(modifiedAttr); } return ret; } - - /** - * Get the {@link ConnectorObject} to modify. - */ - private ConnectorObject GetConnectorObject(ObjectClass oclass, Uid uid, OperationOptions options) { + + /// + /// Get the to modify. + /// + private ConnectorObject GetConnectorObject(ObjectClass oclass, Uid uid, OperationOptions options) + { // attempt to get the connector object.. GetApiOp get = new GetImpl(new SearchImpl((ConnectorOperationalContext)GetOperationalContext(), GetConnector())); return get.GetObject(oclass, uid, options); } - - /** - * Makes things easier if you can trust the input. - */ + + /// + /// Makes things easier if you can trust the input. + /// public static void ValidateInput(ObjectClass objclass, Uid uid, - ICollection attrs, bool isDelta) { + ICollection attrs, bool isDelta) + { Assertions.NullCheck(uid, "uid"); Assertions.NullCheck(objclass, "objclass"); Assertions.NullCheck(attrs, "attrs"); // check to make sure there's not a uid.. - if (ConnectorAttributeUtil.GetUidAttribute(attrs) != null) { + if (ConnectorAttributeUtil.GetUidAttribute(attrs) != null) + { throw new ArgumentException( "Parameter 'attrs' contains a uid."); } // check for things only valid during ADD/DELETE - if (isDelta) { - foreach (ConnectorAttribute attr in attrs) { + if (isDelta) + { + foreach (ConnectorAttribute attr in attrs) + { Assertions.NullCheck(attr, "attr"); // make sure that none of the values are null.. - if (attr.Value == null) { + if (attr.Value == null) + { throw new ArgumentException( "Can not add or remove a 'null' value."); } // make sure that if this an delete/add that it doesn't include // certain attributes because it doesn't make any sense.. String name = attr.Name; - if (OPERATIONAL_ATTRIBUTE_NAMES.Contains(name)) { + if (OPERATIONAL_ATTRIBUTE_NAMES.Contains(name)) + { String msg = String.Format(OPERATIONAL_ATTRIBUTE_ERR, name); throw new ArgumentException(msg); } } } - } + } } #endregion - + #region ValidateImpl - internal class ValidateImpl : APIOperationRunner, ValidateApiOp { - - public ValidateImpl(OperationalContext context) - :base(context) { - } - - /** - * {@inheritDoc} - */ - public void Validate() { + internal class ValidateImpl : APIOperationRunner, ValidateApiOp + { + + public ValidateImpl(OperationalContext context) + : base(context) + { + } + + public void Validate() + { GetOperationalContext().GetConfiguration().Validate(); } } #endregion - -} +} \ No newline at end of file diff --git a/FrameworkInternal/ApiRemote.cs b/FrameworkInternal/ApiRemote.cs index aacb21f9..a4a0b7d7 100644 --- a/FrameworkInternal/ApiRemote.cs +++ b/FrameworkInternal/ApiRemote.cs @@ -45,48 +45,57 @@ using System.Diagnostics; namespace Org.IdentityConnectors.Framework.Impl.Api.Remote { - public class RemoteFrameworkConnection : IDisposable { - + public class RemoteFrameworkConnection : IDisposable + { private TcpClient _socket; private Stream _stream; - + private BinaryObjectSerializer _encoder; private BinaryObjectDeserializer _decoder; - - public RemoteFrameworkConnection(RemoteFrameworkConnectionInfo info) { + + public RemoteFrameworkConnection(RemoteFrameworkConnectionInfo info) + { Init(info); } - - public RemoteFrameworkConnection(TcpClient socket, Stream stream) { - Init(socket,stream); + + public RemoteFrameworkConnection(TcpClient socket, Stream stream) + { + Init(socket, stream); } - + private void Init(RemoteFrameworkConnectionInfo connectionInfo) { - IPAddress [] addresses = + IPAddress[] addresses = Dns.GetHostAddresses(connectionInfo.Host); TcpClient client = new TcpClient(addresses[0].AddressFamily); client.SendTimeout = connectionInfo.Timeout; client.ReceiveTimeout = connectionInfo.Timeout; - client.Connect(addresses[0],connectionInfo.Port); + client.Connect(addresses[0], connectionInfo.Port); Stream stream; - try { + try + { stream = client.GetStream(); } - catch (Exception) { - try { client.Close(); } catch (Exception) {} + catch (Exception) + { + try { client.Close(); } + catch (Exception) { } throw; } - try { - if (connectionInfo.UseSSL) { - if (connectionInfo.CertificateValidationCallback != null) { + try + { + if (connectionInfo.UseSSL) + { + if (connectionInfo.CertificateValidationCallback != null) + { RemoteCertificateValidationCallback callback = connectionInfo.CertificateValidationCallback; - + stream = new SslStream( - stream,false,callback); + stream, false, callback); } - else { + else + { stream = new SslStream(stream, false); } @@ -96,14 +105,17 @@ private void Init(RemoteFrameworkConnectionInfo connectionInfo) false); } } - catch (Exception) { - try { stream.Close(); } catch (Exception) {} - try { client.Close(); } catch (Exception) {} + catch (Exception) + { + try { stream.Close(); } + catch (Exception) { } + try { client.Close(); } + catch (Exception) { } throw; } - Init(client,stream); + Init(client, stream); } - + private void Init(TcpClient socket, Stream stream) { _socket = socket; @@ -113,22 +125,26 @@ private void Init(TcpClient socket, Stream stream) _encoder = fact.NewBinarySerializer(_stream); _decoder = fact.NewBinaryDeserializer(_stream); } - - public void Dispose() { + + public void Dispose() + { Flush(); _stream.Close(); _socket.Close(); } - - public void Flush() { + + public void Flush() + { _encoder.Flush(); } - - public void WriteObject(object obj) { + + public void WriteObject(object obj) + { _encoder.WriteObject(obj); } - - public object ReadObject() { + + public object ReadObject() + { //flush first in case there is any data in the //output buffer Flush(); @@ -136,147 +152,159 @@ public object ReadObject() { } } - /// /// internal class, public only for unit tests /// - public sealed class RemoteConnectorInfoImpl : AbstractConnectorInfo { - - - public RemoteConnectorInfoImpl() { - + public sealed class RemoteConnectorInfoImpl : AbstractConnectorInfo + { + + + public RemoteConnectorInfoImpl() + { + } - + //transient field, not serialized - public RemoteFrameworkConnectionInfo RemoteConnectionInfo {get;set;} - + public RemoteFrameworkConnectionInfo RemoteConnectionInfo { get; set; } + } - - public class RemoteConnectorInfoManagerImpl : ConnectorInfoManager { - - + + public class RemoteConnectorInfoManagerImpl : ConnectorInfoManager + { private IList _connectorInfo; - - private RemoteConnectorInfoManagerImpl() { - + + private RemoteConnectorInfoManagerImpl() + { + } - - public RemoteConnectorInfoManagerImpl(RemoteFrameworkConnectionInfo info) + + public RemoteConnectorInfoManagerImpl(RemoteFrameworkConnectionInfo info) { - using (RemoteFrameworkConnection connection = - new RemoteFrameworkConnection(info)) { + using (RemoteFrameworkConnection connection = + new RemoteFrameworkConnection(info)) + { connection.WriteObject(CultureInfo.CurrentUICulture); connection.WriteObject(info.Key); connection.WriteObject(new HelloRequest()); HelloResponse response = (HelloResponse)connection.ReadObject(); - if ( response.Exception != null ) { + if (response.Exception != null) + { throw response.Exception; } - IList remoteInfos = + IList remoteInfos = response.ConnectorInfos; //populate transient fields not serialized - foreach (RemoteConnectorInfoImpl remoteInfo in remoteInfos ) { - remoteInfo.RemoteConnectionInfo=info; + foreach (RemoteConnectorInfoImpl remoteInfo in remoteInfos) + { + remoteInfo.RemoteConnectionInfo = info; } - _connectorInfo = - CollectionUtil.NewReadOnlyList(remoteInfos); - } - + _connectorInfo = + CollectionUtil.NewReadOnlyList(remoteInfos); + } + } - - /** - * Derives another RemoteConnectorInfoManagerImpl with - * a different RemoteFrameworkConnectionInfo but with the - * same metadata - * @param info - * @return - */ - public RemoteConnectorInfoManagerImpl Derive(RemoteFrameworkConnectionInfo info) { + + /// + /// Derives another RemoteConnectorInfoManagerImpl with + /// a different RemoteFrameworkConnectionInfo but with the + /// same metadata + /// + /// + public RemoteConnectorInfoManagerImpl Derive(RemoteFrameworkConnectionInfo info) + { RemoteConnectorInfoManagerImpl rv = new RemoteConnectorInfoManagerImpl(); - IList remoteInfosObj = + IList remoteInfosObj = (IList)SerializerUtil.CloneObject(_connectorInfo); IList remoteInfos = - CollectionUtil.NewList(remoteInfosObj); - foreach (ConnectorInfo remoteInfo in remoteInfos) { - ((RemoteConnectorInfoImpl)remoteInfo).RemoteConnectionInfo=(info); + CollectionUtil.NewList(remoteInfosObj); + foreach (ConnectorInfo remoteInfo in remoteInfos) + { + ((RemoteConnectorInfoImpl)remoteInfo).RemoteConnectionInfo = (info); } - rv._connectorInfo = + rv._connectorInfo = CollectionUtil.AsReadOnlyList(remoteInfos); return rv; } - - - public ConnectorInfo FindConnectorInfo(ConnectorKey key) { - foreach (ConnectorInfo info in _connectorInfo) { - if ( info.ConnectorKey.Equals(key) ) { + + public ConnectorInfo FindConnectorInfo(ConnectorKey key) + { + foreach (ConnectorInfo info in _connectorInfo) + { + if (info.ConnectorKey.Equals(key)) + { return info; } } return null; } - - public IList ConnectorInfos { - get { + + public IList ConnectorInfos + { + get + { return _connectorInfo; } } - } - - internal class RemoteConnectorFacadeImpl : AbstractConnectorFacade { - - /** - * Builds up the maps of supported operations and calls. - */ - public RemoteConnectorFacadeImpl(APIConfigurationImpl configuration) - :base(configuration) { + + internal class RemoteConnectorFacadeImpl : AbstractConnectorFacade + { + /// + /// Builds up the maps of supported operations and calls. + /// + public RemoteConnectorFacadeImpl(APIConfigurationImpl configuration) + : base(configuration) + { } - - protected override APIOperation GetOperationImplementation(SafeType api) { + + protected override APIOperation GetOperationImplementation(SafeType api) + { InvocationHandler handler = new RemoteOperationInvocationHandler( GetAPIConfiguration(), api); APIOperation proxy = NewAPIOperationProxy(api, handler); // add logging.. proxy = CreateLoggingProxy(api, proxy); - return proxy; + return proxy; } } - - - /** - * Invocation handler for all of our operations - */ - public class RemoteOperationInvocationHandler : InvocationHandler { + + /// + /// Invocation handler for all of our operations + /// + public class RemoteOperationInvocationHandler : InvocationHandler + { private readonly APIConfigurationImpl _configuration; private readonly SafeType _operation; - + public RemoteOperationInvocationHandler(APIConfigurationImpl configuration, - SafeType operation) { + SafeType operation) + { _configuration = configuration; _operation = operation; } - - + + public Object Invoke(Object proxy, MethodInfo method, Object[] args) { //don't proxy toString, hashCode, or equals - if ( method.DeclaringType.Equals(typeof(object))) { + if (method.DeclaringType.Equals(typeof(object))) + { return method.Invoke(this, args); } - + //partition arguments into arguments that can //be simply marshalled as part of the request and //those that are response handlers IList simpleMarshallArgs = CollectionUtil.NewList(args); ObjectStreamHandler streamHandlerArg = - ExtractStreamHandler(ReflectionUtil.GetParameterTypes(method),simpleMarshallArgs); - + ExtractStreamHandler(ReflectionUtil.GetParameterTypes(method), simpleMarshallArgs); + //build the request object - RemoteConnectorInfoImpl connectorInfo = + RemoteConnectorInfoImpl connectorInfo = (RemoteConnectorInfoImpl)_configuration.ConnectorInfo; - RemoteFrameworkConnectionInfo connectionInfo = + RemoteFrameworkConnectionInfo connectionInfo = connectorInfo.RemoteConnectionInfo; OperationRequest request = new OperationRequest( connectorInfo.ConnectorKey, @@ -284,100 +312,117 @@ public Object Invoke(Object proxy, MethodInfo method, Object[] args) _operation, method.Name, simpleMarshallArgs); - + //create the connection - RemoteFrameworkConnection connection = + RemoteFrameworkConnection connection = new RemoteFrameworkConnection(connectionInfo); - try { + try + { connection.WriteObject(CultureInfo.CurrentUICulture); connection.WriteObject(connectionInfo.Key); //send the request connection.WriteObject(request); - + //now process each response stream (if any) - if ( streamHandlerArg != null ) { - HandleStreamResponse(connection,streamHandlerArg); + if (streamHandlerArg != null) + { + HandleStreamResponse(connection, streamHandlerArg); } - + //finally return the actual return value - OperationResponsePart response = + OperationResponsePart response = (OperationResponsePart)connection.ReadObject(); - if ( response.Exception != null ) { + if (response.Exception != null) + { throw response.Exception; } return response.Result; } - finally { + finally + { connection.Dispose(); - } - } - /** - * Handles a stream response until the end of the stream - */ - private static void HandleStreamResponse(RemoteFrameworkConnection connection, ObjectStreamHandler streamHandler) { - Object response; + } + } + /// + /// Handles a stream response until the end of the stream + /// + private static void HandleStreamResponse(RemoteFrameworkConnection connection, ObjectStreamHandler streamHandler) + { + Object response; bool handleMore = true; - while ( true ) { + while (true) + { response = connection.ReadObject(); - if ( response is OperationResponsePart ) { + if (response is OperationResponsePart) + { OperationResponsePart part = (OperationResponsePart)response; - if ( part.Exception != null ) { + if (part.Exception != null) + { throw part.Exception; } object obj = part.Result; - if ( handleMore ) { + if (handleMore) + { handleMore = streamHandler.Handle(obj); } } - else if ( response is OperationResponsePause ) { - if ( handleMore ) { + else if (response is OperationResponsePause) + { + if (handleMore) + { connection.WriteObject(new OperationRequestMoreData()); } - else { + else + { connection.WriteObject(new OperationRequestStopData()); } } - else if ( response is OperationResponseEnd ) { + else if (response is OperationResponseEnd) + { break; } - else { - throw new ConnectorException("Unexpected response: "+response); + else + { + throw new ConnectorException("Unexpected response: " + response); } - } + } } - - /** - * Partitions arguments into regular arguments and - * stream arguments. - * @param paramTypes The param types of the method - * @param arguments The passed-in arguments. As a - * side-effect will be set to just the regular arguments. - * @return The stream handler arguments. - */ - private static ObjectStreamHandler ExtractStreamHandler(Type [] paramTypes, - IList arguments) { - ObjectStreamHandler rv = null; + + /// + /// Partitions arguments into regular arguments and + /// stream arguments. + /// + /// The param types of the method + /// The passed-in arguments. As a + /// side-effect will be set to just the regular arguments. + /// The stream handler arguments. + private static ObjectStreamHandler ExtractStreamHandler(Type[] paramTypes, + IList arguments) + { + ObjectStreamHandler rv = null; IList filteredArguments = new List(); - for ( int i = 0; i < paramTypes.Length; i++ ) { + for (int i = 0; i < paramTypes.Length; i++) + { Type paramType = paramTypes[i]; object arg = arguments[i]; - if (StreamHandlerUtil.IsAdaptableToObjectStreamHandler(paramType)) { + if (StreamHandlerUtil.IsAdaptableToObjectStreamHandler(paramType)) + { ObjectStreamHandler handler = StreamHandlerUtil.AdaptToObjectStreamHandler(paramType, arg); - if ( rv != null ) { + if (rv != null) + { throw new InvalidOperationException("Multiple stream handlers not supported"); } rv = handler; } - else { + else + { filteredArguments.Add(arg); } } arguments.Clear(); - CollectionUtil.AddAll(arguments,filteredArguments); + CollectionUtil.AddAll(arguments, filteredArguments); return rv; } } - - -} +} \ No newline at end of file diff --git a/FrameworkInternal/ApiRemoteMessages.cs b/FrameworkInternal/ApiRemoteMessages.cs index 038e0a78..4aaf4cff 100644 --- a/FrameworkInternal/ApiRemoteMessages.cs +++ b/FrameworkInternal/ApiRemoteMessages.cs @@ -30,214 +30,247 @@ namespace Org.IdentityConnectors.Framework.Impl.Api.Remote.Messages /// /// internal class, public only for unit tests /// - public class HelloRequest : Message { - - public HelloRequest() { + public class HelloRequest : Message + { + + public HelloRequest() + { } - } - + /// /// internal class, public only for unit tests /// - public class HelloResponse : Message { - - /** - * The exception - */ + public class HelloResponse : Message + { + /// + /// The exception + /// private Exception _exception; - - /** - * List of connector infos, containing infos for all the connectors - * on the server. - */ + + /// + /// List of connector infos, containing infos for all the connectors + /// on the server. + /// private IList _connectorInfos; - + public HelloResponse(Exception exception, - IList connectorInfos) { + IList connectorInfos) + { _exception = exception; _connectorInfos = CollectionUtil.NewReadOnlyList(connectorInfos); } - - public Exception Exception { - get { + + public Exception Exception + { + get + { return _exception; } } - - public IList ConnectorInfos { - get { + + public IList ConnectorInfos + { + get + { return _connectorInfos; } } - } /// /// internal class, public only for unit tests /// - - public interface Message { - + public interface Message + { } - + /// /// internal class, public only for unit tests /// - public class OperationRequest : Message { - /** - * The key of the connector to operate on. - */ + public class OperationRequest : Message + { + /// + /// The key of the connector to operate on. + /// private readonly ConnectorKey _connectorKey; - - /** - * The configuration information to use. - */ + + /// + /// The configuration information to use. + /// private readonly APIConfigurationImpl _configuration; - - /** - * The operation to perform. - */ + + /// + /// The operation to perform. + /// private readonly SafeType _operation; - - /** - * The name of the method since operations can have more - * than one method. - * NOTE: this is case-insensitive - */ + + /// + /// The name of the method since operations can have more + /// than one method. + /// + /// + /// NOTE: this is case-insensitive + /// private readonly String _operationMethodName; - - /** - * The arguments to the operation. In general, these correspond - * to the actual arguments of the method. The one exception is - * search - in this case, the callback is not passed. - */ + + /// + /// The arguments to the operation. + /// + /// + /// In general, these correspond + /// to the actual arguments of the method. The one exception is + /// search - in this case, the callback is not passed. + /// private readonly IList _arguments; - + public OperationRequest(ConnectorKey key, APIConfigurationImpl apiConfiguration, SafeType operation, string operationMethodName, - IList arguments) { + IList arguments) + { _connectorKey = key; _configuration = apiConfiguration; _operation = operation; _operationMethodName = operationMethodName; _arguments = CollectionUtil.NewReadOnlyList(arguments); } - - public ConnectorKey ConnectorKey { - get { + + public ConnectorKey ConnectorKey + { + get + { return _connectorKey; } } - - public APIConfigurationImpl Configuration { - get { + + public APIConfigurationImpl Configuration + { + get + { return _configuration; } } - - public SafeType Operation { - get { + + public SafeType Operation + { + get + { return _operation; } } - - public string OperationMethodName { - get { + + public string OperationMethodName + { + get + { return _operationMethodName; } } - - public IList Arguments { - get { + + public IList Arguments + { + get + { return _arguments; } } } - + /// /// internal class, public only for unit tests /// - public class OperationRequestMoreData : Message { - - public OperationRequestMoreData() { + public class OperationRequestMoreData : Message + { + public OperationRequestMoreData() + { } - } - + /// /// internal class, public only for unit tests /// - public class OperationRequestStopData : Message { - - public OperationRequestStopData() { + public class OperationRequestStopData : Message + { + public OperationRequestStopData() + { } - } - + /// /// internal class, public only for unit tests /// - public class OperationResponseEnd : Message { - - public OperationResponseEnd() { + public class OperationResponseEnd : Message + { + public OperationResponseEnd() + { } - } - + /// /// internal class, public only for unit tests /// - public class OperationResponsePart : Message { + public class OperationResponsePart : Message + { private Exception _exception; private Object _result; - - public OperationResponsePart(Exception ex, Object result) { + + public OperationResponsePart(Exception ex, Object result) + { _exception = ex; _result = result; } - - public Exception Exception { - get { + + public Exception Exception + { + get + { return _exception; } } - - public Object Result { - get { + + public Object Result + { + get + { return _result; } } } - + /// /// internal class, public only for unit tests /// - public class OperationResponsePause : Message { - - public OperationResponsePause() { + public class OperationResponsePause : Message + { + public OperationResponsePause() + { } - } - - public class EchoMessage : Message { + + public class EchoMessage : Message + { private object _object; private string _objectXml; - public EchoMessage(object obj, string xml) { + public EchoMessage(object obj, string xml) + { _object = obj; _objectXml = xml; } - public object Object { - get { + public object Object + { + get + { return _object; } } - public string ObjectXml { - get { + public string ObjectXml + { + get + { return _objectXml; } } } -} +} \ No newline at end of file diff --git a/FrameworkInternal/ExceptionUtil.cs b/FrameworkInternal/ExceptionUtil.cs index 9ae2a164..4b06af70 100644 --- a/FrameworkInternal/ExceptionUtil.cs +++ b/FrameworkInternal/ExceptionUtil.cs @@ -65,23 +65,23 @@ public static class ExceptionUtil /// public static void PreserveStackTrace(Exception exception) { - Assertions.NullCheck( exception, "exception" ); + Assertions.NullCheck(exception, "exception"); try { - MethodInfo preserveStackTrace = typeof( Exception ).GetMethod( - PreserveStackTraceMethodName, BindingFlags.Instance | BindingFlags.NonPublic | BindingFlags.InvokeMethod ); + MethodInfo preserveStackTrace = typeof(Exception).GetMethod( + PreserveStackTraceMethodName, BindingFlags.Instance | BindingFlags.NonPublic | BindingFlags.InvokeMethod); - preserveStackTrace.Invoke( exception, null ); + preserveStackTrace.Invoke(exception, null); } - catch( Exception ex ) + catch (Exception ex) { //it should not ever happen, but we have to make sure that if a next release of .Net Framework does not //include the invoked method it will not break the execution - TraceUtil.TraceException( string.Format( - @"Could not set an exception to preserve its stack trace. Exception: ""{0}""", exception ), ex ); + TraceUtil.TraceException(string.Format( + @"Could not set an exception to preserve its stack trace. Exception: ""{0}""", exception), ex); return; } } } -} +} \ No newline at end of file diff --git a/FrameworkInternal/Security.cs b/FrameworkInternal/Security.cs index 772d3f3b..3adf6088 100644 --- a/FrameworkInternal/Security.cs +++ b/FrameworkInternal/Security.cs @@ -28,97 +28,111 @@ namespace Org.IdentityConnectors.Common.Security.Impl { - public class EncryptorFactoryImpl : EncryptorFactory { - + public class EncryptorFactoryImpl : EncryptorFactory + { private readonly Encryptor _defaultEncryptor; - - public EncryptorFactoryImpl() { + + public EncryptorFactoryImpl() + { _defaultEncryptor = new EncryptorImpl(); } - - public override Encryptor GetDefaultEncryptor() { + + public override Encryptor GetDefaultEncryptor() + { return _defaultEncryptor; } } - - public class EncryptorImpl : Encryptor { - - private readonly static byte [] _defaultKeyBytes = + + public class EncryptorImpl : Encryptor + { + private readonly static byte[] _defaultKeyBytes = { (byte) 0x23,(byte) 0x65,(byte) 0x87,(byte) 0x22, (byte) 0x59,(byte) 0x78,(byte) 0x54,(byte) 0x43, (byte) 0x64,(byte) 0x05,(byte) 0x6A,(byte) 0xBD, (byte) 0x34,(byte) 0xA2,(byte) 0x34,(byte) 0x57, }; - private readonly static byte [] _defaultIvBytes = + private readonly static byte[] _defaultIvBytes = { (byte) 0x51,(byte) 0x65,(byte) 0x22,(byte) 0x23, (byte) 0x64,(byte) 0x05,(byte) 0x6A,(byte) 0xBE, (byte) 0x51,(byte) 0x65,(byte) 0x22,(byte) 0x23, (byte) 0x64,(byte) 0x05,(byte) 0x6A,(byte) 0xBE, }; - - - - - public EncryptorImpl() { + + public EncryptorImpl() + { } - - public UnmanagedArray Decrypt(byte[] bytes) { - using (SymmetricAlgorithm algo = Aes.Create()) { + + public UnmanagedArray Decrypt(byte[] bytes) + { + using (SymmetricAlgorithm algo = Aes.Create()) + { algo.Padding = PaddingMode.PKCS7; algo.Mode = CipherMode.CBC; algo.Key = _defaultKeyBytes; - algo.IV = _defaultIvBytes; - using (ICryptoTransform transform = algo.CreateDecryptor()) { - return Decrypt2(bytes,transform); + algo.IV = _defaultIvBytes; + using (ICryptoTransform transform = algo.CreateDecryptor()) + { + return Decrypt2(bytes, transform); } } } - - private unsafe UnmanagedArray Decrypt2(byte [] bytes, ICryptoTransform transform) { - byte [] managedBytes = transform.TransformFinalBlock(bytes,0,bytes.Length); + + private unsafe UnmanagedArray Decrypt2(byte[] bytes, ICryptoTransform transform) + { + byte[] managedBytes = transform.TransformFinalBlock(bytes, 0, bytes.Length); //pin it ASAP. this is a small race condition that is unavoidable due //to the way the crypto API's return byte[]. - fixed(byte*dummy = managedBytes) { - try { + fixed (byte* dummy = managedBytes) + { + try + { UnmanagedByteArray rv = new UnmanagedByteArray(managedBytes.Length); - for ( int i = 0; i < rv.Length; i++ ) { + for (int i = 0; i < rv.Length; i++) + { rv[i] = managedBytes[i]; } return rv; } - finally { + finally + { SecurityUtil.Clear(managedBytes); } } } - - public byte[] Encrypt(UnmanagedArray bytes) { - using (SymmetricAlgorithm algo = Aes.Create()) { + + public byte[] Encrypt(UnmanagedArray bytes) + { + using (SymmetricAlgorithm algo = Aes.Create()) + { algo.Padding = PaddingMode.PKCS7; algo.Mode = CipherMode.CBC; algo.Key = _defaultKeyBytes; - algo.IV = _defaultIvBytes; - using (ICryptoTransform transform = algo.CreateEncryptor()) { - return Encrypt2(bytes,transform); + algo.IV = _defaultIvBytes; + using (ICryptoTransform transform = algo.CreateEncryptor()) + { + return Encrypt2(bytes, transform); } } } - - private unsafe byte[] Encrypt2(UnmanagedArray bytes, ICryptoTransform transform) { - byte [] managedBytes = new byte[bytes.Length]; - fixed(byte*dummy = managedBytes) { - try { - SecurityUtil.UnmanagedBytesToManagedBytes(bytes,managedBytes); - byte [] rv = transform.TransformFinalBlock(managedBytes,0,managedBytes.Length); + + private unsafe byte[] Encrypt2(UnmanagedArray bytes, ICryptoTransform transform) + { + byte[] managedBytes = new byte[bytes.Length]; + fixed (byte* dummy = managedBytes) + { + try + { + SecurityUtil.UnmanagedBytesToManagedBytes(bytes, managedBytes); + byte[] rv = transform.TransformFinalBlock(managedBytes, 0, managedBytes.Length); return rv; } - finally { + finally + { SecurityUtil.Clear(managedBytes); } } } - } -} +} \ No newline at end of file diff --git a/FrameworkInternal/Serializer.cs b/FrameworkInternal/Serializer.cs index b2e8da2b..8030000a 100644 --- a/FrameworkInternal/Serializer.cs +++ b/FrameworkInternal/Serializer.cs @@ -43,370 +43,384 @@ namespace Org.IdentityConnectors.Framework.Impl.Serializer { #region Serialization Framework - internal abstract class AbstractObjectSerializationHandler - : ObjectTypeMapperImpl, ObjectSerializationHandler { - + internal abstract class AbstractObjectSerializationHandler + : ObjectTypeMapperImpl, ObjectSerializationHandler + { + protected AbstractObjectSerializationHandler(Type handledClass, - String type) - :base(handledClass,type) { - } - /** - * Called to serialize the object. - */ - abstract public void Serialize(Object obj, ObjectEncoder encoder) ; - - /** - * Called to deserialize the object. - */ - abstract public Object Deserialize(ObjectDecoder decoder) ; + String type) + : base(handledClass, type) + { + } + /// + /// Called to serialize the object. + /// + abstract public void Serialize(Object obj, ObjectEncoder encoder); + + /// + /// Called to deserialize the object. + /// + abstract public Object Deserialize(ObjectDecoder decoder); } - - + + internal class EnumSerializationHandler : - AbstractObjectSerializationHandler { - - public EnumSerializationHandler (Type clazz, String name) - :base(clazz,name) { - } - - - public override object Deserialize(ObjectDecoder decoder) { - String val = decoder.ReadStringField("value",null); + AbstractObjectSerializationHandler + { + public EnumSerializationHandler(Type clazz, String name) + : base(clazz, name) + { + } + + public override object Deserialize(ObjectDecoder decoder) + { + String val = decoder.ReadStringField("value", null); Type enumClass = HandledObjectType; Object rv = Enum.Parse(enumClass, val); return rv; } - + public override void Serialize(object obj, ObjectEncoder encoder) { Enum e = (Enum)obj; - encoder.WriteStringField("value",Enum.GetName(e.GetType(),e)); + encoder.WriteStringField("value", Enum.GetName(e.GetType(), e)); } - } - - /** - * Interface to abstract away the difference between deserializing - * xml and binary - */ - internal interface ObjectDecoder { - /** - * Reads an object using the appropriate serializer for that object - * @param fieldName. A hint of the field name. Ignored for binary - * serialization. The subelement name for xml serialization - */ + + /// + /// Interface to abstract away the difference between deserializing + /// xml and binary + /// + internal interface ObjectDecoder + { + /// + /// Reads an object using the appropriate serializer for that object + /// + /// A hint of the field name. Ignored for binary + /// serialization. The subelement name for xml serialization Object ReadObjectField(String fieldName, Type expectedType, Object dflt); - - /** - * Reads a bool. - * @param fieldName. A hint of the field name. Ignored for binary - * serialization. The subelement name for xml serialization - */ - bool ReadBooleanField(String fieldName, bool dflt) ; - - /** - * Reads an int. - * @param fieldName. A hint of the field name. Ignored for binary - * serialization. The subelement name for xml serialization - */ - int ReadIntField(String fieldName, int dflt) ; - - /** - * Reads a long. - * @param fieldName. A hint of the field name. Ignored for binary - * serialization. The subelement name for xml serialization - */ - long ReadLongField(String fieldName, long dflt) ; - - /** - * Reads a float. - * @param fieldName. A hint of the field name. Ignored for binary - * serialization. The subelement name for xml serialization - */ - float ReadFloatField(String fieldName, float dflt) ; - - /** - * Reads a double. - * @param fieldName. A hint of the field name. Ignored for binary - * serialization. The subelement name for xml serialization - * @param v. The value to serialize - */ - double ReadDoubleField(String fieldName, double dflt) ; - - /** - * Reads a double. - * @param fieldName. A hint of the field name. Ignored for binary - * serialization. The subelement name for xml serialization - * @param v. The value to serialize - */ - string ReadStringField(String fieldName, string dflt) ; - - /** - * Reads a double. - * @param fieldName. A hint of the field name. Ignored for binary - * serialization. The subelement name for xml serialization - * @param v. The value to serialize - */ - Type ReadClassField(String fieldName, Type dflt) ; - - /** - * Reads the value in-line. - */ - String ReadStringContents() ; - - /** - * Reads the value in-line. - */ - bool ReadBooleanContents() ; - - /** - * Reads the value in-line. - */ - int ReadIntContents() ; - - /** - * reads the value in-line. - */ - long ReadLongContents() ; - - /** - * Reads the value in-line. - */ - float ReadFloatContents() ; - - /** - * reads the value in-line. - */ - double ReadDoubleContents() ; - - /** - * reads the value in-line. - */ - byte [] ReadByteArrayContents() ; - - /** - * reads the value in-line. - */ - Type ReadClassContents() ; - - /** - * Returns the number of anonymous sub-objects. - * @return - */ + + /// + /// Reads a bool. + /// + /// A hint of the field name. Ignored for binary + /// serialization. The subelement name for xml serialization + bool ReadBooleanField(String fieldName, bool dflt); + + /// + /// Reads an int. + /// + /// A hint of the field name. Ignored for binary + /// serialization. The subelement name for xml serialization + int ReadIntField(String fieldName, int dflt); + + /// + /// Reads a long. + /// + /// A hint of the field name. Ignored for binary + /// serialization. The subelement name for xml serialization + long ReadLongField(String fieldName, long dflt); + + /// + /// Reads a float. + /// + /// A hint of the field name. Ignored for binary + /// serialization. The subelement name for xml serialization + float ReadFloatField(String fieldName, float dflt); + + /// + /// Reads a double. + /// + /// A hint of the field name. Ignored for binary + /// serialization. The subelement name for xml serialization + /// The value to serialize + double ReadDoubleField(String fieldName, double dflt); + + /// + /// Reads a double. + /// + /// A hint of the field name. Ignored for binary + /// serialization. The subelement name for xml serialization + /// The value to serialize + string ReadStringField(String fieldName, string dflt); + + /// + /// Reads a double. + /// + /// A hint of the field name. Ignored for binary + /// serialization. The subelement name for xml serialization + /// The value to serialize + Type ReadClassField(String fieldName, Type dflt); + + /// + /// Reads the value in-line. + /// + String ReadStringContents(); + + /// + /// Reads the value in-line. + /// + bool ReadBooleanContents(); + + /// + /// Reads the value in-line. + /// + int ReadIntContents(); + + /// + /// reads the value in-line. + /// + long ReadLongContents(); + + /// + /// Reads the value in-line. + /// + float ReadFloatContents(); + + /// + /// reads the value in-line. + /// + double ReadDoubleContents(); + + /// + /// reads the value in-line. + /// + byte[] ReadByteArrayContents(); + + /// + /// reads the value in-line. + /// + Type ReadClassContents(); + + /// + /// Returns the number of anonymous sub-objects. + /// int GetNumSubObjects(); - - /** - * Reads a sub-object - */ - Object ReadObjectContents(int index) ; - + + /// + /// Reads a sub-object + /// + Object ReadObjectContents(int index); } - - /** - * Interface to abstract away the difference between serializing - * xml and binary - */ - internal interface ObjectEncoder { - /** - * Writes an object using the appropriate serializer for that object - * @param fieldName. A hint of the field name. Ignored for binary - * serialization. Becomes the subelement name for xml serialization - * @param object. The object to serialize - */ - void WriteObjectField(String fieldName, Object obj, bool inline) ; - - /** - * Writes a boolean. - * @param fieldName. A hint of the field name. Ignored for binary - * serialization. Becomes the subelement name for xml serialization - * @param v. The value to serialize - */ - void WriteBooleanField(String fieldName, bool v) ; - - /** - * Writes an int. - * @param fieldName. A hint of the field name. Ignored for binary - * serialization. Becomes the subelement name for xml serialization - * @param v. The value to serialize - */ - void WriteIntField(String fieldName, int v) ; - - /** - * Writes a long. - * @param fieldName. A hint of the field name. Ignored for binary - * serialization. Becomes the subelement name for xml serialization - * @param v. The value to serialize - */ - void WriteLongField(String fieldName, long v) ; - - /** - * Writes a float. - * @param fieldName. A hint of the field name. Ignored for binary - * serialization. Becomes the subelement name for xml serialization - * @param v. The value to serialize - */ - void WriteFloatField(String fieldName, float v) ; - - /** - * Writes a double. - * @param fieldName. A hint of the field name. Ignored for binary - * serialization. Becomes the subelement name for xml serialization - * @param v. The value to serialize - */ - void WriteDoubleField(String fieldName, double v) ; - - /** - * Writes a double. - * @param fieldName. A hint of the field name. Ignored for binary - * serialization. Becomes the subelement name for xml serialization - * @param v. The value to serialize - */ - void WriteStringField(String fieldName, string v) ; - - /** - * Writes a double. - * @param fieldName. A hint of the field name. Ignored for binary - * serialization. Becomes the subelement name for xml serialization - * @param v. The value to serialize - */ - void WriteClassField(String fieldName, Type v) ; - - /** - * Writes the value in-line. - */ - void WriteStringContents(String str) ; - - /** - * Writes the value in-line. - */ - void WriteBooleanContents(bool v) ; - - /** - * Writes the value in-line. - */ - void WriteIntContents(int v) ; - - /** - * Writes the value in-line. - */ - void WriteLongContents(long v) ; - - /** - * Writes the value in-line. - */ - void WriteFloatContents(float v) ; - - /** - * Writes the value in-line. - */ - void WriteDoubleContents(double v) ; - - /** - * Special case for byte [] that uses base64 encoding for XML - */ - void WriteByteArrayContents(byte [] v) ; - - /** - * Writes the value in-line. - */ - void WriteClassContents(Type v) ; - - /** - * Writes a sub-object - */ - void WriteObjectContents(object o) ; + + /// + /// Interface to abstract away the difference between serializing + /// xml and binary + /// + internal interface ObjectEncoder + { + /// + /// Writes an object using the appropriate serializer for that object + /// + /// A hint of the field name. Ignored for binary + /// serialization. Becomes the subelement name for xml serialization + /// The object to serialize + void WriteObjectField(String fieldName, Object obj, bool inline); + + /// + /// Writes a boolean. + /// + /// A hint of the field name. Ignored for binary + /// serialization. Becomes the subelement name for xml serialization + /// The value to serialize + void WriteBooleanField(String fieldName, bool v); + + /// + /// Writes an int. + /// + /// A hint of the field name. Ignored for binary + /// serialization. Becomes the subelement name for xml serialization + /// The value to serialize + void WriteIntField(String fieldName, int v); + + /// + /// Writes a long. + /// + /// A hint of the field name. Ignored for binary + /// serialization. Becomes the subelement name for xml serialization + /// The value to serialize + void WriteLongField(String fieldName, long v); + + /// + /// Writes a float. + /// + /// A hint of the field name. Ignored for binary + /// serialization. Becomes the subelement name for xml serialization + /// The value to serialize + void WriteFloatField(String fieldName, float v); + + /// + /// Writes a double. + /// + /// A hint of the field name. Ignored for binary + /// serialization. Becomes the subelement name for xml serialization + /// The value to serialize + void WriteDoubleField(String fieldName, double v); + + /// + /// Writes a double. + /// + /// A hint of the field name. Ignored for binary + /// serialization. Becomes the subelement name for xml serialization + /// The value to serialize + void WriteStringField(String fieldName, string v); + + /// + /// Writes a double. + /// + /// A hint of the field name. Ignored for binary + /// serialization. Becomes the subelement name for xml serialization + /// The value to serialize + void WriteClassField(String fieldName, Type v); + + /// + /// Writes the value in-line. + /// + void WriteStringContents(String str); + + /// + /// Writes the value in-line. + /// + void WriteBooleanContents(bool v); + + /// + /// Writes the value in-line. + /// + void WriteIntContents(int v); + + /// + /// Writes the value in-line. + /// + void WriteLongContents(long v); + + /// + /// Writes the value in-line. + /// + void WriteFloatContents(float v); + + /// + /// Writes the value in-line. + /// + void WriteDoubleContents(double v); + + /// + /// Special case for byte [] that uses base64 encoding for XML + /// + void WriteByteArrayContents(byte[] v); + + /// + /// Writes the value in-line. + /// + void WriteClassContents(Type v); + + /// + /// Writes a sub-object + /// + void WriteObjectContents(object o); } - - /** - * Interface to be implemented to handle the serialization/ - * deserialization of an object. - */ - internal interface ObjectSerializationHandler : ObjectTypeMapper { - - /** - * Called to serialize the object. - */ - void Serialize(Object obj, ObjectEncoder encoder) ; - - /** - * Called to deserialize the object. - */ - Object Deserialize(ObjectDecoder decoder) ; + + /// + /// Interface to be implemented to handle the serialization/ + /// deserialization of an object. + /// + internal interface ObjectSerializationHandler : ObjectTypeMapper + { + /// + /// Called to serialize the object. + /// + void Serialize(Object obj, ObjectEncoder encoder); + + /// + /// Called to deserialize the object. + /// + Object Deserialize(ObjectDecoder decoder); } - - internal static class ObjectSerializerRegistry { - + + internal static class ObjectSerializerRegistry + { private static readonly IList HANDLERS = new List(); - - - - - private static readonly IDictionary - HANDLERS_BY_SERIAL_TYPE = new Dictionary(); - - static ObjectSerializerRegistry() { - CollectionUtil.AddAll(HANDLERS,Primitives.HANDLERS); - CollectionUtil.AddAll(HANDLERS,OperationMappings.MAPPINGS); - CollectionUtil.AddAll(HANDLERS,APIConfigurationHandlers.HANDLERS); - CollectionUtil.AddAll(HANDLERS,FilterHandlers.HANDLERS); - CollectionUtil.AddAll(HANDLERS,CommonObjectHandlers.HANDLERS); - CollectionUtil.AddAll(HANDLERS,MessageHandlers.HANDLERS); + + private static readonly IDictionary + HANDLERS_BY_SERIAL_TYPE = new Dictionary(); + + static ObjectSerializerRegistry() + { + CollectionUtil.AddAll(HANDLERS, Primitives.HANDLERS); + CollectionUtil.AddAll(HANDLERS, OperationMappings.MAPPINGS); + CollectionUtil.AddAll(HANDLERS, APIConfigurationHandlers.HANDLERS); + CollectionUtil.AddAll(HANDLERS, FilterHandlers.HANDLERS); + CollectionUtil.AddAll(HANDLERS, CommonObjectHandlers.HANDLERS); + CollectionUtil.AddAll(HANDLERS, MessageHandlers.HANDLERS); //object is special - just map the type, but don't actually //serialize - HANDLERS.Add(new ObjectTypeMapperImpl(typeof(object),"Object")); + HANDLERS.Add(new ObjectTypeMapperImpl(typeof(object), "Object")); - foreach (ObjectTypeMapper handler in HANDLERS) { - if (HANDLERS_BY_SERIAL_TYPE.ContainsKey(handler.HandledSerialType)) { + foreach (ObjectTypeMapper handler in HANDLERS) + { + if (HANDLERS_BY_SERIAL_TYPE.ContainsKey(handler.HandledSerialType)) + { throw new Exception("More than one handler of the" + - " same type: "+handler.HandledSerialType); + " same type: " + handler.HandledSerialType); } - HANDLERS_BY_SERIAL_TYPE[handler.HandledSerialType] = + HANDLERS_BY_SERIAL_TYPE[handler.HandledSerialType] = handler; - } - } - - /** - * Mapping by class. Dynamically built since actual class may be - * a subclass. - */ - private static readonly IDictionary - HANDLERS_BY_OBJECT_TYPE = - new Dictionary(); - - - - public static ObjectTypeMapper GetMapperBySerialType(String type) { - return CollectionUtil.GetValue(HANDLERS_BY_SERIAL_TYPE,type,null); - } - - public static ObjectTypeMapper GetMapperByObjectType(Type clazz) { - lock(HANDLERS_BY_OBJECT_TYPE) { - ObjectTypeMapper rv = - CollectionUtil.GetValue(HANDLERS_BY_OBJECT_TYPE,clazz,null); - if ( rv == null ) { - foreach (ObjectTypeMapper handler in HANDLERS) { + } + } + + /// + /// Mapping by class. + /// + /// + /// Dynamically built since actual class may be + /// a subclass. + /// + private static readonly IDictionary + HANDLERS_BY_OBJECT_TYPE = + new Dictionary(); + + public static ObjectTypeMapper GetMapperBySerialType(String type) + { + return CollectionUtil.GetValue(HANDLERS_BY_SERIAL_TYPE, type, null); + } + + public static ObjectTypeMapper GetMapperByObjectType(Type clazz) + { + lock (HANDLERS_BY_OBJECT_TYPE) + { + ObjectTypeMapper rv = + CollectionUtil.GetValue(HANDLERS_BY_OBJECT_TYPE, clazz, null); + if (rv == null) + { + foreach (ObjectTypeMapper handler in HANDLERS) + { IDelegatingObjectTypeMapper delegator = handler as IDelegatingObjectTypeMapper; ObjectTypeMapper effectiveHandler; - if ( delegator != null ) { + if (delegator != null) + { effectiveHandler = delegator.FindMapperDelegate(clazz); } - else { - effectiveHandler = handler; + else + { + effectiveHandler = handler; } - if ( effectiveHandler != null ) { + if (effectiveHandler != null) + { Type handledClass = effectiveHandler.HandledObjectType; - if ( effectiveHandler.MatchSubclasses ) { - if ( handledClass.IsAssignableFrom(clazz) ) { + if (effectiveHandler.MatchSubclasses) + { + if (handledClass.IsAssignableFrom(clazz)) + { rv = effectiveHandler; break; } } - else if ( handledClass.Equals(clazz) ) { + else if (handledClass.Equals(clazz)) + { rv = effectiveHandler; break; } @@ -417,92 +431,103 @@ public static ObjectTypeMapper GetMapperByObjectType(Type clazz) { return rv; } } - public static ObjectSerializationHandler GetHandlerBySerialType(String type) { + public static ObjectSerializationHandler GetHandlerBySerialType(String type) + { ObjectTypeMapper rv = GetMapperBySerialType(type); return rv as ObjectSerializationHandler; } - - public static ObjectSerializationHandler GetHandlerByObjectType(Type clazz) { + + public static ObjectSerializationHandler GetHandlerByObjectType(Type clazz) + { ObjectTypeMapper rv = GetMapperByObjectType(clazz); return rv as ObjectSerializationHandler; } } - + /// /// ObjectTypeMappers can implement this interface as well. If /// they do, they can handle specific generic types as well. /// - internal interface IDelegatingObjectTypeMapper { + internal interface IDelegatingObjectTypeMapper + { ObjectTypeMapper FindMapperDelegate(Type type); } - - /** - * Interface to be implemented to handle the serialization/ - * deserialization of an object. - */ - internal interface ObjectTypeMapper { - - /** - * Returns the type of object being serialized. This is - * an abstract type name that is intended to be language - * neutral. - */ - String HandledSerialType {get;} - - /** - * Returns the java class handled by this handler. - */ - Type HandledObjectType {get;} - - /** - * Should we match subclasses of the given class or only - * the exact class? - */ - bool MatchSubclasses {get;} - + + /// + /// Interface to be implemented to handle the serialization/ + /// deserialization of an object. + /// + internal interface ObjectTypeMapper + { + /// + /// Returns the type of object being serialized. + /// + /// + /// This is + /// an abstract type name that is intended to be language + /// neutral. + /// + String HandledSerialType { get; } + + /// + /// Returns the java class handled by this handler. + /// + Type HandledObjectType { get; } + + /// + /// Should we match subclasses of the given class or only + /// the exact class? + /// + bool MatchSubclasses { get; } } - - internal class ObjectTypeMapperImpl : ObjectTypeMapper { + internal class ObjectTypeMapperImpl : ObjectTypeMapper + { private Type _handledClass; private String _handledType; - - public ObjectTypeMapperImpl(Type handledClass, String handledType) { + + public ObjectTypeMapperImpl(Type handledClass, String handledType) + { _handledClass = handledClass; - _handledType = handledType; + _handledType = handledType; } - - public Type HandledObjectType { - get { + + public Type HandledObjectType + { + get + { return _handledClass; } } - - public String HandledSerialType { - get { + + public String HandledSerialType + { + get + { return _handledType; } } - - public virtual bool MatchSubclasses { - get { + + public virtual bool MatchSubclasses + { + get + { return false; } } - } - + internal abstract class AbstractExceptionHandler : AbstractObjectSerializationHandler where T : Exception { - - protected AbstractExceptionHandler(String typeName) - : base(typeof(T),typeName) { - } - - - public override Object Deserialize(ObjectDecoder decoder) { - String message = decoder.ReadStringField("message",null); + protected AbstractExceptionHandler(String typeName) + : base(typeof(T), typeName) + { + } + + public override Object Deserialize(ObjectDecoder decoder) + { + String message = decoder.ReadStringField("message", null); return CreateException(message); } @@ -511,33 +536,36 @@ public override void Serialize(Object obj, ObjectEncoder encoder) Exception val = (Exception)obj; encoder.WriteStringField("message", val.Message); } - public override bool MatchSubclasses { + public override bool MatchSubclasses + { get { return true; } } protected abstract T CreateException(String message); } - - + + #endregion - + #region Primitives - - internal static class Primitives { + + internal static class Primitives + { public static readonly IList HANDLERS = new List(); - static Primitives() { - HANDLERS.Add(new BooleanHandler(typeof(bool?),"Boolean")); - HANDLERS.Add(new BooleanHandler(typeof(bool),"boolean")); - HANDLERS.Add(new CharacterHandler(typeof(char?),"Character")); - HANDLERS.Add(new CharacterHandler(typeof(char),"char")); - HANDLERS.Add(new IntegerHandler(typeof(int?),"Integer")); - HANDLERS.Add(new IntegerHandler(typeof(int),"int")); - HANDLERS.Add(new LongHandler(typeof(long?),"Long")); - HANDLERS.Add(new LongHandler(typeof(long),"long")); - HANDLERS.Add(new FloatHandler(typeof(float?),"Float")); - HANDLERS.Add(new FloatHandler(typeof(float),"float")); - HANDLERS.Add(new DoubleHandler(typeof(double?),"Double")); - HANDLERS.Add(new DoubleHandler(typeof(double),"double")); + static Primitives() + { + HANDLERS.Add(new BooleanHandler(typeof(bool?), "Boolean")); + HANDLERS.Add(new BooleanHandler(typeof(bool), "boolean")); + HANDLERS.Add(new CharacterHandler(typeof(char?), "Character")); + HANDLERS.Add(new CharacterHandler(typeof(char), "char")); + HANDLERS.Add(new IntegerHandler(typeof(int?), "Integer")); + HANDLERS.Add(new IntegerHandler(typeof(int), "int")); + HANDLERS.Add(new LongHandler(typeof(long?), "Long")); + HANDLERS.Add(new LongHandler(typeof(long), "long")); + HANDLERS.Add(new FloatHandler(typeof(float?), "Float")); + HANDLERS.Add(new FloatHandler(typeof(float), "float")); + HANDLERS.Add(new DoubleHandler(typeof(double?), "Double")); + HANDLERS.Add(new DoubleHandler(typeof(double), "double")); HANDLERS.Add(new StringHandler()); HANDLERS.Add(new URIHandler()); HANDLERS.Add(new FileHandler()); @@ -552,230 +580,287 @@ static Primitives() { HANDLERS.Add(new LocaleHandler()); HANDLERS.Add(new GuardedByteArrayHandler()); HANDLERS.Add(new GuardedStringHandler()); - } - private class BooleanHandler : AbstractObjectSerializationHandler { - public BooleanHandler(Type objectType, String serialType) - : base(objectType,serialType) { - + } + private class BooleanHandler : AbstractObjectSerializationHandler + { + public BooleanHandler(Type objectType, String serialType) + : base(objectType, serialType) + { + } - public override Object Deserialize(ObjectDecoder decoder) { + public override Object Deserialize(ObjectDecoder decoder) + { bool val = decoder.ReadBooleanContents(); return val; } - - public override void Serialize(Object obj, ObjectEncoder encoder) { + + public override void Serialize(Object obj, ObjectEncoder encoder) + { bool val = (bool)obj; encoder.WriteBooleanContents(val); } } - private class CharacterHandler : AbstractObjectSerializationHandler { - public CharacterHandler(Type objectType, String serialType) - : base(objectType,serialType) { - + private class CharacterHandler : AbstractObjectSerializationHandler + { + public CharacterHandler(Type objectType, String serialType) + : base(objectType, serialType) + { + } - public override Object Deserialize(ObjectDecoder decoder) { + public override Object Deserialize(ObjectDecoder decoder) + { String val = decoder.ReadStringContents(); return val.ToCharArray()[0]; } - - public override void Serialize(Object obj, ObjectEncoder encoder) { + + public override void Serialize(Object obj, ObjectEncoder encoder) + { char val = (char)obj; encoder.WriteStringContents(val.ToString()); } } - private class IntegerHandler : AbstractObjectSerializationHandler { - public IntegerHandler(Type objectType, String serialType) - : base(objectType,serialType) { - + private class IntegerHandler : AbstractObjectSerializationHandler + { + public IntegerHandler(Type objectType, String serialType) + : base(objectType, serialType) + { + } - public override Object Deserialize(ObjectDecoder decoder) { + public override Object Deserialize(ObjectDecoder decoder) + { int val = decoder.ReadIntContents(); return val; } - - public override void Serialize(Object obj, ObjectEncoder encoder) { + + public override void Serialize(Object obj, ObjectEncoder encoder) + { int val = (int)obj; encoder.WriteIntContents(val); } } - private class LongHandler : AbstractObjectSerializationHandler { - public LongHandler(Type objectType, String serialType) - : base(objectType,serialType) { - + private class LongHandler : AbstractObjectSerializationHandler + { + public LongHandler(Type objectType, String serialType) + : base(objectType, serialType) + { + } - public override Object Deserialize(ObjectDecoder decoder) { + public override Object Deserialize(ObjectDecoder decoder) + { long val = decoder.ReadLongContents(); return val; } - - public override void Serialize(Object obj, ObjectEncoder encoder) { + + public override void Serialize(Object obj, ObjectEncoder encoder) + { long val = (long)obj; encoder.WriteLongContents(val); } } - private class FloatHandler : AbstractObjectSerializationHandler { - public FloatHandler(Type objectType, String serialType) - : base(objectType,serialType) { - + private class FloatHandler : AbstractObjectSerializationHandler + { + public FloatHandler(Type objectType, String serialType) + : base(objectType, serialType) + { + } - public override Object Deserialize(ObjectDecoder decoder) { + public override Object Deserialize(ObjectDecoder decoder) + { float val = decoder.ReadFloatContents(); return val; } - - public override void Serialize(Object obj, ObjectEncoder encoder) { + + public override void Serialize(Object obj, ObjectEncoder encoder) + { float val = (float)obj; encoder.WriteFloatContents(val); } } - private class DoubleHandler : AbstractObjectSerializationHandler { - public DoubleHandler(Type objectType, String serialType) - : base(objectType,serialType) { - + private class DoubleHandler : AbstractObjectSerializationHandler + { + public DoubleHandler(Type objectType, String serialType) + : base(objectType, serialType) + { + } - public override Object Deserialize(ObjectDecoder decoder) { + public override Object Deserialize(ObjectDecoder decoder) + { double val = decoder.ReadDoubleContents(); return val; } - - public override void Serialize(Object obj, ObjectEncoder encoder) { + + public override void Serialize(Object obj, ObjectEncoder encoder) + { double val = (double)obj; encoder.WriteDoubleContents(val); } } - private class StringHandler : AbstractObjectSerializationHandler { - public StringHandler() - : base(typeof(string),"String") { - + private class StringHandler : AbstractObjectSerializationHandler + { + public StringHandler() + : base(typeof(string), "String") + { + } - public override Object Deserialize(ObjectDecoder decoder) { + public override Object Deserialize(ObjectDecoder decoder) + { string val = decoder.ReadStringContents(); return val; } - - public override void Serialize(Object obj, ObjectEncoder encoder) { + + public override void Serialize(Object obj, ObjectEncoder encoder) + { string val = (string)obj; encoder.WriteStringContents(val); } } - private class URIHandler : AbstractObjectSerializationHandler { - public URIHandler() - : base(typeof(Uri),"URI") { - + private class URIHandler : AbstractObjectSerializationHandler + { + public URIHandler() + : base(typeof(Uri), "URI") + { + } - public override Object Deserialize(ObjectDecoder decoder) { + public override Object Deserialize(ObjectDecoder decoder) + { string val = decoder.ReadStringContents(); return new Uri(val); } - - public override void Serialize(Object obj, ObjectEncoder encoder) { + + public override void Serialize(Object obj, ObjectEncoder encoder) + { Uri val = (Uri)obj; encoder.WriteStringContents(val.ToString()); } } - private class FileHandler : AbstractObjectSerializationHandler { - public FileHandler() - : base(typeof(FileName),"File") { - + private class FileHandler : AbstractObjectSerializationHandler + { + public FileHandler() + : base(typeof(FileName), "File") + { + } - public override Object Deserialize(ObjectDecoder decoder) { + public override Object Deserialize(ObjectDecoder decoder) + { string val = decoder.ReadStringContents(); return new FileName(val); } - - public override void Serialize(Object obj, ObjectEncoder encoder) { + + public override void Serialize(Object obj, ObjectEncoder encoder) + { FileName val = (FileName)obj; encoder.WriteStringContents(val.Path); } } - private class BigDecimalHandler : AbstractObjectSerializationHandler { - public BigDecimalHandler() - : base(typeof(BigDecimal),"BigDecimal") { - + private class BigDecimalHandler : AbstractObjectSerializationHandler + { + public BigDecimalHandler() + : base(typeof(BigDecimal), "BigDecimal") + { + } - public override Object Deserialize(ObjectDecoder decoder) { + public override Object Deserialize(ObjectDecoder decoder) + { BigInteger unscaled = - new BigInteger(decoder.ReadStringField("unscaled",null)); - int scale = decoder.ReadIntField("scale",0); - return new BigDecimal(unscaled,scale); + new BigInteger(decoder.ReadStringField("unscaled", null)); + int scale = decoder.ReadIntField("scale", 0); + return new BigDecimal(unscaled, scale); } - - public override void Serialize(Object obj, ObjectEncoder encoder) { + + public override void Serialize(Object obj, ObjectEncoder encoder) + { BigDecimal val = (BigDecimal)obj; encoder.WriteStringField("unscaled", val.UnscaledValue.Value); encoder.WriteIntField("scale", val.Scale); } } - private class BigIntegerHandler : AbstractObjectSerializationHandler { - public BigIntegerHandler() - : base(typeof(BigInteger),"BigInteger") { - + private class BigIntegerHandler : AbstractObjectSerializationHandler + { + public BigIntegerHandler() + : base(typeof(BigInteger), "BigInteger") + { + } - public override Object Deserialize(ObjectDecoder decoder) { + public override Object Deserialize(ObjectDecoder decoder) + { string val = decoder.ReadStringContents(); return new BigInteger(val); } - - public override void Serialize(Object obj, ObjectEncoder encoder) { + + public override void Serialize(Object obj, ObjectEncoder encoder) + { BigInteger val = (BigInteger)obj; encoder.WriteStringContents(val.Value); } } - private class ByteArrayHandler : AbstractObjectSerializationHandler { - public ByteArrayHandler() - : base(typeof(byte[]),"ByteArray") { - + private class ByteArrayHandler : AbstractObjectSerializationHandler + { + public ByteArrayHandler() + : base(typeof(byte[]), "ByteArray") + { + } - public override Object Deserialize(ObjectDecoder decoder) { + public override Object Deserialize(ObjectDecoder decoder) + { return decoder.ReadByteArrayContents(); } - - public override void Serialize(Object obj, ObjectEncoder encoder) { - byte [] val = (byte[])obj; + + public override void Serialize(Object obj, ObjectEncoder encoder) + { + byte[] val = (byte[])obj; encoder.WriteByteArrayContents(val); } } - private class ClassHandler : AbstractObjectSerializationHandler { - public ClassHandler() - : base(typeof(Type),"Class") { - + private class ClassHandler : AbstractObjectSerializationHandler + { + public ClassHandler() + : base(typeof(Type), "Class") + { + } - public override Object Deserialize(ObjectDecoder decoder) { + public override Object Deserialize(ObjectDecoder decoder) + { return decoder.ReadClassContents(); } - - public override void Serialize(Object obj, ObjectEncoder encoder) { + + public override void Serialize(Object obj, ObjectEncoder encoder) + { Type val = (Type)obj; encoder.WriteClassContents(val); } - + /// /// In C#, the actual Type of Type is RuntimeType /// - public override bool MatchSubclasses { + public override bool MatchSubclasses + { get { return true; } } - } - - private class MapEntry { + + private class MapEntry + { internal object key; internal object val; - public MapEntry(object key, object val) { + public MapEntry(object key, object val) + { this.key = key; this.val = val; } } - private class MapEntryHandler : - AbstractObjectSerializationHandler { - - public MapEntryHandler() - : base(typeof(MapEntry),"MapEntry") { + private class MapEntryHandler : + AbstractObjectSerializationHandler + { + + public MapEntryHandler() + : base(typeof(MapEntry), "MapEntry") + { } - public override Object Deserialize(ObjectDecoder decoder) { + public override Object Deserialize(ObjectDecoder decoder) + { Object key = decoder.ReadObjectContents(0); Object val = decoder.ReadObjectContents(1); - return new MapEntry(key,val); + return new MapEntry(key, val); } public override void Serialize(Object obj, ObjectEncoder encoder) @@ -785,47 +870,56 @@ public override void Serialize(Object obj, ObjectEncoder encoder) encoder.WriteObjectContents(entry.val); } } - private class MapHandler : + private class MapHandler : AbstractObjectSerializationHandler, - IDelegatingObjectTypeMapper { - - public MapHandler() - : base(typeof(IDictionary),"Map") { + IDelegatingObjectTypeMapper + { + public MapHandler() + : base(typeof(IDictionary), "Map") + { } - public ObjectTypeMapper FindMapperDelegate(Type type) { + public ObjectTypeMapper FindMapperDelegate(Type type) + { //get the IDictionary interface that this type implements Type interfaceType = ReflectionUtil.FindInHierarchyOf - (typeof(IDictionary<,>),type); - if ( interfaceType != null ) { - Type [] keyAndValue = interfaceType.GetGenericArguments(); - if (keyAndValue.Length != 2) { - throw new Exception("Cannot serialize type: "+type); + (typeof(IDictionary<,>), type); + if (interfaceType != null) + { + Type[] keyAndValue = interfaceType.GetGenericArguments(); + if (keyAndValue.Length != 2) + { + throw new Exception("Cannot serialize type: " + type); } Type mapHandlerRawType = typeof(MapHandler<,>); - Type mapHandlerType = + Type mapHandlerType = mapHandlerRawType.MakeGenericType(keyAndValue); return (ObjectTypeMapper)Activator.CreateInstance(mapHandlerType); } return null; } - public override Object Deserialize(ObjectDecoder decoder) { + public override Object Deserialize(ObjectDecoder decoder) + { bool caseInsensitive = - decoder.ReadBooleanField("caseInsensitive",false); - if (caseInsensitive) { + decoder.ReadBooleanField("caseInsensitive", false); + if (caseInsensitive) + { IDictionary rv = CollectionUtil.NewCaseInsensitiveDictionary(); int count = decoder.GetNumSubObjects(); - for (int i = 0; i < count; i++) { + for (int i = 0; i < count; i++) + { MapEntry entry = (MapEntry)decoder.ReadObjectContents(i); - rv[""+entry.key] = entry.val; + rv["" + entry.key] = entry.val; } return rv; } - else { - IDictionary rv = - new Dictionary(); + else + { + IDictionary rv = + new Dictionary(); int count = decoder.GetNumSubObjects(); - for (int i = 0; i < count; i++) { + for (int i = 0; i < count; i++) + { MapEntry entry = (MapEntry)decoder.ReadObjectContents(i); rv[entry.key] = entry.val; } @@ -835,55 +929,67 @@ public override Object Deserialize(ObjectDecoder decoder) { public override void Serialize(Object obj, ObjectEncoder encoder) { - IDictionary map = (IDictionary)obj; - if (CollectionUtil.IsCaseInsensitiveDictionary(map)) { - encoder.WriteBooleanField("caseInsensitive",true); + IDictionary map = (IDictionary)obj; + if (CollectionUtil.IsCaseInsensitiveDictionary(map)) + { + encoder.WriteBooleanField("caseInsensitive", true); } - else if ( map is SortedDictionary ) { + else if (map is SortedDictionary) + { throw new Exception("Serialization of SortedDictionary not supported"); } - foreach (KeyValuePair entry in map) { - MapEntry myEntry = new MapEntry(entry.Key,entry.Value); + foreach (KeyValuePair entry in map) + { + MapEntry myEntry = new MapEntry(entry.Key, entry.Value); encoder.WriteObjectContents(myEntry); } } - - public override bool MatchSubclasses { + + public override bool MatchSubclasses + { get { return true; } } } - private class ListHandler : + private class ListHandler : AbstractObjectSerializationHandler, - IDelegatingObjectTypeMapper { - - public ListHandler() - : base(typeof(IList),"List") { + IDelegatingObjectTypeMapper + { + + public ListHandler() + : base(typeof(IList), "List") + { } - public ObjectTypeMapper FindMapperDelegate(Type type) { + public ObjectTypeMapper FindMapperDelegate(Type type) + { //in C#, arrays implement IList - if (type.IsArray) { + if (type.IsArray) + { return null; } //get the IList interface that this type implements Type interfaceType = ReflectionUtil.FindInHierarchyOf - (typeof(IList<>),type); - if ( interfaceType != null ) { - Type [] val = interfaceType.GetGenericArguments(); - if (val.Length != 1) { - throw new Exception("Cannot serialize type: "+type); + (typeof(IList<>), type); + if (interfaceType != null) + { + Type[] val = interfaceType.GetGenericArguments(); + if (val.Length != 1) + { + throw new Exception("Cannot serialize type: " + type); } Type listHandlerRawType = typeof(ListHandler<>); - Type listHandlerType = + Type listHandlerType = listHandlerRawType.MakeGenericType(val); return (ObjectTypeMapper)Activator.CreateInstance(listHandlerType); } return null; } - public override Object Deserialize(ObjectDecoder decoder) { - IList rv = + public override Object Deserialize(ObjectDecoder decoder) + { + IList rv = new List(); int count = decoder.GetNumSubObjects(); - for ( int i = 0; i < count; i++ ){ + for (int i = 0; i < count; i++) + { rv.Add(decoder.ReadObjectContents(i)); } return rv; @@ -892,60 +998,73 @@ public override Object Deserialize(ObjectDecoder decoder) { public override void Serialize(Object obj, ObjectEncoder encoder) { IList list = (IList)obj; - foreach (T o in list) { + foreach (T o in list) + { encoder.WriteObjectContents(o); } } - - public override bool MatchSubclasses { + + public override bool MatchSubclasses + { get { return true; } } } - private class SetHandler : + private class SetHandler : AbstractObjectSerializationHandler, - IDelegatingObjectTypeMapper { - - public SetHandler() - : base(typeof(ICollection),"Set") { + IDelegatingObjectTypeMapper + { + + public SetHandler() + : base(typeof(ICollection), "Set") + { } - public ObjectTypeMapper FindMapperDelegate(Type type) { + public ObjectTypeMapper FindMapperDelegate(Type type) + { //in C#, arrays implement IList - if (type.IsArray) { + if (type.IsArray) + { return null; } //get the IList interface that this type implements Type interfaceType = ReflectionUtil.FindInHierarchyOf - (typeof(ICollection<>),type); - if ( interfaceType != null ) { - Type [] val = interfaceType.GetGenericArguments(); - if (val.Length != 1) { - throw new Exception("Cannot serialize type: "+type); + (typeof(ICollection<>), type); + if (interfaceType != null) + { + Type[] val = interfaceType.GetGenericArguments(); + if (val.Length != 1) + { + throw new Exception("Cannot serialize type: " + type); } Type setHandlerRawType = typeof(SetHandler<>); - Type setHandlerType = + Type setHandlerType = setHandlerRawType.MakeGenericType(val); return (ObjectTypeMapper)Activator.CreateInstance(setHandlerType); } return null; } - public override Object Deserialize(ObjectDecoder decoder) { + public override Object Deserialize(ObjectDecoder decoder) + { bool caseInsensitive = - decoder.ReadBooleanField("caseInsensitive",false); - if (caseInsensitive) { + decoder.ReadBooleanField("caseInsensitive", false); + if (caseInsensitive) + { ICollection rv = CollectionUtil.NewCaseInsensitiveSet(); int count = decoder.GetNumSubObjects(); - for (int i = 0; i < count; i++) { - rv.Add(""+decoder.ReadObjectContents(i)); + for (int i = 0; i < count; i++) + { + rv.Add("" + decoder.ReadObjectContents(i)); } return rv; } - else { - ICollection rv = + else + { + ICollection rv = new HashSet(); int count = decoder.GetNumSubObjects(); - for (int i = 0; i < count; i++) { + for (int i = 0; i < count; i++) + { rv.Add(decoder.ReadObjectContents(i)); } return rv; @@ -955,32 +1074,39 @@ public override Object Deserialize(ObjectDecoder decoder) { public override void Serialize(Object obj, ObjectEncoder encoder) { ICollection list = (ICollection)obj; - if (CollectionUtil.IsCaseInsensitiveSet(list)) { - encoder.WriteBooleanField("caseInsensitive",true); + if (CollectionUtil.IsCaseInsensitiveSet(list)) + { + encoder.WriteBooleanField("caseInsensitive", true); } - foreach (T o in list) { + foreach (T o in list) + { encoder.WriteObjectContents(o); } } - - public override bool MatchSubclasses { + + public override bool MatchSubclasses + { get { return true; } } } - private class LocaleHandler : AbstractObjectSerializationHandler { - public LocaleHandler() - : base(typeof(CultureInfo),"Locale") { - + private class LocaleHandler : AbstractObjectSerializationHandler + { + public LocaleHandler() + : base(typeof(CultureInfo), "Locale") + { + } - public override Object Deserialize(ObjectDecoder decoder) { - string language = decoder.ReadStringField("language",""); - string country = decoder.ReadStringField("country",""); - string variant = decoder.ReadStringField("variant",""); - Locale locale = new Locale(language,country,variant); + public override Object Deserialize(ObjectDecoder decoder) + { + string language = decoder.ReadStringField("language", ""); + string country = decoder.ReadStringField("country", ""); + string variant = decoder.ReadStringField("variant", ""); + Locale locale = new Locale(language, country, variant); return locale.ToCultureInfo(); } - - public override void Serialize(Object obj, ObjectEncoder encoder) { + + public override void Serialize(Object obj, ObjectEncoder encoder) + { CultureInfo cultureInfo = (CultureInfo)obj; Locale locale = Locale.FindLocale(cultureInfo); encoder.WriteStringField("language", locale.Language); @@ -990,44 +1116,54 @@ public override void Serialize(Object obj, ObjectEncoder encoder) { } } - private class GuardedByteArrayHandler : AbstractObjectSerializationHandler { - public GuardedByteArrayHandler() - : base(typeof(GuardedByteArray),"GuardedByteArray") { - + private class GuardedByteArrayHandler : AbstractObjectSerializationHandler + { + public GuardedByteArrayHandler() + : base(typeof(GuardedByteArray), "GuardedByteArray") + { + } - public override Object Deserialize(ObjectDecoder decoder) { - byte [] encryptedBytes = null; - UnmanagedArray clearBytes = null; - try { + public override Object Deserialize(ObjectDecoder decoder) + { + byte[] encryptedBytes = null; + UnmanagedArray clearBytes = null; + try + { encryptedBytes = decoder.ReadByteArrayContents(); clearBytes = EncryptorFactory.GetInstance().GetDefaultEncryptor().Decrypt(encryptedBytes); GuardedByteArray rv = new GuardedByteArray(); - for ( int i = 0; i < clearBytes.Length; i++ ) { + for (int i = 0; i < clearBytes.Length; i++) + { rv.AppendByte(clearBytes[i]); } return rv; } - finally { - if ( clearBytes != null ) { + finally + { + if (clearBytes != null) + { clearBytes.Dispose(); } - SecurityUtil.Clear(encryptedBytes); + SecurityUtil.Clear(encryptedBytes); } } - - public override void Serialize(Object obj, ObjectEncoder encoder) { + + public override void Serialize(Object obj, ObjectEncoder encoder) + { GuardedByteArray str = (GuardedByteArray)obj; str.Access( - clearBytes=> - { - byte [] encryptedBytes = null; - try { - encryptedBytes = EncryptorFactory.GetInstance().GetDefaultEncryptor().Encrypt(clearBytes); - encoder.WriteByteArrayContents(encryptedBytes); - } - finally { - SecurityUtil.Clear(encryptedBytes); - } + clearBytes => + { + byte[] encryptedBytes = null; + try + { + encryptedBytes = EncryptorFactory.GetInstance().GetDefaultEncryptor().Encrypt(clearBytes); + encoder.WriteByteArrayContents(encryptedBytes); + } + finally + { + SecurityUtil.Clear(encryptedBytes); + } }); } } @@ -1099,337 +1235,382 @@ public override void Serialize(Object obj, ObjectEncoder encoder) #endregion #region APIConfigurationHandlers - internal static class APIConfigurationHandlers { + internal static class APIConfigurationHandlers + { public static readonly IList HANDLERS = new List(); - static APIConfigurationHandlers() { - HANDLERS.Add( new ConnectionPoolingConfigurationHandler() ); - HANDLERS.Add( new ConfigurationPropertyHandler() ); - HANDLERS.Add( new ConfigurationPropertiesHandler() ); - HANDLERS.Add( new APIConfigurationHandler() ); - HANDLERS.Add( new ConnectorMessagesHandler() ); - HANDLERS.Add( new ConnectorKeyHandler() ); - HANDLERS.Add( new ConnectorInfoHandler() ); - } - private class ConnectionPoolingConfigurationHandler : AbstractObjectSerializationHandler { - public ConnectionPoolingConfigurationHandler() - : base(typeof(ObjectPoolConfiguration),"ObjectPoolConfiguration") { - - } - public override Object Deserialize(ObjectDecoder decoder) { - ObjectPoolConfiguration rv = + static APIConfigurationHandlers() + { + HANDLERS.Add(new ConnectionPoolingConfigurationHandler()); + HANDLERS.Add(new ConfigurationPropertyHandler()); + HANDLERS.Add(new ConfigurationPropertiesHandler()); + HANDLERS.Add(new APIConfigurationHandler()); + HANDLERS.Add(new ConnectorMessagesHandler()); + HANDLERS.Add(new ConnectorKeyHandler()); + HANDLERS.Add(new ConnectorInfoHandler()); + } + private class ConnectionPoolingConfigurationHandler : AbstractObjectSerializationHandler + { + public ConnectionPoolingConfigurationHandler() + : base(typeof(ObjectPoolConfiguration), "ObjectPoolConfiguration") + { + + } + public override Object Deserialize(ObjectDecoder decoder) + { + ObjectPoolConfiguration rv = new ObjectPoolConfiguration(); - rv.MaxObjects=(decoder.ReadIntField("maxObjects",rv.MaxObjects)); - rv.MaxIdle=(decoder.ReadIntField("maxIdle",rv.MaxIdle)); - rv.MaxWait=(decoder.ReadLongField("maxWait",rv.MaxWait)); - rv.MinEvictableIdleTimeMillis=( - decoder.ReadLongField("minEvictableIdleTimeMillis",rv.MinEvictableIdleTimeMillis)); - rv.MinIdle=( - decoder.ReadIntField("minIdle",rv.MinIdle)); + rv.MaxObjects = (decoder.ReadIntField("maxObjects", rv.MaxObjects)); + rv.MaxIdle = (decoder.ReadIntField("maxIdle", rv.MaxIdle)); + rv.MaxWait = (decoder.ReadLongField("maxWait", rv.MaxWait)); + rv.MinEvictableIdleTimeMillis = ( + decoder.ReadLongField("minEvictableIdleTimeMillis", rv.MinEvictableIdleTimeMillis)); + rv.MinIdle = ( + decoder.ReadIntField("minIdle", rv.MinIdle)); return rv; } - - public override void Serialize(Object obj, ObjectEncoder encoder) { - ObjectPoolConfiguration val = + + public override void Serialize(Object obj, ObjectEncoder encoder) + { + ObjectPoolConfiguration val = (ObjectPoolConfiguration)obj; encoder.WriteIntField("maxObjects", val.MaxObjects); - encoder.WriteIntField("maxIdle", + encoder.WriteIntField("maxIdle", val.MaxIdle); - encoder.WriteLongField("maxWait", + encoder.WriteLongField("maxWait", val.MaxWait); encoder.WriteLongField("minEvictableIdleTimeMillis", val.MinEvictableIdleTimeMillis); - encoder.WriteIntField("minIdle", + encoder.WriteIntField("minIdle", val.MinIdle); } } - private class ConfigurationPropertyHandler : AbstractObjectSerializationHandler { - public ConfigurationPropertyHandler() - : base(typeof(ConfigurationPropertyImpl),"ConfigurationProperty") { - + private class ConfigurationPropertyHandler : AbstractObjectSerializationHandler + { + public ConfigurationPropertyHandler() + : base(typeof(ConfigurationPropertyImpl), "ConfigurationProperty") + { + } - public override Object Deserialize(ObjectDecoder decoder) { + public override Object Deserialize(ObjectDecoder decoder) + { ConfigurationPropertyImpl rv = new ConfigurationPropertyImpl(); - rv.Order=(decoder.ReadIntField("order",0)); - rv.IsConfidential=(decoder.ReadBooleanField("confidential",false)); - rv.IsRequired=decoder.ReadBooleanField("required",false); - rv.Name=(decoder.ReadStringField("name",null)); - rv.HelpMessageKey=( - decoder.ReadStringField("helpMessageKey",null)); - rv.DisplayMessageKey=( - decoder.ReadStringField("displayMessageKey",null)); - rv.ValueType=( - decoder.ReadClassField("type",null)); - rv.Value=( - decoder.ReadObjectField("value",null,null)); + rv.Order = (decoder.ReadIntField("order", 0)); + rv.IsConfidential = (decoder.ReadBooleanField("confidential", false)); + rv.IsRequired = decoder.ReadBooleanField("required", false); + rv.Name = (decoder.ReadStringField("name", null)); + rv.HelpMessageKey = ( + decoder.ReadStringField("helpMessageKey", null)); + rv.DisplayMessageKey = ( + decoder.ReadStringField("displayMessageKey", null)); + rv.ValueType = ( + decoder.ReadClassField("type", null)); + rv.Value = ( + decoder.ReadObjectField("value", null, null)); ICollection operationsObj = - (ICollection)decoder.ReadObjectField("operations",typeof(ICollection),null); + (ICollection)decoder.ReadObjectField("operations", typeof(ICollection), null); ICollection> operations = new HashSet>(); - foreach (object o in operationsObj) { + foreach (object o in operationsObj) + { Type type = (Type)o; operations.Add(SafeType.ForRawType(type)); } rv.Operations = operations; - + return rv; } - - public override void Serialize(Object obj, ObjectEncoder encoder) { + + public override void Serialize(Object obj, ObjectEncoder encoder) + { ConfigurationPropertyImpl val = (ConfigurationPropertyImpl)obj; - encoder.WriteIntField("order", + encoder.WriteIntField("order", val.Order); - encoder.WriteBooleanField("confidential", + encoder.WriteBooleanField("confidential", val.IsConfidential); encoder.WriteBooleanField("required", val.IsRequired); - encoder.WriteStringField("name", + encoder.WriteStringField("name", val.Name); encoder.WriteStringField("helpMessageKey", val.HelpMessageKey); - encoder.WriteStringField("displayMessageKey", + encoder.WriteStringField("displayMessageKey", val.DisplayMessageKey); - encoder.WriteClassField("type", + encoder.WriteClassField("type", val.ValueType); encoder.WriteObjectField("value", val.Value, false); ICollection operationsObj = new HashSet(); - foreach(SafeType op in val.Operations) { + foreach (SafeType op in val.Operations) + { operationsObj.Add(op.RawType); } - encoder.WriteObjectField("operations",operationsObj,true); + encoder.WriteObjectField("operations", operationsObj, true); } } - private class ConfigurationPropertiesHandler : AbstractObjectSerializationHandler { - public ConfigurationPropertiesHandler() - : base(typeof(ConfigurationPropertiesImpl),"ConfigurationProperties") { - + private class ConfigurationPropertiesHandler : AbstractObjectSerializationHandler + { + public ConfigurationPropertiesHandler() + : base(typeof(ConfigurationPropertiesImpl), "ConfigurationProperties") + { + } - public override Object Deserialize(ObjectDecoder decoder) { - ConfigurationPropertiesImpl rv = + public override Object Deserialize(ObjectDecoder decoder) + { + ConfigurationPropertiesImpl rv = new ConfigurationPropertiesImpl(); IList props = new List (); int count = decoder.GetNumSubObjects(); - for ( int i = 0; i < count; i++ ) { + for (int i = 0; i < count; i++) + { ConfigurationPropertyImpl prop = (ConfigurationPropertyImpl)decoder.ReadObjectContents(i); props.Add(prop); } - rv.Properties = props; + rv.Properties = props; return rv; } - - public override void Serialize(Object obj, ObjectEncoder encoder) { + + public override void Serialize(Object obj, ObjectEncoder encoder) + { ConfigurationPropertiesImpl val = (ConfigurationPropertiesImpl)obj; IList props = val.Properties; - foreach (ConfigurationPropertyImpl prop in props) { + foreach (ConfigurationPropertyImpl prop in props) + { encoder.WriteObjectContents(prop); } } } - private class APIConfigurationHandler : AbstractObjectSerializationHandler { - public APIConfigurationHandler() - : base(typeof(APIConfigurationImpl),"APIConfiguration") { - + private class APIConfigurationHandler : AbstractObjectSerializationHandler + { + public APIConfigurationHandler() + : base(typeof(APIConfigurationImpl), "APIConfiguration") + { + } - public override Object Deserialize(ObjectDecoder decoder) { + public override Object Deserialize(ObjectDecoder decoder) + { APIConfigurationImpl rv = new APIConfigurationImpl(); - rv.IsConnectorPoolingSupported=( - decoder.ReadBooleanField("connectorPoolingSupported",false)); - rv.ConnectorPoolConfiguration=( + rv.IsConnectorPoolingSupported = ( + decoder.ReadBooleanField("connectorPoolingSupported", false)); + rv.ConnectorPoolConfiguration = ( (ObjectPoolConfiguration) - decoder.ReadObjectField("connectorPoolConfiguration",null,null)); - rv.ConfigurationProperties=((ConfigurationPropertiesImpl) - decoder.ReadObjectField("ConfigurationProperties",typeof(ConfigurationPropertiesImpl),null)); + decoder.ReadObjectField("connectorPoolConfiguration", null, null)); + rv.ConfigurationProperties = ((ConfigurationPropertiesImpl) + decoder.ReadObjectField("ConfigurationProperties", typeof(ConfigurationPropertiesImpl), null)); IDictionary timeoutMapObj = - (IDictionary)decoder.ReadObjectField("timeoutMap",null,null); - IDictionary,int> timeoutMap = - new Dictionary,int>(); - foreach (KeyValuePair entry in timeoutMapObj) { + (IDictionary)decoder.ReadObjectField("timeoutMap", null, null); + IDictionary, int> timeoutMap = + new Dictionary, int>(); + foreach (KeyValuePair entry in timeoutMapObj) + { Type type = (Type)entry.Key; int val = (int)entry.Value; timeoutMap[SafeType.ForRawType(type)] = val; } - rv.TimeoutMap=timeoutMap; + rv.TimeoutMap = timeoutMap; ICollection supportedOperationsObj = - (ICollection)decoder.ReadObjectField("SupportedOperations",typeof(ICollection),null); + (ICollection)decoder.ReadObjectField("SupportedOperations", typeof(ICollection), null); ICollection> supportedOperations = new HashSet>(); - foreach (object obj in supportedOperationsObj) { + foreach (object obj in supportedOperationsObj) + { Type type = (Type)obj; supportedOperations.Add(SafeType.ForRawType(type)); } - rv.SupportedOperations=supportedOperations; - rv.ProducerBufferSize=(decoder.ReadIntField("producerBufferSize",0)); + rv.SupportedOperations = supportedOperations; + rv.ProducerBufferSize = (decoder.ReadIntField("producerBufferSize", 0)); return rv; } - - public override void Serialize(Object obj, ObjectEncoder encoder) { + + public override void Serialize(Object obj, ObjectEncoder encoder) + { APIConfigurationImpl val = (APIConfigurationImpl)obj; - + ICollection supportedOperations = new HashSet(); - if ( val.SupportedOperations != null ) { - foreach (SafeType op in val.SupportedOperations ) { + if (val.SupportedOperations != null) + { + foreach (SafeType op in val.SupportedOperations) + { supportedOperations.Add(op.RawType); } } - IDictionary timeoutMap = - new Dictionary(); - if ( val.TimeoutMap != null ) { - foreach (KeyValuePair, int> entry in val.TimeoutMap) { + IDictionary timeoutMap = + new Dictionary(); + if (val.TimeoutMap != null) + { + foreach (KeyValuePair, int> entry in val.TimeoutMap) + { timeoutMap[entry.Key.RawType] = entry.Value; } } - - encoder.WriteIntField("producerBufferSize", + + encoder.WriteIntField("producerBufferSize", val.ProducerBufferSize); encoder.WriteBooleanField("connectorPoolingSupported", val.IsConnectorPoolingSupported); encoder.WriteObjectField("connectorPoolConfiguration", - val.ConnectorPoolConfiguration,false); + val.ConnectorPoolConfiguration, false); encoder.WriteObjectField("ConfigurationProperties", - val.ConfigurationProperties,true); + val.ConfigurationProperties, true); encoder.WriteObjectField("timeoutMap", - timeoutMap,false); - encoder.WriteObjectField("SupportedOperations", - supportedOperations,true); + timeoutMap, false); + encoder.WriteObjectField("SupportedOperations", + supportedOperations, true); } } - private class ConnectorMessagesHandler : AbstractObjectSerializationHandler { - public ConnectorMessagesHandler() - : base(typeof(ConnectorMessagesImpl),"ConnectorMessages") { - + private class ConnectorMessagesHandler : AbstractObjectSerializationHandler + { + public ConnectorMessagesHandler() + : base(typeof(ConnectorMessagesImpl), "ConnectorMessages") + { + } - public override Object Deserialize(ObjectDecoder decoder) { + public override Object Deserialize(ObjectDecoder decoder) + { ConnectorMessagesImpl rv = new ConnectorMessagesImpl(); - IDictionary catalogsObj = - (IDictionary)decoder.ReadObjectField("catalogs",null,null); - IDictionary> - catalogs = new Dictionary>(); - foreach (KeyValuePair entry in catalogsObj) { + IDictionary catalogsObj = + (IDictionary)decoder.ReadObjectField("catalogs", null, null); + IDictionary> + catalogs = new Dictionary>(); + foreach (KeyValuePair entry in catalogsObj) + { CultureInfo key = (CultureInfo)entry.Key; - IDictionary valObj = (IDictionary)entry.Value; - IDictionary val = - CollectionUtil.NewDictionary(valObj); + IDictionary valObj = (IDictionary)entry.Value; + IDictionary val = + CollectionUtil.NewDictionary(valObj); catalogs[key] = val; } - rv.Catalogs=(catalogs); + rv.Catalogs = (catalogs); return rv; } - - public override void Serialize(Object obj, ObjectEncoder encoder) { + + public override void Serialize(Object obj, ObjectEncoder encoder) + { ConnectorMessagesImpl val = (ConnectorMessagesImpl)obj; - encoder.WriteObjectField("catalogs", - val.Catalogs,false); + encoder.WriteObjectField("catalogs", + val.Catalogs, false); } } - - private class ConnectorKeyHandler : AbstractObjectSerializationHandler { - public ConnectorKeyHandler() - : base(typeof(ConnectorKey),"ConnectorKey") { - + + private class ConnectorKeyHandler : AbstractObjectSerializationHandler + { + public ConnectorKeyHandler() + : base(typeof(ConnectorKey), "ConnectorKey") + { + } - public override Object Deserialize(ObjectDecoder decoder) { - String bundleName = - decoder.ReadStringField("bundleName",null); + public override Object Deserialize(ObjectDecoder decoder) + { + String bundleName = + decoder.ReadStringField("bundleName", null); String bundleVersion = - decoder.ReadStringField("bundleVersion",null); - String connectorName = - decoder.ReadStringField("connectorName",null); - return new ConnectorKey(bundleName,bundleVersion,connectorName); + decoder.ReadStringField("bundleVersion", null); + String connectorName = + decoder.ReadStringField("connectorName", null); + return new ConnectorKey(bundleName, bundleVersion, connectorName); } - - public override void Serialize(Object obj, ObjectEncoder encoder) { + + public override void Serialize(Object obj, ObjectEncoder encoder) + { ConnectorKey val = (ConnectorKey)obj; encoder.WriteStringField("bundleName", val.BundleName); encoder.WriteStringField("bundleVersion", val.BundleVersion); - encoder.WriteStringField("connectorName", + encoder.WriteStringField("connectorName", val.ConnectorName); } } - - private class ConnectorInfoHandler : AbstractObjectSerializationHandler { - public ConnectorInfoHandler() - : base(typeof(RemoteConnectorInfoImpl),"ConnectorInfo") { - + + private class ConnectorInfoHandler : AbstractObjectSerializationHandler + { + public ConnectorInfoHandler() + : base(typeof(RemoteConnectorInfoImpl), "ConnectorInfo") + { + } - public override Object Deserialize(ObjectDecoder decoder) { + public override Object Deserialize(ObjectDecoder decoder) + { RemoteConnectorInfoImpl rv = new RemoteConnectorInfoImpl(); - rv.ConnectorDisplayNameKey=( - decoder.ReadStringField("connectorDisplayNameKey",null)); - rv.ConnectorKey=((ConnectorKey) - decoder.ReadObjectField("ConnectorKey",typeof(ConnectorKey),null)); - rv.Messages=((ConnectorMessagesImpl) - decoder.ReadObjectField("ConnectorMessages",typeof(ConnectorMessagesImpl),null)); - rv.DefaultAPIConfiguration=((APIConfigurationImpl) - decoder.ReadObjectField("APIConfiguration",typeof(APIConfigurationImpl),null)); + rv.ConnectorDisplayNameKey = ( + decoder.ReadStringField("connectorDisplayNameKey", null)); + rv.ConnectorKey = ((ConnectorKey) + decoder.ReadObjectField("ConnectorKey", typeof(ConnectorKey), null)); + rv.Messages = ((ConnectorMessagesImpl) + decoder.ReadObjectField("ConnectorMessages", typeof(ConnectorMessagesImpl), null)); + rv.DefaultAPIConfiguration = ((APIConfigurationImpl) + decoder.ReadObjectField("APIConfiguration", typeof(APIConfigurationImpl), null)); return rv; } - - public override void Serialize(Object obj, ObjectEncoder encoder) { + + public override void Serialize(Object obj, ObjectEncoder encoder) + { RemoteConnectorInfoImpl val = (RemoteConnectorInfoImpl)obj; encoder.WriteStringField("connectorDisplayNameKey", val.ConnectorDisplayNameKey); encoder.WriteObjectField("ConnectorKey", - val.ConnectorKey,true); - encoder.WriteObjectField("ConnectorMessages", - val.Messages,true); - encoder.WriteObjectField("APIConfiguration", - val.DefaultAPIConfiguration,true); + val.ConnectorKey, true); + encoder.WriteObjectField("ConnectorMessages", + val.Messages, true); + encoder.WriteObjectField("APIConfiguration", + val.DefaultAPIConfiguration, true); } } } #endregion - - #region ObjectSerializerFactoryImpl - public class ObjectSerializerFactoryImpl : ObjectSerializerFactory { - - public override BinaryObjectDeserializer NewBinaryDeserializer(Stream i) { + #region ObjectSerializerFactoryImpl + public class ObjectSerializerFactoryImpl : ObjectSerializerFactory + { + public override BinaryObjectDeserializer NewBinaryDeserializer(Stream i) + { return new BinaryObjectDecoder(i); } - - public override BinaryObjectSerializer NewBinarySerializer(Stream os) { + + public override BinaryObjectSerializer NewBinarySerializer(Stream os) + { return new BinaryObjectEncoder(os); } - public override XmlObjectSerializer NewXmlSerializer(TextWriter w, + public override XmlObjectSerializer NewXmlSerializer(TextWriter w, bool includeHeader, - bool multiObject) { - return new XmlObjectSerializerImpl(w,includeHeader,multiObject); + bool multiObject) + { + return new XmlObjectSerializerImpl(w, includeHeader, multiObject); } - - public override void DeserializeXmlStream(TextReader reader, + + public override void DeserializeXmlStream(TextReader reader, XmlObjectResultsHandler handler, - bool validate) { + bool validate) + { XmlObjectParser.parse(reader, handler, validate); } - + } #endregion - + #region OperationMappings - internal static class OperationMappings { - + internal static class OperationMappings + { public static readonly IList MAPPINGS = new List(); - - static OperationMappings() { + + static OperationMappings() + { MAPPINGS.Add(new ObjectTypeMapperImpl(typeof(AuthenticationApiOp), "AuthenticationApiOp")); MAPPINGS.Add(new ObjectTypeMapperImpl(typeof(ResolveUsernameApiOp), @@ -1457,183 +1638,224 @@ static OperationMappings() { MAPPINGS.Add(new ObjectTypeMapperImpl(typeof(SyncApiOp), "SyncApiOp")); } - + } #endregion #region CommonObjectHandlers - internal static class CommonObjectHandlers { + internal static class CommonObjectHandlers + { public static readonly IList HANDLERS = new List(); - static CommonObjectHandlers() { - - HANDLERS.Add( new AlreadyExistsExceptionHandler() ); - HANDLERS.Add( new ConfigurationExceptionHandler() ); - HANDLERS.Add( new ConnectionBrokenExceptionHandler() ); - HANDLERS.Add( new ConnectionFailedExceptionHandler() ); - HANDLERS.Add( new ConnectorIOExceptionHandler() ); - HANDLERS.Add( new PasswordExpiredExceptionHandler() ); - HANDLERS.Add( new InvalidPasswordExceptionHandler() ); - HANDLERS.Add( new UnknownUidExceptionHandler() ); - HANDLERS.Add( new InvalidCredentialExceptionHandler() ); - HANDLERS.Add( new PermissionDeniedExceptionHandler() ); - HANDLERS.Add( new ConnectorSecurityExceptionHandler() ); - HANDLERS.Add( new OperationTimeoutExceptionHandler() ); - HANDLERS.Add( new ConnectorExceptionHandler() ); - HANDLERS.Add( new ArgumentExceptionHandler()); - HANDLERS.Add( new RuntimeExceptionHandler() ); - HANDLERS.Add( new ExceptionHandler() ); - HANDLERS.Add( new ThrowableHandler() ); - HANDLERS.Add( new AttributeHandler() ); - HANDLERS.Add( new AttributeInfoHandler() ); - HANDLERS.Add( new ConnectorObjectHandler() ); - HANDLERS.Add( new NameHandler() ); - HANDLERS.Add( new ObjectClassHandler() ); - HANDLERS.Add( new ObjectClassInfoHandler() ); - HANDLERS.Add( new SchemaHandler() ); - HANDLERS.Add( new UidHandler() ); - HANDLERS.Add( new ScriptHandler()); - HANDLERS.Add( new ScriptContextHandler() ); - HANDLERS.Add( new OperationOptionsHandler() ); - HANDLERS.Add( new OperationOptionInfoHandler() ); - HANDLERS.Add( new EnumSerializationHandler(typeof(ConnectorAttributeInfo.Flags), + static CommonObjectHandlers() + { + HANDLERS.Add(new AlreadyExistsExceptionHandler()); + HANDLERS.Add(new ConfigurationExceptionHandler()); + HANDLERS.Add(new ConnectionBrokenExceptionHandler()); + HANDLERS.Add(new ConnectionFailedExceptionHandler()); + HANDLERS.Add(new ConnectorIOExceptionHandler()); + HANDLERS.Add(new PasswordExpiredExceptionHandler()); + HANDLERS.Add(new InvalidPasswordExceptionHandler()); + HANDLERS.Add(new UnknownUidExceptionHandler()); + HANDLERS.Add(new InvalidCredentialExceptionHandler()); + HANDLERS.Add(new PermissionDeniedExceptionHandler()); + HANDLERS.Add(new ConnectorSecurityExceptionHandler()); + HANDLERS.Add(new OperationTimeoutExceptionHandler()); + HANDLERS.Add(new ConnectorExceptionHandler()); + HANDLERS.Add(new ArgumentExceptionHandler()); + HANDLERS.Add(new RuntimeExceptionHandler()); + HANDLERS.Add(new ExceptionHandler()); + HANDLERS.Add(new ThrowableHandler()); + HANDLERS.Add(new AttributeHandler()); + HANDLERS.Add(new AttributeInfoHandler()); + HANDLERS.Add(new ConnectorObjectHandler()); + HANDLERS.Add(new NameHandler()); + HANDLERS.Add(new ObjectClassHandler()); + HANDLERS.Add(new ObjectClassInfoHandler()); + HANDLERS.Add(new SchemaHandler()); + HANDLERS.Add(new UidHandler()); + HANDLERS.Add(new ScriptHandler()); + HANDLERS.Add(new ScriptContextHandler()); + HANDLERS.Add(new OperationOptionsHandler()); + HANDLERS.Add(new OperationOptionInfoHandler()); + HANDLERS.Add(new EnumSerializationHandler(typeof(ConnectorAttributeInfo.Flags), "AttributeInfoFlag")); - HANDLERS.Add( new EnumSerializationHandler(typeof(SyncDeltaType), - "SyncDeltaType") ); - HANDLERS.Add( new SyncTokenHandler() ); - HANDLERS.Add( new SyncDeltaHandler() ); - HANDLERS.Add( new QualifiedUidHandler() ); - } - - private class AlreadyExistsExceptionHandler : AbstractExceptionHandler { - public AlreadyExistsExceptionHandler() - : base("AlreadyExistsException") { - - } - protected override AlreadyExistsException CreateException(String msg) { + HANDLERS.Add(new EnumSerializationHandler(typeof(SyncDeltaType), + "SyncDeltaType")); + HANDLERS.Add(new SyncTokenHandler()); + HANDLERS.Add(new SyncDeltaHandler()); + HANDLERS.Add(new QualifiedUidHandler()); + } + + private class AlreadyExistsExceptionHandler : AbstractExceptionHandler + { + public AlreadyExistsExceptionHandler() + : base("AlreadyExistsException") + { + + } + protected override AlreadyExistsException CreateException(String msg) + { return new AlreadyExistsException(msg); } } - private class ConfigurationExceptionHandler : AbstractExceptionHandler { - public ConfigurationExceptionHandler() - : base("ConfigurationException") { - + private class ConfigurationExceptionHandler : AbstractExceptionHandler + { + public ConfigurationExceptionHandler() + : base("ConfigurationException") + { + } - protected override ConfigurationException CreateException(String msg) { + protected override ConfigurationException CreateException(String msg) + { return new ConfigurationException(msg); } } - private class ConnectionBrokenExceptionHandler : AbstractExceptionHandler { - public ConnectionBrokenExceptionHandler() - : base("ConnectionBrokenException") { - + private class ConnectionBrokenExceptionHandler : AbstractExceptionHandler + { + public ConnectionBrokenExceptionHandler() + : base("ConnectionBrokenException") + { + } - protected override ConnectionBrokenException CreateException(String msg) { + protected override ConnectionBrokenException CreateException(String msg) + { return new ConnectionBrokenException(msg); } } - private class ConnectionFailedExceptionHandler : AbstractExceptionHandler { - public ConnectionFailedExceptionHandler() - : base("ConnectionFailedException") { - + private class ConnectionFailedExceptionHandler : AbstractExceptionHandler + { + public ConnectionFailedExceptionHandler() + : base("ConnectionFailedException") + { + } - protected override ConnectionFailedException CreateException(String msg) { + protected override ConnectionFailedException CreateException(String msg) + { return new ConnectionFailedException(msg); } } - private class ConnectorIOExceptionHandler : AbstractExceptionHandler { - public ConnectorIOExceptionHandler() - : base("ConnectorIOException") { - + private class ConnectorIOExceptionHandler : AbstractExceptionHandler + { + public ConnectorIOExceptionHandler() + : base("ConnectorIOException") + { + } - protected override ConnectorIOException CreateException(String msg) { + protected override ConnectorIOException CreateException(String msg) + { return new ConnectorIOException(msg); } } - private class PasswordExpiredExceptionHandler : AbstractExceptionHandler { - public PasswordExpiredExceptionHandler() - : base("PasswordExpiredException") { - + private class PasswordExpiredExceptionHandler : AbstractExceptionHandler + { + public PasswordExpiredExceptionHandler() + : base("PasswordExpiredException") + { + } - - public override Object Deserialize(ObjectDecoder decoder) { + + public override Object Deserialize(ObjectDecoder decoder) + { Uid uid = (Uid)decoder.ReadObjectField("Uid", typeof(Uid), null); PasswordExpiredException ex = (PasswordExpiredException)base.Deserialize(decoder); ex.Uid = uid; return ex; } - + public override void Serialize(Object obj, ObjectEncoder encoder) { base.Serialize(obj, encoder); PasswordExpiredException val = (PasswordExpiredException)obj; encoder.WriteObjectField("Uid", val.Uid, true); } - protected override PasswordExpiredException CreateException(String msg) { + protected override PasswordExpiredException CreateException(String msg) + { return new PasswordExpiredException(msg); } } - private class InvalidPasswordExceptionHandler : AbstractExceptionHandler { - public InvalidPasswordExceptionHandler() - : base("InvalidPasswordException") { - + private class InvalidPasswordExceptionHandler : AbstractExceptionHandler + { + public InvalidPasswordExceptionHandler() + : base("InvalidPasswordException") + { + } - protected override InvalidPasswordException CreateException(String msg) { + protected override InvalidPasswordException CreateException(String msg) + { return new InvalidPasswordException(msg); } } - private class UnknownUidExceptionHandler : AbstractExceptionHandler { - public UnknownUidExceptionHandler() - : base("UnknownUidException") { - + private class UnknownUidExceptionHandler : AbstractExceptionHandler + { + public UnknownUidExceptionHandler() + : base("UnknownUidException") + { + } - protected override UnknownUidException CreateException(String msg) { + protected override UnknownUidException CreateException(String msg) + { return new UnknownUidException(msg); } } - private class InvalidCredentialExceptionHandler : AbstractExceptionHandler { - public InvalidCredentialExceptionHandler() - : base("InvalidCredentialException") { - + private class InvalidCredentialExceptionHandler : AbstractExceptionHandler + { + public InvalidCredentialExceptionHandler() + : base("InvalidCredentialException") + { + } - protected override InvalidCredentialException CreateException(String msg) { + protected override InvalidCredentialException CreateException(String msg) + { return new InvalidCredentialException(msg); } } - private class PermissionDeniedExceptionHandler : AbstractExceptionHandler { - public PermissionDeniedExceptionHandler() - : base("PermissionDeniedException") { - + private class PermissionDeniedExceptionHandler : AbstractExceptionHandler + { + public PermissionDeniedExceptionHandler() + : base("PermissionDeniedException") + { + } - protected override PermissionDeniedException CreateException(String msg) { + protected override PermissionDeniedException CreateException(String msg) + { return new PermissionDeniedException(msg); } } - private class ConnectorSecurityExceptionHandler : AbstractExceptionHandler { - public ConnectorSecurityExceptionHandler() - : base("ConnectorSecurityException") { - + private class ConnectorSecurityExceptionHandler : AbstractExceptionHandler + { + public ConnectorSecurityExceptionHandler() + : base("ConnectorSecurityException") + { + } - protected override ConnectorSecurityException CreateException(String msg) { + protected override ConnectorSecurityException CreateException(String msg) + { return new ConnectorSecurityException(msg); } } - private class OperationTimeoutExceptionHandler : AbstractExceptionHandler { - public OperationTimeoutExceptionHandler() - : base("OperationTimeoutException") { - + private class OperationTimeoutExceptionHandler : AbstractExceptionHandler + { + public OperationTimeoutExceptionHandler() + : base("OperationTimeoutException") + { + } - protected override OperationTimeoutException CreateException(String msg) { + protected override OperationTimeoutException CreateException(String msg) + { return new OperationTimeoutException(msg); } } - private class ConnectorExceptionHandler : AbstractExceptionHandler { - public ConnectorExceptionHandler() - : base("ConnectorException") { - + private class ConnectorExceptionHandler : AbstractExceptionHandler + { + public ConnectorExceptionHandler() + : base("ConnectorException") + { + } - protected override ConnectorException CreateException(String msg) { + protected override ConnectorException CreateException(String msg) + { return new ConnectorException(msg); } } @@ -1642,99 +1864,121 @@ protected override ConnectorException CreateException(String msg) { //Exception. //when going from C# to Java, these always become //RuntimeException - private class RuntimeExceptionHandler : AbstractExceptionHandler { - public RuntimeExceptionHandler() - : base("RuntimeException") { - + private class RuntimeExceptionHandler : AbstractExceptionHandler + { + public RuntimeExceptionHandler() + : base("RuntimeException") + { + } - protected override Exception CreateException(String msg) { + protected override Exception CreateException(String msg) + { return new Exception(msg); } } - private class ArgumentExceptionHandler : AbstractExceptionHandler { + private class ArgumentExceptionHandler : AbstractExceptionHandler + { public ArgumentExceptionHandler() - : base("IllegalArgumentException") { - + : base("IllegalArgumentException") + { + } - protected override ArgumentException CreateException(string msg) { + protected override ArgumentException CreateException(string msg) + { return new ArgumentException(msg); } } - private class ExceptionHandler : AbstractExceptionHandler { - public ExceptionHandler() - : base("Exception") { - + private class ExceptionHandler : AbstractExceptionHandler + { + public ExceptionHandler() + : base("Exception") + { + } - protected override Exception CreateException(String msg) { + protected override Exception CreateException(String msg) + { return new Exception(msg); } } - private class ThrowableHandler : AbstractExceptionHandler { - public ThrowableHandler() - : base("Throwable") { - + private class ThrowableHandler : AbstractExceptionHandler + { + public ThrowableHandler() + : base("Throwable") + { + } - protected override Exception CreateException(String msg) { + protected override Exception CreateException(String msg) + { return new Exception(msg); } } - - private abstract class AbstractAttributeHandler - : AbstractObjectSerializationHandler + + private abstract class AbstractAttributeHandler + : AbstractObjectSerializationHandler where T : ConnectorAttribute { - - protected AbstractAttributeHandler(String typeName) - :base(typeof(T),typeName) { + + protected AbstractAttributeHandler(String typeName) + : base(typeof(T), typeName) + { } - - - public override sealed Object Deserialize(ObjectDecoder decoder) { - String name = decoder.ReadStringField("name",null); - IList val = (IList)decoder.ReadObjectField("Values",typeof(IList),null); + + + public override sealed Object Deserialize(ObjectDecoder decoder) + { + String name = decoder.ReadStringField("name", null); + IList val = (IList)decoder.ReadObjectField("Values", typeof(IList), null); return CreateAttribute(name, val); } - + public override sealed void Serialize(Object obj, ObjectEncoder encoder) { ConnectorAttribute val = (ConnectorAttribute)obj; encoder.WriteStringField("name", val.Name); encoder.WriteObjectField("Values", val.Value, true); } - + protected abstract T CreateAttribute(String name, IList val); } - + private class AttributeHandler - : AbstractAttributeHandler { + : AbstractAttributeHandler + { public AttributeHandler() - : base("Attribute") { - + : base("Attribute") + { + } - protected override ConnectorAttribute CreateAttribute(String name, IList value) { + protected override ConnectorAttribute CreateAttribute(String name, IList value) + { return ConnectorAttributeBuilder.Build(name, value); } } - - private class AttributeInfoHandler : AbstractObjectSerializationHandler { - public AttributeInfoHandler() - : base(typeof(ConnectorAttributeInfo),"AttributeInfo") { - + + private class AttributeInfoHandler : AbstractObjectSerializationHandler + { + public AttributeInfoHandler() + : base(typeof(ConnectorAttributeInfo), "AttributeInfo") + { + } - public override Object Deserialize(ObjectDecoder decoder) { + public override Object Deserialize(ObjectDecoder decoder) + { ConnectorAttributeInfoBuilder builder = new ConnectorAttributeInfoBuilder(); - builder.Name=( - decoder.ReadStringField("name",null)); - builder.ValueType=( - decoder.ReadClassField("type",null)); + builder.Name = ( + decoder.ReadStringField("name", null)); + builder.ValueType = ( + decoder.ReadClassField("type", null)); ConnectorAttributeInfo.Flags flags = ConnectorAttributeInfo.Flags.NONE; int count = decoder.GetNumSubObjects(); - for ( int i = 0; i < count; i++ ) { + for (int i = 0; i < count; i++) + { object o = decoder.ReadObjectContents(i); - if ( o is ConnectorAttributeInfo.Flags ) { - ConnectorAttributeInfo.Flags f = + if (o is ConnectorAttributeInfo.Flags) + { + ConnectorAttributeInfo.Flags f = (ConnectorAttributeInfo.Flags)o; flags |= f; } @@ -1742,158 +1986,188 @@ public override Object Deserialize(ObjectDecoder decoder) { builder.InfoFlags = flags; return builder.Build(); } - - public override void Serialize(Object obj, ObjectEncoder encoder) { + + public override void Serialize(Object obj, ObjectEncoder encoder) + { ConnectorAttributeInfo val = (ConnectorAttributeInfo)obj; encoder.WriteStringField("name", val.Name); encoder.WriteClassField("type", val.ValueType); ConnectorAttributeInfo.Flags flags = val.InfoFlags; - foreach (Enum e in Enum.GetValues(typeof(ConnectorAttributeInfo.Flags))) { - ConnectorAttributeInfo.Flags flag = + foreach (Enum e in Enum.GetValues(typeof(ConnectorAttributeInfo.Flags))) + { + ConnectorAttributeInfo.Flags flag = (ConnectorAttributeInfo.Flags)e; - if ( (flags & flag) != 0 ) { + if ((flags & flag) != 0) + { encoder.WriteObjectContents(flag); } } } } - - private class ConnectorObjectHandler : AbstractObjectSerializationHandler { - public ConnectorObjectHandler() - : base(typeof(ConnectorObject),"ConnectorObject") { - + + private class ConnectorObjectHandler : AbstractObjectSerializationHandler + { + public ConnectorObjectHandler() + : base(typeof(ConnectorObject), "ConnectorObject") + { + } - public override Object Deserialize(ObjectDecoder decoder) { + public override Object Deserialize(ObjectDecoder decoder) + { ObjectClass oclass = - (ObjectClass)decoder.ReadObjectField("ObjectClass",typeof(ObjectClass),null); + (ObjectClass)decoder.ReadObjectField("ObjectClass", typeof(ObjectClass), null); ICollection attsObj = - (ICollection)decoder.ReadObjectField("Attributes",typeof(ICollection),null); + (ICollection)decoder.ReadObjectField("Attributes", typeof(ICollection), null); ICollection atts = - CollectionUtil.NewSet(attsObj); - return new ConnectorObject(oclass,atts); + CollectionUtil.NewSet(attsObj); + return new ConnectorObject(oclass, atts); } - - public override void Serialize(Object obj, ObjectEncoder encoder) { + + public override void Serialize(Object obj, ObjectEncoder encoder) + { ConnectorObject val = (ConnectorObject)obj; - encoder.WriteObjectField("ObjectClass",val.ObjectClass,true); - encoder.WriteObjectField("Attributes", val.GetAttributes(),true); + encoder.WriteObjectField("ObjectClass", val.ObjectClass, true); + encoder.WriteObjectField("Attributes", val.GetAttributes(), true); } } private class NameHandler - : AbstractObjectSerializationHandler { + : AbstractObjectSerializationHandler + { public NameHandler() - : base(typeof(Name),"Name") { - + : base(typeof(Name), "Name") + { + } - public override Object Deserialize(ObjectDecoder decoder) { + public override Object Deserialize(ObjectDecoder decoder) + { String str = decoder.ReadStringContents(); return new Name(str); } - - public override void Serialize(Object obj, ObjectEncoder encoder) { + + public override void Serialize(Object obj, ObjectEncoder encoder) + { Name val = (Name)obj; encoder.WriteStringContents(val.GetNameValue()); } } - private class ObjectClassHandler : AbstractObjectSerializationHandler { - public ObjectClassHandler() - : base(typeof(ObjectClass),"ObjectClass") { - + private class ObjectClassHandler : AbstractObjectSerializationHandler + { + public ObjectClassHandler() + : base(typeof(ObjectClass), "ObjectClass") + { + } - public override object Deserialize(ObjectDecoder decoder) { - string type = decoder.ReadStringField("type",null); + public override object Deserialize(ObjectDecoder decoder) + { + string type = decoder.ReadStringField("type", null); return new ObjectClass(type); } - - public override void Serialize(object obj, ObjectEncoder encoder) { - ObjectClass val = (ObjectClass)obj; - encoder.WriteStringField("type",val.GetObjectClassValue()); + + public override void Serialize(object obj, ObjectEncoder encoder) + { + ObjectClass val = (ObjectClass)obj; + encoder.WriteStringField("type", val.GetObjectClassValue()); + } + } + + private class ObjectClassInfoHandler : AbstractObjectSerializationHandler + { + public ObjectClassInfoHandler() + : base(typeof(ObjectClassInfo), "ObjectClassInfo") + { + + } + public override Object Deserialize(ObjectDecoder decoder) + { + string type = + decoder.ReadStringField("type", null); + bool container = + decoder.ReadBooleanField("container", false); + + ICollection attrInfoObj = + (ICollection)decoder.ReadObjectField("AttributeInfos", typeof(ICollection), null); + + ICollection attrInfo = + CollectionUtil.NewSet(attrInfoObj); + + return new ObjectClassInfo(type, attrInfo, container); + } + + public override void Serialize(Object obj, ObjectEncoder encoder) + { + ObjectClassInfo val = (ObjectClassInfo)obj; + encoder.WriteStringField("type", val.ObjectType); + encoder.WriteBooleanField("container", val.IsContainer); + encoder.WriteObjectField("AttributeInfos", val.ConnectorAttributeInfos, true); } } - - private class ObjectClassInfoHandler : AbstractObjectSerializationHandler { - public ObjectClassInfoHandler() - : base(typeof(ObjectClassInfo),"ObjectClassInfo") { - + private class SchemaHandler : AbstractObjectSerializationHandler + { + public SchemaHandler() + : base(typeof(Schema), "Schema") + { + } - public override Object Deserialize(ObjectDecoder decoder) { - string type = - decoder.ReadStringField("type",null); - bool container = - decoder.ReadBooleanField("container", false); - - ICollection attrInfoObj = - (ICollection)decoder.ReadObjectField("AttributeInfos",typeof(ICollection),null); - - ICollection attrInfo = - CollectionUtil.NewSet(attrInfoObj); - - return new ObjectClassInfo(type,attrInfo,container); - } - - public override void Serialize(Object obj, ObjectEncoder encoder) { - ObjectClassInfo val = (ObjectClassInfo)obj; - encoder.WriteStringField("type",val.ObjectType); - encoder.WriteBooleanField("container",val.IsContainer); - encoder.WriteObjectField("AttributeInfos", val.ConnectorAttributeInfos,true); - } - } - private class SchemaHandler : AbstractObjectSerializationHandler { - public SchemaHandler() - : base(typeof(Schema),"Schema") { - - } - public override Object Deserialize(ObjectDecoder decoder) { - + public override Object Deserialize(ObjectDecoder decoder) + { + ICollection objectClassesObj = - (ICollection)decoder.ReadObjectField("ObjectClassInfos",typeof(ICollection),null); - ICollection objectClasses = - CollectionUtil.NewSet(objectClassesObj); - IDictionary objectClassesByName = new Dictionary(); - foreach (ObjectClassInfo info in objectClasses) { + (ICollection)decoder.ReadObjectField("ObjectClassInfos", typeof(ICollection), null); + ICollection objectClasses = + CollectionUtil.NewSet(objectClassesObj); + IDictionary objectClassesByName = new Dictionary(); + foreach (ObjectClassInfo info in objectClasses) + { objectClassesByName[info.ObjectType] = info; } ICollection operationOptionsObj = - (ICollection)decoder.ReadObjectField("OperationOptionInfos",typeof(ICollection),null); + (ICollection)decoder.ReadObjectField("OperationOptionInfos", typeof(ICollection), null); ICollection operationOptions = - CollectionUtil.NewSet(operationOptionsObj); - IDictionary optionsByName = new Dictionary(); - foreach (OperationOptionInfo info in operationOptions) { + CollectionUtil.NewSet(operationOptionsObj); + IDictionary optionsByName = new Dictionary(); + foreach (OperationOptionInfo info in operationOptions) + { optionsByName[info.Name] = info; } IDictionary objectClassNamesByOperationObj = - (IDictionary)decoder.ReadObjectField("objectClassesByOperation",null,null); - IDictionary,ICollection> + (IDictionary)decoder.ReadObjectField("objectClassesByOperation", null, null); + IDictionary, ICollection> objectClassesByOperation = new Dictionary, ICollection>(); - foreach (KeyValuePair entry in objectClassNamesByOperationObj) { + foreach (KeyValuePair entry in objectClassNamesByOperationObj) + { SafeType op = SafeType.ForRawType((Type)entry.Key); ICollection namesObj = (ICollection)entry.Value; ICollection infos = new HashSet(); - foreach (object name in namesObj) { - ObjectClassInfo objectClass = CollectionUtil.GetValue(objectClassesByName,(string)name,null); - if ( objectClass != null ) { + foreach (object name in namesObj) + { + ObjectClassInfo objectClass = CollectionUtil.GetValue(objectClassesByName, (string)name, null); + if (objectClass != null) + { infos.Add(objectClass); } } objectClassesByOperation[op] = infos; } IDictionary optionsByOperationObj = - (IDictionary)decoder.ReadObjectField("optionsByOperation",null,null); - IDictionary,ICollection> + (IDictionary)decoder.ReadObjectField("optionsByOperation", null, null); + IDictionary, ICollection> optionsByOperation = new Dictionary, ICollection>(); - foreach (KeyValuePair entry in optionsByOperationObj) { + foreach (KeyValuePair entry in optionsByOperationObj) + { SafeType op = SafeType.ForRawType((Type)entry.Key); ICollection namesObj = (ICollection)entry.Value; ICollection infos = new HashSet(); - foreach (object name in namesObj) { - OperationOptionInfo info = CollectionUtil.GetValue(optionsByName,(string)name,null); - if ( info != null ) { + foreach (object name in namesObj) + { + OperationOptionInfo info = CollectionUtil.GetValue(optionsByName, (string)name, null); + if (info != null) + { infos.Add(info); } } @@ -1904,51 +2178,60 @@ public override Object Deserialize(ObjectDecoder decoder) { objectClassesByOperation, optionsByOperation); } - - public override void Serialize(Object obj, ObjectEncoder encoder) { + + public override void Serialize(Object obj, ObjectEncoder encoder) + { Schema val = (Schema)obj; - encoder.WriteObjectField("ObjectClassInfos", val.ObjectClassInfo,true); - encoder.WriteObjectField("OperationOptionInfos", val.OperationOptionInfo,true); - IDictionary> - objectClassNamesByOperation = new Dictionary>(); - IDictionary> - optionNamesByOperation = new Dictionary>(); - - - foreach (KeyValuePair, ICollection> - entry in val.SupportedObjectClassesByOperation) { + encoder.WriteObjectField("ObjectClassInfos", val.ObjectClassInfo, true); + encoder.WriteObjectField("OperationOptionInfos", val.OperationOptionInfo, true); + IDictionary> + objectClassNamesByOperation = new Dictionary>(); + IDictionary> + optionNamesByOperation = new Dictionary>(); + + + foreach (KeyValuePair, ICollection> + entry in val.SupportedObjectClassesByOperation) + { ICollection value = entry.Value; ICollection names = new HashSet(); - foreach (ObjectClassInfo info in value) { + foreach (ObjectClassInfo info in value) + { names.Add(info.ObjectType); } objectClassNamesByOperation[entry.Key.RawType] = names; } - foreach (KeyValuePair, ICollection> - entry in val.SupportedOptionsByOperation) { + foreach (KeyValuePair, ICollection> + entry in val.SupportedOptionsByOperation) + { ICollection value = entry.Value; ICollection names = new HashSet(); - foreach (OperationOptionInfo info in value) { + foreach (OperationOptionInfo info in value) + { names.Add(info.Name); } optionNamesByOperation[entry.Key.RawType] = names; } - encoder.WriteObjectField("objectClassesByOperation",objectClassNamesByOperation,false); - encoder.WriteObjectField("optionsByOperation",optionNamesByOperation,false); + encoder.WriteObjectField("objectClassesByOperation", objectClassNamesByOperation, false); + encoder.WriteObjectField("optionsByOperation", optionNamesByOperation, false); } } private class UidHandler - : AbstractObjectSerializationHandler { + : AbstractObjectSerializationHandler + { public UidHandler() - : base(typeof(Uid),"Uid") { - + : base(typeof(Uid), "Uid") + { + } - public override Object Deserialize(ObjectDecoder decoder) { + public override Object Deserialize(ObjectDecoder decoder) + { String str = decoder.ReadStringContents(); return new Uid(str); } - - public override void Serialize(Object obj, ObjectEncoder encoder) { + + public override void Serialize(Object obj, ObjectEncoder encoder) + { Uid val = (Uid)obj; encoder.WriteStringContents(val.GetUidValue()); } @@ -1956,10 +2239,12 @@ public override void Serialize(Object obj, ObjectEncoder encoder) { private class ScriptHandler : AbstractObjectSerializationHandler { public ScriptHandler() - : base(typeof(Script), "Script") { + : base(typeof(Script), "Script") + { } - public override Object Deserialize(ObjectDecoder decoder) { + public override Object Deserialize(ObjectDecoder decoder) + { ScriptBuilder builder = new ScriptBuilder(); builder.ScriptLanguage = decoder.ReadStringField("scriptLanguage", null); builder.ScriptText = (String)decoder.ReadObjectField("scriptText", typeof(string), null); @@ -1975,129 +2260,153 @@ public override void Serialize(Object obj, ObjectEncoder encoder) } private class ScriptContextHandler : AbstractObjectSerializationHandler { - public ScriptContextHandler() - : base(typeof(ScriptContext),"ScriptContext") { - + public ScriptContextHandler() + : base(typeof(ScriptContext), "ScriptContext") + { + } - public override Object Deserialize(ObjectDecoder decoder) { + public override Object Deserialize(ObjectDecoder decoder) + { String scriptLanguage = - decoder.ReadStringField("scriptLanguage",null); - IDictionary arguments = - (IDictionary)decoder.ReadObjectField("scriptArguments",null,null); + decoder.ReadStringField("scriptLanguage", null); + IDictionary arguments = + (IDictionary)decoder.ReadObjectField("scriptArguments", null, null); String scriptText = - (String)decoder.ReadObjectField("scriptText",typeof(string),null); + (String)decoder.ReadObjectField("scriptText", typeof(string), null); IDictionary arguments2 = - CollectionUtil.NewDictionary(arguments); - return new ScriptContext(scriptLanguage,scriptText,arguments2); + CollectionUtil.NewDictionary(arguments); + return new ScriptContext(scriptLanguage, scriptText, arguments2); } - - public override void Serialize(Object obj, ObjectEncoder encoder) { + + public override void Serialize(Object obj, ObjectEncoder encoder) + { ScriptContext val = (ScriptContext)obj; encoder.WriteStringField("scriptLanguage", val.ScriptLanguage); - encoder.WriteObjectField("scriptArguments", val.ScriptArguments,false); - encoder.WriteObjectField("scriptText", val.ScriptText,true); + encoder.WriteObjectField("scriptArguments", val.ScriptArguments, false); + encoder.WriteObjectField("scriptText", val.ScriptText, true); } } - private class OperationOptionsHandler : AbstractObjectSerializationHandler { - public OperationOptionsHandler() - : base(typeof(OperationOptions),"OperationOptions") { - + private class OperationOptionsHandler : AbstractObjectSerializationHandler + { + public OperationOptionsHandler() + : base(typeof(OperationOptions), "OperationOptions") + { + } - public override Object Deserialize(ObjectDecoder decoder) { - IDictionary options = - (IDictionary)decoder.ReadObjectField("options",null,null); + public override Object Deserialize(ObjectDecoder decoder) + { + IDictionary options = + (IDictionary)decoder.ReadObjectField("options", null, null); IDictionary options2 = - CollectionUtil.NewDictionary(options); + CollectionUtil.NewDictionary(options); return new OperationOptions(options2); } - - public override void Serialize(Object obj, ObjectEncoder encoder) { + + public override void Serialize(Object obj, ObjectEncoder encoder) + { OperationOptions val = (OperationOptions)obj; - encoder.WriteObjectField("options", val.Options,false); + encoder.WriteObjectField("options", val.Options, false); } } - private class OperationOptionInfoHandler : AbstractObjectSerializationHandler { - public OperationOptionInfoHandler() - : base(typeof(OperationOptionInfo),"OperationOptionInfo") { - + private class OperationOptionInfoHandler : AbstractObjectSerializationHandler + { + public OperationOptionInfoHandler() + : base(typeof(OperationOptionInfo), "OperationOptionInfo") + { + } - public override Object Deserialize(ObjectDecoder decoder) { - String name = decoder.ReadStringField("name",null); - Type type = decoder.ReadClassField("type",null); - return new OperationOptionInfo(name,type); + public override Object Deserialize(ObjectDecoder decoder) + { + String name = decoder.ReadStringField("name", null); + Type type = decoder.ReadClassField("type", null); + return new OperationOptionInfo(name, type); } - - public override void Serialize(Object obj, ObjectEncoder encoder) { + + public override void Serialize(Object obj, ObjectEncoder encoder) + { OperationOptionInfo val = (OperationOptionInfo)obj; encoder.WriteStringField("name", val.Name); encoder.WriteClassField("type", val.OptionType); } } - private class SyncTokenHandler : AbstractObjectSerializationHandler { - public SyncTokenHandler() - : base(typeof(SyncToken),"SyncToken") { - + private class SyncTokenHandler : AbstractObjectSerializationHandler + { + public SyncTokenHandler() + : base(typeof(SyncToken), "SyncToken") + { + } - public override Object Deserialize(ObjectDecoder decoder) { - Object value = decoder.ReadObjectField("value",null,null); + public override Object Deserialize(ObjectDecoder decoder) + { + Object value = decoder.ReadObjectField("value", null, null); return new SyncToken(value); } - - public override void Serialize(Object obj, ObjectEncoder encoder) { + + public override void Serialize(Object obj, ObjectEncoder encoder) + { SyncToken val = (SyncToken)obj; - encoder.WriteObjectField("value", val.Value,false); + encoder.WriteObjectField("value", val.Value, false); } } - private class SyncDeltaHandler : AbstractObjectSerializationHandler { - public SyncDeltaHandler() - : base(typeof(SyncDelta),"SyncDelta") { - + private class SyncDeltaHandler : AbstractObjectSerializationHandler + { + public SyncDeltaHandler() + : base(typeof(SyncDelta), "SyncDelta") + { + } - public override Object Deserialize(ObjectDecoder decoder) { + public override Object Deserialize(ObjectDecoder decoder) + { SyncDeltaBuilder builder = new SyncDeltaBuilder(); - builder.DeltaType=((SyncDeltaType)decoder.ReadObjectField("SyncDeltaType",typeof(SyncDeltaType),null)); - builder.Token=((SyncToken)decoder.ReadObjectField("SyncToken",typeof(SyncToken),null)); - builder.PreviousUid=((Uid)decoder.ReadObjectField("PreviousUid",typeof(Uid),null)); - builder.Uid=((Uid)decoder.ReadObjectField("Uid",typeof(Uid),null)); - builder.Object=((ConnectorObject)decoder.ReadObjectField("ConnectorObject",typeof(ConnectorObject),null)); + builder.DeltaType = ((SyncDeltaType)decoder.ReadObjectField("SyncDeltaType", typeof(SyncDeltaType), null)); + builder.Token = ((SyncToken)decoder.ReadObjectField("SyncToken", typeof(SyncToken), null)); + builder.PreviousUid = ((Uid)decoder.ReadObjectField("PreviousUid", typeof(Uid), null)); + builder.Uid = ((Uid)decoder.ReadObjectField("Uid", typeof(Uid), null)); + builder.Object = ((ConnectorObject)decoder.ReadObjectField("ConnectorObject", typeof(ConnectorObject), null)); return builder.Build(); } - - public override void Serialize(Object obj, ObjectEncoder encoder) { + + public override void Serialize(Object obj, ObjectEncoder encoder) + { SyncDelta val = (SyncDelta)obj; - encoder.WriteObjectField("SyncDeltaType", val.DeltaType,true); - encoder.WriteObjectField("SyncToken", val.Token,true); - encoder.WriteObjectField("PreviousUid", val.PreviousUid,true); - encoder.WriteObjectField("Uid", val.Uid,true); + encoder.WriteObjectField("SyncDeltaType", val.DeltaType, true); + encoder.WriteObjectField("SyncToken", val.Token, true); + encoder.WriteObjectField("PreviousUid", val.PreviousUid, true); + encoder.WriteObjectField("Uid", val.Uid, true); encoder.WriteObjectField("ConnectorObject", val.Object, true); } } - private class QualifiedUidHandler : AbstractObjectSerializationHandler { - public QualifiedUidHandler() - : base(typeof(QualifiedUid),"QualifiedUid") { - + private class QualifiedUidHandler : AbstractObjectSerializationHandler + { + public QualifiedUidHandler() + : base(typeof(QualifiedUid), "QualifiedUid") + { + } - public override Object Deserialize(ObjectDecoder decoder) { - ObjectClass objectClass = (ObjectClass)decoder.ReadObjectField("ObjectClass", typeof(ObjectClass),null); - Uid uid = (Uid)decoder.ReadObjectField("Uid",typeof(Uid),null); - return new QualifiedUid(objectClass,uid); + public override Object Deserialize(ObjectDecoder decoder) + { + ObjectClass objectClass = (ObjectClass)decoder.ReadObjectField("ObjectClass", typeof(ObjectClass), null); + Uid uid = (Uid)decoder.ReadObjectField("Uid", typeof(Uid), null); + return new QualifiedUid(objectClass, uid); } - - public override void Serialize(Object obj, ObjectEncoder encoder) { + + public override void Serialize(Object obj, ObjectEncoder encoder) + { QualifiedUid val = (QualifiedUid)obj; encoder.WriteObjectField("ObjectClass", val.ObjectClass, true); encoder.WriteObjectField("Uid", val.Uid, true); } } - } #endregion #region FilterHandlers - internal static class FilterHandlers { + internal static class FilterHandlers + { public static readonly IList HANDLERS = new List(); - static FilterHandlers() { + static FilterHandlers() + { HANDLERS.Add(new AndFilterHandler()); HANDLERS.Add(new ContainsFilterHandler()); HANDLERS.Add(new EndsWithFilterHandler()); @@ -2112,180 +2421,224 @@ static FilterHandlers() { HANDLERS.Add(new ContainsAllValuesFilterHandler()); } - private abstract class CompositeFilterHandler - : AbstractObjectSerializationHandler - where T : CompositeFilter { - - protected CompositeFilterHandler(String typeName) - : base(typeof(T),typeName) { + private abstract class CompositeFilterHandler + : AbstractObjectSerializationHandler + where T : CompositeFilter + { + + protected CompositeFilterHandler(String typeName) + : base(typeof(T), typeName) + { } - - - public override sealed Object Deserialize(ObjectDecoder decoder) { + + + public override sealed Object Deserialize(ObjectDecoder decoder) + { Filter left = (Filter)decoder.ReadObjectContents(0); Filter right = (Filter)decoder.ReadObjectContents(1); return CreateFilter(left, right); } - - public override sealed void Serialize(Object obj, ObjectEncoder encoder) { + + public override sealed void Serialize(Object obj, ObjectEncoder encoder) + { CompositeFilter val = (CompositeFilter)obj; encoder.WriteObjectContents(val.Left); encoder.WriteObjectContents(val.Right); } - + protected abstract T CreateFilter(Filter left, Filter right); } - - private abstract class AttributeFilterHandler + + private abstract class AttributeFilterHandler : AbstractObjectSerializationHandler - where T : AttributeFilter { - - protected AttributeFilterHandler(String typeName) - : base(typeof(T),typeName) { - } - - - public override sealed Object Deserialize(ObjectDecoder decoder) { - ConnectorAttribute attribute = (ConnectorAttribute)decoder.ReadObjectField("attribute",null,null); + where T : AttributeFilter + { + + protected AttributeFilterHandler(String typeName) + : base(typeof(T), typeName) + { + } + + + public override sealed Object Deserialize(ObjectDecoder decoder) + { + ConnectorAttribute attribute = (ConnectorAttribute)decoder.ReadObjectField("attribute", null, null); return CreateFilter(attribute); } - + public override sealed void Serialize(Object obj, ObjectEncoder encoder) { AttributeFilter val = (AttributeFilter)obj; encoder.WriteObjectField("attribute", val.GetAttribute(), false); } - + protected abstract T CreateFilter(ConnectorAttribute attribute); } - - - - private class AndFilterHandler : CompositeFilterHandler { - public AndFilterHandler() - : base("AndFilter") { + + + + private class AndFilterHandler : CompositeFilterHandler + { + public AndFilterHandler() + : base("AndFilter") + { } - protected override AndFilter CreateFilter(Filter left, Filter right) { - return new AndFilter(left,right); + protected override AndFilter CreateFilter(Filter left, Filter right) + { + return new AndFilter(left, right); } } - - private class ContainsFilterHandler : AttributeFilterHandler { - public ContainsFilterHandler() - : base("ContainsFilter") { + + private class ContainsFilterHandler : AttributeFilterHandler + { + public ContainsFilterHandler() + : base("ContainsFilter") + { } - protected override ContainsFilter CreateFilter(ConnectorAttribute attribute) { + protected override ContainsFilter CreateFilter(ConnectorAttribute attribute) + { return new ContainsFilter(attribute); } } - private class EndsWithFilterHandler : AttributeFilterHandler { - public EndsWithFilterHandler() - : base("EndsWithFilter") { + private class EndsWithFilterHandler : AttributeFilterHandler + { + public EndsWithFilterHandler() + : base("EndsWithFilter") + { } - protected override EndsWithFilter CreateFilter(ConnectorAttribute attribute) { + protected override EndsWithFilter CreateFilter(ConnectorAttribute attribute) + { return new EndsWithFilter(attribute); } } - - private class EqualsFilterHandler : AttributeFilterHandler { - public EqualsFilterHandler() - : base("EqualsFilter") { + + private class EqualsFilterHandler : AttributeFilterHandler + { + public EqualsFilterHandler() + : base("EqualsFilter") + { } - protected override EqualsFilter CreateFilter(ConnectorAttribute attribute) { + protected override EqualsFilter CreateFilter(ConnectorAttribute attribute) + { return new EqualsFilter(attribute); } } - - private class GreaterThanFilterHandler : AttributeFilterHandler { - public GreaterThanFilterHandler() - : base("GreaterThanFilter") { + + private class GreaterThanFilterHandler : AttributeFilterHandler + { + public GreaterThanFilterHandler() + : base("GreaterThanFilter") + { } - protected override GreaterThanFilter CreateFilter(ConnectorAttribute attribute) { + protected override GreaterThanFilter CreateFilter(ConnectorAttribute attribute) + { return new GreaterThanFilter(attribute); } } - - private class GreaterThanOrEqualFilterHandler : AttributeFilterHandler { - public GreaterThanOrEqualFilterHandler() - : base("GreaterThanOrEqualFilter") { + + private class GreaterThanOrEqualFilterHandler : AttributeFilterHandler + { + public GreaterThanOrEqualFilterHandler() + : base("GreaterThanOrEqualFilter") + { } - protected override GreaterThanOrEqualFilter CreateFilter(ConnectorAttribute attribute) { + protected override GreaterThanOrEqualFilter CreateFilter(ConnectorAttribute attribute) + { return new GreaterThanOrEqualFilter(attribute); } } - private class LessThanFilterHandler : AttributeFilterHandler { - public LessThanFilterHandler() - : base("LessThanFilter") { + private class LessThanFilterHandler : AttributeFilterHandler + { + public LessThanFilterHandler() + : base("LessThanFilter") + { } - protected override LessThanFilter CreateFilter(ConnectorAttribute attribute) { + protected override LessThanFilter CreateFilter(ConnectorAttribute attribute) + { return new LessThanFilter(attribute); } } - private class LessThanOrEqualFilterHandler : AttributeFilterHandler { - public LessThanOrEqualFilterHandler() - : base("LessThanOrEqualFilter") { + private class LessThanOrEqualFilterHandler : AttributeFilterHandler + { + public LessThanOrEqualFilterHandler() + : base("LessThanOrEqualFilter") + { } - protected override LessThanOrEqualFilter CreateFilter(ConnectorAttribute attribute) { + protected override LessThanOrEqualFilter CreateFilter(ConnectorAttribute attribute) + { return new LessThanOrEqualFilter(attribute); } } private class NotFilterHandler - : AbstractObjectSerializationHandler { - - public NotFilterHandler() - : base(typeof(NotFilter),"NotFilter") { - } - - - public override sealed Object Deserialize(ObjectDecoder decoder) { + : AbstractObjectSerializationHandler + { + + public NotFilterHandler() + : base(typeof(NotFilter), "NotFilter") + { + } + + + public override sealed Object Deserialize(ObjectDecoder decoder) + { Filter filter = (Filter)decoder.ReadObjectContents(0); return new NotFilter(filter); } - + public override sealed void Serialize(Object obj, ObjectEncoder encoder) { NotFilter val = (NotFilter)obj; encoder.WriteObjectContents(val.Filter); } - + } - private class OrFilterHandler : CompositeFilterHandler { - public OrFilterHandler() - : base("OrFilter") { + private class OrFilterHandler : CompositeFilterHandler + { + public OrFilterHandler() + : base("OrFilter") + { } - protected override OrFilter CreateFilter(Filter left, Filter right) { - return new OrFilter(left,right); + protected override OrFilter CreateFilter(Filter left, Filter right) + { + return new OrFilter(left, right); } } - private class StartsWithFilterHandler : AttributeFilterHandler { - public StartsWithFilterHandler() - : base("StartsWithFilter") { + private class StartsWithFilterHandler : AttributeFilterHandler + { + public StartsWithFilterHandler() + : base("StartsWithFilter") + { } - protected override StartsWithFilter CreateFilter(ConnectorAttribute attribute) { + protected override StartsWithFilter CreateFilter(ConnectorAttribute attribute) + { return new StartsWithFilter(attribute); } } - - private class ContainsAllValuesFilterHandler : AttributeFilterHandler { - public ContainsAllValuesFilterHandler() - : base("ContainsAllValuesFilter") { + + private class ContainsAllValuesFilterHandler : AttributeFilterHandler + { + public ContainsAllValuesFilterHandler() + : base("ContainsAllValuesFilter") + { } - protected override ContainsAllValuesFilter CreateFilter(ConnectorAttribute attribute) { + protected override ContainsAllValuesFilter CreateFilter(ConnectorAttribute attribute) + { return new ContainsAllValuesFilter(attribute); } } - } #endregion #region MessageHandlers - internal static class MessageHandlers { + internal static class MessageHandlers + { public static readonly IList HANDLERS = new List(); - static MessageHandlers() { + static MessageHandlers() + { HANDLERS.Add(new HelloRequestHandler()); HANDLERS.Add(new HelloResponseHandler()); HANDLERS.Add(new OperationRequestHandler()); @@ -2293,128 +2646,143 @@ static MessageHandlers() { HANDLERS.Add(new OperationResponsePartHandler()); HANDLERS.Add(new OperationRequestMoreDataHandler()); HANDLERS.Add(new OperationRequestStopDataHandler()); - HANDLERS.Add(new OperationResponsePauseHandler()); + HANDLERS.Add(new OperationResponsePauseHandler()); HANDLERS.Add(new EchoMessageHandler()); } private class HelloRequestHandler - : AbstractObjectSerializationHandler { - - public HelloRequestHandler() - : base(typeof(HelloRequest),"HelloRequest") { - } - - - public override sealed Object Deserialize(ObjectDecoder decoder) { + : AbstractObjectSerializationHandler + { + + public HelloRequestHandler() + : base(typeof(HelloRequest), "HelloRequest") + { + } + + + public override sealed Object Deserialize(ObjectDecoder decoder) + { return new HelloRequest(); } - + public override sealed void Serialize(Object obj, ObjectEncoder encoder) { } - + } private class HelloResponseHandler - : AbstractObjectSerializationHandler { - - public HelloResponseHandler() - : base(typeof(HelloResponse),"HelloResponse") { - } - - - public override sealed Object Deserialize(ObjectDecoder decoder) { + : AbstractObjectSerializationHandler + { + + public HelloResponseHandler() + : base(typeof(HelloResponse), "HelloResponse") + { + } + + + public override sealed Object Deserialize(ObjectDecoder decoder) + { Exception exception = - (Exception)decoder.ReadObjectField("exception",null,null); + (Exception)decoder.ReadObjectField("exception", null, null); IList connectorInfosObj = - (IList)decoder.ReadObjectField("ConnectorInfos",typeof(IList),null); + (IList)decoder.ReadObjectField("ConnectorInfos", typeof(IList), null); IList connectorInfos = - CollectionUtil.NewList(connectorInfosObj); - return new HelloResponse(exception,connectorInfos); + CollectionUtil.NewList(connectorInfosObj); + return new HelloResponse(exception, connectorInfos); } - + public override sealed void Serialize(Object obj, ObjectEncoder encoder) { HelloResponse val = (HelloResponse)obj; encoder.WriteObjectField("exception", val.Exception, false); encoder.WriteObjectField("ConnectorInfos", val.ConnectorInfos, true); } - + } private class OperationRequestHandler - : AbstractObjectSerializationHandler { - - public OperationRequestHandler() - : base(typeof(OperationRequest),"OperationRequest") { - } - - - public override sealed Object Deserialize(ObjectDecoder decoder) { - ConnectorKey connectorKey = - (ConnectorKey)decoder.ReadObjectField("ConnectorKey",typeof(ConnectorKey),null); + : AbstractObjectSerializationHandler + { + + public OperationRequestHandler() + : base(typeof(OperationRequest), "OperationRequest") + { + } + + + public override sealed Object Deserialize(ObjectDecoder decoder) + { + ConnectorKey connectorKey = + (ConnectorKey)decoder.ReadObjectField("ConnectorKey", typeof(ConnectorKey), null); APIConfigurationImpl configuration = - (APIConfigurationImpl)decoder.ReadObjectField("APIConfiguration",typeof(APIConfigurationImpl),null); - Type operation = - decoder.ReadClassField("operation",null); + (APIConfigurationImpl)decoder.ReadObjectField("APIConfiguration", typeof(APIConfigurationImpl), null); + Type operation = + decoder.ReadClassField("operation", null); string operationMethodName = - decoder.ReadStringField("operationMethodName",null); + decoder.ReadStringField("operationMethodName", null); IList arguments = (IList) - decoder.ReadObjectField("Arguments",typeof(IList),null); + decoder.ReadObjectField("Arguments", typeof(IList), null); return new OperationRequest(connectorKey, configuration, SafeType.ForRawType(operation), operationMethodName, arguments); } - + public override sealed void Serialize(Object obj, ObjectEncoder encoder) { - OperationRequest val = + OperationRequest val = (OperationRequest)obj; - encoder.WriteClassField("operation", + encoder.WriteClassField("operation", val.Operation.RawType); encoder.WriteStringField("operationMethodName", val.OperationMethodName); - encoder.WriteObjectField("ConnectorKey", - val.ConnectorKey,true); - encoder.WriteObjectField("APIConfiguration", - val.Configuration,true); - encoder.WriteObjectField("Arguments", - val.Arguments,true); + encoder.WriteObjectField("ConnectorKey", + val.ConnectorKey, true); + encoder.WriteObjectField("APIConfiguration", + val.Configuration, true); + encoder.WriteObjectField("Arguments", + val.Arguments, true); } - + } private class OperationResponseEndHandler - : AbstractObjectSerializationHandler { - - public OperationResponseEndHandler() - : base(typeof(OperationResponseEnd),"OperationResponseEnd") { - } - - - public override sealed Object Deserialize(ObjectDecoder decoder) { + : AbstractObjectSerializationHandler + { + + public OperationResponseEndHandler() + : base(typeof(OperationResponseEnd), "OperationResponseEnd") + { + } + + + public override sealed Object Deserialize(ObjectDecoder decoder) + { return new OperationResponseEnd(); } - + public override sealed void Serialize(Object obj, ObjectEncoder encoder) { } } private class OperationResponsePartHandler - : AbstractObjectSerializationHandler { - - public OperationResponsePartHandler() - : base(typeof(OperationResponsePart),"OperationResponsePart") { - } - - - public override sealed Object Deserialize(ObjectDecoder decoder) { + : AbstractObjectSerializationHandler + { + + public OperationResponsePartHandler() + : base(typeof(OperationResponsePart), "OperationResponsePart") + { + } + + + public override sealed Object Deserialize(ObjectDecoder decoder) + { Exception exception = - (Exception)decoder.ReadObjectField("exception",null,null); + (Exception)decoder.ReadObjectField("exception", null, null); Object result = - decoder.ReadObjectField("result",null,null); - - return new OperationResponsePart(exception,result); + decoder.ReadObjectField("result", null, null); + + return new OperationResponsePart(exception, result); } - + public override sealed void Serialize(Object obj, ObjectEncoder encoder) { OperationResponsePart val = (OperationResponsePart)obj; @@ -2423,75 +2791,85 @@ public override sealed void Serialize(Object obj, ObjectEncoder encoder) } } private class OperationRequestMoreDataHandler - : AbstractObjectSerializationHandler { - - public OperationRequestMoreDataHandler() - : base(typeof(OperationRequestMoreData),"OperationRequestMoreData") { - } - - - public override sealed Object Deserialize(ObjectDecoder decoder) { + : AbstractObjectSerializationHandler + { + + public OperationRequestMoreDataHandler() + : base(typeof(OperationRequestMoreData), "OperationRequestMoreData") + { + } + + + public override sealed Object Deserialize(ObjectDecoder decoder) + { return new OperationRequestMoreData(); } - + public override sealed void Serialize(Object obj, ObjectEncoder encoder) { } } private class OperationRequestStopDataHandler - : AbstractObjectSerializationHandler { - - public OperationRequestStopDataHandler() - : base(typeof(OperationRequestStopData),"OperationRequestStopData") { - } - - - public override sealed Object Deserialize(ObjectDecoder decoder) { + : AbstractObjectSerializationHandler + { + + public OperationRequestStopDataHandler() + : base(typeof(OperationRequestStopData), "OperationRequestStopData") + { + } + + + public override sealed Object Deserialize(ObjectDecoder decoder) + { return new OperationRequestStopData(); } - + public override sealed void Serialize(Object obj, ObjectEncoder encoder) { } } private class OperationResponsePauseHandler - : AbstractObjectSerializationHandler { - - public OperationResponsePauseHandler() - : base(typeof(OperationResponsePause),"OperationResponsePause") { - } - - - public override sealed Object Deserialize(ObjectDecoder decoder) { + : AbstractObjectSerializationHandler + { + + public OperationResponsePauseHandler() + : base(typeof(OperationResponsePause), "OperationResponsePause") + { + } + + + public override sealed Object Deserialize(ObjectDecoder decoder) + { return new OperationResponsePause(); } - + public override sealed void Serialize(Object obj, ObjectEncoder encoder) { } } private class EchoMessageHandler - : AbstractObjectSerializationHandler { - - public EchoMessageHandler() - : base(typeof(EchoMessage),"EchoMessage") { - } - - - public override sealed Object Deserialize(ObjectDecoder decoder) { - return new EchoMessage(decoder.ReadObjectField("value",null,null), - (String)decoder.ReadObjectField("objectXml",typeof(string),null)); - } - + : AbstractObjectSerializationHandler + { + + public EchoMessageHandler() + : base(typeof(EchoMessage), "EchoMessage") + { + } + + + public override sealed Object Deserialize(ObjectDecoder decoder) + { + return new EchoMessage(decoder.ReadObjectField("value", null, null), + (String)decoder.ReadObjectField("objectXml", typeof(string), null)); + } + public override sealed void Serialize(Object obj, ObjectEncoder encoder) { EchoMessage val = (EchoMessage)obj; - encoder.WriteObjectField("value",val.Object,false); - encoder.WriteObjectField("objectXml",val.ObjectXml,true); + encoder.WriteObjectField("value", val.Object, false); + encoder.WriteObjectField("objectXml", val.ObjectXml, true); } } - } #endregion - -} +} \ No newline at end of file diff --git a/FrameworkInternal/SerializerBinary.cs b/FrameworkInternal/SerializerBinary.cs index c685a403..20e19b2c 100644 --- a/FrameworkInternal/SerializerBinary.cs +++ b/FrameworkInternal/SerializerBinary.cs @@ -30,142 +30,158 @@ using System.Text; namespace Org.IdentityConnectors.Framework.Impl.Serializer.Binary { + internal class InternalEncoder + { + /// + /// Mapping from type name to the ID we serialize so we only have to + /// + private IDictionary _constantPool = + new Dictionary(); - internal class InternalEncoder { - - - /** - * Mapping from type name to the ID we serialize so we only have to - */ - private IDictionary _constantPool = - new Dictionary(); - private IList _constantBuffer = new List(); private IList _outputBufferStack = new List(); internal Stream _rootOutput; private bool _firstObject = true; - - public InternalEncoder(Stream output) { + + public InternalEncoder(Stream output) + { _rootOutput = output; } - - public void WriteObject(ObjectEncoder encoder, Object obj) { - - if (_firstObject) { + + public void WriteObject(ObjectEncoder encoder, Object obj) + { + + if (_firstObject) + { WriteInt(BinaryObjectEncoder.OBJECT_MAGIC); WriteInt(BinaryObjectEncoder.ENCODING_VERSION); _firstObject = false; } - + MemoryStream objectBuffer = new MemoryStream(); _outputBufferStack.Add(objectBuffer); - - if ( obj == null ) { + + if (obj == null) + { WriteByte(BinaryObjectEncoder.OBJECT_TYPE_NULL); } - else { + else + { Type clazz = obj.GetType(); WriteClass(clazz); ObjectSerializationHandler handler = ObjectSerializerRegistry.GetHandlerByObjectType(clazz); - if ( handler == null ) { + if (handler == null) + { //we may have special handlers for certain types of arrays //if handler is null, treat like any other array - if ( obj is Array ) { + if (obj is Array) + { Array array = (Array)obj; int length = array.Length; - for ( int i = 0; i < length; i++ ) { + for (int i = 0; i < length; i++) + { Object val = array.GetValue(i); StartAnonymousField(); - WriteObject(encoder,val); + WriteObject(encoder, val); EndField(); } } - else { - throw new ConnectorException("No serializer for class: "+clazz); + else + { + throw new ConnectorException("No serializer for class: " + clazz); } } - else { + else + { handler.Serialize(obj, encoder); } } - + //write end-object into the current obj buffer WriteByte(BinaryObjectEncoder.FIELD_TYPE_END_OBJECT); - + //pop the stack - _outputBufferStack.RemoveAt(_outputBufferStack.Count-1); - + _outputBufferStack.RemoveAt(_outputBufferStack.Count - 1); + //it's a top-level object, flush the constant pool - if (_outputBufferStack.Count == 0) { + if (_outputBufferStack.Count == 0) + { WriteInt(_constantBuffer.Count); - foreach (String constant in _constantBuffer) { - WriteString(constant,false); + foreach (String constant in _constantBuffer) + { + WriteString(constant, false); WriteInt(_constantPool[constant]); - } + } _constantBuffer.Clear(); } - + //now write the actual object objectBuffer.Close(); - byte [] bytes = objectBuffer.ToArray(); + byte[] bytes = objectBuffer.ToArray(); WriteBytes(bytes); } - - public void WriteClass(Type clazz) + + public void WriteClass(Type clazz) { ObjectSerializationHandler handler = ObjectSerializerRegistry.GetHandlerByObjectType(clazz); ObjectTypeMapper mapper = ObjectSerializerRegistry.GetMapperByObjectType(clazz); - if ( handler == null && clazz.IsArray ) { + if (handler == null && clazz.IsArray) + { //we may have special handlers for certain types of arrays //if handler is null, treat like any other array WriteByte(BinaryObjectEncoder.OBJECT_TYPE_ARRAY); WriteClass(clazz.GetElementType()); } - else if ( mapper == null ) { - throw new ConnectorException("No serializer for class: "+clazz); + else if (mapper == null) + { + throw new ConnectorException("No serializer for class: " + clazz); } - else { + else + { String typeName = mapper.HandledSerialType; WriteByte(BinaryObjectEncoder.OBJECT_TYPE_CLASS); - WriteString(typeName,true); + WriteString(typeName, true); } } - public void StartAnonymousField() { + public void StartAnonymousField() + { WriteByte(BinaryObjectEncoder.FIELD_TYPE_ANONYMOUS_FIELD); MemoryStream buf = new MemoryStream(); _outputBufferStack.Add(buf); } - - public void StartField(String name) { + + public void StartField(String name) + { WriteByte(BinaryObjectEncoder.FIELD_TYPE_NAMED_FIELD); - WriteString(name,true); + WriteString(name, true); MemoryStream buf = new MemoryStream(); _outputBufferStack.Add(buf); } - - public void EndField() { - MemoryStream buf = _outputBufferStack[_outputBufferStack.Count-1]; - _outputBufferStack.RemoveAt(_outputBufferStack.Count-1); + + public void EndField() + { + MemoryStream buf = _outputBufferStack[_outputBufferStack.Count - 1]; + _outputBufferStack.RemoveAt(_outputBufferStack.Count - 1); buf.Close(); - byte [] bytes = buf.ToArray(); + byte[] bytes = buf.ToArray(); WriteByteArray(bytes); } - - public void WriteInt(int v) + + public void WriteInt(int v) { Stream output = GetCurrentOutput(); output.WriteByte((byte)(0xff & (v >> 24))); output.WriteByte((byte)(0xff & (v >> 16))); - output.WriteByte((byte)(0xff & (v >> 8))); - output.WriteByte((byte)(0xff & v)); + output.WriteByte((byte)(0xff & (v >> 8))); + output.WriteByte((byte)(0xff & v)); } - - public void WriteLong(long v) + + public void WriteLong(long v) { Stream output = GetCurrentOutput(); output.WriteByte((byte)(0xff & (v >> 56))); @@ -174,602 +190,706 @@ public void WriteLong(long v) output.WriteByte((byte)(0xff & (v >> 32))); output.WriteByte((byte)(0xff & (v >> 24))); output.WriteByte((byte)(0xff & (v >> 16))); - output.WriteByte((byte)(0xff & (v >> 8))); + output.WriteByte((byte)(0xff & (v >> 8))); output.WriteByte((byte)(0xff & v)); } - - public void WriteDouble(double l) + + public void WriteDouble(double l) { long val = BitConverter.DoubleToInt64Bits(l); WriteLong(val); } - - public void WriteByteArray(byte[] v) + + public void WriteByteArray(byte[] v) { WriteInt(v.Length); WriteBytes(v); } - - public void WriteByte(byte b) + + public void WriteByte(byte b) { GetCurrentOutput().WriteByte(b); } - + public void WriteBoolean(bool b) { WriteByte(b ? (byte)1 : (byte)0); } - - public void WriteString(String str, bool intern) + + public void WriteString(String str, bool intern) { - if ( intern ) { + if (intern) + { int code = InternIdentifier(str); WriteInt(code); return; } - byte [] bytes = Encoding.UTF8.GetBytes(str); + byte[] bytes = Encoding.UTF8.GetBytes(str); WriteByteArray(bytes); } - - private int InternIdentifier(String name) { - int code = CollectionUtil.GetValue(_constantPool,name,-1); - if ( code == -1 ) { + + private int InternIdentifier(String name) + { + int code = CollectionUtil.GetValue(_constantPool, name, -1); + if (code == -1) + { code = _constantPool.Count; _constantPool[name] = code; _constantBuffer.Add(name); } return code; } - - private void WriteBytes(byte [] v) + + private void WriteBytes(byte[] v) { //only write if length > 0 - C# seems to have a problem with //zero-length byte arrays - if ( v.Length > 0 ) { - GetCurrentOutput().Write(v,0,v.Length); + if (v.Length > 0) + { + GetCurrentOutput().Write(v, 0, v.Length); } } - - private Stream GetCurrentOutput() { - if (_outputBufferStack.Count == 0) { + + private Stream GetCurrentOutput() + { + if (_outputBufferStack.Count == 0) + { return _rootOutput; } - else { - MemoryStream buf = _outputBufferStack[_outputBufferStack.Count-1]; + else + { + MemoryStream buf = _outputBufferStack[_outputBufferStack.Count - 1]; return buf; } } - } - - - - internal class BinaryObjectEncoder : ObjectEncoder,BinaryObjectSerializer { - - - + + internal class BinaryObjectEncoder : ObjectEncoder, BinaryObjectSerializer + { public const int ENCODING_VERSION = 1; - + public const int OBJECT_MAGIC = 0xFAFB; - - public const byte OBJECT_TYPE_NULL = 60; - public const byte OBJECT_TYPE_CLASS = 61; - public const byte OBJECT_TYPE_ARRAY = 62; - - public const byte FIELD_TYPE_ANONYMOUS_FIELD = 70; - public const byte FIELD_TYPE_NAMED_FIELD = 71; - public const byte FIELD_TYPE_END_OBJECT = 72; - - + + public const byte OBJECT_TYPE_NULL = 60; + public const byte OBJECT_TYPE_CLASS = 61; + public const byte OBJECT_TYPE_ARRAY = 62; + + public const byte FIELD_TYPE_ANONYMOUS_FIELD = 70; + public const byte FIELD_TYPE_NAMED_FIELD = 71; + public const byte FIELD_TYPE_END_OBJECT = 72; + + private InternalEncoder _internalEncoder; - - - - public BinaryObjectEncoder(Stream output) { - _internalEncoder = new InternalEncoder(new BufferedStream(output,4096)); + public BinaryObjectEncoder(Stream output) + { + _internalEncoder = new InternalEncoder(new BufferedStream(output, 4096)); } - - public void Flush() { + + public void Flush() + { _internalEncoder._rootOutput.Flush(); } - - public void Close() { + + public void Close() + { _internalEncoder._rootOutput.Close(); } - - public void WriteObject(Object o) { - _internalEncoder.WriteObject(this,o); + + public void WriteObject(Object o) + { + _internalEncoder.WriteObject(this, o); } - - public void WriteBooleanContents(bool v) { + + public void WriteBooleanContents(bool v) + { _internalEncoder.StartAnonymousField(); _internalEncoder.WriteBoolean(v); _internalEncoder.EndField(); } - - public void WriteBooleanField(String fieldName, bool v) { + + public void WriteBooleanField(String fieldName, bool v) + { _internalEncoder.StartField(fieldName); _internalEncoder.WriteBoolean(v); _internalEncoder.EndField(); } - - public void WriteByteArrayContents(byte[] v) { + + public void WriteByteArrayContents(byte[] v) + { _internalEncoder.StartAnonymousField(); _internalEncoder.WriteByteArray(v); _internalEncoder.EndField(); } - - public void WriteClassContents(Type v) { + + public void WriteClassContents(Type v) + { _internalEncoder.StartAnonymousField(); _internalEncoder.WriteClass(v); _internalEncoder.EndField(); } - - public void WriteClassField(string fieldName, Type v) { - if ( v != null ) { + + public void WriteClassField(string fieldName, Type v) + { + if (v != null) + { _internalEncoder.StartField(fieldName); _internalEncoder.WriteClass(v); - _internalEncoder.EndField(); + _internalEncoder.EndField(); } } - - public void WriteDoubleContents(double v) { + + public void WriteDoubleContents(double v) + { _internalEncoder.StartAnonymousField(); _internalEncoder.WriteDouble(v); _internalEncoder.EndField(); } - - public void WriteDoubleField(String fieldName, double v) { + + public void WriteDoubleField(String fieldName, double v) + { _internalEncoder.StartField(fieldName); _internalEncoder.WriteDouble(v); _internalEncoder.EndField(); } - - public void WriteFloatContents(float v) { + + public void WriteFloatContents(float v) + { _internalEncoder.StartAnonymousField(); //write as double since C# only knows how to deal with that _internalEncoder.WriteDouble((double)v); _internalEncoder.EndField(); } - - public void WriteFloatField(String fieldName, float v) { + + public void WriteFloatField(String fieldName, float v) + { _internalEncoder.StartField(fieldName); //write as double since C# only knows how to deal with that _internalEncoder.WriteDouble((double)v); _internalEncoder.EndField(); } - - public void WriteIntContents(int v) { + + public void WriteIntContents(int v) + { _internalEncoder.StartAnonymousField(); _internalEncoder.WriteInt(v); _internalEncoder.EndField(); } - - public void WriteIntField(String fieldName, int v) { + + public void WriteIntField(String fieldName, int v) + { _internalEncoder.StartField(fieldName); _internalEncoder.WriteInt(v); _internalEncoder.EndField(); } - - public void WriteLongContents(long v) { + + public void WriteLongContents(long v) + { _internalEncoder.StartAnonymousField(); _internalEncoder.WriteLong(v); _internalEncoder.EndField(); } - - public void WriteLongField(String fieldName, long v) { + + public void WriteLongField(String fieldName, long v) + { _internalEncoder.StartField(fieldName); _internalEncoder.WriteLong(v); _internalEncoder.EndField(); } - - public void WriteObjectContents(Object obj) { + + public void WriteObjectContents(Object obj) + { _internalEncoder.StartAnonymousField(); - _internalEncoder.WriteObject(this,obj); + _internalEncoder.WriteObject(this, obj); _internalEncoder.EndField(); } - public void WriteObjectField(String fieldName, Object obj, bool inline) { + public void WriteObjectField(String fieldName, Object obj, bool inline) + { _internalEncoder.StartField(fieldName); - _internalEncoder.WriteObject(this,obj); + _internalEncoder.WriteObject(this, obj); _internalEncoder.EndField(); } - - public void WriteStringContents(String str) { + + public void WriteStringContents(String str) + { _internalEncoder.StartAnonymousField(); - _internalEncoder.WriteString(str,false); + _internalEncoder.WriteString(str, false); _internalEncoder.EndField(); } - - public void WriteStringField(String fieldName, String val) { - if ( val != null ) { + + public void WriteStringField(String fieldName, String val) + { + if (val != null) + { _internalEncoder.StartField(fieldName); - _internalEncoder.WriteString(val,false); - _internalEncoder.EndField(); + _internalEncoder.WriteString(val, false); + _internalEncoder.EndField(); } } } - - internal class ReadState { - public IDictionary objectFields = new Dictionary(); + + internal class ReadState + { + public IDictionary objectFields = new Dictionary(); public IList anonymousFields = new List(); public Stream currentInput; - public ReadState() { + public ReadState() + { } - public bool StartField(String name) { + public bool StartField(String name) + { currentInput = null; - byte [] content = CollectionUtil.GetValue(objectFields,name,null); - if ( content == null ) { + byte[] content = CollectionUtil.GetValue(objectFields, name, null); + if (content == null) + { return false; } - else { + else + { currentInput = new MemoryStream(content); return true; } } - public void StartAnonymousField(int index) { - if ( index >= anonymousFields.Count ) { + public void StartAnonymousField(int index) + { + if (index >= anonymousFields.Count) + { throw new ConnectorException("Anonymous content not found"); } currentInput = new MemoryStream(anonymousFields[index]); } } - - internal class InternalDecoder { - private readonly byte [] _int_buf = new byte[4]; - private readonly byte [] _long_buf = new byte[8]; - private readonly byte [] _byte_buf = new byte[1]; - + + internal class InternalDecoder + { + private readonly byte[] _int_buf = new byte[4]; + private readonly byte[] _long_buf = new byte[8]; + private readonly byte[] _byte_buf = new byte[1]; + private bool _firstObject = true; - + private readonly IDictionary _constantPool = new Dictionary(); private readonly IList _readStateStack = new List(); internal readonly Stream _rootInput; - - public InternalDecoder(Stream input) { + + public InternalDecoder(Stream input) + { _rootInput = input; } - - public Object ReadObject(ObjectDecoder decoder) { - - if (_firstObject) { + + public Object ReadObject(ObjectDecoder decoder) + { + + if (_firstObject) + { int magic = ReadInt(); - if ( magic != BinaryObjectEncoder.OBJECT_MAGIC) { - throw new ConnectorException("Bad magic number: "+magic); + if (magic != BinaryObjectEncoder.OBJECT_MAGIC) + { + throw new ConnectorException("Bad magic number: " + magic); } int version = ReadInt(); - if ( version != BinaryObjectEncoder.ENCODING_VERSION ) { - throw new ConnectorException("Unexpected version: "+version); + if (version != BinaryObjectEncoder.ENCODING_VERSION) + { + throw new ConnectorException("Unexpected version: " + version); } _firstObject = false; } - + //if it's a top-level object, it's proceeded by a constant pool if (_readStateStack.Count == 0) { int size = ReadInt(); - for ( int i = 0; i < size; i++ ) { + for (int i = 0; i < size; i++) + { String constant = ReadString(false); int code = ReadInt(); _constantPool[code] = constant; } } - + Type clazz = ReadClass(); - + ReadState state = new ReadState(); - while(true) { + while (true) + { byte type = ReadByte(); - if ( type == BinaryObjectEncoder.FIELD_TYPE_END_OBJECT ) { + if (type == BinaryObjectEncoder.FIELD_TYPE_END_OBJECT) + { break; } - else if ( type == BinaryObjectEncoder.FIELD_TYPE_ANONYMOUS_FIELD ) { - byte [] bytes = ReadByteArray(); + else if (type == BinaryObjectEncoder.FIELD_TYPE_ANONYMOUS_FIELD) + { + byte[] bytes = ReadByteArray(); state.anonymousFields.Add(bytes); } - else if ( type == BinaryObjectEncoder.FIELD_TYPE_NAMED_FIELD ) { + else if (type == BinaryObjectEncoder.FIELD_TYPE_NAMED_FIELD) + { String fieldName = ReadString(true); - byte [] bytes = ReadByteArray(); + byte[] bytes = ReadByteArray(); state.objectFields[fieldName] = bytes; } - else { - throw new ConnectorException("Unknown type: "+type); + else + { + throw new ConnectorException("Unknown type: " + type); } } _readStateStack.Add(state); - + Object rv; - if ( clazz == null ) { + if (clazz == null) + { rv = null; } - else { + else + { ObjectSerializationHandler handler = ObjectSerializerRegistry.GetHandlerByObjectType(clazz); - if ( handler == null ) { + if (handler == null) + { //we may have special handlers for certain types of arrays //if handler is null, treat like any other array - if ( clazz.IsArray ) { + if (clazz.IsArray) + { int length = GetNumAnonymousFields(); Array array = Array.CreateInstance(clazz.GetElementType(), length); - for ( int i = 0; i < length; i++) { + for (int i = 0; i < length; i++) + { StartAnonymousField(i); Object element = ReadObject(decoder); - array.SetValue( element, i ); + array.SetValue(element, i); } rv = array; } - else { - throw new ConnectorException("No deserializer for type: "+clazz); + else + { + throw new ConnectorException("No deserializer for type: " + clazz); } } - else { + else + { rv = handler.Deserialize(decoder); } } - _readStateStack.RemoveAt(_readStateStack.Count-1); + _readStateStack.RemoveAt(_readStateStack.Count - 1); return rv; } - - public Type ReadClass() { + + public Type ReadClass() + { int type = ReadByte(); - if ( type == BinaryObjectEncoder.OBJECT_TYPE_NULL ) { + if (type == BinaryObjectEncoder.OBJECT_TYPE_NULL) + { return null; } - else if ( type == BinaryObjectEncoder.OBJECT_TYPE_ARRAY ) { + else if (type == BinaryObjectEncoder.OBJECT_TYPE_ARRAY) + { Type componentClass = ReadClass(); return componentClass.MakeArrayType(); } - else if ( type == BinaryObjectEncoder.OBJECT_TYPE_CLASS ) { + else if (type == BinaryObjectEncoder.OBJECT_TYPE_CLASS) + { String typeName = ReadString(true); ObjectTypeMapper mapper = ObjectSerializerRegistry.GetMapperBySerialType(typeName); - if ( mapper == null ) { - throw new ConnectorException("No deserializer for type: "+typeName); + if (mapper == null) + { + throw new ConnectorException("No deserializer for type: " + typeName); } return mapper.HandledObjectType; } - else { - throw new ConnectorException("Bad type value: "+type); + else + { + throw new ConnectorException("Bad type value: " + type); } } - - public int GetNumAnonymousFields() { - ReadState readState = _readStateStack[_readStateStack.Count-1]; + + public int GetNumAnonymousFields() + { + ReadState readState = _readStateStack[_readStateStack.Count - 1]; return readState.anonymousFields.Count; } - - public void StartAnonymousField(int index) { - ReadState readState = _readStateStack[_readStateStack.Count-1]; + + public void StartAnonymousField(int index) + { + ReadState readState = _readStateStack[_readStateStack.Count - 1]; readState.StartAnonymousField(index); } - - public bool StartField(String name) { - ReadState readState = _readStateStack[_readStateStack.Count-1]; + + public bool StartField(String name) + { + ReadState readState = _readStateStack[_readStateStack.Count - 1]; return readState.StartField(name); } - - public int ReadInt() { + + public int ReadInt() + { ReadByteArrayFully(_int_buf); return (((_int_buf[0] & 0xff) << 24) | ((_int_buf[1] & 0xff) << 16) | ((_int_buf[2] & 0xff) << 8) | (_int_buf[3] & 0xff)); } - - public long ReadLong() { - ReadByteArrayFully(_long_buf); + + public long ReadLong() + { + ReadByteArrayFully(_long_buf); return (((long)(_long_buf[0] & 0xff) << 56) | ((long)(_long_buf[1] & 0xff) << 48) | ((long)(_long_buf[2] & 0xff) << 40) | ((long)(_long_buf[3] & 0xff) << 32) | ((long)(_long_buf[4] & 0xff) << 24) | ((long)(_long_buf[5] & 0xff) << 16) | - ((long)(_long_buf[6] & 0xff) << 8) | + ((long)(_long_buf[6] & 0xff) << 8) | ((long)(_long_buf[7] & 0xff))); } - - public double ReadDouble() { + + public double ReadDouble() + { long v = ReadLong(); return BitConverter.Int64BitsToDouble(v); } - - public byte [] ReadByteArray() { + + public byte[] ReadByteArray() + { int len = ReadInt(); - byte [] bytes = new byte[len]; + byte[] bytes = new byte[len]; ReadByteArrayFully(bytes); - return bytes; + return bytes; } - + public byte ReadByte() { ReadByteArrayFully(_byte_buf); return _byte_buf[0]; } - - public bool ReadBoolean() { + + public bool ReadBoolean() + { byte b = ReadByte(); return b != 0; } - - public String ReadString(bool interned) { - if ( interned ) { + + public String ReadString(bool interned) + { + if (interned) + { int code = ReadInt(); - String name = CollectionUtil.GetValue(_constantPool,code,null); - if ( name == null ) { - throw new ConnectorException("Undeclared code: "+code); + String name = CollectionUtil.GetValue(_constantPool, code, null); + if (name == null) + { + throw new ConnectorException("Undeclared code: " + code); } return name; } - byte [] bytes = ReadByteArray(); + byte[] bytes = ReadByteArray(); return Encoding.UTF8.GetString(bytes); } - - private Stream GetCurrentInput() { - if (_readStateStack.Count > 0) { - ReadState state = _readStateStack[_readStateStack.Count-1]; + + private Stream GetCurrentInput() + { + if (_readStateStack.Count > 0) + { + ReadState state = _readStateStack[_readStateStack.Count - 1]; return state.currentInput; } - else { + else + { return _rootInput; } } - - private void ReadByteArrayFully(byte [] bytes) + + private void ReadByteArrayFully(byte[] bytes) { int pos = 0; - while ( pos < bytes.Length ) { - int count = GetCurrentInput().Read(bytes,pos,bytes.Length-pos); - if ( count <= 0 ) { + while (pos < bytes.Length) + { + int count = GetCurrentInput().Read(bytes, pos, bytes.Length - pos); + if (count <= 0) + { throw new EndOfStreamException(); } - pos+=count; - } + pos += count; + } } } - - internal class BinaryObjectDecoder : ObjectDecoder,BinaryObjectDeserializer { + + internal class BinaryObjectDecoder : ObjectDecoder, BinaryObjectDeserializer + { private InternalDecoder _internalDecoder; - - public BinaryObjectDecoder(Stream inp) { - _internalDecoder = new InternalDecoder(new BufferedStream(inp,4096)); + + public BinaryObjectDecoder(Stream inp) + { + _internalDecoder = new InternalDecoder(new BufferedStream(inp, 4096)); } - - public void Close() { + + public void Close() + { _internalDecoder._rootInput.Close(); } - - public Object ReadObject() { + + public Object ReadObject() + { return _internalDecoder.ReadObject(this); } - - public bool ReadBooleanContents() { + + public bool ReadBooleanContents() + { _internalDecoder.StartAnonymousField(0); return _internalDecoder.ReadBoolean(); } - - public bool ReadBooleanField(String fieldName, bool dflt) { - if (_internalDecoder.StartField(fieldName)) { + + public bool ReadBooleanField(String fieldName, bool dflt) + { + if (_internalDecoder.StartField(fieldName)) + { return _internalDecoder.ReadBoolean(); } - else { + else + { return dflt; } } - - public byte[] ReadByteArrayContents() { + + public byte[] ReadByteArrayContents() + { _internalDecoder.StartAnonymousField(0); return _internalDecoder.ReadByteArray(); } - - public Type ReadClassContents() { + + public Type ReadClassContents() + { _internalDecoder.StartAnonymousField(0); return _internalDecoder.ReadClass(); } - - public Type ReadClassField(string fieldName, Type dflt) { - if (_internalDecoder.StartField(fieldName)) { + + public Type ReadClassField(string fieldName, Type dflt) + { + if (_internalDecoder.StartField(fieldName)) + { return _internalDecoder.ReadClass(); } - else { + else + { return dflt; } } - - public double ReadDoubleContents() { + + public double ReadDoubleContents() + { _internalDecoder.StartAnonymousField(0); return _internalDecoder.ReadDouble(); } - - public double ReadDoubleField(String fieldName, double dflt) { - if (_internalDecoder.StartField(fieldName)) { + + public double ReadDoubleField(String fieldName, double dflt) + { + if (_internalDecoder.StartField(fieldName)) + { return ReadDoubleContents(); } - else { + else + { return dflt; } } - - public float ReadFloatContents() { + + public float ReadFloatContents() + { _internalDecoder.StartAnonymousField(0); //read as double since C# only knows how to deal with that return (float)_internalDecoder.ReadDouble(); } - - public float ReadFloatField(String fieldName, float dflt) { - if (_internalDecoder.StartField(fieldName)) { + + public float ReadFloatField(String fieldName, float dflt) + { + if (_internalDecoder.StartField(fieldName)) + { //read as double since C# only knows how to deal with that return (float)_internalDecoder.ReadDouble(); } - else { + else + { return dflt; } } - - public int ReadIntContents() { + + public int ReadIntContents() + { _internalDecoder.StartAnonymousField(0); return _internalDecoder.ReadInt(); } - - public int ReadIntField(String fieldName, int dflt) { - if (_internalDecoder.StartField(fieldName)) { + + public int ReadIntField(String fieldName, int dflt) + { + if (_internalDecoder.StartField(fieldName)) + { return _internalDecoder.ReadInt(); } - else { + else + { return dflt; } } - - public long ReadLongContents() { + + public long ReadLongContents() + { _internalDecoder.StartAnonymousField(0); return _internalDecoder.ReadLong(); } - - public long ReadLongField(String fieldName, long dflt) { - if (_internalDecoder.StartField(fieldName)) { + + public long ReadLongField(String fieldName, long dflt) + { + if (_internalDecoder.StartField(fieldName)) + { return _internalDecoder.ReadLong(); } - else { + else + { return dflt; } } - + public int GetNumSubObjects() { return _internalDecoder.GetNumAnonymousFields(); } - - public Object ReadObjectContents(int index) { + + public Object ReadObjectContents(int index) + { _internalDecoder.StartAnonymousField(index); return _internalDecoder.ReadObject(this); } - - public Object ReadObjectField(String fieldName, Type expected, Object dflt) { - if (_internalDecoder.StartField(fieldName)) { + + public Object ReadObjectField(String fieldName, Type expected, Object dflt) + { + if (_internalDecoder.StartField(fieldName)) + { return _internalDecoder.ReadObject(this); } - else { + else + { return dflt; } } - - public String ReadStringContents() { + + public String ReadStringContents() + { _internalDecoder.StartAnonymousField(0); return _internalDecoder.ReadString(false); } - - public String ReadStringField(String fieldName, String dflt) { - if (_internalDecoder.StartField(fieldName)) { + + public String ReadStringField(String fieldName, String dflt) + { + if (_internalDecoder.StartField(fieldName)) + { return _internalDecoder.ReadString(false); } - else { + else + { return dflt; } } - } - - -} +} \ No newline at end of file diff --git a/FrameworkInternal/SerializerXml.cs b/FrameworkInternal/SerializerXml.cs index b8b851a9..59d52a98 100644 --- a/FrameworkInternal/SerializerXml.cs +++ b/FrameworkInternal/SerializerXml.cs @@ -32,524 +32,633 @@ using Org.IdentityConnectors.Framework.Common.Serializer; namespace Org.IdentityConnectors.Framework.Impl.Serializer.Xml { - public class XmlObjectEncoder : ObjectEncoder { - + public class XmlObjectEncoder : ObjectEncoder + { private StringBuilder _rootBuilder; private XmlWriter _writer; - - public XmlObjectEncoder(StringBuilder builder) { + + public XmlObjectEncoder(StringBuilder builder) + { Assertions.NullCheck(builder, "builder"); _rootBuilder = builder; } - - public String WriteObject(Object o) { + + public String WriteObject(Object o) + { XmlWriterSettings settings = new XmlWriterSettings(); settings.Indent = true; settings.OmitXmlDeclaration = true; _writer = XmlWriter.Create(_rootBuilder, settings); - String rv = WriteObjectInternal(o,false); + String rv = WriteObjectInternal(o, false); _writer.Close(); return rv; } - - public void WriteBooleanContents(bool v) { + + public void WriteBooleanContents(bool v) + { WriteStringContentsInternal(EncodeBoolean(v)); } - - public void WriteBooleanField(String fieldName, bool v) { - WriteAttributeInternal(fieldName,EncodeBoolean(v)); + + public void WriteBooleanField(String fieldName, bool v) + { + WriteAttributeInternal(fieldName, EncodeBoolean(v)); } - - public void WriteByteArrayContents(byte[] v) { + + public void WriteByteArrayContents(byte[] v) + { WriteStringContentsInternal(EncodeByteArray(v)); } - - public void WriteClassContents(Type v) { + + public void WriteClassContents(Type v) + { WriteStringContentsInternal(EncodeClass(v)); } - - public void WriteClassField(String name, Type v) { - if ( v != null ) { + + public void WriteClassField(String name, Type v) + { + if (v != null) + { WriteAttributeInternal(name, EncodeClass(v)); } } - - public void WriteDoubleContents(double v) { + + public void WriteDoubleContents(double v) + { WriteStringContentsInternal(EncodeDouble(v)); } - - public void WriteDoubleField(String fieldName, double v) { - WriteAttributeInternal(fieldName,EncodeDouble(v)); + + public void WriteDoubleField(String fieldName, double v) + { + WriteAttributeInternal(fieldName, EncodeDouble(v)); } - - public void WriteFloatContents(float v) { + + public void WriteFloatContents(float v) + { WriteStringContentsInternal(EncodeFloat(v)); } - - public void WriteFloatField(String fieldName, float v) { - WriteAttributeInternal(fieldName,EncodeFloat(v)); + + public void WriteFloatField(String fieldName, float v) + { + WriteAttributeInternal(fieldName, EncodeFloat(v)); } - - public void WriteIntContents(int v) { + + public void WriteIntContents(int v) + { WriteStringContentsInternal(EncodeInt(v)); } - - public void WriteIntField(String fieldName, int v) { - WriteAttributeInternal(fieldName,EncodeInt(v)); + + public void WriteIntField(String fieldName, int v) + { + WriteAttributeInternal(fieldName, EncodeInt(v)); } - - public void WriteLongContents(long v) { + + public void WriteLongContents(long v) + { WriteStringContentsInternal(EncodeLong(v)); } - - public void WriteLongField(String fieldName, long v) { - WriteAttributeInternal(fieldName,EncodeLong(v)); + + public void WriteLongField(String fieldName, long v) + { + WriteAttributeInternal(fieldName, EncodeLong(v)); } - - public void WriteObjectContents(Object o) { - WriteObjectInternal(o,false); + + public void WriteObjectContents(Object o) + { + WriteObjectInternal(o, false); } - - public void WriteObjectField(String fieldName, Object obj, bool inline) { + + public void WriteObjectField(String fieldName, Object obj, bool inline) + { if (inline && obj == null) { return; //don't write anything } BeginElement(fieldName); - WriteObjectInternal(obj,inline); + WriteObjectInternal(obj, inline); EndElement(); } - - public void WriteStringContents(String str) { + + public void WriteStringContents(String str) + { WriteStringContentsInternal(str); } - - public void WriteStringField(String fieldName, String str) { - if ( str != null ) { + + public void WriteStringField(String fieldName, String str) + { + if (str != null) + { WriteAttributeInternal(fieldName, str); } } - - internal static String EncodeBoolean(bool b) { + + internal static String EncodeBoolean(bool b) + { return b.ToString(); } - - private static String EncodeByteArray(byte [] bytes) { + + private static String EncodeByteArray(byte[] bytes) + { return Convert.ToBase64String(bytes); } - - private static String EncodeClass(Type clazz) { + + private static String EncodeClass(Type clazz) + { ObjectSerializationHandler handler = ObjectSerializerRegistry.GetHandlerByObjectType(clazz); ObjectTypeMapper mapper = ObjectSerializerRegistry.GetMapperByObjectType(clazz); - if ( handler == null && clazz.IsArray ) { + if (handler == null && clazz.IsArray) + { //we may have special handlers for certain types of arrays //if handler is null, treat like any other array - return EncodeClass(clazz.GetElementType())+"[]"; + return EncodeClass(clazz.GetElementType()) + "[]"; } - else if ( mapper == null ) { - throw new ConnectorException("No serializer for class: "+clazz); + else if (mapper == null) + { + throw new ConnectorException("No serializer for class: " + clazz); } - else { + else + { String typeName = mapper.HandledSerialType; return typeName; } } - - internal static String EncodeDouble(double d) { + + internal static String EncodeDouble(double d) + { return d.ToString("R"); } - - internal static String EncodeFloat(float d) { + + internal static String EncodeFloat(float d) + { return d.ToString("R"); } - - internal static String EncodeInt(int d) { + + internal static String EncodeInt(int d) + { return d.ToString(); } - - internal static String EncodeLong(long d) { + + internal static String EncodeLong(long d) + { return d.ToString(); } - - /** - * Writes the object - * @param object - * @param inline - * @return The type name (regardless of whether it was inlined) - */ - String WriteObjectInternal(Object obj, bool inline) { - if ( obj == null ) { - if ( inline ) { + + /// + /// Writes the object + /// + /// + /// + /// The type name (regardless of whether it was inlined) + String WriteObjectInternal(Object obj, bool inline) + { + if (obj == null) + { + if (inline) + { throw new ArgumentException("null cannot be inlined"); } BeginElement("null"); EndElement(); return "null"; } - else { + else + { Type clazz = obj.GetType(); ObjectSerializationHandler handler = ObjectSerializerRegistry.GetHandlerByObjectType(clazz); - if ( handler == null ) { + if (handler == null) + { //we may have special handlers for certain types of arrays //if handler is null, treat like any other array - if ( clazz.IsArray ) { - if (!inline) { + if (clazz.IsArray) + { + if (!inline) + { String componentTypeName = EncodeClass(clazz.GetElementType()); BeginElement("Array"); WriteAttributeInternal("componentType", componentTypeName); } Array array = (Array)obj; int length = array.Length; - for ( int i = 0; i < length; i++ ) { + for (int i = 0; i < length; i++) + { Object val = array.GetValue(i); - WriteObjectInternal(val,false); + WriteObjectInternal(val, false); } - if (!inline) { + if (!inline) + { EndElement(); } return "Array"; } - else { - throw new ConnectorException("No serializer for class: "+clazz); + else + { + throw new ConnectorException("No serializer for class: " + clazz); } } - else { + else + { String typeName = EncodeClass(clazz); - if (!inline) { + if (!inline) + { BeginElement(typeName); } handler.Serialize(obj, this); - if (!inline) { + if (!inline) + { EndElement(); } return typeName; } } - + } - + ////////////////////////////////////////////////////////////////// // // xml encoding // ///////////////////////////////////////////////////////////////// - - - - private void BeginElement(String name) { + + private void BeginElement(String name) + { _writer.WriteStartElement(name); } - - private void EndElement() { + + private void EndElement() + { _writer.WriteEndElement(); } - private void WriteAttributeInternal(String fieldName, String str) { - _writer.WriteAttributeString(fieldName,str); + private void WriteAttributeInternal(String fieldName, String str) + { + _writer.WriteAttributeString(fieldName, str); } - private void WriteStringContentsInternal(String str) { + private void WriteStringContentsInternal(String str) + { _writer.WriteString(str); } } - - public class XmlObjectDecoder : ObjectDecoder { - + + public class XmlObjectDecoder : ObjectDecoder + { + private readonly XmlElement _node; private readonly Type _expectedClass; - - public XmlObjectDecoder(XmlElement node, Type expectedClass) { + + public XmlObjectDecoder(XmlElement node, Type expectedClass) + { _node = node; _expectedClass = expectedClass; } - - public Object ReadObject() { + + public Object ReadObject() + { return ReadObjectInternal(); } - - public bool ReadBooleanContents() { + + public bool ReadBooleanContents() + { return DecodeBoolean(ReadStringContentsInternal()); } - - public bool ReadBooleanField(String fieldName, bool dflt) { - return DecodeBoolean(ReadStringAttributeInternal(fieldName,XmlObjectEncoder.EncodeBoolean(dflt))); + + public bool ReadBooleanField(String fieldName, bool dflt) + { + return DecodeBoolean(ReadStringAttributeInternal(fieldName, XmlObjectEncoder.EncodeBoolean(dflt))); } - - public byte[] ReadByteArrayContents() { + + public byte[] ReadByteArrayContents() + { return DecodeByteArray(ReadStringContentsInternal()); } - - public Type ReadClassContents() { + + public Type ReadClassContents() + { return DecodeClass(ReadStringContentsInternal()); } - - public Type ReadClassField(String name, Type dflt) { + + public Type ReadClassField(String name, Type dflt) + { String val = ReadStringAttributeInternal(name, null); - if ( val == null ) { + if (val == null) + { return dflt; } - else { + else + { return DecodeClass(val); } } - - public double ReadDoubleContents() { + + public double ReadDoubleContents() + { return DecodeDouble(ReadStringContentsInternal()); } - - public double ReadDoubleField(String fieldName, double dflt) { - return DecodeDouble(ReadStringAttributeInternal(fieldName,XmlObjectEncoder.EncodeDouble(dflt))); + + public double ReadDoubleField(String fieldName, double dflt) + { + return DecodeDouble(ReadStringAttributeInternal(fieldName, XmlObjectEncoder.EncodeDouble(dflt))); } - - public float ReadFloatContents() { + + public float ReadFloatContents() + { return DecodeFloat(ReadStringContentsInternal()); } - - public float ReadFloatField(String fieldName, float dflt) { - return DecodeFloat(ReadStringAttributeInternal(fieldName,XmlObjectEncoder.EncodeFloat(dflt))); + + public float ReadFloatField(String fieldName, float dflt) + { + return DecodeFloat(ReadStringAttributeInternal(fieldName, XmlObjectEncoder.EncodeFloat(dflt))); } - - public int ReadIntContents() { + + public int ReadIntContents() + { return DecodeInt(ReadStringContentsInternal()); } - - public int ReadIntField(String fieldName, int dflt) { - return DecodeInt(ReadStringAttributeInternal(fieldName,XmlObjectEncoder.EncodeInt(dflt))); + + public int ReadIntField(String fieldName, int dflt) + { + return DecodeInt(ReadStringAttributeInternal(fieldName, XmlObjectEncoder.EncodeInt(dflt))); } - - public long ReadLongContents() { + + public long ReadLongContents() + { return DecodeLong(ReadStringContentsInternal()); } - - public long ReadLongField(String fieldName, long dflt) { - return DecodeLong(ReadStringAttributeInternal(fieldName,XmlObjectEncoder.EncodeLong(dflt))); + + public long ReadLongField(String fieldName, long dflt) + { + return DecodeLong(ReadStringAttributeInternal(fieldName, XmlObjectEncoder.EncodeLong(dflt))); } - - public int GetNumSubObjects() { + + public int GetNumSubObjects() + { int count = 0; for (XmlElement subElement = XmlUtil.GetFirstChildElement(_node); subElement != null; - subElement = XmlUtil.GetNextElement(subElement)) { + subElement = XmlUtil.GetNextElement(subElement)) + { count++; } return count; } - - public Object ReadObjectContents(int index) { + + public Object ReadObjectContents(int index) + { XmlElement subElement = XmlUtil.GetFirstChildElement(_node); - for ( int i = 0; i < index; i++) { + for (int i = 0; i < index; i++) + { subElement = XmlUtil.GetNextElement(subElement); } - - if ( subElement == null ) { - throw new ConnectorException("Missing subelement number: "+index); + + if (subElement == null) + { + throw new ConnectorException("Missing subelement number: " + index); } - - return new XmlObjectDecoder(subElement,null).ReadObject(); + + return new XmlObjectDecoder(subElement, null).ReadObject(); } - - public Object ReadObjectField(String fieldName, Type expected, Object dflt) { + + public Object ReadObjectField(String fieldName, Type expected, Object dflt) + { XmlElement child = XmlUtil.FindImmediateChildElement(_node, fieldName); - if ( child == null ) { + if (child == null) + { return dflt; } - if ( expected != null ) { - return new XmlObjectDecoder(child,expected).ReadObject(); + if (expected != null) + { + return new XmlObjectDecoder(child, expected).ReadObject(); } XmlElement subElement = XmlUtil.GetFirstChildElement(child); - if ( subElement == null ) { + if (subElement == null) + { return dflt; } //if they specify null, don't apply defaults - return new XmlObjectDecoder(subElement,null).ReadObject(); + return new XmlObjectDecoder(subElement, null).ReadObject(); } - - public String ReadStringContents() { + + public String ReadStringContents() + { String rv = ReadStringContentsInternal(); return rv == null ? "" : rv; } - - public String ReadStringField(String fieldName, String dflt) { + + public String ReadStringField(String fieldName, String dflt) + { return ReadStringAttributeInternal(fieldName, dflt); } - - private String ReadStringContentsInternal() { + + private String ReadStringContentsInternal() + { String xml = XmlUtil.GetContent(_node); - return xml; + return xml; } - - private String ReadStringAttributeInternal(String name, String dflt) { + + private String ReadStringAttributeInternal(String name, String dflt) + { XmlAttribute attr = _node.GetAttributeNode(name); - if ( attr == null ) { + if (attr == null) + { return dflt; } return attr.Value; } - - private bool DecodeBoolean(String v) { + + private bool DecodeBoolean(String v) + { return Boolean.Parse(v); } - - private byte [] DecodeByteArray(String base64) { + + private byte[] DecodeByteArray(String base64) + { return Convert.FromBase64String(base64); } - - private Type DecodeClass(String type) { - if ( type.EndsWith("[]") ) { - String componentName = type.Substring(0,type.Length-"[]".Length); + + private Type DecodeClass(String type) + { + if (type.EndsWith("[]")) + { + String componentName = type.Substring(0, type.Length - "[]".Length); Type componentClass = DecodeClass(componentName); Type arrayClass = componentClass.MakeArrayType(); - return arrayClass; + return arrayClass; } - else { + else + { ObjectTypeMapper mapper = ObjectSerializerRegistry.GetMapperBySerialType(type); - if ( mapper == null ) { - throw new ConnectorException("No deserializer for type: "+type); + if (mapper == null) + { + throw new ConnectorException("No deserializer for type: " + type); } Type clazz = mapper.HandledObjectType; return clazz; } } - - private double DecodeDouble(String val) { + + private double DecodeDouble(String val) + { return Double.Parse(val); } - - private float DecodeFloat(String val) { + + private float DecodeFloat(String val) + { return Single.Parse(val); } - - private int DecodeInt(String val) { + + private int DecodeInt(String val) + { return Int32.Parse(val); } - - private long DecodeLong(String val) { + + private long DecodeLong(String val) + { return Int64.Parse(val); } - - private Object ReadObjectInternal() { - if (_expectedClass != null) { + + private Object ReadObjectInternal() + { + if (_expectedClass != null) + { ObjectSerializationHandler handler = ObjectSerializerRegistry.GetHandlerByObjectType(_expectedClass); - if ( handler == null ) { - if (_expectedClass.IsArray) { + if (handler == null) + { + if (_expectedClass.IsArray) + { IList temp = new List(); for (XmlElement child = XmlUtil.GetFirstChildElement(_node); child != null; - child = XmlUtil.GetNextElement(child)) { - XmlObjectDecoder sub = new XmlObjectDecoder(child,null); + child = XmlUtil.GetNextElement(child)) + { + XmlObjectDecoder sub = new XmlObjectDecoder(child, null); Object obj = sub.ReadObject(); temp.Add(obj); } int length = temp.Count; - Array array = Array.CreateInstance(_expectedClass.GetElementType(),length); - for ( int i = 0; i < length; i++) { + Array array = Array.CreateInstance(_expectedClass.GetElementType(), length); + for (int i = 0; i < length; i++) + { Object element = temp[i]; - array.SetValue(element,i); + array.SetValue(element, i); } - return array; + return array; } - else { - throw new ConnectorException("No deserializer for type: "+_expectedClass); + else + { + throw new ConnectorException("No deserializer for type: " + _expectedClass); } } - else { + else + { return handler.Deserialize(this); - } + } } - else if ( _node.LocalName.Equals("null") ) { + else if (_node.LocalName.Equals("null")) + { return null; } - else if (_node.LocalName.Equals("Array")) { + else if (_node.LocalName.Equals("Array")) + { String componentType = XmlUtil.GetAttribute(_node, "componentType"); - if ( componentType == null ) { + if (componentType == null) + { componentType = "Object"; } Type componentClass = DecodeClass(componentType); IList temp = new List(); for (XmlElement child = XmlUtil.GetFirstChildElement(_node); child != null; - child = XmlUtil.GetNextElement(child)) { - XmlObjectDecoder sub = new XmlObjectDecoder(child,null); + child = XmlUtil.GetNextElement(child)) + { + XmlObjectDecoder sub = new XmlObjectDecoder(child, null); Object obj = sub.ReadObject(); temp.Add(obj); } int length = temp.Count; - Array array = Array.CreateInstance(componentClass, + Array array = Array.CreateInstance(componentClass, length); - for ( int i = 0; i < length; i++) { + for (int i = 0; i < length; i++) + { Object element = temp[i]; - array.SetValue(element,i); + array.SetValue(element, i); } return array; } - else { + else + { Type clazz = DecodeClass(_node.LocalName); ObjectSerializationHandler handler = ObjectSerializerRegistry.GetHandlerByObjectType(clazz); - if ( handler == null ) { - throw new ConnectorException("No deserializer for type: "+clazz); + if (handler == null) + { + throw new ConnectorException("No deserializer for type: " + clazz); } - else { + else + { return handler.Deserialize(this); } } } - } - - public class XmlObjectParser { + public class XmlObjectParser + { public static void parse(TextReader inputSource, - XmlObjectResultsHandler handler, + XmlObjectResultsHandler handler, bool validate) { - XmlReaderSettings mySettings = + XmlReaderSettings mySettings = new XmlReaderSettings(); - if ( validate ) { + if (validate) + { mySettings.ValidationType = ValidationType.DTD; } mySettings.ProhibitDtd = false; mySettings.XmlResolver = new MyEntityResolver(validate); - XmlReader reader = XmlReader.Create(inputSource,mySettings); + XmlReader reader = XmlReader.Create(inputSource, mySettings); MyParser parser = new MyParser(handler); parser.Parse(reader); } - - + private class MyEntityResolver : XmlResolver { - private readonly bool _validate; - + private readonly bool _validate; + public MyEntityResolver(bool validate) { _validate = validate; } - + public override Object GetEntity(Uri absoluteUri, string role, Type ofObject) { String text = null; - if (absoluteUri.AbsolutePath.EndsWith(XmlObjectSerializerImpl.CONNECTORS_DTD)) { - if (!_validate) { + if (absoluteUri.AbsolutePath.EndsWith(XmlObjectSerializerImpl.CONNECTORS_DTD)) + { + if (!_validate) + { text = ""; } - else { + else + { text = GetDTD(); } } - if ( text != null ) { - byte [] bytes = Encoding.UTF8.GetBytes(text); + if (text != null) + { + byte[] bytes = Encoding.UTF8.GetBytes(text); return new MemoryStream(bytes); } return null; } - - public override ICredentials Credentials { - set { - + + public override ICredentials Credentials + { + set + { + } } private static String GetDTD() @@ -561,52 +670,60 @@ private static String GetDTD() return contents; } } - - private class MyParser + + private class MyParser { - /** - * The document for the current top-level element. with each top-level element, - * we discard the previous to avoid accumulating memory - */ + /// + /// The document for the current top-level element. + /// + /// + /// with each top-level element, + /// we discard the previous to avoid accumulating memory + /// private XmlDocument _currentTopLevelElementDocument; - - - /** - * Stack of elements we are creating - */ + + + /// + /// Stack of elements we are creating + /// private IList _elementStack = new List(10); - - /** - * Results handler that we write our objects to - */ + + /// + /// Results handler that we write our objects to + /// private readonly XmlObjectResultsHandler _handler; - - /** - * Is the handler still handing - */ + + /// + /// Is the handler still handing + /// private bool _stillHandling = true; - - + + public MyParser(XmlObjectResultsHandler handler) { - _handler = handler; + _handler = handler; } - + public void Parse(XmlReader reader) { - while ( _stillHandling && reader.Read() ) { + while (_stillHandling && reader.Read()) + { XmlNodeType nodeType = reader.NodeType; - switch (nodeType) { + switch (nodeType) + { case XmlNodeType.Element: StartElement(reader.LocalName); bool empty = reader.IsEmptyElement; - if (reader.MoveToFirstAttribute()) { - AddAttribute(reader.LocalName,reader.Value); - while (reader.MoveToNextAttribute()) { - AddAttribute(reader.LocalName,reader.Value); + if (reader.MoveToFirstAttribute()) + { + AddAttribute(reader.LocalName, reader.Value); + while (reader.MoveToNextAttribute()) + { + AddAttribute(reader.LocalName, reader.Value); } } - if ( empty ) { + if (empty) + { EndElement(); } break; @@ -622,47 +739,49 @@ public void Parse(XmlReader reader) } } } - + private XmlElement GetCurrentElement() { - if (_elementStack.Count > 0 ) + if (_elementStack.Count > 0) { - return _elementStack[_elementStack.Count-1]; + return _elementStack[_elementStack.Count - 1]; } else { return null; } } - - private void AddText(String text) + + private void AddText(String text) { XmlElement currentElement = GetCurrentElement(); - if ( currentElement != null ) + if (currentElement != null) { currentElement.AppendChild(_currentTopLevelElementDocument.CreateTextNode(text)); } } - + private void EndElement() { if (_elementStack.Count > 0) //we don't push the top-level MULTI_OBJECT_ELEMENT on the stack { - XmlElement element = _elementStack[_elementStack.Count-1]; - _elementStack.RemoveAt(_elementStack.Count-1); - if (_elementStack.Count == 0) { + XmlElement element = _elementStack[_elementStack.Count - 1]; + _elementStack.RemoveAt(_elementStack.Count - 1); + if (_elementStack.Count == 0) + { _currentTopLevelElementDocument = null; - if (_stillHandling) { - XmlObjectDecoder decoder = new XmlObjectDecoder(element,null); + if (_stillHandling) + { + XmlObjectDecoder decoder = new XmlObjectDecoder(element, null); Object obj = decoder.ReadObject(); _stillHandling = _handler(obj); } } - } + } } - - - private void StartElement(String localName) + + + private void StartElement(String localName) { XmlElement element = null; if (_elementStack.Count == 0) @@ -675,83 +794,95 @@ private void StartElement(String localName) } else { - element = + element = _currentTopLevelElementDocument.CreateElement(localName); GetCurrentElement().AppendChild(element); } - if (element != null) + if (element != null) { _elementStack.Add(element); } } - + private void AddAttribute(String name, String val) { XmlElement element = GetCurrentElement(); - if ( element != null ) + if (element != null) { element.SetAttribute(name, val); } - } - } + } + } } - public class XmlObjectSerializerImpl : XmlObjectSerializer { - + public class XmlObjectSerializerImpl : XmlObjectSerializer + { + public const String MULTI_OBJECT_ELEMENT = "MultiObject"; - public const String CONNECTORS_DTD = "connectors.dtd"; - + public const String CONNECTORS_DTD = "connectors.dtd"; + private readonly TextWriter _output; - + private readonly bool _multiObject; - + private readonly bool _includeHeader; - + private bool _firstObjectWritten; - + private bool _documentEnded; - - public XmlObjectSerializerImpl(TextWriter output, bool includeHeader, bool multiObject) { + + public XmlObjectSerializerImpl(TextWriter output, bool includeHeader, bool multiObject) + { _output = output; _includeHeader = includeHeader; _multiObject = multiObject; } - - - /** - * Writes the next object to the stream. - * @param object The object to write. - * @see ObjectSerializerFactory for a list of supported types. - * @throws ConnectorException if there is more than one object - * and this is not configured for multi-object document. - */ - public void WriteObject(Object obj) { - if (_documentEnded) { - throw new InvalidOperationException("Attempt to writeObject after the document is already closed"); + + + /// + /// Writes the next object to the stream. + /// + /// The object to write. + /// + /// if there is more than one object + /// and this is not configured for multi-object document. + public void WriteObject(Object obj) + { + if (_documentEnded) + { + throw new InvalidOperationException("Attempt to writeObject after the document is already closed"); } StringBuilder buf = new StringBuilder(); XmlObjectEncoder encoder = new XmlObjectEncoder(buf); String elementName = encoder.WriteObject(obj); - if (!_firstObjectWritten) { + if (!_firstObjectWritten) + { StartDocument(elementName); } - else { - if (!_multiObject) { + else + { + if (!_multiObject) + { throw new InvalidOperationException("Attempt to write multiple objects on a single-object document"); } } Write(buf.ToString()); _firstObjectWritten = true; } - - public void Flush() { + + public void Flush() + { _output.Flush(); } - - public void Close( bool closeStream ) { - if (!_documentEnded) { - if (!_firstObjectWritten) { - if (!_multiObject) { + + public void Close(bool closeStream) + { + if (!_documentEnded) + { + if (!_firstObjectWritten) + { + if (!_multiObject) + { throw new InvalidOperationException("Attempt to write zero objects on a single-object document"); } StartDocument(null); @@ -759,36 +890,41 @@ public void Close( bool closeStream ) { WriteEndDocument(); _documentEnded = true; } - if (closeStream) { + if (closeStream) + { _output.Close(); } } - - private void StartDocument(String firstElement) { - if (_includeHeader) { + + private void StartDocument(String firstElement) + { + if (_includeHeader) + { String docType = _multiObject ? MULTI_OBJECT_ELEMENT : firstElement; String line1 = "\n"; - String line2 = "\n"; + String line2 = "\n"; Write(line1); Write(line2); } - if (_multiObject) { - String line3 = "<"+MULTI_OBJECT_ELEMENT+">\n"; + if (_multiObject) + { + String line3 = "<" + MULTI_OBJECT_ELEMENT + ">\n"; Write(line3); } } - - private void WriteEndDocument() { - if (_multiObject) { - String line1 = "\n"; - Write(line1); + + private void WriteEndDocument() + { + if (_multiObject) + { + String line1 = "\n"; + Write(line1); } } - - private void Write(String str) { + + private void Write(String str) + { _output.Write(str); } - } - -} +} \ No newline at end of file diff --git a/FrameworkInternal/Server.cs b/FrameworkInternal/Server.cs index ec6f1520..3316b670 100644 --- a/FrameworkInternal/Server.cs +++ b/FrameworkInternal/Server.cs @@ -49,346 +49,412 @@ using Org.IdentityConnectors.Framework.Impl.Api.Local; using Org.IdentityConnectors.Framework.Impl.Api.Local.Operations; using Org.IdentityConnectors.Framework.Impl.Api.Remote; + namespace Org.IdentityConnectors.Framework.Server { - /** - * Connector server interface. - */ - public abstract class ConnectorServer { - + /// + /// Connector server interface. + /// + public abstract class ConnectorServer + { // At some point we might make this pluggable, but for now, hard-code - private const String IMPL_NAME + private const String IMPL_NAME = "Org.IdentityConnectors.Framework.Impl.Server.ConnectorServerImpl"; - - - /** - * The port to listen on; - */ + + /// + /// The port to listen on; + /// private int _port = 0; - - /** - * Base 64 sha1 hash of the connector server key - */ + + /// + /// Base 64 sha1 hash of the connector server key + /// + /// private String _keyHash; - - /** - * The number of connections to queue - */ + + /// + /// The number of connections to queue + /// private int _maxConnections = 300; - - /** - * The minimum number of worker threads - */ + + /// + /// The minimum number of worker threads + /// private int _minWorkers = 10; - - /** - * The maximum number of worker threads - */ + + /// + /// The maximum number of worker threads + /// private int _maxWorkers = 100; - - /** - * The network interface address to use. - */ + + /// + /// The network interface address to use. + /// private IPAddress _ifAddress = null; - - /** - * Listen on SSL - */ + + /// + /// Listen on SSL + /// private bool _useSSL = false; - - /** - * The server certificate to use - */ + + /// + /// The server certificate to use + /// private X509Certificate _serverCertificate = null; - - /** - * Get the singleton instance of the {@link ConnectorServer}. - */ - public static ConnectorServer NewInstance() { - SafeType type = - SafeType.ForRawType(Type.GetType(IMPL_NAME,true)); + + /// + /// Get the singleton instance of the . + /// + public static ConnectorServer NewInstance() + { + SafeType type = + SafeType.ForRawType(Type.GetType(IMPL_NAME, true)); return type.CreateInstance(); } - - private void AssertNotStarted() { - if ( IsStarted() ) { + + private void AssertNotStarted() + { + if (IsStarted()) + { throw new InvalidOperationException("Operation cannot be performed " + - "while server is running"); + "while server is running"); } } - - /** - * Returns the port to listen on. - * @return The port to listen on. - */ - public int Port { - get { + + /// + /// Returns the port to listen on. + /// + /// The port to listen on. + public int Port + { + get + { return _port; } - set { + set + { AssertNotStarted(); - _port = value; + _port = value; } } - - /** - * Returns the max connections to queue - * @return The max connections to queue - */ - public int MaxConnections { - get { + + /// + /// Returns the max connections to queue + /// + /// The max connections to queue + public int MaxConnections + { + get + { return _maxConnections; } - set { + set + { AssertNotStarted(); _maxConnections = value; } } - - - /** - * Returns the max worker threads to allow. - * @return The max worker threads to allow. - */ - public int MaxWorkers { - get { + + /// + /// Returns the max worker threads to allow. + /// + /// The max worker threads to allow. + public int MaxWorkers + { + get + { return _maxWorkers; } - set { + set + { AssertNotStarted(); _maxWorkers = value; } } - - - - /** - * Returns the min worker threads to allow. - * @return The min worker threads to allow. - */ - public int MinWorkers { - get { + + /// + /// Returns the min worker threads to allow. + /// + /// The min worker threads to allow. + public int MinWorkers + { + get + { return _minWorkers; } - set { + set + { AssertNotStarted(); _minWorkers = value; } } - - - - /** - * Returns the network interface address to bind to. May be null. - * @return The network interface address to bind to or null. - */ - public IPAddress IfAddress { - get { + + /// + /// Returns the network interface address to bind to. + /// + /// + /// May be null. + /// + /// The network interface address to bind to or null. + public IPAddress IfAddress + { + get + { return _ifAddress; } - set { + set + { AssertNotStarted(); _ifAddress = value; } } - - /** - * Returns true iff we are to use SSL. - * @return true iff we are to use SSL. - */ - public bool UseSSL { - get { + + /// + /// Returns true iff we are to use SSL. + /// + /// true iff we are to use SSL. + public bool UseSSL + { + get + { return _useSSL; } - set { + set + { AssertNotStarted(); _useSSL = value; } } - - - /** - * Returns the certificate to use for the SSL connection. - */ - public X509Certificate ServerCertificate { - get { + + /// + /// Returns the certificate to use for the SSL connection. + /// + public X509Certificate ServerCertificate + { + get + { return _serverCertificate; } - set { + set + { AssertNotStarted(); _serverCertificate = value; } } - - public String KeyHash { - get { + + public String KeyHash + { + get + { return _keyHash; } - set { + set + { AssertNotStarted(); _keyHash = value; } } - - /** - * Produces a thread dump of all pending requests - */ + + /// + /// Produces a thread dump of all pending requests + /// abstract public void DumpRequests(); - - /** - * Starts the server. All server settings must be configured prior - * to calling. The following methods are required to be called: - *
    - *
  • {@link #Port(int)}
  • - *
  • {@link #KeyHash(String)}
  • - *
- */ + + /// + /// Starts the server. + /// + /// + /// All server settings must be configured prior + /// to calling. The following methods are required to be called: + /// + /// + /// + /// + /// + /// + /// + /// + /// + /// + /// abstract public void Start(); - - /** - * Stops the server gracefully. Returns when all in-progress connections - * have been serviced. - */ + + /// + /// Stops the server gracefully. + /// + /// + /// Returns when all in-progress connections + /// have been serviced. + /// abstract public void Stop(); - - /** - * Return true iff the server is started. Note that started is a - * logical state (start method has been called). It does not necessarily - * reflect the health of the server - * @return true iff the server is started. - */ + + /// + /// Return true iff the server is started. + /// + /// + /// Note that started is a + /// logical state (start method has been called). It does not necessarily + /// reflect the health of the server + /// + /// true iff the server is started. abstract public bool IsStarted(); } } namespace Org.IdentityConnectors.Framework.Impl.Server { - public class ConnectionProcessor { - - + public class ConnectionProcessor + { private class RemoteResultsHandler : ObjectStreamHandler { private const int PAUSE_INTERVAL = 200; private readonly RemoteFrameworkConnection _connection; private long _count = 0; - public RemoteResultsHandler(RemoteFrameworkConnection conn) + public RemoteResultsHandler(RemoteFrameworkConnection conn) { _connection = conn; } - - public bool Handle(Object obj) { - try { - OperationResponsePart part = - new OperationResponsePart(null,obj); + + public bool Handle(Object obj) + { + try + { + OperationResponsePart part = + new OperationResponsePart(null, obj); _connection.WriteObject(part); _count++; - if ( _count % PAUSE_INTERVAL == 0 ) { + if (_count % PAUSE_INTERVAL == 0) + { _connection.WriteObject(new OperationResponsePause()); - Object message = + Object message = _connection.ReadObject(); - return message is OperationRequestMoreData; + return message is OperationRequestMoreData; } - else { + else + { return true; } } - catch (IOException e) { + catch (IOException e) + { throw new BrokenConnectionException(e); } - catch (Exception) { + catch (Exception) + { throw; } } } - + private readonly ConnectorServerImpl _server; private readonly RemoteFrameworkConnection _connection; - + public ConnectionProcessor(ConnectorServerImpl server, - RemoteFrameworkConnection connection) { + RemoteFrameworkConnection connection) + { _server = server; _connection = connection; } - - public void Run() { - try { + + public void Run() + { + try + { _server.BeginRequest(); - try { - while ( true ) { + try + { + while (true) + { bool keepGoing = ProcessRequest(); - if (!keepGoing) { + if (!keepGoing) + { break; } } } - finally { - try { + finally + { + try + { _connection.Dispose(); } - catch (Exception e) { - TraceUtil.TraceException(null,e); + catch (Exception e) + { + TraceUtil.TraceException(null, e); } } } - catch (Exception e) { - TraceUtil.TraceException(null,e); + catch (Exception e) + { + TraceUtil.TraceException(null, e); } - finally { + finally + { _server.EndRequest(); } } - - private bool ProcessRequest() + + private bool ProcessRequest() { - CultureInfo locale; - try { + try + { locale = (CultureInfo)_connection.ReadObject(); } - catch (EndOfStreamException) { + catch (EndOfStreamException) + { return false; } - + //We can't set this because C# does not like language-neutral //cultures for CurrentCulture - this tends to blow up //TODO: think more about this... //Thread.CurrentThread.CurrentCulture = locale; - Thread.CurrentThread.CurrentUICulture = locale; - + Thread.CurrentThread.CurrentUICulture = locale; + GuardedString key = (GuardedString)_connection.ReadObject(); bool authorized; - try { + try + { authorized = key.VerifyBase64SHA1Hash(_server.KeyHash); } - finally { + finally + { key.Dispose(); } Org.IdentityConnectors.Framework.Common.Exceptions.InvalidCredentialException authException = null; - if (!authorized) { + if (!authorized) + { authException = new Org.IdentityConnectors.Framework.Common.Exceptions.InvalidCredentialException("Remote framework key is invalid"); } Object requestObject = _connection.ReadObject(); - if ( requestObject is HelloRequest ) { - if ( authException != null ) { + if (requestObject is HelloRequest) + { + if (authException != null) + { HelloResponse response = - new HelloResponse(authException,null); - _connection.WriteObject(response); + new HelloResponse(authException, null); + _connection.WriteObject(response); } - else { - HelloResponse response = + else + { + HelloResponse response = ProcessHelloRequest((HelloRequest)requestObject); _connection.WriteObject(response); } } - else if ( requestObject is OperationRequest ) { - if ( authException != null ) { + else if (requestObject is OperationRequest) + { + if (authException != null) + { OperationResponsePart part = - new OperationResponsePart(authException,null); + new OperationResponsePart(authException, null); _connection.WriteObject(part); } - else { + else + { OperationRequest opRequest = (OperationRequest)requestObject; OperationResponsePart part = @@ -396,136 +462,164 @@ private bool ProcessRequest() _connection.WriteObject(part); } } - else if (requestObject is EchoMessage) { - if ( authException != null ) { + else if (requestObject is EchoMessage) + { + if (authException != null) + { //echo message probably doesn't need auth, but //it couldn't hurt - actually it does for test connection EchoMessage part = - new EchoMessage(authException,null); + new EchoMessage(authException, null); _connection.WriteObject(part); } - else { + else + { EchoMessage message = (EchoMessage)requestObject; Object obj = message.Object; String xml = message.ObjectXml; - if ( xml != null ) { - Console.WriteLine("xml: \n"+xml); + if (xml != null) + { + Console.WriteLine("xml: \n" + xml); Object xmlClone = - SerializerUtil.DeserializeXmlObject(xml,true); + SerializerUtil.DeserializeXmlObject(xml, true); xml = - SerializerUtil.SerializeXmlObject(xmlClone,true); + SerializerUtil.SerializeXmlObject(xmlClone, true); } - EchoMessage message2 = new EchoMessage(obj,xml); + EchoMessage message2 = new EchoMessage(obj, xml); _connection.WriteObject(message2); } } - else { - throw new Exception("Unexpected request: "+requestObject); + else + { + throw new Exception("Unexpected request: " + requestObject); } return true; } - - private ConnectorInfoManager GetConnectorInfoManager() { + + private ConnectorInfoManager GetConnectorInfoManager() + { return ConnectorInfoManagerFactory.GetInstance().GetLocalManager(); } - - private HelloResponse ProcessHelloRequest(HelloRequest request) { + + private HelloResponse ProcessHelloRequest(HelloRequest request) + { IList connectorInfo; Exception exception = null; - try { + try + { ConnectorInfoManager manager = GetConnectorInfoManager(); IList localInfos = manager.ConnectorInfos; connectorInfo = new List(); - foreach (ConnectorInfo localInfo in localInfos) { - LocalConnectorInfoImpl localInfoImpl = + foreach (ConnectorInfo localInfo in localInfos) + { + LocalConnectorInfoImpl localInfoImpl = (LocalConnectorInfoImpl)localInfo; RemoteConnectorInfoImpl remoteInfo = localInfoImpl.ToRemote(); connectorInfo.Add(remoteInfo); } } - catch (Exception e) { - TraceUtil.TraceException(null,e); + catch (Exception e) + { + TraceUtil.TraceException(null, e); exception = e; connectorInfo = null; } - return new HelloResponse(exception,connectorInfo); + return new HelloResponse(exception, connectorInfo); } - - private MethodInfo GetOperationMethod(OperationRequest request) { - MethodInfo [] methods = + + private MethodInfo GetOperationMethod(OperationRequest request) + { + MethodInfo[] methods = request.Operation.RawType.GetMethods(); MethodInfo found = null; - foreach (MethodInfo m in methods) { - if ( m.Name.ToUpper().Equals(request.OperationMethodName.ToUpper()) ) { - if ( found != null ) { + foreach (MethodInfo m in methods) + { + if (m.Name.ToUpper().Equals(request.OperationMethodName.ToUpper())) + { + if (found != null) + { throw new ConnectorException("APIOperations are expected " - +"to have exactly one method of a given name: "+request.Operation); + + "to have exactly one method of a given name: " + request.Operation); } found = m; } } - - if ( found == null ) { + + if (found == null) + { throw new ConnectorException("APIOperations are expected " - +"to have exactly one method of a given name: "+request.Operation+" "+methods.Length); + + "to have exactly one method of a given name: " + request.Operation + " " + methods.Length); } - return found; + return found; } - - private OperationResponsePart - ProcessOperationRequest(OperationRequest request) { + + private OperationResponsePart + ProcessOperationRequest(OperationRequest request) + { Object result; Exception exception = null; - try { + try + { MethodInfo method = GetOperationMethod(request); APIOperation operation = GetAPIOperation(request); IList arguments = request.Arguments; IList argumentsAndStreamHandlers = PopulateStreamHandlers(ReflectionUtil.GetParameterTypes(method), arguments); - try { - Object [] args = argumentsAndStreamHandlers.ToArray(); - FixupArguments(method,args); + try + { + Object[] args = argumentsAndStreamHandlers.ToArray(); + FixupArguments(method, args); result = method.Invoke(operation, args); } - catch (TargetInvocationException e) { + catch (TargetInvocationException e) + { Exception root = e.InnerException; - ExceptionUtil.PreserveStackTrace( root ); + ExceptionUtil.PreserveStackTrace(root); throw root; } bool anyStreams = argumentsAndStreamHandlers.Count > arguments.Count; - if ( anyStreams ) { - try { + if (anyStreams) + { + try + { _connection.WriteObject(new OperationResponseEnd()); } - catch (IOException e) { + catch (IOException e) + { throw new BrokenConnectionException(e); } } } - catch (BrokenConnectionException w) { + catch (BrokenConnectionException w) + { //at this point the stream is broken - just give up throw w.GetIOException(); } - catch (Exception e) { - TraceUtil.TraceException(null,e); + catch (Exception e) + { + TraceUtil.TraceException(null, e); exception = e; result = null; } - return new OperationResponsePart(exception,result); + return new OperationResponsePart(exception, result); } - - private IList PopulateStreamHandlers(Type [] paramTypes, IList arguments) { + + private IList PopulateStreamHandlers(Type[] paramTypes, IList arguments) + { IList rv = new List(); bool firstStream = true; IEnumerator argIt = arguments.GetEnumerator(); - foreach (Type paramType in paramTypes) { - if ( StreamHandlerUtil.IsAdaptableToObjectStreamHandler(paramType) ) { - if (!firstStream) { + foreach (Type paramType in paramTypes) + { + if (StreamHandlerUtil.IsAdaptableToObjectStreamHandler(paramType)) + { + if (!firstStream) + { throw new InvalidOperationException("At most one stream handler is supported"); } ObjectStreamHandler osh = @@ -533,14 +627,15 @@ private IList PopulateStreamHandlers(Type [] paramTypes, IList a rv.Add(StreamHandlerUtil.AdaptFromObjectStreamHandler(paramType, osh)); firstStream = false; } - else { + else + { argIt.MoveNext(); rv.Add(argIt.Current); } } return rv; } - + /// /// When arguments are serialized, we loose the /// generic-type of collections. We must fix @@ -549,201 +644,234 @@ private IList PopulateStreamHandlers(Type [] paramTypes, IList a /// /// private void FixupArguments(MethodInfo method, - object [] args) + object[] args) { - Type [] paramTypes = + Type[] paramTypes = ReflectionUtil.GetParameterTypes(method); - if (paramTypes.Length != args.Length) { - throw new ArgumentException("Number of arguments does not match for method: "+method); + if (paramTypes.Length != args.Length) + { + throw new ArgumentException("Number of arguments does not match for method: " + method); } - for ( int i = 0; i < args.Length; i++ ) { + for (int i = 0; i < args.Length; i++) + { args[i] = FixupArgument(paramTypes[i], args[i]); } } - + private object FixupArgument(Type expectedType, - object argument) + object argument) { //at some point, we might want this to be more general-purpose //for now we just handle those cases that we need to - if (typeof(ICollection).Equals(expectedType)) { - ICollection val = + if (typeof(ICollection).Equals(expectedType)) + { + ICollection val = (ICollection)argument; - return CollectionUtil.NewSet(val); + return CollectionUtil.NewSet(val); } - else { + else + { return argument; } } - + private APIOperation GetAPIOperation(OperationRequest request) { ConnectorInfoManager manager = GetConnectorInfoManager(); ConnectorInfo info = manager.FindConnectorInfo( request.ConnectorKey); - if ( info == null ) { + if (info == null) + { throw new Exception("No such connector: " - +request.ConnectorKey); + + request.ConnectorKey); } - APIConfigurationImpl config = + APIConfigurationImpl config = request.Configuration; - + //re-wire the configuration with its connector info - config.ConnectorInfo=(AbstractConnectorInfo)info; - - ConnectorFacade facade = + config.ConnectorInfo = (AbstractConnectorInfo)info; + + ConnectorFacade facade = ConnectorFacadeFactory.GetInstance().NewInstance(config); - + return facade.GetOperation(request.Operation); } - - private class BrokenConnectionException : Exception { - - - public BrokenConnectionException(IOException ex) - : base("",ex) { + + private class BrokenConnectionException : Exception + { + + + public BrokenConnectionException(IOException ex) + : base("", ex) + { } - - public IOException GetIOException() { + + public IOException GetIOException() + { return (IOException)InnerException; } } - + } - class ConnectionListener { - - /** - * This is the size of our internal queue. For now I have this - * relatively small because I want the OS to manage the connect - * queue coming in. That way it can properly turn away excessive - * requests - */ + class ConnectionListener + { + /// + /// This is the size of our internal queue. + /// + /// + /// For now I have this + /// relatively small because I want the OS to manage the connect + /// queue coming in. That way it can properly turn away excessive + /// requests + /// private const int INTERNAL_QUEUE_SIZE = 2; - - - /** - * The server object that we are using - */ + + + /// + /// The server object that we are using + /// private readonly ConnectorServerImpl _server; - - /** - * The server socket. This must be bound at the time - * of creation. - */ + + /// + /// The server socket. + /// + /// + /// This must be bound at the time + /// of creation. + /// private readonly TcpListener _socket; - - /** - * Pool of executors - */ + + /// + /// Pool of executors + /// //TODO: add a thread pool //private readonly ExecutorService _threadPool; - - /** - * Set to indicated we need to start shutting down - */ + + /// + /// Set to indicated we need to start shutting down + /// private bool _stopped = false; - + private Thread _thisThread; - + private readonly Object MUTEX = new Object(); - - /** - * Creates the listener thread - * @param server The server object - * @param socket The socket (should already be bound) - */ + + /// + /// Creates the listener thread + /// + /// The server object + /// The socket (should already be bound) public ConnectionListener(ConnectorServerImpl server, - TcpListener socket) { + TcpListener socket) + { _server = server; _socket = socket; - _thisThread = new Thread(Run) {Name = "ConnectionListener", IsBackground = false}; + _thisThread = new Thread(Run) { Name = "ConnectionListener", IsBackground = false }; //TODO: thread pool -/* _threadPool = - new ThreadPoolExecutor - (server.getMinWorkers(), - server.getMaxWorkers(), - 30, //idle time timeout - TimeUnit.SECONDS, - new ArrayBlockingQueue( - INTERNAL_QUEUE_SIZE, - true)); //fair*/ - } - - public void Start() { + /* _threadPool = + new ThreadPoolExecutor + (server.getMinWorkers(), + server.getMaxWorkers(), + 30, //idle time timeout + TimeUnit.SECONDS, + new ArrayBlockingQueue( + INTERNAL_QUEUE_SIZE, + true)); //fair*/ + } + + public void Start() + { _thisThread.Start(); } - - public void Run() { - Trace.TraceInformation("Server started on port: "+_server.Port); - while (!IsStopped()) { - try { + + public void Run() + { + Trace.TraceInformation("Server started on port: " + _server.Port); + while (!IsStopped()) + { + try + { TcpClient connection = null; Stream stream = null; - try { + try + { connection = _socket.AcceptTcpClient(); stream = connection.GetStream(); - if ( _server.UseSSL ) + if (_server.UseSSL) { - SslStream sslStream = new SslStream(stream,false); + SslStream sslStream = new SslStream(stream, false); stream = sslStream; sslStream.AuthenticateAsServer(_server.ServerCertificate, false, SslProtocols.Tls, - false); + false); } - + ConnectionProcessor processor = new ConnectionProcessor(_server, - new RemoteFrameworkConnection(connection,stream)); + new RemoteFrameworkConnection(connection, stream)); Thread thread = new Thread(processor.Run); thread.IsBackground = false; - thread.Start(); + thread.Start(); } - catch (Exception) { - if ( stream != null ) + catch (Exception) + { + if (stream != null) { - try { stream.Close(); } catch (Exception) {} + try { stream.Close(); } + catch (Exception) { } } - if ( connection != null ) + if (connection != null) { - try { connection.Close(); } catch (Exception) {} + try { connection.Close(); } + catch (Exception) { } } throw; } } - catch (Exception e) { + catch (Exception e) + { //log the error unless it's because we've stopped - if (!IsStopped() || !(e is SocketException)) { - TraceUtil.TraceException("Error processing request",e); + if (!IsStopped() || !(e is SocketException)) + { + TraceUtil.TraceException("Error processing request", e); } //wait a second before trying again - if (!IsStopped()) { + if (!IsStopped()) + { Thread.Sleep(1000); } } } } - - private void MarkStopped() { - lock(MUTEX) { + + private void MarkStopped() + { + lock (MUTEX) + { _stopped = true; } } - - private bool IsStopped() { - lock(MUTEX) { + + private bool IsStopped() + { + lock (MUTEX) + { return _stopped; } } - - public void Shutdown() { - if (Object.ReferenceEquals(Thread.CurrentThread,_thisThread)) { + + public void Shutdown() + { + if (Object.ReferenceEquals(Thread.CurrentThread, _thisThread)) + { throw new ArgumentException("Shutdown may not be called from this thread"); } - if (!IsStopped()) { + if (!IsStopped()) + { //set the stopped flag so we no its a normal //shutdown and don't log the SocketException MarkStopped(); @@ -759,29 +887,35 @@ public void Shutdown() { } } - internal class RequestStats { - public RequestStats() { + internal class RequestStats + { + public RequestStats() + { } public Thread RequestThread { get; set; } public long StartTimeMillis { get; set; } public long RequestID { get; set; } } - - public class ConnectorServerImpl : ConnectorServer { + + public class ConnectorServerImpl : ConnectorServer + { private readonly IDictionary - _pendingRequests = CollectionUtil.NewIdentityDictionary(); + _pendingRequests = CollectionUtil.NewIdentityDictionary(); private ConnectionListener _listener; private Object COUNT_LOCK = new Object(); private long _requestCount = 0; - - public override bool IsStarted() { + + public override bool IsStarted() + { return _listener != null; } - - public void BeginRequest() { + + public void BeginRequest() + { long requestID; - lock(COUNT_LOCK) { + lock (COUNT_LOCK) + { requestID = _requestCount++; } Thread requestThread = Thread.CurrentThread; @@ -790,77 +924,95 @@ public void BeginRequest() { DateTimeUtil.GetCurrentUtcTimeMillis(); stats.RequestThread = Thread.CurrentThread; stats.RequestID = requestID; - lock (_pendingRequests) { + lock (_pendingRequests) + { _pendingRequests[stats.RequestThread] = stats; - } + } } - - public void EndRequest() { - lock (_pendingRequests) { + + public void EndRequest() + { + lock (_pendingRequests) + { _pendingRequests.Remove(Thread.CurrentThread); - } + } } - public override void DumpRequests() { + public override void DumpRequests() + { long currentTime = DateTimeUtil.GetCurrentUtcTimeMillis(); IDictionary pending; - lock(_pendingRequests) { + lock (_pendingRequests) + { pending = new Dictionary(_pendingRequests); } StringBuilder builder = new StringBuilder(); builder.Append("****Pending Requests Summary*****"); - foreach (RequestStats stats in pending.Values) { - DumpStats(stats,builder,currentTime); + foreach (RequestStats stats in pending.Values) + { + DumpStats(stats, builder, currentTime); } //here we purposefully use write line since //we always want to see it. in general, don't //use this method Trace.WriteLine(builder.ToString()); } - + private void DumpStats(RequestStats stats, StringBuilder builder, long currentTime) { - builder.AppendLine("**Request #"+stats.RequestID+" pending for "+(currentTime-stats.StartTimeMillis)+" millis."); + builder.AppendLine("**Request #" + stats.RequestID + " pending for " + (currentTime - stats.StartTimeMillis) + " millis."); StackTrace stackTrace = GetStackTrace(stats.RequestThread); - if ( stackTrace == null ) { + if (stackTrace == null) + { builder.AppendLine(" "); } - else { + else + { builder.AppendLine(stackTrace.ToString()); } } - - private static StackTrace GetStackTrace(Thread thread) { + + private static StackTrace GetStackTrace(Thread thread) + { bool suspended = false; - try { + try + { thread.Suspend(); suspended = true; - return new StackTrace(thread,true); + return new StackTrace(thread, true); } - catch (ThreadStateException) { + catch (ThreadStateException) + { return null; //we missed this one } - finally { - if ( suspended ) { + finally + { + if (suspended) + { thread.Resume(); } } } - - public override void Start() { - if ( IsStarted() ) { + + public override void Start() + { + if (IsStarted()) + { throw new InvalidOperationException("Server is already running."); } - if ( Port == 0 ) { + if (Port == 0) + { throw new InvalidOperationException("Port must be set prior to starting server."); } - if (KeyHash == null) { - throw new InvalidOperationException("Key hash must be set prior to starting server."); + if (KeyHash == null) + { + throw new InvalidOperationException("Key hash must be set prior to starting server."); } - if ( UseSSL && ServerCertificate == null ) { + if (UseSSL && ServerCertificate == null) + { throw new InvalidOperationException("ServerCertificate must be set if using SSL."); } //make sure we are configured properly @@ -869,33 +1021,34 @@ public override void Start() { _pendingRequests.Clear(); TcpListener socket = CreateServerSocket(); - ConnectionListener listener = new ConnectionListener(this,socket); - listener.Start(); + ConnectionListener listener = new ConnectionListener(this, socket); + listener.Start(); _listener = listener; } - - private TcpListener CreateServerSocket() { + + private TcpListener CreateServerSocket() + { IPAddress addr = IfAddress; - - if ( addr == null ) { + + if (addr == null) + { addr = IOUtil.GetIPAddress("0.0.0.0"); } - TcpListener rv = new TcpListener(addr,Port); + TcpListener rv = new TcpListener(addr, Port); //TODO: specify accept count rv.Start(); return rv; } - - - public override void Stop() { - if (_listener != null) { + + + public override void Stop() + { + if (_listener != null) + { _listener.Shutdown(); _listener = null; } ConnectorFacadeFactory.GetInstance().Dispose(); } - } - - -} +} \ No newline at end of file diff --git a/FrameworkInternal/Test.cs b/FrameworkInternal/Test.cs index 7427b1f7..11d65483 100644 --- a/FrameworkInternal/Test.cs +++ b/FrameworkInternal/Test.cs @@ -38,44 +38,48 @@ using Org.IdentityConnectors.Test.Common.Spi; namespace Org.IdentityConnectors.Framework.Impl.Test -{ - public class TestHelpersImpl : TestHelpersSpi { - - /** - * Method for convenient testing of local connectors. - */ +{ + public class TestHelpersImpl : TestHelpersSpi + { + /// + /// Method for convenient testing of local connectors. + /// public APIConfiguration CreateTestConfiguration(SafeType clazz, - Configuration config) { + Configuration config) + { LocalConnectorInfoImpl info = new LocalConnectorInfoImpl(); - info.ConnectorConfigurationClass=SafeType.Get(config); - info.ConnectorClass=(clazz); - info.ConnectorDisplayNameKey=("DUMMY_DISPLAY_NAME"); - info.ConnectorKey=( - new ConnectorKey(clazz.RawType.Name+".bundle", + info.ConnectorConfigurationClass = SafeType.Get(config); + info.ConnectorClass = (clazz); + info.ConnectorDisplayNameKey = ("DUMMY_DISPLAY_NAME"); + info.ConnectorKey = ( + new ConnectorKey(clazz.RawType.Name + ".bundle", "1.0", clazz.RawType.Name)); - info.Messages=(this.CreateDummyMessages()); + info.Messages = (this.CreateDummyMessages()); APIConfigurationImpl rv = new APIConfigurationImpl(); - rv.IsConnectorPoolingSupported=( + rv.IsConnectorPoolingSupported = ( IsConnectorPoolingSupported(clazz)); ConfigurationPropertiesImpl properties = CSharpClassProperties.CreateConfigurationProperties(config); - rv.ConfigurationProperties=(properties); - rv.ConnectorInfo=(info); - rv.SupportedOperations=( + rv.ConfigurationProperties = (properties); + rv.ConnectorInfo = (info); + rv.SupportedOperations = ( FrameworkUtil.GetDefaultSupportedOperations(clazz)); - info.DefaultAPIConfiguration=( + info.DefaultAPIConfiguration = ( rv); return rv; } - - public void FillConfiguration(Configuration config, IDictionary configData) { + + public void FillConfiguration(Configuration config, IDictionary configData) + { IDictionary configDataCopy = new Dictionary(configData); ConfigurationPropertiesImpl configProps = CSharpClassProperties.CreateConfigurationProperties(config); - foreach (string propName in configProps.PropertyNames) { + foreach (string propName in configProps.PropertyNames) + { object value; - if (configDataCopy.TryGetValue(propName, out value)) { + if (configDataCopy.TryGetValue(propName, out value)) + { // Remove the entry from the config map, so that at the end // the map only contains entries that were not assigned to a config property. configDataCopy.Remove(propName); @@ -83,59 +87,66 @@ public void FillConfiguration(Configuration config, IDictionary } } // The config map now contains entries that were not assigned to a config property. - foreach (string propName in configDataCopy.Keys) { + foreach (string propName in configDataCopy.Keys) + { Trace.TraceWarning("Configuration property {0} does not exist!", propName); } CSharpClassProperties.MergeIntoBean(configProps, config); } - private static bool IsConnectorPoolingSupported(SafeType clazz) { - return ReflectionUtil.IsParentTypeOf(typeof(PoolableConnector),clazz.RawType); + private static bool IsConnectorPoolingSupported(SafeType clazz) + { + return ReflectionUtil.IsParentTypeOf(typeof(PoolableConnector), clazz.RawType); } - /** - * Performs a raw, unfiltered search at the SPI level, - * eliminating duplicates from the result set. - * @param search The search SPI - * @param oclass The object class - passed through to - * connector so it may be null if the connecor - * allowing it to be null. (This is convenient for - * unit tests, but will not be the case in general) - * @param filter The filter to search on - * @param handler The result handler - * @param options The options - may be null - will - * be cast to an empty OperationOptions - */ + /// + /// Performs a raw, unfiltered search at the SPI level, + /// eliminating duplicates from the result set. + /// + /// The search SPI + /// The object class - passed through to + /// connector so it may be null if the connecor + /// allowing it to be null. (This is convenient for + /// unit tests, but will not be the case in general) + /// The filter to search on + /// The result handler + /// The options - may be null - will + /// be cast to an empty OperationOptions public void Search(SearchOp search, - ObjectClass oclass, - Filter filter, + ObjectClass oclass, + Filter filter, ResultsHandler handler, OperationOptions options) where T : class { - if ( options == null ) { + if (options == null) + { options = new OperationOptionsBuilder().Build(); } RawSearcherImpl.RawSearch( search, oclass, filter, handler, options); } - - public ConnectorMessages CreateDummyMessages() { + + public ConnectorMessages CreateDummyMessages() + { return new DummyConnectorMessages(); } - - private class DummyConnectorMessages : ConnectorMessages { - public String Format(String key, String dflt, params Object [] args) { + + private class DummyConnectorMessages : ConnectorMessages + { + public String Format(String key, String dflt, params Object[] args) + { StringBuilder builder = new StringBuilder(); builder.Append(key); builder.Append(": "); String sep = ""; - foreach (Object arg in args ) { + foreach (Object arg in args) + { builder.Append(sep); builder.Append(arg); - sep=", "; + sep = ", "; } return builder.ToString(); } - } + } } -} +} \ No newline at end of file diff --git a/FrameworkTests/CollectionUtilTests.cs b/FrameworkTests/CollectionUtilTests.cs index 7ec17517..9f4c74ae 100644 --- a/FrameworkTests/CollectionUtilTests.cs +++ b/FrameworkTests/CollectionUtilTests.cs @@ -31,41 +31,42 @@ namespace FrameworkTests public class CollectionUtilTests { [Test] - public void TestEquals() { + public void TestEquals() + { Assert.IsTrue(CollectionUtil.Equals(null, null)); Assert.IsFalse(CollectionUtil.Equals(null, "str")); Assert.IsTrue(CollectionUtil.Equals("str", "str")); - - byte [] arr1 = new byte[] { 1,2,3 }; - byte [] arr2 = new byte[] { 1,2,3 }; - byte [] arr3 = new byte[] { 1,2,4 }; - byte [] arr4 = new byte[] { 1,2 }; - int [] arr5 = new int[] {1,2,3}; - + + byte[] arr1 = new byte[] { 1, 2, 3 }; + byte[] arr2 = new byte[] { 1, 2, 3 }; + byte[] arr3 = new byte[] { 1, 2, 4 }; + byte[] arr4 = new byte[] { 1, 2 }; + int[] arr5 = new int[] { 1, 2, 3 }; + Assert.IsTrue(CollectionUtil.Equals(arr1, arr2)); Assert.IsFalse(CollectionUtil.Equals(arr2, arr3)); Assert.IsFalse(CollectionUtil.Equals(arr2, arr4)); Assert.IsFalse(CollectionUtil.Equals(arr2, arr5)); - + IList list1 = new List(); IList list2 = new List(); list1.Add(arr1); list2.Add(arr2); - + Assert.IsTrue(CollectionUtil.Equals(list1, list2)); - + list2.Add(arr2); Assert.IsFalse(CollectionUtil.Equals(list1, list2)); - + list1.Add(arr1); Assert.IsTrue(CollectionUtil.Equals(list1, list2)); - + list1.Add(arr1); list2.Add(arr3); Assert.IsFalse(CollectionUtil.Equals(list1, list2)); - - IDictionary map1 = new Dictionary(); - IDictionary map2 = new Dictionary(); + + IDictionary map1 = new Dictionary(); + IDictionary map2 = new Dictionary(); map1["key1"] = arr1; map2["key1"] = arr2; Assert.IsTrue(CollectionUtil.Equals(map1, map2)); @@ -75,7 +76,7 @@ public void TestEquals() { Assert.IsTrue(CollectionUtil.Equals(map1, map2)); map1["key2"] = arr3; Assert.IsFalse(CollectionUtil.Equals(map1, map2)); - + ICollection set1 = new HashSet(); ICollection set2 = new HashSet(); set1.Add("val"); @@ -87,74 +88,75 @@ public void TestEquals() { Assert.IsTrue(CollectionUtil.Equals(set1, set2)); } [Test] - public void TestHashCode() { - Assert.AreEqual(0,CollectionUtil.GetHashCode(null)); - Assert.AreEqual("str".GetHashCode(),CollectionUtil.GetHashCode("str")); - - byte [] arr1 = new byte[] { 1,2,3 }; - byte [] arr2 = new byte[] { 1,2,3 }; - byte [] arr3 = new byte[] { 1,2,4 }; - byte [] arr4 = new byte[] { 1,2 }; - int [] arr5 = new int[] {1,2,3}; - - Assert.AreEqual(CollectionUtil.GetHashCode(arr1), + public void TestHashCode() + { + Assert.AreEqual(0, CollectionUtil.GetHashCode(null)); + Assert.AreEqual("str".GetHashCode(), CollectionUtil.GetHashCode("str")); + + byte[] arr1 = new byte[] { 1, 2, 3 }; + byte[] arr2 = new byte[] { 1, 2, 3 }; + byte[] arr3 = new byte[] { 1, 2, 4 }; + byte[] arr4 = new byte[] { 1, 2 }; + int[] arr5 = new int[] { 1, 2, 3 }; + + Assert.AreEqual(CollectionUtil.GetHashCode(arr1), CollectionUtil.GetHashCode(arr2)); - Assert.IsFalse(CollectionUtil.GetHashCode(arr2) == + Assert.IsFalse(CollectionUtil.GetHashCode(arr2) == CollectionUtil.GetHashCode(arr3)); - Assert.IsFalse(CollectionUtil.GetHashCode(arr2) == + Assert.IsFalse(CollectionUtil.GetHashCode(arr2) == CollectionUtil.GetHashCode(arr4)); - Assert.IsTrue(CollectionUtil.GetHashCode(arr2) == + Assert.IsTrue(CollectionUtil.GetHashCode(arr2) == CollectionUtil.GetHashCode(arr5)); - + List list1 = new List(); List list2 = new List(); list1.Add(arr1); list2.Add(arr2); - - Assert.IsTrue(CollectionUtil.GetHashCode(list1) == + + Assert.IsTrue(CollectionUtil.GetHashCode(list1) == CollectionUtil.GetHashCode(list2)); - + list2.Add(arr2); - Assert.IsFalse(CollectionUtil.GetHashCode(list1) == + Assert.IsFalse(CollectionUtil.GetHashCode(list1) == CollectionUtil.GetHashCode(list2)); - + list1.Add(arr1); - Assert.IsTrue(CollectionUtil.GetHashCode(list1) == + Assert.IsTrue(CollectionUtil.GetHashCode(list1) == CollectionUtil.GetHashCode(list2)); - + list1.Add(arr1); list2.Add(arr3); - Assert.IsFalse(CollectionUtil.GetHashCode(list1) == + Assert.IsFalse(CollectionUtil.GetHashCode(list1) == CollectionUtil.GetHashCode(list2)); - - Dictionary map1 = new Dictionary(); - Dictionary map2 = new Dictionary(); + + Dictionary map1 = new Dictionary(); + Dictionary map2 = new Dictionary(); map1["key1"] = arr1; map2["key1"] = arr2; - Assert.IsTrue(CollectionUtil.GetHashCode(map1) == + Assert.IsTrue(CollectionUtil.GetHashCode(map1) == CollectionUtil.GetHashCode(map2)); map2["key2"] = arr2; - Assert.IsFalse(CollectionUtil.GetHashCode(map1) == + Assert.IsFalse(CollectionUtil.GetHashCode(map1) == CollectionUtil.GetHashCode(map2)); map1["key2"] = arr1; - Assert.IsTrue(CollectionUtil.GetHashCode(map1) == + Assert.IsTrue(CollectionUtil.GetHashCode(map1) == CollectionUtil.GetHashCode(map2)); map1["key2"] = arr3; - Assert.IsFalse(CollectionUtil.GetHashCode(map1) == + Assert.IsFalse(CollectionUtil.GetHashCode(map1) == CollectionUtil.GetHashCode(map2)); - + HashSet set1 = new HashSet(); HashSet set2 = new HashSet(); set1.Add("val"); set2.Add("val"); - Assert.IsTrue(CollectionUtil.GetHashCode(set1) == + Assert.IsTrue(CollectionUtil.GetHashCode(set1) == CollectionUtil.GetHashCode(set2)); set2.Add("val2"); - Assert.IsFalse(CollectionUtil.GetHashCode(set1) == + Assert.IsFalse(CollectionUtil.GetHashCode(set1) == CollectionUtil.GetHashCode(set2)); set1.Add("val2"); - Assert.IsTrue(CollectionUtil.GetHashCode(set1) == + Assert.IsTrue(CollectionUtil.GetHashCode(set1) == CollectionUtil.GetHashCode(set2)); } } -} +} \ No newline at end of file diff --git a/FrameworkTests/ConnectorFacadeExceptionTests.cs b/FrameworkTests/ConnectorFacadeExceptionTests.cs index 606e4660..a2eef988 100644 --- a/FrameworkTests/ConnectorFacadeExceptionTests.cs +++ b/FrameworkTests/ConnectorFacadeExceptionTests.cs @@ -82,7 +82,7 @@ public void Test() { //do not insert file info, as the stack trace of the exception and the dumped stack trace //will always differ in the line numbers - _stackTrace = new StackTrace( false ); + _stackTrace = new StackTrace(false); throw new EUTestException(); } #endregion @@ -107,17 +107,17 @@ public void TestStackTraceOfExceptionThrownByConnectorFacade() ConnectorFacadeFactory factory = ConnectorFacadeFactory.GetInstance(); Configuration config = new MockConfiguration(); ConnectorFacade facade = factory.NewInstance( - TestHelpers.CreateTestConfiguration( SafeType.Get(), config ) ); + TestHelpers.CreateTestConfiguration(SafeType.Get(), config)); try { facade.Test(); - Assert.Fail( "Exception was not thrown" ); + Assert.Fail("Exception was not thrown"); } - catch( EUTestException eutex ) + catch (EUTestException eutex) { - ExceptionUtilTestHelpers.AssertStackTrace( eutex, SpyConnector.StackTrace ); + ExceptionUtilTestHelpers.AssertStackTrace(eutex, SpyConnector.StackTrace); } } } diff --git a/FrameworkTests/ConnectorFacadeTests.cs b/FrameworkTests/ConnectorFacadeTests.cs index 219d3c27..68a50ea2 100755 --- a/FrameworkTests/ConnectorFacadeTests.cs +++ b/FrameworkTests/ConnectorFacadeTests.cs @@ -35,7 +35,8 @@ using Org.IdentityConnectors.Framework.Spi.Operations; using Org.IdentityConnectors.Test.Common; -namespace FrameworkTests { +namespace FrameworkTests +{ [TestFixture] public class ConnectorFacadeTests @@ -411,4 +412,4 @@ static string GetAndRemoveMethodName(IList calls) return result; } } -} +} \ No newline at end of file diff --git a/FrameworkTests/ConnectorInfoManagerTests.cs b/FrameworkTests/ConnectorInfoManagerTests.cs index b17700ce..1e97ed42 100644 --- a/FrameworkTests/ConnectorInfoManagerTests.cs +++ b/FrameworkTests/ConnectorInfoManagerTests.cs @@ -48,11 +48,14 @@ public class ConnectorInfoManagerTests private static ConnectorInfo FindConnectorInfo (ConnectorInfoManager manager, string version, - string connectorName) { - foreach (ConnectorInfo info in manager.ConnectorInfos) { + string connectorName) + { + foreach (ConnectorInfo info in manager.ConnectorInfos) + { ConnectorKey key = info.ConnectorKey; - if ( version.Equals(key.BundleVersion) && - connectorName.Equals(key.ConnectorName) ) { + if (version.Equals(key.BundleVersion) && + connectorName.Equals(key.ConnectorName)) + { //intentionally ineffecient to test //more code return manager.FindConnectorInfo(key); @@ -62,41 +65,43 @@ private static ConnectorInfo FindConnectorInfo } [TearDown] - public void TearDown() { + public void TearDown() + { ShutdownConnnectorInfoManager(); } - + [Test] - public void TestClassLoading() { - ConnectorInfoManager manager = + public void TestClassLoading() + { + ConnectorInfoManager manager = GetConnectorInfoManager(); - ConnectorInfo info1 = + ConnectorInfo info1 = FindConnectorInfo(manager, "1.0.0.0", "org.identityconnectors.testconnector.TstConnector"); Assert.IsNotNull(info1); - ConnectorInfo info2 = + ConnectorInfo info2 = FindConnectorInfo(manager, "2.0.0.0", "org.identityconnectors.testconnector.TstConnector"); - + Assert.IsNotNull(info2); - + ConnectorFacade facade1 = ConnectorFacadeFactory.GetInstance().NewInstance(info1.CreateDefaultAPIConfiguration()); - + ConnectorFacade facade2 = ConnectorFacadeFactory.GetInstance().NewInstance(info2.CreateDefaultAPIConfiguration()); ICollection attrs = new HashSet(); - Assert.AreEqual("1.0", facade1.Create(ObjectClass.ACCOUNT,attrs,null).GetUidValue()); - Assert.AreEqual("2.0", facade2.Create(ObjectClass.ACCOUNT,attrs,null).GetUidValue()); + Assert.AreEqual("1.0", facade1.Create(ObjectClass.ACCOUNT, attrs, null).GetUidValue()); + Assert.AreEqual("2.0", facade2.Create(ObjectClass.ACCOUNT, attrs, null).GetUidValue()); } - + [Test] public void TestAPIConfiguration() { - ConnectorInfoManager manager = + ConnectorInfoManager manager = GetConnectorInfoManager(); ConnectorInfo info = FindConnectorInfo(manager, @@ -104,213 +109,239 @@ public void TestAPIConfiguration() "org.identityconnectors.testconnector.TstConnector"); Assert.IsNotNull(info); APIConfiguration api = info.CreateDefaultAPIConfiguration(); - + ConfigurationProperties props = api.ConfigurationProperties; - ConfigurationProperty property = props.GetProperty("tstField"); - + ConfigurationProperty property = props.GetProperty("tstField"); + Assert.IsNotNull(property); ICollection> operations = property.Operations; Assert.AreEqual(1, operations.Count); Assert.IsTrue(operations.Contains(SafeType.Get())); - + Thread.CurrentThread.CurrentUICulture = new CultureInfo("en"); - Assert.AreEqual("Help for test field.",property.GetHelpMessage(null)); - Assert.AreEqual("Display for test field.",property.GetDisplayName(null)); + Assert.AreEqual("Help for test field.", property.GetHelpMessage(null)); + Assert.AreEqual("Display for test field.", property.GetDisplayName(null)); Assert.AreEqual("Test Framework Value", info.Messages.Format("TEST_FRAMEWORK_KEY", "empty")); - + CultureInfo eslocale = new CultureInfo("es"); Thread.CurrentThread.CurrentUICulture = eslocale; - Assert.AreEqual("tstField.help_es",property.GetHelpMessage(null)); - Assert.AreEqual("tstField.display_es",property.GetDisplayName(null)); - + Assert.AreEqual("tstField.help_es", property.GetHelpMessage(null)); + Assert.AreEqual("tstField.display_es", property.GetDisplayName(null)); + CultureInfo esESlocale = new CultureInfo("es-ES"); Thread.CurrentThread.CurrentUICulture = esESlocale; - Assert.AreEqual("tstField.help_es-ES",property.GetHelpMessage(null)); - Assert.AreEqual("tstField.display_es-ES",property.GetDisplayName(null)); - + Assert.AreEqual("tstField.help_es-ES", property.GetHelpMessage(null)); + Assert.AreEqual("tstField.display_es-ES", property.GetDisplayName(null)); + CultureInfo esARlocale = new CultureInfo("es-AR"); Thread.CurrentThread.CurrentUICulture = esARlocale; - Assert.AreEqual("tstField.help_es",property.GetHelpMessage(null)); - Assert.AreEqual("tstField.display_es",property.GetDisplayName(null)); - + Assert.AreEqual("tstField.help_es", property.GetHelpMessage(null)); + Assert.AreEqual("tstField.display_es", property.GetDisplayName(null)); + ConnectorFacadeFactory facf = ConnectorFacadeFactory.GetInstance(); ConnectorFacade facade = facf.NewInstance(api); // call the various create/update/delete commands.. facade.Schema(); } - + [Test] - public void TestValidate() { - ConnectorInfoManager manager = + public void TestValidate() + { + ConnectorInfoManager manager = GetConnectorInfoManager(); - ConnectorInfo info = + ConnectorInfo info = FindConnectorInfo(manager, "1.0.0.0", "org.identityconnectors.testconnector.TstConnector"); - + APIConfiguration api = info.CreateDefaultAPIConfiguration(); - + ConfigurationProperties props = api.ConfigurationProperties; - ConfigurationProperty property = props.GetProperty("failValidation"); - property.Value=false; + ConfigurationProperty property = props.GetProperty("failValidation"); + property.Value = false; ConnectorFacadeFactory facf = ConnectorFacadeFactory.GetInstance(); ConnectorFacade facade = facf.NewInstance(api); facade.Validate(); - property.Value=true; + property.Value = true; facade = facf.NewInstance(api); - try { + try + { Thread.CurrentThread.CurrentUICulture = new CultureInfo("en"); facade.Validate(); Assert.Fail("exception expected"); } - catch (ConnectorException e) { + catch (ConnectorException e) + { Assert.AreEqual("validation failed en", e.Message); } - try { + try + { Thread.CurrentThread.CurrentUICulture = new CultureInfo("es"); facade.Validate(); Assert.Fail("exception expected"); } - catch (ConnectorException e) { + catch (ConnectorException e) + { Assert.AreEqual("validation failed es", e.Message); } } - /** - * Main purpose of this is to test searching with - * many results and that we can properly handle - * stopping in the middle of this. There's a bunch of - * code in the remote stuff that is there to handle this - * in particular that we want to excercise. - */ - [Test] - public void TestSearchWithManyResults() { - ConnectorInfoManager manager = + /// + /// Main purpose of this is to test searching with + /// many results and that we can properly handle + /// stopping in the middle of this. + /// + /// + /// There's a bunch of + /// code in the remote stuff that is there to handle this + /// in particular that we want to excercise. + /// + [Test] + public void TestSearchWithManyResults() + { + ConnectorInfoManager manager = GetConnectorInfoManager(); - ConnectorInfo info = + ConnectorInfo info = FindConnectorInfo(manager, "1.0.0.0", "org.identityconnectors.testconnector.TstConnector"); - + APIConfiguration api = info.CreateDefaultAPIConfiguration(); - + ConfigurationProperties props = api.ConfigurationProperties; - ConfigurationProperty property = props.GetProperty("numResults"); - + ConfigurationProperty property = props.GetProperty("numResults"); + //1000 is several times the remote size between pauses - property.Value=1000; - + property.Value = 1000; + ConnectorFacadeFactory facf = ConnectorFacadeFactory.GetInstance(); ConnectorFacade facade = facf.NewInstance(api); - + IList results = new List(); - - facade.Search(ObjectClass.ACCOUNT,null, - obj => { - results.Add(obj); - return true; - },null); - - Assert.AreEqual(1000,results.Count); - for ( int i = 0; i < results.Count; i++ ) { + + facade.Search(ObjectClass.ACCOUNT, null, + obj => + { + results.Add(obj); + return true; + }, null); + + Assert.AreEqual(1000, results.Count); + for (int i = 0; i < results.Count; i++) + { ConnectorObject obj = results[i]; Assert.AreEqual(i.ToString(), obj.Uid.GetUidValue()); } - + results.Clear(); - - facade.Search(ObjectClass.ACCOUNT,null, - obj => { - if ( results.Count < 500 ) { - results.Add(obj); - return true; - } - else { - return false; - } - },null); - - Assert.AreEqual(500,results.Count); - for ( int i = 0; i < results.Count; i++ ) { + + facade.Search(ObjectClass.ACCOUNT, null, + obj => + { + if (results.Count < 500) + { + results.Add(obj); + return true; + } + else + { + return false; + } + }, null); + + Assert.AreEqual(500, results.Count); + for (int i = 0; i < results.Count; i++) + { ConnectorObject obj = results[i]; Assert.AreEqual(i.ToString(), obj.Uid.GetUidValue()); } } - /** - * Main purpose of this is to test sync with - * many results and that we can properly handle - * stopping in the middle of this. There's a bunch of - * code in the remote stuff that is there to handle this - * in particular that we want to excercise. - */ + /// + /// Main purpose of this is to test sync with + /// many results and that we can properly handle + /// stopping in the middle of this. + /// + /// + /// There's a bunch of + /// code in the remote stuff that is there to handle this + /// in particular that we want to excercise. + /// [Test] - public void TestSyncWithManyResults() { - ConnectorInfoManager manager = + public void TestSyncWithManyResults() + { + ConnectorInfoManager manager = GetConnectorInfoManager(); - ConnectorInfo info = + ConnectorInfo info = FindConnectorInfo(manager, "1.0.0.0", "org.identityconnectors.testconnector.TstConnector"); - + APIConfiguration api = info.CreateDefaultAPIConfiguration(); - + ConfigurationProperties props = api.ConfigurationProperties; - ConfigurationProperty property = props.GetProperty("numResults"); - + ConfigurationProperty property = props.GetProperty("numResults"); + //1000 is several times the remote size between pauses - property.Value=(1000); - + property.Value = (1000); + ConnectorFacadeFactory facf = ConnectorFacadeFactory.GetInstance(); ConnectorFacade facade = facf.NewInstance(api); - + SyncToken latest = facade.GetLatestSyncToken(ObjectClass.ACCOUNT); - Assert.AreEqual("mylatest",latest.Value); + Assert.AreEqual("mylatest", latest.Value); IList results = new List(); - - facade.Sync(ObjectClass.ACCOUNT, null, obj => { - results.Add(obj); - return true; - },null); - - Assert.AreEqual(1000,results.Count); - for ( int i = 0; i < results.Count; i++ ) { + + facade.Sync(ObjectClass.ACCOUNT, null, obj => + { + results.Add(obj); + return true; + }, null); + + Assert.AreEqual(1000, results.Count); + for (int i = 0; i < results.Count; i++) + { SyncDelta obj = results[i]; Assert.AreEqual(i.ToString(), obj.Uid.GetUidValue()); } - + results.Clear(); - + facade.Sync(ObjectClass.ACCOUNT, - null, - obj => { - if ( results.Count < 500 ) { - results.Add(obj); - return true; - } - else { - return false; - } - },null); - - Assert.AreEqual(500,results.Count); - for ( int i = 0; i < results.Count; i++ ) { + null, + obj => + { + if (results.Count < 500) + { + results.Add(obj); + return true; + } + else + { + return false; + } + }, null); + + Assert.AreEqual(500, results.Count); + for (int i = 0; i < results.Count; i++) + { SyncDelta obj = results[i]; Assert.AreEqual(i.ToString(), obj.Uid.GetUidValue()); } } - + [Test] - public void TestConnectionPooling() { + public void TestConnectionPooling() + { ConnectorPoolManager.Dispose(); - ConnectorInfoManager manager = + ConnectorInfoManager manager = GetConnectorInfoManager(); - ConnectorInfo info1 = + ConnectorInfo info1 = FindConnectorInfo(manager, "1.0.0.0", "org.identityconnectors.testconnector.TstConnector"); @@ -320,138 +351,144 @@ public void TestConnectionPooling() { //trigger TstConnection.init to be called APIConfiguration config2 = info1.CreateDefaultAPIConfiguration(); - config2.ConfigurationProperties.GetProperty("resetConnectionCount").Value=(true); + config2.ConfigurationProperties.GetProperty("resetConnectionCount").Value = (true); ConnectorFacade facade2 = ConnectorFacadeFactory.GetInstance().NewInstance(config2); facade2.Schema(); //force instantiation } - + APIConfiguration config = info1.CreateDefaultAPIConfiguration(); - - config.ConnectorPoolConfiguration.MinIdle=(0); - config.ConnectorPoolConfiguration.MaxIdle=(0); - + + config.ConnectorPoolConfiguration.MinIdle = (0); + config.ConnectorPoolConfiguration.MaxIdle = (0); + ConnectorFacade facade1 = ConnectorFacadeFactory.GetInstance().NewInstance(config); - + OperationOptionsBuilder builder = new OperationOptionsBuilder(); builder.SetOption("testPooling", "true"); OperationOptions options = builder.Build(); ICollection attrs = CollectionUtil.NewReadOnlySet(); - Assert.AreEqual("1", facade1.Create(ObjectClass.ACCOUNT,attrs,options).GetUidValue()); - Assert.AreEqual("2", facade1.Create(ObjectClass.ACCOUNT,attrs,options).GetUidValue()); - Assert.AreEqual("3", facade1.Create(ObjectClass.ACCOUNT,attrs,options).GetUidValue()); - Assert.AreEqual("4", facade1.Create(ObjectClass.ACCOUNT,attrs,options).GetUidValue()); + Assert.AreEqual("1", facade1.Create(ObjectClass.ACCOUNT, attrs, options).GetUidValue()); + Assert.AreEqual("2", facade1.Create(ObjectClass.ACCOUNT, attrs, options).GetUidValue()); + Assert.AreEqual("3", facade1.Create(ObjectClass.ACCOUNT, attrs, options).GetUidValue()); + Assert.AreEqual("4", facade1.Create(ObjectClass.ACCOUNT, attrs, options).GetUidValue()); config = info1.CreateDefaultAPIConfiguration(); - config.ConnectorPoolConfiguration.MinIdle=(1); - config.ConnectorPoolConfiguration.MaxIdle=(2); + config.ConnectorPoolConfiguration.MinIdle = (1); + config.ConnectorPoolConfiguration.MaxIdle = (2); facade1 = ConnectorFacadeFactory.GetInstance().NewInstance(config); - Assert.AreEqual("5", facade1.Create(ObjectClass.ACCOUNT,attrs,options).GetUidValue()); - Assert.AreEqual("5", facade1.Create(ObjectClass.ACCOUNT,attrs,options).GetUidValue()); - Assert.AreEqual("5", facade1.Create(ObjectClass.ACCOUNT,attrs,options).GetUidValue()); - Assert.AreEqual("5", facade1.Create(ObjectClass.ACCOUNT,attrs,options).GetUidValue()); + Assert.AreEqual("5", facade1.Create(ObjectClass.ACCOUNT, attrs, options).GetUidValue()); + Assert.AreEqual("5", facade1.Create(ObjectClass.ACCOUNT, attrs, options).GetUidValue()); + Assert.AreEqual("5", facade1.Create(ObjectClass.ACCOUNT, attrs, options).GetUidValue()); + Assert.AreEqual("5", facade1.Create(ObjectClass.ACCOUNT, attrs, options).GetUidValue()); } - + [Test] public void TestScripting() { - ConnectorInfoManager manager = + ConnectorInfoManager manager = GetConnectorInfoManager(); - ConnectorInfo info = + ConnectorInfo info = FindConnectorInfo(manager, "1.0.0.0", "org.identityconnectors.testconnector.TstConnector"); APIConfiguration api = info.CreateDefaultAPIConfiguration(); - - + + ConnectorFacadeFactory facf = ConnectorFacadeFactory.GetInstance(); ConnectorFacade facade = facf.NewInstance(api); - + ScriptContextBuilder builder = new ScriptContextBuilder(); builder.AddScriptArgument("arg1", "value1"); builder.AddScriptArgument("arg2", "value2"); - builder.ScriptLanguage=("BOO"); - + builder.ScriptLanguage = ("BOO"); + //test that they can run the script and access the //connector object { String SCRIPT = "connector.concat(arg1,arg2)"; - builder.ScriptText=(SCRIPT); - String result = (String)facade.RunScriptOnConnector(builder.Build(), + builder.ScriptText = (SCRIPT); + String result = (String)facade.RunScriptOnConnector(builder.Build(), null); - + Assert.AreEqual("value1value2", result); } - + //test that they can access a class in the class loader { String SCRIPT = - "import org.identityconnectors.testconnector\n"+ + "import org.identityconnectors.testconnector\n" + "TstConnector.getVersion()"; - builder.ScriptText=(SCRIPT); - String result = (String)facade.RunScriptOnConnector(builder.Build(), + builder.ScriptText = (SCRIPT); + String result = (String)facade.RunScriptOnConnector(builder.Build(), null); Assert.AreEqual("1.0", result); } - + //test that they cannot access a class in internal { Type clazz = typeof(ConfigurationPropertyImpl); - + String SCRIPT = - "import "+clazz.Namespace+"\n"+ - clazz.Name+"()"; - builder.ScriptText=(SCRIPT); - try { - facade.RunScriptOnConnector(builder.Build(), + "import " + clazz.Namespace + "\n" + + clazz.Name + "()"; + builder.ScriptText = (SCRIPT); + try + { + facade.RunScriptOnConnector(builder.Build(), null); Assert.Fail("exception expected"); } - catch (Exception e) { + catch (Exception e) + { String msg = e.Message; - String expectedMessage = - "Namespace '"+clazz.Namespace+"' not found"; - Assert.IsTrue( + String expectedMessage = + "Namespace '" + clazz.Namespace + "' not found"; + Assert.IsTrue( msg.Contains(expectedMessage), - "Unexpected message: "+msg); + "Unexpected message: " + msg); } } - + // test that they can access a class in common { Type clazz = typeof(ConnectorAttributeBuilder); String SCRIPT = - "import "+clazz.Namespace+"\n"+ + "import " + clazz.Namespace + "\n" + clazz.Name + ".Build(\"myattr\")"; - builder.ScriptText=(SCRIPT); - ConnectorAttribute attr = (ConnectorAttribute) facade.RunScriptOnConnector(builder.Build(), null); + builder.ScriptText = (SCRIPT); + ConnectorAttribute attr = (ConnectorAttribute)facade.RunScriptOnConnector(builder.Build(), null); Assert.AreEqual("myattr", attr.Name); } } - protected virtual ConnectorInfoManager GetConnectorInfoManager() { + protected virtual ConnectorInfoManager GetConnectorInfoManager() + { ConnectorInfoManagerFactory fact = ConnectorInfoManagerFactory.GetInstance(); ConnectorInfoManager manager = fact.GetLocalManager(); return manager; } - - protected virtual void ShutdownConnnectorInfoManager() { + + protected virtual void ShutdownConnnectorInfoManager() + { ConnectorFacadeFactory.GetInstance().Dispose(); } } - + [TestFixture] - public class RemoteConnectorInfoManagerClearTests : ConnectorInfoManagerTests { + public class RemoteConnectorInfoManagerClearTests : ConnectorInfoManagerTests + { private ConnectorServer _server; - - protected override ConnectorInfoManager GetConnectorInfoManager() { + + protected override ConnectorInfoManager GetConnectorInfoManager() + { TestUtil.InitializeLogging(); - + GuardedString str = new GuardedString(); str.AppendChar('c'); str.AppendChar('h'); @@ -466,46 +503,50 @@ protected override ConnectorInfoManager GetConnectorInfoManager() { const int PORT = 8758; #else const int PORT = 8759; -#endif +#endif _server = ConnectorServer.NewInstance(); - _server.Port=PORT; - _server.IfAddress=(IOUtil.GetIPAddress("127.0.0.1")); - _server.KeyHash=str.GetBase64SHA1Hash(); + _server.Port = PORT; + _server.IfAddress = (IOUtil.GetIPAddress("127.0.0.1")); + _server.KeyHash = str.GetBase64SHA1Hash(); _server.Start(); //while ( true ) { // Thread.Sleep(1000); //} ConnectorInfoManagerFactory fact = ConnectorInfoManagerFactory.GetInstance(); - + RemoteFrameworkConnectionInfo connInfo = new - RemoteFrameworkConnectionInfo("127.0.0.1",PORT,str); - + RemoteFrameworkConnectionInfo("127.0.0.1", PORT, str); + ConnectorInfoManager manager = fact.GetRemoteManager(connInfo); - + return manager; } - - protected override void ShutdownConnnectorInfoManager() { - if (_server != null) { + + protected override void ShutdownConnnectorInfoManager() + { + if (_server != null) + { _server.Stop(); _server = null; } } } - internal class MyCertificateValidationCallback + internal class MyCertificateValidationCallback { public bool Validate(Object sender, X509Certificate certificate, X509Chain chain, - SslPolicyErrors sslPolicyErrors) { - Trace.TraceInformation("validating: "+certificate.Subject); + SslPolicyErrors sslPolicyErrors) + { + Trace.TraceInformation("validating: " + certificate.Subject); return true; } } - + [TestFixture] - public class RemoteConnectorInfoManagerSSLTests : ConnectorInfoManagerTests { + public class RemoteConnectorInfoManagerSSLTests : ConnectorInfoManagerTests + { //To generate test certificate do the following: // @@ -514,9 +555,10 @@ public class RemoteConnectorInfoManagerSSLTests : ConnectorInfoManagerTests { //In MMC, go to the certificate, export private ConnectorServer _server; private const String CERT_PATH = "../../../server.pfx"; - protected override ConnectorInfoManager GetConnectorInfoManager() { + protected override ConnectorInfoManager GetConnectorInfoManager() + { TestUtil.InitializeLogging(); - + GuardedString str = new GuardedString(); str.AppendChar('c'); str.AppendChar('h'); @@ -526,27 +568,27 @@ protected override ConnectorInfoManager GetConnectorInfoManager() { str.AppendChar('e'); str.AppendChar('i'); str.AppendChar('t'); - + #if DEBUG const int PORT = 8762; #else const int PORT = 8761; #endif - + /*X509Store store = new X509Store("TestCertificateStore", StoreLocation.CurrentUser); store.Open(OpenFlags.ReadOnly|OpenFlags.OpenExistingOnly); X509Certificate certificate = store.Certificates[0]; store.Close();*/ - + X509Certificate2 certificate = new X509Certificate2(CERT_PATH, "changeit"); //Trace.TraceInformation("certificate: "+certificate); _server = ConnectorServer.NewInstance(); - _server.Port=PORT; - _server.KeyHash=str.GetBase64SHA1Hash(); - _server.IfAddress=(IOUtil.GetIPAddress("localhost")); + _server.Port = PORT; + _server.KeyHash = str.GetBase64SHA1Hash(); + _server.IfAddress = (IOUtil.GetIPAddress("localhost")); _server.UseSSL = true; _server.ServerCertificate = certificate; _server.Start(); @@ -563,20 +605,19 @@ protected override ConnectorInfoManager GetConnectorInfoManager() { true, callback.Validate, 60000); - + ConnectorInfoManager manager = fact.GetRemoteManager(connInfo); - + return manager; } - - protected override void ShutdownConnnectorInfoManager() { - if (_server != null) { + + protected override void ShutdownConnnectorInfoManager() + { + if (_server != null) + { _server.Stop(); _server = null; } } } - - - -} +} \ No newline at end of file diff --git a/FrameworkTests/ExceptionUtilTests.cs b/FrameworkTests/ExceptionUtilTests.cs index 6ba3ddd2..feb4ac8d 100644 --- a/FrameworkTests/ExceptionUtilTests.cs +++ b/FrameworkTests/ExceptionUtilTests.cs @@ -44,20 +44,20 @@ internal class ExceptionUtilTestHelpers /// The captured stack trace to test against. public static void AssertStackTrace(Exception exception, StackTrace stackTrace) { - Trace.TraceInformation( "Stack trace from the failing method: {0}", stackTrace.ToString() ); - Trace.TraceInformation( "Exception stack trace: {0}", exception.StackTrace ); + Trace.TraceInformation("Stack trace from the failing method: {0}", stackTrace.ToString()); + Trace.TraceInformation("Exception stack trace: {0}", exception.StackTrace); string fullST = stackTrace.ToString(); - int newLinePos = fullST.IndexOf( Environment.NewLine ); - string failingMethodExpected = fullST.Substring( 0, (newLinePos == -1) ? fullST.Length : newLinePos ); + int newLinePos = fullST.IndexOf(Environment.NewLine); + string failingMethodExpected = fullST.Substring(0, (newLinePos == -1) ? fullST.Length : newLinePos); - newLinePos = exception.StackTrace.IndexOf( Environment.NewLine ); - string failingMethodActual = exception.StackTrace.Substring( 0, (newLinePos == -1) ? fullST.Length : newLinePos ); + newLinePos = exception.StackTrace.IndexOf(Environment.NewLine); + string failingMethodActual = exception.StackTrace.Substring(0, (newLinePos == -1) ? fullST.Length : newLinePos); //check if the first line of the stack trace fetched from the failing method is contained in the //first line of the exception's stack trace, i.e. the method which threw the exception is in the //stack trace of the exception - Assert.That( failingMethodActual.Contains( failingMethodExpected ) ); + Assert.That(failingMethodActual.Contains(failingMethodExpected)); } } @@ -79,38 +79,38 @@ public void TestPreserveStackTrace() //delegate to the failing method Action foo = () => { - stackTrace = new StackTrace( false ); + stackTrace = new StackTrace(false); throw new InvalidOperationException(); }; foo(); - Assert.Fail( "Exception was not thrown - 1" ); + Assert.Fail("Exception was not thrown - 1"); } - catch( InvalidOperationException iopex ) + catch (InvalidOperationException iopex) { //wrap the exception to make sure that there is an //inner exception that can be re-thrown later on - throw new ArgumentException( string.Empty, iopex ); + throw new ArgumentException(string.Empty, iopex); }; }; bar(); - Assert.Fail( "Exception was not thrown - 2" ); + Assert.Fail("Exception was not thrown - 2"); } - catch( ArgumentException aex ) + catch (ArgumentException aex) { //preserve the stack trace of the nested exception and re-throw it - ExceptionUtil.PreserveStackTrace( aex.InnerException ); + ExceptionUtil.PreserveStackTrace(aex.InnerException); throw aex.InnerException; } - Assert.Fail( "Exception was not thrown - 3" ); + Assert.Fail("Exception was not thrown - 3"); } - catch( InvalidOperationException iopex ) + catch (InvalidOperationException iopex) { - ExceptionUtilTestHelpers.AssertStackTrace( iopex, stackTrace ); + ExceptionUtilTestHelpers.AssertStackTrace(iopex, stackTrace); } } } diff --git a/FrameworkTests/FilterTranslatorTests.cs b/FrameworkTests/FilterTranslatorTests.cs index 1286e763..da942314 100644 --- a/FrameworkTests/FilterTranslatorTests.cs +++ b/FrameworkTests/FilterTranslatorTests.cs @@ -29,136 +29,159 @@ using Org.IdentityConnectors.Framework.Common.Objects.Filters; namespace FrameworkTests { - + internal class AllFiltersTranslator : AbstractFilterTranslator { - protected override String CreateAndExpression(String leftExpression, String rightExpression) { - return "( & " + leftExpression+" "+rightExpression+" )"; + protected override String CreateAndExpression(String leftExpression, String rightExpression) + { + return "( & " + leftExpression + " " + rightExpression + " )"; } - - protected override String CreateOrExpression(String leftExpression, String rightExpression) { - return "( | " + leftExpression+" "+rightExpression+" )"; + + protected override String CreateOrExpression(String leftExpression, String rightExpression) + { + return "( | " + leftExpression + " " + rightExpression + " )"; } - - protected override String CreateContainsExpression(ContainsFilter filter, bool not) { - String rv = "( CONTAINS "+filter.GetName()+" "+filter.GetValue()+" )"; - return Not(rv,not); + + protected override String CreateContainsExpression(ContainsFilter filter, bool not) + { + String rv = "( CONTAINS " + filter.GetName() + " " + filter.GetValue() + " )"; + return Not(rv, not); } - - protected override String CreateEndsWithExpression(EndsWithFilter filter, bool not) { - String rv = "( ENDS-WITH "+filter.GetName()+" "+filter.GetValue()+" )"; - return Not(rv,not); + + protected override String CreateEndsWithExpression(EndsWithFilter filter, bool not) + { + String rv = "( ENDS-WITH " + filter.GetName() + " " + filter.GetValue() + " )"; + return Not(rv, not); } - - protected override String CreateEqualsExpression(EqualsFilter filter, bool not) { - String rv = "( = "+filter.GetAttribute().Name+" ["+filter.GetAttribute().Value[0]+"] )"; - return Not(rv,not); + + protected override String CreateEqualsExpression(EqualsFilter filter, bool not) + { + String rv = "( = " + filter.GetAttribute().Name + " [" + filter.GetAttribute().Value[0] + "] )"; + return Not(rv, not); } - - protected override String CreateGreaterThanExpression(GreaterThanFilter filter, bool not) { - String rv = "( > "+filter.GetName()+" "+filter.GetValue()+" )"; - return Not(rv,not); + + protected override String CreateGreaterThanExpression(GreaterThanFilter filter, bool not) + { + String rv = "( > " + filter.GetName() + " " + filter.GetValue() + " )"; + return Not(rv, not); } - - protected override String CreateGreaterThanOrEqualExpression(GreaterThanOrEqualFilter filter, bool not) { - String rv = "( >= "+filter.GetName()+" "+filter.GetValue()+" )"; - return Not(rv,not); + + protected override String CreateGreaterThanOrEqualExpression(GreaterThanOrEqualFilter filter, bool not) + { + String rv = "( >= " + filter.GetName() + " " + filter.GetValue() + " )"; + return Not(rv, not); } - - protected override String CreateLessThanExpression(LessThanFilter filter, bool not) { - String rv = "( < "+filter.GetName()+" "+filter.GetValue()+" )"; - return Not(rv,not); + + protected override String CreateLessThanExpression(LessThanFilter filter, bool not) + { + String rv = "( < " + filter.GetName() + " " + filter.GetValue() + " )"; + return Not(rv, not); } - - protected override String CreateLessThanOrEqualExpression(LessThanOrEqualFilter filter, bool not) { - String rv = "( <= "+filter.GetName()+" "+filter.GetValue()+" )"; - return Not(rv,not); + + protected override String CreateLessThanOrEqualExpression(LessThanOrEqualFilter filter, bool not) + { + String rv = "( <= " + filter.GetName() + " " + filter.GetValue() + " )"; + return Not(rv, not); } - - protected override String CreateStartsWithExpression(StartsWithFilter filter, bool not) { - String rv = "( STARTS-WITH "+filter.GetName()+" "+filter.GetValue()+" )"; - return Not(rv,not); + + protected override String CreateStartsWithExpression(StartsWithFilter filter, bool not) + { + String rv = "( STARTS-WITH " + filter.GetName() + " " + filter.GetValue() + " )"; + return Not(rv, not); } - protected override String CreateContainsAllValuesExpression(ContainsAllValuesFilter filter, bool not) { - String rv = "( CONTAINS-ALL-VALUES "+filter.GetAttribute()+" )"; - return Not(rv,not); + protected override String CreateContainsAllValuesExpression(ContainsAllValuesFilter filter, bool not) + { + String rv = "( CONTAINS-ALL-VALUES " + filter.GetAttribute() + " )"; + return Not(rv, not); } - private String Not(String orig, bool not) { - if ( not ) { + private String Not(String orig, bool not) + { + if (not) + { return "( ! " + orig + " )"; } - else { + else + { return orig; } } - } - - /** - * Everything but Or - */ + + /// + /// Everything but Or + /// internal class NoOrTranslator : AllFiltersTranslator { - protected override String CreateOrExpression(String leftExpression, String rightExpression) { + protected override String CreateOrExpression(String leftExpression, String rightExpression) + { return null; } } - /** - * Everything but EndsWith - */ + /// + /// Everything but EndsWith + /// internal class NoEndsWithTranslator : AllFiltersTranslator { - protected override String CreateEndsWithExpression(EndsWithFilter filter, bool not) { + protected override String CreateEndsWithExpression(EndsWithFilter filter, bool not) + { return null; } } - /** - * Everything but EndsWith,Or - */ + /// + /// Everything but EndsWith,Or + /// internal class NoEndsWithNoOrTranslator : AllFiltersTranslator { - protected override String CreateOrExpression(String leftExpression, String rightExpression) { + protected override String CreateOrExpression(String leftExpression, String rightExpression) + { return null; } - protected override String CreateEndsWithExpression(EndsWithFilter filter, bool not) { + protected override String CreateEndsWithExpression(EndsWithFilter filter, bool not) + { return null; } } - - /** - * Everything but And - */ + + /// + /// Everything but And + /// internal class NoAndTranslator : AllFiltersTranslator { - protected override String CreateAndExpression(String leftExpression, String rightExpression) { + protected override String CreateAndExpression(String leftExpression, String rightExpression) + { return null; } } - /** - * Everything but And - */ + /// + /// Everything but And + /// internal class NoAndNoEndsWithTranslator : AllFiltersTranslator { - protected override String CreateAndExpression(String leftExpression, String rightExpression) { + protected override String CreateAndExpression(String leftExpression, String rightExpression) + { return null; } - protected override String CreateEndsWithExpression(EndsWithFilter filter, bool not) { + protected override String CreateEndsWithExpression(EndsWithFilter filter, bool not) + { return null; } } - /** - * Everything but And - */ + /// + /// Everything but And + /// internal class NoAndNoOrNoEndsWithTranslator : AllFiltersTranslator { - protected override String CreateAndExpression(String leftExpression, String rightExpression) { + protected override String CreateAndExpression(String leftExpression, String rightExpression) + { return null; } - protected override String CreateEndsWithExpression(EndsWithFilter filter, bool not) { + protected override String CreateEndsWithExpression(EndsWithFilter filter, bool not) + { return null; } - protected override String CreateOrExpression(String leftExpression, String rightExpression) { + protected override String CreateOrExpression(String leftExpression, String rightExpression) + { return null; } @@ -167,19 +190,22 @@ protected override String CreateOrExpression(String leftExpression, String right public class FilterTranslatorTests { - /** - * Test all operations when everything is fully implemented. - * Test not normalization as well. - */ + /// + /// Test all operations when everything is fully implemented. + /// + /// + /// Test not normalization as well. + /// [Test] - public void TestBasics() { + public void TestBasics() + { ConnectorAttribute attribute = ConnectorAttributeBuilder.Build("att-name", "att-value"); ConnectorAttribute attribute2 = ConnectorAttributeBuilder.Build("att-name2", "att-value2"); AllFiltersTranslator translator = new AllFiltersTranslator(); - + { Filter filter = FilterBuilder.Contains(attribute); @@ -187,14 +213,14 @@ public void TestBasics() { String actual = TranslateSingle(translator, filter); Assert.AreEqual(expected, actual); - + filter = FilterBuilder.Not(filter); - expected = "( ! "+expected+" )"; + expected = "( ! " + expected + " )"; actual = TranslateSingle(translator, filter); Assert.AreEqual(expected, actual); } - + { Filter filter = FilterBuilder.EndsWith(attribute); @@ -202,14 +228,14 @@ public void TestBasics() { String actual = TranslateSingle(translator, filter); Assert.AreEqual(expected, actual); - + filter = FilterBuilder.Not(filter); - expected = "( ! "+expected+" )"; + expected = "( ! " + expected + " )"; actual = TranslateSingle(translator, filter); Assert.AreEqual(expected, actual); } - + { Filter filter = FilterBuilder.EqualTo(attribute); @@ -217,14 +243,14 @@ public void TestBasics() { String actual = TranslateSingle(translator, filter); Assert.AreEqual(expected, actual); - + filter = FilterBuilder.Not(filter); - expected = "( ! "+expected+" )"; + expected = "( ! " + expected + " )"; actual = TranslateSingle(translator, filter); Assert.AreEqual(expected, actual); } - + { Filter filter = FilterBuilder.GreaterThan(attribute); @@ -232,14 +258,14 @@ public void TestBasics() { String actual = TranslateSingle(translator, filter); Assert.AreEqual(expected, actual); - + filter = FilterBuilder.Not(filter); - expected = "( ! "+expected+" )"; + expected = "( ! " + expected + " )"; actual = TranslateSingle(translator, filter); Assert.AreEqual(expected, actual); } - + { Filter filter = FilterBuilder.GreaterThanOrEqualTo(attribute); @@ -247,14 +273,14 @@ public void TestBasics() { String actual = TranslateSingle(translator, filter); Assert.AreEqual(expected, actual); - + filter = FilterBuilder.Not(filter); - expected = "( ! "+expected+" )"; + expected = "( ! " + expected + " )"; actual = TranslateSingle(translator, filter); Assert.AreEqual(expected, actual); } - + { Filter filter = FilterBuilder.LessThan(attribute); @@ -262,14 +288,14 @@ public void TestBasics() { String actual = TranslateSingle(translator, filter); Assert.AreEqual(expected, actual); - + filter = FilterBuilder.Not(filter); - expected = "( ! "+expected+" )"; + expected = "( ! " + expected + " )"; actual = TranslateSingle(translator, filter); Assert.AreEqual(expected, actual); } - + { Filter filter = FilterBuilder.LessThanOrEqualTo(attribute); @@ -277,14 +303,14 @@ public void TestBasics() { String actual = TranslateSingle(translator, filter); Assert.AreEqual(expected, actual); - + filter = FilterBuilder.Not(filter); - expected = "( ! "+expected+" )"; + expected = "( ! " + expected + " )"; actual = TranslateSingle(translator, filter); Assert.AreEqual(expected, actual); } - + { Filter filter = FilterBuilder.StartsWith(attribute); @@ -292,29 +318,29 @@ public void TestBasics() { String actual = TranslateSingle(translator, filter); Assert.AreEqual(expected, actual); - + filter = FilterBuilder.Not(filter); - expected = "( ! "+expected+" )"; + expected = "( ! " + expected + " )"; actual = TranslateSingle(translator, filter); Assert.AreEqual(expected, actual); } - + { Filter filter = FilterBuilder.ContainsAllValues(attribute); - String expected = "( CONTAINS-ALL-VALUES "+attribute+" )"; + String expected = "( CONTAINS-ALL-VALUES " + attribute + " )"; String actual = TranslateSingle(translator, filter); Assert.AreEqual(expected, actual); - + filter = FilterBuilder.Not(filter); - expected = "( ! "+expected+" )"; + expected = "( ! " + expected + " )"; actual = TranslateSingle(translator, filter); Assert.AreEqual(expected, actual); } - + //and { Filter left = @@ -326,22 +352,22 @@ public void TestBasics() { Filter filter = FilterBuilder.And(left, right); String expected = - "( & "+expectedLeft+" "+expectedRight+" )"; + "( & " + expectedLeft + " " + expectedRight + " )"; String actual = TranslateSingle(translator, filter); Assert.AreEqual(expected, actual); - + filter = FilterBuilder.Not(filter); - expectedLeft = "( ! "+expectedLeft+" )"; - expectedRight = "( ! "+expectedRight+" )"; + expectedLeft = "( ! " + expectedLeft + " )"; + expectedRight = "( ! " + expectedRight + " )"; expected = - "( | "+expectedLeft+" "+expectedRight+" )"; + "( | " + expectedLeft + " " + expectedRight + " )"; actual = TranslateSingle(translator, filter); Assert.AreEqual(expected, actual); - + } - + //or { Filter left = @@ -353,21 +379,21 @@ public void TestBasics() { Filter filter = FilterBuilder.Or(left, right); String expected = - "( | "+expectedLeft+" "+expectedRight+" )"; + "( | " + expectedLeft + " " + expectedRight + " )"; String actual = TranslateSingle(translator, filter); Assert.AreEqual(expected, actual); - + filter = FilterBuilder.Not(filter); - expectedLeft = "( ! "+expectedLeft+" )"; - expectedRight = "( ! "+expectedRight+" )"; + expectedLeft = "( ! " + expectedLeft + " )"; + expectedRight = "( ! " + expectedRight + " )"; expected = - "( & "+expectedLeft+" "+expectedRight+" )"; + "( & " + expectedLeft + " " + expectedRight + " )"; actual = TranslateSingle(translator, filter); - Assert.AreEqual(expected, actual); + Assert.AreEqual(expected, actual); } - + //double-negative { Filter filter = @@ -379,15 +405,18 @@ public void TestBasics() { TranslateSingle(translator, filter); Assert.AreEqual(expected, actual); } - + } - - /** - * (a OR b) AND ( c OR d) needs to become - * (a AND c) OR ( a AND d) OR (b AND c) OR (b AND d) is - * OR is not implemented. Otherwise it should stay - * as-is. - */ + + /// + /// (a OR b) AND ( c OR d) needs to become + /// (a AND c) OR ( a AND d) OR (b AND c) OR (b AND d) is + /// OR is not implemented. + /// + /// + /// Otherwise it should stay + /// as-is. + /// [Test] public void TestDistribution() { @@ -399,35 +428,36 @@ public void TestDistribution() FilterBuilder.Contains(ConnectorAttributeBuilder.Build("c", "c")); Filter d = FilterBuilder.Contains(ConnectorAttributeBuilder.Build("d", "d")); - + Filter filter = FilterBuilder.And( FilterBuilder.Or(a, b), FilterBuilder.Or(c, d)); String expected = "( & ( | ( CONTAINS a a ) ( CONTAINS b b ) ) ( | ( CONTAINS c c ) ( CONTAINS d d ) ) )"; String actual = - TranslateSingle(new AllFiltersTranslator(),filter); - + TranslateSingle(new AllFiltersTranslator(), filter); + Assert.AreEqual(expected, actual); - + IList results = new NoOrTranslator().Translate(filter); Assert.AreEqual(4, results.Count); - - Assert.AreEqual("( & ( CONTAINS a a ) ( CONTAINS c c ) )", + + Assert.AreEqual("( & ( CONTAINS a a ) ( CONTAINS c c ) )", results[0]); - Assert.AreEqual("( & ( CONTAINS a a ) ( CONTAINS d d ) )", + Assert.AreEqual("( & ( CONTAINS a a ) ( CONTAINS d d ) )", results[1]); - Assert.AreEqual("( & ( CONTAINS b b ) ( CONTAINS c c ) )", + Assert.AreEqual("( & ( CONTAINS b b ) ( CONTAINS c c ) )", results[2]); - Assert.AreEqual("( & ( CONTAINS b b ) ( CONTAINS d d ) )", + Assert.AreEqual("( & ( CONTAINS b b ) ( CONTAINS d d ) )", results[3]); } - + //test simplification //-no leaf [Test] - public void TestSimplifyNoLeaf() { + public void TestSimplifyNoLeaf() + { Filter a = FilterBuilder.Contains(ConnectorAttributeBuilder.Build("a", "a")); Filter b = @@ -436,20 +466,21 @@ public void TestSimplifyNoLeaf() { FilterBuilder.EndsWith(ConnectorAttributeBuilder.Build("c", "c")); Filter d = FilterBuilder.Contains(ConnectorAttributeBuilder.Build("d", "d")); - + Filter filter = FilterBuilder.And( FilterBuilder.Or(a, b), FilterBuilder.Or(c, d)); String expected = "( | ( CONTAINS a a ) ( CONTAINS b b ) )"; String actual = - TranslateSingle(new NoEndsWithTranslator(),filter); + TranslateSingle(new NoEndsWithTranslator(), filter); Assert.AreEqual(expected, actual); - + } //-no leaf + no or [Test] - public void TestSimplifyNoLeafNoOr() { + public void TestSimplifyNoLeafNoOr() + { Filter a = FilterBuilder.Contains(ConnectorAttributeBuilder.Build("a", "a")); Filter b = @@ -458,7 +489,7 @@ public void TestSimplifyNoLeafNoOr() { FilterBuilder.EndsWith(ConnectorAttributeBuilder.Build("c", "c")); Filter d = FilterBuilder.Contains(ConnectorAttributeBuilder.Build("d", "d")); - + Filter filter = FilterBuilder.And( FilterBuilder.Or(a, b), @@ -466,16 +497,17 @@ public void TestSimplifyNoLeafNoOr() { IList results = new NoEndsWithNoOrTranslator().Translate(filter); Assert.AreEqual(2, results.Count); - Assert.AreEqual("( CONTAINS a a )", + Assert.AreEqual("( CONTAINS a a )", results[0]); - Assert.AreEqual("( CONTAINS b b )", + Assert.AreEqual("( CONTAINS b b )", results[1]); - + } - + //-no and [Test] - public void TestSimplifyNoAnd() { + public void TestSimplifyNoAnd() + { Filter a = FilterBuilder.Contains(ConnectorAttributeBuilder.Build("a", "a")); Filter b = @@ -484,20 +516,21 @@ public void TestSimplifyNoAnd() { FilterBuilder.EndsWith(ConnectorAttributeBuilder.Build("c", "c")); Filter d = FilterBuilder.Contains(ConnectorAttributeBuilder.Build("d", "d")); - + Filter filter = FilterBuilder.And( FilterBuilder.Or(a, b), FilterBuilder.Or(c, d)); String expected = "( | ( CONTAINS a a ) ( CONTAINS b b ) )"; String actual = - TranslateSingle(new NoAndTranslator(),filter); - Assert.AreEqual(expected, actual); + TranslateSingle(new NoAndTranslator(), filter); + Assert.AreEqual(expected, actual); } - + //-no and+no leaf [Test] - public void TestSimplifyNoAndNoLeaf() { + public void TestSimplifyNoAndNoLeaf() + { Filter a = FilterBuilder.Contains(ConnectorAttributeBuilder.Build("a", "a")); Filter b = @@ -506,16 +539,16 @@ public void TestSimplifyNoAndNoLeaf() { FilterBuilder.EndsWith(ConnectorAttributeBuilder.Build("c", "c")); Filter d = FilterBuilder.Contains(ConnectorAttributeBuilder.Build("d", "d")); - + Filter filter = FilterBuilder.And( FilterBuilder.Or(a, b), FilterBuilder.Or(c, d)); String expected = "( | ( CONTAINS a a ) ( CONTAINS b b ) )"; String actual = - TranslateSingle(new NoAndNoEndsWithTranslator(),filter); - Assert.AreEqual(expected, actual); - + TranslateSingle(new NoAndNoEndsWithTranslator(), filter); + Assert.AreEqual(expected, actual); + a = FilterBuilder.Contains(ConnectorAttributeBuilder.Build("a", "a")); b = @@ -524,16 +557,16 @@ public void TestSimplifyNoAndNoLeaf() { FilterBuilder.Contains(ConnectorAttributeBuilder.Build("c", "c")); d = FilterBuilder.Contains(ConnectorAttributeBuilder.Build("d", "d")); - + filter = FilterBuilder.And( FilterBuilder.Or(a, b), FilterBuilder.Or(c, d)); expected = "( | ( CONTAINS c c ) ( CONTAINS d d ) )"; actual = - TranslateSingle(new NoAndNoEndsWithTranslator(),filter); - Assert.AreEqual(expected, actual); - + TranslateSingle(new NoAndNoEndsWithTranslator(), filter); + Assert.AreEqual(expected, actual); + a = FilterBuilder.Contains(ConnectorAttributeBuilder.Build("a", "a")); b = @@ -542,19 +575,20 @@ public void TestSimplifyNoAndNoLeaf() { FilterBuilder.Contains(ConnectorAttributeBuilder.Build("c", "c")); d = FilterBuilder.EndsWith(ConnectorAttributeBuilder.Build("d", "d")); - + filter = FilterBuilder.And( FilterBuilder.Or(a, b), FilterBuilder.Or(c, d)); - IList results = + IList results = new NoAndNoEndsWithTranslator().Translate(filter); - Assert.AreEqual(0, results.Count); + Assert.AreEqual(0, results.Count); } - + //-no and, no or, no leaf [Test] - public void TestSimplifyNoAndNoOrNoLeaf() { + public void TestSimplifyNoAndNoOrNoLeaf() + { Filter a = FilterBuilder.Contains(ConnectorAttributeBuilder.Build("a", "a")); Filter b = @@ -563,7 +597,7 @@ public void TestSimplifyNoAndNoOrNoLeaf() { FilterBuilder.EndsWith(ConnectorAttributeBuilder.Build("c", "c")); Filter d = FilterBuilder.Contains(ConnectorAttributeBuilder.Build("d", "d")); - + Filter filter = FilterBuilder.And( FilterBuilder.Or(a, b), @@ -571,11 +605,11 @@ public void TestSimplifyNoAndNoOrNoLeaf() { IList results = new NoAndNoOrNoEndsWithTranslator().Translate(filter); Assert.AreEqual(2, results.Count); - Assert.AreEqual("( CONTAINS a a )", + Assert.AreEqual("( CONTAINS a a )", results[0]); - Assert.AreEqual("( CONTAINS b b )", + Assert.AreEqual("( CONTAINS b b )", results[1]); - + a = FilterBuilder.Contains(ConnectorAttributeBuilder.Build("a", "a")); b = @@ -591,19 +625,19 @@ public void TestSimplifyNoAndNoOrNoLeaf() { results = new NoAndNoOrNoEndsWithTranslator().Translate(filter); Assert.AreEqual(2, results.Count); - Assert.AreEqual("( CONTAINS c c )", + Assert.AreEqual("( CONTAINS c c )", results[0]); - Assert.AreEqual("( CONTAINS d d )", + Assert.AreEqual("( CONTAINS d d )", results[1]); - } - + private static String TranslateSingle(AbstractFilterTranslator translator, - Filter filter) { + Filter filter) + { IList translated = translator.Translate(filter); Assert.AreEqual(1, translated.Count); return translated[0]; } } -} +} \ No newline at end of file diff --git a/FrameworkTests/GuardedByteArrayTests.cs b/FrameworkTests/GuardedByteArrayTests.cs index 2863dcb8..17f05c48 100755 --- a/FrameworkTests/GuardedByteArrayTests.cs +++ b/FrameworkTests/GuardedByteArrayTests.cs @@ -42,56 +42,64 @@ public void TestBasics() ss.AppendByte(0x01); ss.AppendByte(0x02); byte[] decrypted = DecryptToByteArray(ss); - Assert.AreEqual(new byte[] { 0x00, 0x01, 0x02} ,decrypted); + Assert.AreEqual(new byte[] { 0x00, 0x01, 0x02 }, decrypted); String hash = ss.GetBase64SHA1Hash(); Assert.IsTrue(ss.VerifyBase64SHA1Hash(hash)); ss.AppendByte(0x03); Assert.IsFalse(ss.VerifyBase64SHA1Hash(hash)); } [Test] - public void TestRange() { - - for ( byte i = 0; i < 0xFF; i++) { + public void TestRange() + { + + for (byte i = 0; i < 0xFF; i++) + { byte expected = i; GuardedByteArray gba = new GuardedByteArray(); gba = (GuardedByteArray)SerializerUtil.CloneObject(gba); gba.AppendByte(i); - gba.Access(clearChars => { - int v = (byte)clearChars[0]; - Assert.AreEqual(expected, v); - }); - + gba.Access(clearChars => + { + int v = (byte)clearChars[0]; + Assert.AreEqual(expected, v); + }); + } } - + [Test] - public void TestEquals() { + public void TestEquals() + { GuardedByteArray arr1 = new GuardedByteArray(); GuardedByteArray arr2 = new GuardedByteArray(); Assert.AreEqual(arr1, arr2); arr2.AppendByte(0x02); - Assert.AreNotEqual(arr1,arr2); + Assert.AreNotEqual(arr1, arr2); arr1.AppendByte(0x02); Assert.AreEqual(arr1, arr2); } - - /** - * Highly insecure method! Do not do this in production - * code. This is only for test purposes - */ - private byte[] DecryptToByteArray(GuardedByteArray str) { + + /// + /// Highly insecure method! Do not do this in production + /// code. + /// + /// + /// This is only for test purposes + /// + private byte[] DecryptToByteArray(GuardedByteArray str) + { byte[] result = null; str.Access( - array=> - { - result = new byte[array.Length]; - for ( int i = 0 ; i < array.Length; i++) - { - result[i] = array[i]; - } - }); + array => + { + result = new byte[array.Length]; + for (int i = 0; i < array.Length; i++) + { + result[i] = array[i]; + } + }); return result; } } -} +} \ No newline at end of file diff --git a/FrameworkTests/GuardedStringTests.cs b/FrameworkTests/GuardedStringTests.cs index 8e15b28e..75cd8a41 100644 --- a/FrameworkTests/GuardedStringTests.cs +++ b/FrameworkTests/GuardedStringTests.cs @@ -45,56 +45,63 @@ public void TestBasics() ss.AppendChar('a'); ss.AppendChar('r'); String decrypted = DecryptToString(ss); - Assert.AreEqual("foobar",decrypted); + Assert.AreEqual("foobar", decrypted); String hash = ss.GetBase64SHA1Hash(); Assert.IsTrue(ss.VerifyBase64SHA1Hash(hash)); ss.AppendChar('2'); Assert.IsFalse(ss.VerifyBase64SHA1Hash(hash)); } [Test] - public void TestUnicode() { - - for ( int i = 0; i < 0xFFFF; i++) { + public void TestUnicode() + { + + for (int i = 0; i < 0xFFFF; i++) + { int expected = i; char c = (char)i; GuardedString gs = new GuardedString(); gs = (GuardedString)SerializerUtil.CloneObject(gs); gs.AppendChar(c); - gs.Access(clearChars=> { - int v = (int)clearChars[0]; - Assert.AreEqual(expected, v); - }); - + gs.Access(clearChars => + { + int v = (int)clearChars[0]; + Assert.AreEqual(expected, v); + }); + } } - + [Test] - public void TestEquals() { + public void TestEquals() + { GuardedString str1 = new GuardedString(); GuardedString str2 = new GuardedString(); Assert.AreEqual(str1, str2); str2.AppendChar('2'); - Assert.AreNotEqual(str1,str2); + Assert.AreNotEqual(str1, str2); str1.AppendChar('2'); Assert.AreEqual(str1, str2); } - - /** - * Highly insecure method! Do not do this in production - * code. This is only for test purposes - */ - private String DecryptToString(GuardedString str) { + /// + /// Highly insecure method! Do not do this in production + /// code. + /// + /// + /// This is only for test purposes + /// + private String DecryptToString(GuardedString str) + { StringBuilder buf = new StringBuilder(); str.Access( - array=> - { - for ( int i = 0 ; i < array.Length; i++) - { - buf.Append(array[i]); - } - }); + array => + { + for (int i = 0; i < array.Length; i++) + { + buf.Append(array[i]); + } + }); return buf.ToString(); } } -} +} \ No newline at end of file diff --git a/FrameworkTests/LocaleTests.cs b/FrameworkTests/LocaleTests.cs index 1119afec..3de09b88 100644 --- a/FrameworkTests/LocaleTests.cs +++ b/FrameworkTests/LocaleTests.cs @@ -31,14 +31,14 @@ namespace FrameworkTests [TestFixture] public class LocaleTests { - - + + [Test] public void TestJava2CSharp() { HashSet cultures = new HashSet(CultureInfo.GetCultures(CultureTypes.AllCultures)); - + TestJavaLocale(cultures, new Locale("ar", "", ""), "Arabic"); TestJavaLocale(cultures, new Locale("be", "", ""), "Belarusian"); TestJavaLocale(cultures, new Locale("bg", "", ""), "Bulgarian"); @@ -91,8 +91,8 @@ public void TestJava2CSharp() TestJavaLocale(cultures, new Locale("ar", "OM", ""), "Arabic (Oman)"); TestJavaLocale(cultures, new Locale("ar", "QA", ""), "Arabic (Qatar)"); TestJavaLocale(cultures, new Locale("ar", "SA", ""), "Arabic (Saudi Arabia)"); - TestJavaLocale(cultures, - new Locale("ar", "SD", ""), + TestJavaLocale(cultures, + new Locale("ar", "SD", ""), "Arabic (Sudan)", new Locale("ar")); TestJavaLocale(cultures, new Locale("ar", "SY", ""), "Arabic (Syria)"); @@ -112,7 +112,7 @@ public void TestJava2CSharp() TestJavaLocale(cultures, new Locale("en", "CA", ""), "English (Canada)"); TestJavaLocale(cultures, new Locale("en", "GB", ""), "English (United Kingdom)"); TestJavaLocale(cultures, new Locale("en", "IE", ""), "English (Ireland)"); - TestJavaLocale(cultures, new Locale("en", "IN", ""), + TestJavaLocale(cultures, new Locale("en", "IN", ""), "English (India)", new Locale("en")); TestJavaLocale(cultures, new Locale("en", "NZ", ""), "English (New Zealand)"); @@ -168,17 +168,17 @@ public void TestJava2CSharp() TestJavaLocale(cultures, new Locale("sk", "SK", ""), "Slovak (Slovakia)"); TestJavaLocale(cultures, new Locale("sl", "SI", ""), "Slovenian (Slovenia)"); TestJavaLocale(cultures, new Locale("sq", "AL", ""), "Albanian (Albania)"); - TestJavaLocale(cultures, new Locale("sr", "BA", ""), + TestJavaLocale(cultures, new Locale("sr", "BA", ""), "Serbian (Bosnia and Herzegovina)", new Locale("sr")); - TestJavaLocale(cultures, new Locale("sr", "CS", ""), + TestJavaLocale(cultures, new Locale("sr", "CS", ""), "Serbian (Serbia and Montenegro)", new Locale("sr")); TestJavaLocale(cultures, new Locale("sv", "SE", ""), "Swedish (Sweden)"); TestJavaLocale(cultures, new Locale("th", "TH", ""), "Thai (Thailand)"); - TestJavaLocale(cultures, new Locale("th", "TH", "TH"), + TestJavaLocale(cultures, new Locale("th", "TH", "TH"), "Thai (Thailand,TH)", - new Locale("th","TH")); + new Locale("th", "TH")); TestJavaLocale(cultures, new Locale("tr", "TR", ""), "Turkish (Turkey)"); TestJavaLocale(cultures, new Locale("uk", "UA", ""), "Ukrainian (Ukraine)"); TestJavaLocale(cultures, new Locale("vi", "VN", ""), "Vietnamese (Vietnam)"); @@ -190,21 +190,24 @@ public void TestJava2CSharp() // Console.WriteLine("remaining: "+info+" "+info.DisplayName+" "+info.TwoLetterISOLanguageName); //} } - private void TestJavaLocale(HashSet cultures, - Locale original, - String display) { - TestJavaLocale(cultures,original,display,null); + private void TestJavaLocale(HashSet cultures, + Locale original, + String display) + { + TestJavaLocale(cultures, original, display, null); } - private void TestJavaLocale(HashSet cultures, - Locale original, + private void TestJavaLocale(HashSet cultures, + Locale original, String display, - Locale expected) { - if ( expected == null ) { + Locale expected) + { + if (expected == null) + { expected = original; } CultureInfo cinfo = original.ToCultureInfo(); Locale actual = Locale.FindLocale(cinfo); - Assert.AreEqual(expected,actual,display+" ("+original+") "+" didn't map"); + Assert.AreEqual(expected, actual, display + " (" + original + ") " + " didn't map"); } } -} +} \ No newline at end of file diff --git a/FrameworkTests/MockConnector.cs b/FrameworkTests/MockConnector.cs index e7f78967..e3aefa48 100755 --- a/FrameworkTests/MockConnector.cs +++ b/FrameworkTests/MockConnector.cs @@ -124,12 +124,12 @@ public static void AddCall(string methodName, params object[] args) } public class MockAllOpsConnector : MockConnector, CreateOp, - DeleteOp, UpdateOp, SearchOp, UpdateAttributeValuesOp, AuthenticateOp, - ResolveUsernameOp, TestOp, ScriptOnConnectorOp, ScriptOnResourceOp - { + DeleteOp, UpdateOp, SearchOp, UpdateAttributeValuesOp, AuthenticateOp, + ResolveUsernameOp, TestOp, ScriptOnConnectorOp, ScriptOnResourceOp + { public object RunScriptOnConnector(ScriptContext request, - OperationOptions options) + OperationOptions options) { Assert.IsNotNull(request); Assert.IsNotNull(options); @@ -138,7 +138,7 @@ public object RunScriptOnConnector(ScriptContext request, } public object RunScriptOnResource(ScriptContext request, - OperationOptions options) + OperationOptions options) { Assert.IsNotNull(request); Assert.IsNotNull(options); @@ -147,7 +147,7 @@ public object RunScriptOnResource(ScriptContext request, } public Uid Create(ObjectClass oclass, ICollection attrs, - OperationOptions options) + OperationOptions options) { Assert.IsNotNull(attrs); AddCall("Create", attrs); @@ -155,7 +155,7 @@ public Uid Create(ObjectClass oclass, ICollection attrs, } public void Delete(ObjectClass objClass, Uid uid, - OperationOptions options) + OperationOptions options) { Assert.IsNotNull(uid); Assert.IsNotNull(objClass); @@ -163,30 +163,30 @@ public void Delete(ObjectClass objClass, Uid uid, } public Uid Update(ObjectClass objclass, Uid uid, ICollection attrs, - OperationOptions options) + OperationOptions options) { Assert.IsNotNull(objclass); Assert.IsNotNull(attrs); AddCall("Update", objclass, attrs); return null; } - + public Uid AddAttributeValues(ObjectClass objclass, Uid uid, - ICollection valuesToAdd, OperationOptions options) + ICollection valuesToAdd, OperationOptions options) { AddCall("AddAttributeValues", objclass, valuesToAdd); return null; } - + public Uid RemoveAttributeValues(ObjectClass objclass, Uid uid, - ICollection valuesToRemove, OperationOptions options) + ICollection valuesToRemove, OperationOptions options) { AddCall("RemoveAttributeValues", objclass, valuesToRemove); return null; } - + public FilterTranslator CreateFilterTranslator(ObjectClass oclass, - OperationOptions options) + OperationOptions options) { Assert.IsNotNull(oclass); Assert.IsNotNull(options); @@ -196,7 +196,7 @@ public FilterTranslator CreateFilterTranslator(ObjectClass oclass, } public void ExecuteQuery(ObjectClass oclass, string query, - ResultsHandler handler, OperationOptions options) + ResultsHandler handler, OperationOptions options) { Assert.IsNotNull(oclass); Assert.IsNotNull(handler); @@ -205,7 +205,7 @@ public void ExecuteQuery(ObjectClass oclass, string query, } public Uid Authenticate(ObjectClass objectClass, string username, GuardedString password, - OperationOptions options) + OperationOptions options) { Assert.IsNotNull(username); Assert.IsNotNull(password); @@ -220,7 +220,7 @@ public Uid ResolveUsername(ObjectClass objectClass, string username, OperationOp return null; } - public void Test() + public void Test() { AddCall("Test"); } @@ -230,7 +230,7 @@ public class MockUpdateConnector : Connector, UpdateOp, SearchOp { private Configuration _cfg; - + public void Dispose() { // nothing to do this is a mock connector.. @@ -278,7 +278,7 @@ public Uid Update(ObjectClass objclass, Uid uid, ICollection objects[idx] = obj; return obj.Uid; } - + public FilterTranslator CreateFilterTranslator(ObjectClass oclass, OperationOptions options) { //no translation - ok since this is just for tests @@ -325,9 +325,10 @@ public MockConfiguration(bool failvalidation) public bool Fail { - get; set; + get; + set; } - + public override void Validate() { if (fail) @@ -337,4 +338,4 @@ public override void Validate() } } -} +} \ No newline at end of file diff --git a/FrameworkTests/ObjectNormalizerFacadeTests.cs b/FrameworkTests/ObjectNormalizerFacadeTests.cs index 6fdfc6f8..f9a75d10 100644 --- a/FrameworkTests/ObjectNormalizerFacadeTests.cs +++ b/FrameworkTests/ObjectNormalizerFacadeTests.cs @@ -33,119 +33,124 @@ namespace FrameworkTests [TestFixture] public class ObjectNormalizerFacadeTests { - public class MyAttributeNormalizer : AttributeNormalizer { - public ConnectorAttribute NormalizeAttribute(ObjectClass oclass, ConnectorAttribute attribute) { - if ( attribute.Is("foo")) { + public class MyAttributeNormalizer : AttributeNormalizer + { + public ConnectorAttribute NormalizeAttribute(ObjectClass oclass, ConnectorAttribute attribute) + { + if (attribute.Is("foo")) + { String val = ConnectorAttributeUtil.GetStringValue(attribute); - return ConnectorAttributeBuilder.Build("foo",val.Trim()); + return ConnectorAttributeBuilder.Build("foo", val.Trim()); } - else { + else + { return attribute; } } } - + private ConnectorAttribute CreateTestAttribute() { - return ConnectorAttributeBuilder.Build("foo"," bar "); + return ConnectorAttributeBuilder.Build("foo", " bar "); } - + private ConnectorAttribute CreateNormalizedTestAttribute() { - return ConnectorAttributeBuilder.Build("foo","bar"); + return ConnectorAttributeBuilder.Build("foo", "bar"); } - - private ObjectNormalizerFacade CreateTestNormalizer() + + private ObjectNormalizerFacade CreateTestNormalizer() { ObjectNormalizerFacade facade = new ObjectNormalizerFacade(ObjectClass.ACCOUNT, new MyAttributeNormalizer()); return facade; } - + private void AssertNormalizedFilter(Filter expectedNormalizedFilter, - Filter filter) { - ObjectNormalizerFacade facade = + Filter filter) + { + ObjectNormalizerFacade facade = CreateTestNormalizer(); filter = facade.NormalizeFilter(filter); String expectedXml = SerializerUtil.SerializeXmlObject(expectedNormalizedFilter, false); String actualXml = SerializerUtil.SerializeXmlObject(filter, false); Assert.AreEqual(expectedXml, actualXml); } - - - + + + [Test] public void TestEndsWith() { Filter expected = FilterBuilder.EndsWith(CreateNormalizedTestAttribute()); - Filter filter = - FilterBuilder.EndsWith(CreateTestAttribute()); - AssertNormalizedFilter(expected,filter); - } + Filter filter = + FilterBuilder.EndsWith(CreateTestAttribute()); + AssertNormalizedFilter(expected, filter); + } [Test] public void TestStartsWith() { Filter expected = FilterBuilder.StartsWith(CreateNormalizedTestAttribute()); - Filter filter = - FilterBuilder.StartsWith(CreateTestAttribute()); - AssertNormalizedFilter(expected,filter); + Filter filter = + FilterBuilder.StartsWith(CreateTestAttribute()); + AssertNormalizedFilter(expected, filter); } [Test] public void TestContains() { Filter expected = FilterBuilder.Contains(CreateNormalizedTestAttribute()); - Filter filter = - FilterBuilder.Contains(CreateTestAttribute()); - AssertNormalizedFilter(expected,filter); + Filter filter = + FilterBuilder.Contains(CreateTestAttribute()); + AssertNormalizedFilter(expected, filter); } [Test] public void TestEqualTo() { Filter expected = FilterBuilder.EqualTo(CreateNormalizedTestAttribute()); - Filter filter = - FilterBuilder.EqualTo(CreateTestAttribute()); - AssertNormalizedFilter(expected,filter); + Filter filter = + FilterBuilder.EqualTo(CreateTestAttribute()); + AssertNormalizedFilter(expected, filter); } [Test] public void TestGreaterThanOrEqualTo() { Filter expected = FilterBuilder.GreaterThanOrEqualTo(CreateNormalizedTestAttribute()); - Filter filter = - FilterBuilder.GreaterThanOrEqualTo(CreateTestAttribute()); - AssertNormalizedFilter(expected,filter); + Filter filter = + FilterBuilder.GreaterThanOrEqualTo(CreateTestAttribute()); + AssertNormalizedFilter(expected, filter); } [Test] public void TestLessThanOrEqualTo() { Filter expected = FilterBuilder.LessThanOrEqualTo(CreateNormalizedTestAttribute()); - Filter filter = - FilterBuilder.LessThanOrEqualTo(CreateTestAttribute()); - AssertNormalizedFilter(expected,filter); + Filter filter = + FilterBuilder.LessThanOrEqualTo(CreateTestAttribute()); + AssertNormalizedFilter(expected, filter); } [Test] public void TestLessThan() { Filter expected = FilterBuilder.LessThan(CreateNormalizedTestAttribute()); - Filter filter = - FilterBuilder.LessThan(CreateTestAttribute()); - AssertNormalizedFilter(expected,filter); + Filter filter = + FilterBuilder.LessThan(CreateTestAttribute()); + AssertNormalizedFilter(expected, filter); } [Test] public void TestGreaterThan() { Filter expected = FilterBuilder.GreaterThan(CreateNormalizedTestAttribute()); - Filter filter = - FilterBuilder.GreaterThan(CreateTestAttribute()); - AssertNormalizedFilter(expected,filter); + Filter filter = + FilterBuilder.GreaterThan(CreateTestAttribute()); + AssertNormalizedFilter(expected, filter); } [Test] public void TestAnd() @@ -153,10 +158,10 @@ public void TestAnd() Filter expected = FilterBuilder.And(FilterBuilder.Contains(CreateNormalizedTestAttribute()), FilterBuilder.Contains(CreateNormalizedTestAttribute())); - Filter filter = + Filter filter = FilterBuilder.And(FilterBuilder.Contains(CreateTestAttribute()), FilterBuilder.Contains(CreateTestAttribute())); - AssertNormalizedFilter(expected,filter); + AssertNormalizedFilter(expected, filter); } [Test] public void TestOr() @@ -164,30 +169,30 @@ public void TestOr() Filter expected = FilterBuilder.Or(FilterBuilder.Contains(CreateNormalizedTestAttribute()), FilterBuilder.Contains(CreateNormalizedTestAttribute())); - Filter filter = + Filter filter = FilterBuilder.Or(FilterBuilder.Contains(CreateTestAttribute()), FilterBuilder.Contains(CreateTestAttribute())); - AssertNormalizedFilter(expected,filter); + AssertNormalizedFilter(expected, filter); } [Test] public void TestNot() { Filter expected = FilterBuilder.Not(FilterBuilder.Contains(CreateNormalizedTestAttribute())); - Filter filter = + Filter filter = FilterBuilder.Not(FilterBuilder.Contains(CreateTestAttribute())); - AssertNormalizedFilter(expected,filter); + AssertNormalizedFilter(expected, filter); } [Test] public void TestContainsAllValues() { Filter expected = FilterBuilder.ContainsAllValues(CreateNormalizedTestAttribute()); - Filter filter = - FilterBuilder.ContainsAllValues(CreateTestAttribute()); - AssertNormalizedFilter(expected,filter); + Filter filter = + FilterBuilder.ContainsAllValues(CreateTestAttribute()); + AssertNormalizedFilter(expected, filter); } - [Test] + [Test] public void TestConnectorObject() { ConnectorObjectBuilder builder = @@ -206,7 +211,7 @@ public void TestConnectorObject() Assert.AreEqual(expected, v2); Assert.IsFalse(expected.Equals(v1)); } - + [Test] public void TestSyncDelta() { @@ -216,31 +221,31 @@ public void TestSyncDelta() objbuilder.SetUid("myuid"); objbuilder.AddAttribute(CreateTestAttribute()); ConnectorObject obj = objbuilder.Build(); - + SyncDeltaBuilder builder = new SyncDeltaBuilder(); - builder.DeltaType=(SyncDeltaType.DELETE); - builder.Token=(new SyncToken("mytoken")); - builder.Uid=(new Uid("myuid")); - builder.Object=(obj); - SyncDelta v1 = builder.Build(); - SyncDelta v2 = CreateTestNormalizer().NormalizeSyncDelta(v1); - builder = - new SyncDeltaBuilder(); - builder.DeltaType=(SyncDeltaType.DELETE); - builder.Token=(new SyncToken("mytoken")); - builder.Uid=(new Uid("myuid")); - objbuilder = - new ConnectorObjectBuilder(); - objbuilder.SetName("myname"); - objbuilder.SetUid("myuid"); - objbuilder.AddAttribute(CreateNormalizedTestAttribute()); - builder.Object = objbuilder.Build(); - SyncDelta expected = builder.Build(); - Assert.AreEqual(expected, v2); - Assert.IsFalse(expected.Equals(v1)); - - } - + builder.DeltaType = (SyncDeltaType.DELETE); + builder.Token = (new SyncToken("mytoken")); + builder.Uid = (new Uid("myuid")); + builder.Object = (obj); + SyncDelta v1 = builder.Build(); + SyncDelta v2 = CreateTestNormalizer().NormalizeSyncDelta(v1); + builder = + new SyncDeltaBuilder(); + builder.DeltaType = (SyncDeltaType.DELETE); + builder.Token = (new SyncToken("mytoken")); + builder.Uid = (new Uid("myuid")); + objbuilder = + new ConnectorObjectBuilder(); + objbuilder.SetName("myname"); + objbuilder.SetUid("myuid"); + objbuilder.AddAttribute(CreateNormalizedTestAttribute()); + builder.Object = objbuilder.Build(); + SyncDelta expected = builder.Build(); + Assert.AreEqual(expected, v2); + Assert.IsFalse(expected.Equals(v1)); + + } + } -} +} \ No newline at end of file diff --git a/FrameworkTests/ObjectPoolTests.cs b/FrameworkTests/ObjectPoolTests.cs index 90c1e7ed..a4003051 100644 --- a/FrameworkTests/ObjectPoolTests.cs +++ b/FrameworkTests/ObjectPoolTests.cs @@ -36,188 +36,214 @@ namespace FrameworkTests [TestFixture] public class ObjectPoolTests { - private class MyTestConnection { + private class MyTestConnection + { private bool _isGood = true; - - public void Test() { - if (!_isGood) { + + public void Test() + { + if (!_isGood) + { throw new ConnectorException("Connection is bad"); } } - - public void Dispose() { + + public void Dispose() + { _isGood = false; } - - public bool IsGood { - get { + + public bool IsGood + { + get + { return _isGood; } } } - - private class MyTestConnectionFactory : ObjectPoolHandler { - + + private class MyTestConnectionFactory : ObjectPoolHandler + { + private bool _createBadConnection = false; private int _totalCreatedConnections = 0; - - public MyTestConnection NewObject() { + + public MyTestConnection NewObject() + { _totalCreatedConnections++; MyTestConnection rv = new MyTestConnection(); - if (_createBadConnection) { + if (_createBadConnection) + { rv.Dispose(); } return rv; } - public void TestObject(MyTestConnection obj) { + public void TestObject(MyTestConnection obj) + { obj.Test(); } - public void DisposeObject(MyTestConnection obj) { + public void DisposeObject(MyTestConnection obj) + { obj.Dispose(); } - - public int TotalCreatedConnections { - get { + + public int TotalCreatedConnections + { + get + { return _totalCreatedConnections; } } - - public bool CreateBadConnection { - set { + + public bool CreateBadConnection + { + set + { _createBadConnection = value; } - } + } } - - private class MyTestThread { + + private class MyTestThread + { private readonly ObjectPool _pool; private readonly int _numIterations; private Exception _exception; private Thread _thisThread; public MyTestThread(ObjectPool pool, - int numIterations) { + int numIterations) + { _pool = pool; _numIterations = numIterations; _thisThread = new Thread(Run); } - public void Start() { + public void Start() + { _thisThread.Start(); } - - public void Run() { - try { - for ( int i = 0; i < _numIterations; i++ ) { + + public void Run() + { + try + { + for (int i = 0; i < _numIterations; i++) + { MyTestConnection con = _pool.BorrowObject(); - Thread.Sleep(300); + Thread.Sleep(300); _pool.ReturnObject(con); } } - catch (Exception e) { + catch (Exception e) + { _exception = e; } } - public void Shutdown() { + public void Shutdown() + { _thisThread.Join(); - if (_exception != null) { + if (_exception != null) + { throw _exception; } } - + } - + [Test] public void TestWithManyThreads() { int NUM_ITERATIONS = 10; int NUM_THREADS = 10; - int MAX_CONNECTIONS = NUM_THREADS-3; //make sure we get some waiting + int MAX_CONNECTIONS = NUM_THREADS - 3; //make sure we get some waiting ObjectPoolConfiguration config = new ObjectPoolConfiguration(); - config.MaxObjects=(MAX_CONNECTIONS); - config.MaxIdle=(MAX_CONNECTIONS); - config.MinIdle=(MAX_CONNECTIONS); - config.MinEvictableIdleTimeMillis=(60*1000); - config.MaxWait=(60*1000); + config.MaxObjects = (MAX_CONNECTIONS); + config.MaxIdle = (MAX_CONNECTIONS); + config.MinIdle = (MAX_CONNECTIONS); + config.MinEvictableIdleTimeMillis = (60 * 1000); + config.MaxWait = (60 * 1000); MyTestConnectionFactory fact = new MyTestConnectionFactory(); - - ObjectPool pool = new ObjectPool(fact,config); - - MyTestThread [] threads = new MyTestThread[NUM_THREADS]; - for (int i = 0; i < threads.Length; i++) { - threads[i] = new MyTestThread(pool,NUM_ITERATIONS); + + ObjectPool pool = new ObjectPool(fact, config); + + MyTestThread[] threads = new MyTestThread[NUM_THREADS]; + for (int i = 0; i < threads.Length; i++) + { + threads[i] = new MyTestThread(pool, NUM_ITERATIONS); threads[i].Start(); } - - foreach (MyTestThread thread in threads) { + + foreach (MyTestThread thread in threads) + { thread.Shutdown(); } - + //these should be the same since we never //should have disposed anything Assert.AreEqual(MAX_CONNECTIONS, fact.TotalCreatedConnections); ObjectPool.Statistics stats = pool.GetStatistics(); Assert.AreEqual(0, stats.NumActive); Assert.AreEqual(MAX_CONNECTIONS, stats.NumIdle); - + pool.Shutdown(); stats = pool.GetStatistics(); Assert.AreEqual(0, stats.NumActive); - Assert.AreEqual(0, stats.NumIdle); - + Assert.AreEqual(0, stats.NumIdle); + } - + [Test] public void TestBadConnection() { int MAX_CONNECTIONS = 3; ObjectPoolConfiguration config = new ObjectPoolConfiguration(); - config.MaxObjects=(MAX_CONNECTIONS); - config.MaxIdle=(MAX_CONNECTIONS); - config.MinIdle=(MAX_CONNECTIONS); - config.MinEvictableIdleTimeMillis=(60*1000); - config.MaxWait=(60*1000); + config.MaxObjects = (MAX_CONNECTIONS); + config.MaxIdle = (MAX_CONNECTIONS); + config.MinIdle = (MAX_CONNECTIONS); + config.MinEvictableIdleTimeMillis = (60 * 1000); + config.MaxWait = (60 * 1000); MyTestConnectionFactory fact = new MyTestConnectionFactory(); - - ObjectPool pool = new ObjectPool(fact,config); - + + ObjectPool pool = new ObjectPool(fact, config); + //borrow first connection and return MyTestConnection conn = pool.BorrowObject(); Assert.AreEqual(1, fact.TotalCreatedConnections); pool.ReturnObject(conn); Assert.AreEqual(1, fact.TotalCreatedConnections); - + //re-borrow same connection and return conn = pool.BorrowObject(); Assert.AreEqual(1, fact.TotalCreatedConnections); pool.ReturnObject(conn); Assert.AreEqual(1, fact.TotalCreatedConnections); - + //dispose and make sure we get a new connection - conn.Dispose(); + conn.Dispose(); conn = pool.BorrowObject(); Assert.AreEqual(2, fact.TotalCreatedConnections); pool.ReturnObject(conn); Assert.AreEqual(2, fact.TotalCreatedConnections); } - + [Test] public void TestIdleCleanup() { ObjectPoolConfiguration config = new ObjectPoolConfiguration(); - config.MaxObjects=(3); - config.MaxIdle=(2); - config.MinIdle=(1); - config.MinEvictableIdleTimeMillis=(3000); - config.MaxWait=(60*1000); + config.MaxObjects = (3); + config.MaxIdle = (2); + config.MinIdle = (1); + config.MinEvictableIdleTimeMillis = (3000); + config.MaxWait = (60 * 1000); MyTestConnectionFactory fact = new MyTestConnectionFactory(); - - ObjectPool pool = new ObjectPool(fact,config); - + + ObjectPool pool = new ObjectPool(fact, config); + MyTestConnection conn1 = (MyTestConnection)pool.BorrowObject(); MyTestConnection conn2 = (MyTestConnection)pool.BorrowObject(); MyTestConnection conn3 = (MyTestConnection)pool.BorrowObject(); - + Assert.AreEqual(3, fact.TotalCreatedConnections); pool.ReturnObject(conn1); Assert.AreEqual(1, pool.GetStatistics().NumIdle); @@ -228,7 +254,7 @@ public void TestIdleCleanup() Assert.AreEqual(false, conn1.IsGood); Assert.AreEqual(true, conn2.IsGood); Assert.AreEqual(true, conn3.IsGood); - Thread.Sleep(((int)(config.MinEvictableIdleTimeMillis+1000))); + Thread.Sleep(((int)(config.MinEvictableIdleTimeMillis + 1000))); MyTestConnection conn4 = (MyTestConnection)pool.BorrowObject(); Assert.AreSame(conn3, conn4); Assert.AreEqual(false, conn1.IsGood); @@ -236,19 +262,21 @@ public void TestIdleCleanup() Assert.AreEqual(true, conn3.IsGood); Assert.AreEqual(true, conn4.IsGood); } - + [Test] public void TestCreateBadConnection() { MyTestConnectionFactory fact = new MyTestConnectionFactory(); - fact.CreateBadConnection=(true); - - ObjectPool pool = new ObjectPool(fact,new ObjectPoolConfiguration()); - try { + fact.CreateBadConnection = (true); + + ObjectPool pool = new ObjectPool(fact, new ObjectPoolConfiguration()); + try + { pool.BorrowObject(); Assert.Fail("expected exception"); } - catch (ConnectorException e) { + catch (ConnectorException e) + { Assert.AreEqual("Connection is bad", e.Message); } } diff --git a/FrameworkTests/ObjectSerializationTests.cs b/FrameworkTests/ObjectSerializationTests.cs index b3c1ef0c..02746c14 100644 --- a/FrameworkTests/ObjectSerializationTests.cs +++ b/FrameworkTests/ObjectSerializationTests.cs @@ -49,177 +49,189 @@ namespace FrameworkTests { [TestFixture] public class ObjectSerializationTests - { + { [Test] public void TestBoolean() { bool v1 = true; bool v2 = (bool)CloneObject(v1); Assert.AreEqual(v1, v2); - + v1 = false; v2 = (bool)CloneObject(v1); Assert.AreEqual(v1, v2); } [Test] - public void TestCharacter() { + public void TestCharacter() + { char v1 = 'h'; char v2 = (char)CloneObject(v1); Assert.AreEqual(v1, v2); } [Test] - public void TestInteger() { + public void TestInteger() + { int v1 = 12345; int v2 = (int)CloneObject(v1); - Assert.AreEqual(v1,v2); - + Assert.AreEqual(v1, v2); + v1 = Int32.MinValue; v2 = (int)CloneObject(v1); - Assert.AreEqual(v1,v2); - + Assert.AreEqual(v1, v2); + v1 = Int32.MaxValue; v2 = (int)CloneObject(v1); - Assert.AreEqual(v1,v2); - + Assert.AreEqual(v1, v2); + v1 = -1; v2 = (int)CloneObject(v1); - Assert.AreEqual(v1,v2); + Assert.AreEqual(v1, v2); } - + [Test] - public void TestLong() { + public void TestLong() + { long v1 = 12345; long v2 = (long)CloneObject(v1); - Assert.AreEqual(v1,v2); - + Assert.AreEqual(v1, v2); + v1 = Int64.MinValue; v2 = (long)CloneObject(v1); - Assert.AreEqual(v1,v2); - + Assert.AreEqual(v1, v2); + v1 = Int64.MaxValue; v2 = (long)CloneObject(v1); - Assert.AreEqual(v1,v2); - + Assert.AreEqual(v1, v2); + v1 = -1; v2 = (long)CloneObject(v1); - Assert.AreEqual(v1,v2); + Assert.AreEqual(v1, v2); } - + [Test] - public void TestFloat() { + public void TestFloat() + { float v1 = 1.1F; float v2 = (float)CloneObject(v1); - Assert.IsTrue(!Object.ReferenceEquals(v1,v2)); - Assert.AreEqual(v1,v2); - + Assert.IsTrue(!Object.ReferenceEquals(v1, v2)); + Assert.AreEqual(v1, v2); + v1 = Single.Epsilon; v2 = (float)CloneObject(v1); - Assert.AreEqual(v1,v2); - + Assert.AreEqual(v1, v2); + v1 = Single.NaN; v2 = (float)CloneObject(v1); - Assert.AreEqual(v1,v2); - + Assert.AreEqual(v1, v2); + v1 = Single.NegativeInfinity; v2 = (float)CloneObject(v1); - Assert.AreEqual(v1,v2); - + Assert.AreEqual(v1, v2); + v1 = Single.PositiveInfinity; v2 = (float)CloneObject(v1); - Assert.AreEqual(v1,v2); - + Assert.AreEqual(v1, v2); + v1 = Single.MinValue; v2 = (float)CloneObject(v1); - Assert.AreEqual(v1,v2); - + Assert.AreEqual(v1, v2); + v1 = Single.MaxValue; v2 = (float)CloneObject(v1); - Assert.AreEqual(v1,v2); + Assert.AreEqual(v1, v2); } - + [Test] - public void TestDouble() { + public void TestDouble() + { double v1 = 1.1; double v2 = (double)CloneObject(v1); - Assert.IsTrue(!Object.ReferenceEquals(v1,v2)); - Assert.AreEqual(v1,v2); - + Assert.IsTrue(!Object.ReferenceEquals(v1, v2)); + Assert.AreEqual(v1, v2); + v1 = Double.Epsilon; v2 = (double)CloneObject(v1); - Assert.AreEqual(v1,v2); - + Assert.AreEqual(v1, v2); + v1 = Double.NaN; v2 = (double)CloneObject(v1); - Assert.AreEqual(v1,v2); - + Assert.AreEqual(v1, v2); + v1 = Double.NegativeInfinity; v2 = (double)CloneObject(v1); - Assert.AreEqual(v1,v2); - + Assert.AreEqual(v1, v2); + v1 = Double.PositiveInfinity; v2 = (double)CloneObject(v1); - Assert.AreEqual(v1,v2); - + Assert.AreEqual(v1, v2); + v1 = Double.MaxValue; v2 = (double)CloneObject(v1); - Assert.AreEqual(v1,v2); - + Assert.AreEqual(v1, v2); + v1 = Double.MinValue; v2 = (double)CloneObject(v1); - Assert.AreEqual(v1,v2); + Assert.AreEqual(v1, v2); } - + [Test] - public void TestString() { + public void TestString() + { string v1 = "abcd"; string v2 = (string)CloneObject(v1); - Assert.IsTrue(!Object.ReferenceEquals(v1,v2)); - Assert.AreEqual(v1,v2); + Assert.IsTrue(!Object.ReferenceEquals(v1, v2)); + Assert.AreEqual(v1, v2); } - + [Test] - public void TestURI() { + public void TestURI() + { Uri v1 = new Uri("mailto:java-net@java.sun.com"); Uri v2 = (Uri)CloneObject(v1); - Assert.IsTrue(!Object.ReferenceEquals(v1,v2)); + Assert.IsTrue(!Object.ReferenceEquals(v1, v2)); Assert.AreEqual(v1, v2); } - + [Test] - public void TestFile() { + public void TestFile() + { FileName v1 = new FileName("c:/foo.txt"); FileName v2 = (FileName)CloneObject(v1); - Assert.IsTrue(!Object.ReferenceEquals(v1,v2)); + Assert.IsTrue(!Object.ReferenceEquals(v1, v2)); Assert.AreEqual(v1, v2); } - + [Test] - public void TestBigInteger() { + public void TestBigInteger() + { BigInteger v1 = new BigInteger("983423442347324324324"); BigInteger v2 = (BigInteger)CloneObject(v1); Assert.AreEqual(v1, v2); } - + [Test] - public void TestBigDecimal() { - BigDecimal v1 = new BigDecimal(new BigInteger("9847324324324"),45); + public void TestBigDecimal() + { + BigDecimal v1 = new BigDecimal(new BigInteger("9847324324324"), 45); BigDecimal v2 = (BigDecimal)CloneObject(v1); Assert.AreEqual(v1, v2); } - + [Test] - public void TestByteArray() { - byte [] v1 = {1,2,3}; - byte [] v2 = (byte[])CloneObject(v1); - Assert.AreEqual(3,v2.Length); - Assert.AreEqual(1,v2[0]); - Assert.AreEqual(2,v2[1]); - Assert.AreEqual(3,v2[2]); - Assert.AreEqual(v1,v2); + public void TestByteArray() + { + byte[] v1 = { 1, 2, 3 }; + byte[] v2 = (byte[])CloneObject(v1); + Assert.AreEqual(3, v2.Length); + Assert.AreEqual(1, v2[0]); + Assert.AreEqual(2, v2[1]); + Assert.AreEqual(3, v2[2]); + Assert.AreEqual(v1, v2); } - + [Test] - public void TestClasses() { + public void TestClasses() + { Assert.AreEqual(typeof(bool), CloneObject(typeof(bool))); Assert.AreEqual(typeof(bool?), @@ -236,151 +248,161 @@ public void TestClasses() { //a serializer for it Assert.IsTrue( CollectionUtil.SetsEqual( - CollectionUtil.NewSet(FrameworkUtil.GetAllSupportedConfigTypes()), + CollectionUtil.NewSet(FrameworkUtil.GetAllSupportedConfigTypes()), (ICollection)CloneObject(FrameworkUtil.GetAllSupportedConfigTypes()))); //if this fails, we have added a new type and we need to add //a serializer for it Assert.IsTrue( CollectionUtil.SetsEqual( - CollectionUtil.NewSet(FrameworkUtil.GetAllSupportedAttributeTypes()), + CollectionUtil.NewSet(FrameworkUtil.GetAllSupportedAttributeTypes()), (ICollection)CloneObject(FrameworkUtil.GetAllSupportedAttributeTypes()))); ICollection apiOperations = new HashSet(); - foreach (SafeType op in FrameworkUtil.AllAPIOperations()) { + foreach (SafeType op in FrameworkUtil.AllAPIOperations()) + { apiOperations.Add(op.RawType); } //if this fails, need to add to the OperationMappings class Assert.IsTrue( CollectionUtil.SetsEqual( - CollectionUtil.NewSet(apiOperations), + CollectionUtil.NewSet(apiOperations), (ICollection)CloneObject(apiOperations))); } - + [Test] - public void TestArrays() { - int [] v1 = {1,2,3}; - int [] v2 = (int[])CloneObject(v1); + public void TestArrays() + { + int[] v1 = { 1, 2, 3 }; + int[] v2 = (int[])CloneObject(v1); Assert.AreEqual(3, v2.Length); - Assert.AreEqual(1,v2[0]); - Assert.AreEqual(2,v2[1]); - Assert.AreEqual(3,v2[2]); + Assert.AreEqual(1, v2[0]); + Assert.AreEqual(2, v2[1]); + Assert.AreEqual(3, v2[2]); } - - + + [Test] - public void TestObjectArrays() { - object [] v1 = {"1","2","3"}; - object [] v2 = (object[])CloneObject(v1); + public void TestObjectArrays() + { + object[] v1 = { "1", "2", "3" }; + object[] v2 = (object[])CloneObject(v1); Assert.AreEqual(3, v2.Length); - Assert.AreEqual("1",v2[0]); - Assert.AreEqual("2",v2[1]); - Assert.AreEqual("3",v2[2]); + Assert.AreEqual("1", v2[0]); + Assert.AreEqual("2", v2[1]); + Assert.AreEqual("3", v2[2]); } - + [Test] - public void TestDictionary() { - IDictionary map = new Dictionary(); + public void TestDictionary() + { + IDictionary map = new Dictionary(); map["key1"] = "val1"; map["key2"] = "val2"; - IDictionary map2 = (IDictionary)CloneObject(map); - Assert.AreEqual("val1",map["key1"]); - Assert.AreEqual("val2",map["key2"]); - - IDictionary map3 = new Dictionary(); + IDictionary map2 = (IDictionary)CloneObject(map); + Assert.AreEqual("val1", map["key1"]); + Assert.AreEqual("val2", map["key2"]); + + IDictionary map3 = new Dictionary(); map3["key1"] = "val1"; map3["key2"] = "val2"; - IDictionary map4 = (IDictionary)CloneObject(map3); - Assert.AreEqual("val1",map4["key1"]); - Assert.AreEqual("val2",map4["key2"]); + IDictionary map4 = (IDictionary)CloneObject(map3); + Assert.AreEqual("val1", map4["key1"]); + Assert.AreEqual("val2", map4["key2"]); } [Test] - public void TestCaseInsensitiveMap() { + public void TestCaseInsensitiveMap() + { HashSet set = new HashSet(); set.Add(ConnectorAttributeBuilder.Build("foo1")); set.Add(ConnectorAttributeBuilder.Build("foo2")); - IDictionary map = ConnectorAttributeUtil.ToMap(set); + IDictionary map = ConnectorAttributeUtil.ToMap(set); Assert.IsTrue(map.ContainsKey("Foo1")); Assert.IsTrue(map.ContainsKey("Foo2")); - IDictionary map2 = (IDictionary)CloneObject(map); + IDictionary map2 = (IDictionary)CloneObject(map); Assert.IsTrue(map2.ContainsKey("Foo1")); - Assert.IsTrue(map2.ContainsKey("Foo2")); + Assert.IsTrue(map2.ContainsKey("Foo2")); } - + [Test] - public void TestList() { + public void TestList() + { IList v1 = new List(); v1.Add("val1"); v1.Add("val2"); IList v2 = (IList)CloneObject(v1); - Assert.AreEqual(2,v2.Count); - Assert.AreEqual("val1",v2[0]); - Assert.AreEqual("val2",v2[1]); + Assert.AreEqual(2, v2.Count); + Assert.AreEqual("val1", v2[0]); + Assert.AreEqual("val2", v2[1]); IList v3 = new List(); v3.Add("val1"); v3.Add("val2"); - + IList v4 = (IList)CloneObject(v3); - Assert.AreEqual(2,v4.Count); - Assert.AreEqual("val1",v4[0]); - Assert.AreEqual("val2",v4[1]); + Assert.AreEqual(2, v4.Count); + Assert.AreEqual("val1", v4[0]); + Assert.AreEqual("val2", v4[1]); } - + [Test] - public void TestSet() { + public void TestSet() + { //underneath the covers, this creates an //ICollection - we need to test that ICollection //becomes a Set - ICollection v1 = - CollectionUtil.NewReadOnlySet("val1","val2"); + ICollection v1 = + CollectionUtil.NewReadOnlySet("val1", "val2"); HashSet v2 = (HashSet)CloneObject(v1); - Assert.AreEqual(2,v2.Count); + Assert.AreEqual(2, v2.Count); Assert.IsTrue(v2.Contains("val1")); Assert.IsTrue(v2.Contains("val2")); HashSet v3 = new HashSet(); v3.Add("val1"); v3.Add("val2"); - + HashSet v4 = (HashSet)CloneObject(v3); - Assert.AreEqual(2,v4.Count); + Assert.AreEqual(2, v4.Count); Assert.IsTrue(v4.Contains("val1")); Assert.IsTrue(v4.Contains("val2")); } - + [Test] - public void TestCaseInsensitiveSet() { + public void TestCaseInsensitiveSet() + { ICollection v1 = CollectionUtil.NewCaseInsensitiveSet(); v1.Add("foo"); v1.Add("foo2"); ICollection v2 = (ICollection)CloneObject(v1); Assert.IsTrue(v2.Contains("Foo")); Assert.IsTrue(v2.Contains("Foo2")); - } - - [Test] - public void TestCultureInfo() { + } + + [Test] + public void TestCultureInfo() + { //use this one since it uses all 3 fields of locale CultureInfo v1 = new CultureInfo("nn-NO"); CultureInfo v2 = (CultureInfo)CloneObject(v1); - Assert.AreEqual(v1,v2); + Assert.AreEqual(v1, v2); } - + [Test] - public void TestObjectPoolConfiguration() { + public void TestObjectPoolConfiguration() + { ObjectPoolConfiguration v1 = new ObjectPoolConfiguration(); v1.MaxObjects = 1; v1.MaxIdle = 2; v1.MaxWait = 3; v1.MinEvictableIdleTimeMillis = 4; - v1.MinIdle = 5; - ObjectPoolConfiguration v2 = + v1.MinIdle = 5; + ObjectPoolConfiguration v2 = (ObjectPoolConfiguration)CloneObject(v1); - Assert.IsTrue(!Object.ReferenceEquals(v1,v2)); - - Assert.AreEqual(v1,v2); - + Assert.IsTrue(!Object.ReferenceEquals(v1, v2)); + + Assert.AreEqual(v1, v2); + Assert.AreEqual(1, v2.MaxObjects); Assert.AreEqual(2, v2.MaxIdle); Assert.AreEqual(3, v2.MaxWait); @@ -388,18 +410,19 @@ public void TestObjectPoolConfiguration() { Assert.AreEqual(5, v2.MinIdle); } [Test] - public void TestConfigurationProperty() { + public void TestConfigurationProperty() + { ConfigurationPropertyImpl v1 = new ConfigurationPropertyImpl(); v1.Order = (1); - v1.IsConfidential=(true); - v1.IsRequired=true; - v1.Name=("foo"); - v1.HelpMessageKey=("help key"); - v1.DisplayMessageKey=("display key"); - v1.Value=("bar"); - v1.ValueType=(typeof(string)); + v1.IsConfidential = (true); + v1.IsRequired = true; + v1.Name = ("foo"); + v1.HelpMessageKey = ("help key"); + v1.DisplayMessageKey = ("display key"); + v1.Value = ("bar"); + v1.ValueType = (typeof(string)); v1.Operations = FrameworkUtil.AllAPIOperations(); - + ConfigurationPropertyImpl v2 = (ConfigurationPropertyImpl) CloneObject(v1); Assert.AreEqual(1, v2.Order); @@ -411,62 +434,64 @@ public void TestConfigurationProperty() { Assert.AreEqual("bar", v2.Value); Assert.AreEqual(typeof(string), v2.ValueType); Assert.IsTrue(CollectionUtil.Equals( - FrameworkUtil.AllAPIOperations(),v2.Operations)); + FrameworkUtil.AllAPIOperations(), v2.Operations)); } - + [Test] - public void TestConfigurationProperties() { + public void TestConfigurationProperties() + { ConfigurationPropertyImpl prop1 = new ConfigurationPropertyImpl(); prop1.Order = (1); - prop1.IsConfidential=(true); - prop1.Name=("foo"); - prop1.HelpMessageKey=("help key"); - prop1.DisplayMessageKey=("display key"); - prop1.Value=("bar"); - prop1.ValueType=(typeof(string)); - prop1.Operations=null; - + prop1.IsConfidential = (true); + prop1.Name = ("foo"); + prop1.HelpMessageKey = ("help key"); + prop1.DisplayMessageKey = ("display key"); + prop1.Value = ("bar"); + prop1.ValueType = (typeof(string)); + prop1.Operations = null; + ConfigurationPropertiesImpl v1 = new ConfigurationPropertiesImpl(); - v1.Properties=(CollectionUtil.NewReadOnlyList(prop1)); + v1.Properties = (CollectionUtil.NewReadOnlyList(prop1)); v1.SetPropertyValue("foo", "bar"); - + ConfigurationPropertiesImpl v2 = (ConfigurationPropertiesImpl) CloneObject(v1); Assert.AreEqual("bar", v2.GetProperty("foo").Value); } - + [Test] - public void TestAPIConfiguration() { + public void TestAPIConfiguration() + { ConfigurationPropertyImpl prop1 = new ConfigurationPropertyImpl(); prop1.Order = (1); - prop1.IsConfidential=(true); - prop1.Name=("foo"); - prop1.HelpMessageKey=("help key"); - prop1.DisplayMessageKey=("display key"); - prop1.Value=("bar"); - prop1.ValueType=(typeof(string)); - prop1.Operations=null; - + prop1.IsConfidential = (true); + prop1.Name = ("foo"); + prop1.HelpMessageKey = ("help key"); + prop1.DisplayMessageKey = ("display key"); + prop1.Value = ("bar"); + prop1.ValueType = (typeof(string)); + prop1.Operations = null; + ConfigurationPropertiesImpl props1 = new ConfigurationPropertiesImpl(); - props1.Properties=(CollectionUtil.NewReadOnlyList(prop1)); - + props1.Properties = (CollectionUtil.NewReadOnlyList(prop1)); + APIConfigurationImpl v1 = new APIConfigurationImpl(); - v1.ConnectorPoolConfiguration=(new ObjectPoolConfiguration()); - v1.ConfigurationProperties=(props1); - v1.IsConnectorPoolingSupported=(true); - v1.ProducerBufferSize=(200); - v1.SupportedOperations=(FrameworkUtil.AllAPIOperations()); - IDictionary,int> map = - CollectionUtil.NewDictionary,int>(SafeType.Get(),6); - v1.TimeoutMap=(map); - + v1.ConnectorPoolConfiguration = (new ObjectPoolConfiguration()); + v1.ConfigurationProperties = (props1); + v1.IsConnectorPoolingSupported = (true); + v1.ProducerBufferSize = (200); + v1.SupportedOperations = (FrameworkUtil.AllAPIOperations()); + IDictionary, int> map = + CollectionUtil.NewDictionary, int>(SafeType.Get(), 6); + v1.TimeoutMap = (map); + APIConfigurationImpl v2 = (APIConfigurationImpl) CloneObject(v1); Assert.IsTrue(!Object.ReferenceEquals(v1, v2)); Assert.IsNotNull(v2.ConnectorPoolConfiguration); Assert.IsNotNull(v2.ConfigurationProperties); - Assert.AreEqual(v1.ConnectorPoolConfiguration,v2.ConnectorPoolConfiguration); - Assert.AreEqual(v1.ConfigurationProperties,v2.ConfigurationProperties); + Assert.AreEqual(v1.ConnectorPoolConfiguration, v2.ConnectorPoolConfiguration); + Assert.AreEqual(v1.ConfigurationProperties, v2.ConfigurationProperties); Assert.IsTrue(v2.IsConnectorPoolingSupported); Assert.AreEqual(200, v2.ProducerBufferSize); Assert.IsTrue(CollectionUtil.SetsEqual( @@ -474,18 +499,19 @@ public void TestAPIConfiguration() { v2.SupportedOperations)); Assert.AreEqual(map, v2.TimeoutMap); } - + [Test] - public void TestConnectorMessages() { + public void TestConnectorMessages() + { ConnectorMessagesImpl v1 = new ConnectorMessagesImpl(); - IDictionary defaultMap = new Dictionary(); - defaultMap["key1"]="val1"; - IDictionary> messages = - new Dictionary>(); - messages[new CultureInfo("en")]=defaultMap; - messages[new CultureInfo("")]=defaultMap; - v1.Catalogs=(messages); - + IDictionary defaultMap = new Dictionary(); + defaultMap["key1"] = "val1"; + IDictionary> messages = + new Dictionary>(); + messages[new CultureInfo("en")] = defaultMap; + messages[new CultureInfo("")] = defaultMap; + v1.Catalogs = (messages); + ConnectorMessagesImpl v2 = (ConnectorMessagesImpl) CloneObject(v1); Assert.IsTrue( @@ -495,54 +521,57 @@ public void TestConnectorMessages() { CollectionUtil.SetsEqual(messages[new CultureInfo("en")], v2.Catalogs[new CultureInfo("en")])); } - + [Test] - public void TestRemoteConnectorInfo() { + public void TestRemoteConnectorInfo() + { RemoteConnectorInfoImpl v1 = new RemoteConnectorInfoImpl(); - v1.Messages=(new ConnectorMessagesImpl()); - v1.ConnectorKey=(new ConnectorKey("my bundle", + v1.Messages = (new ConnectorMessagesImpl()); + v1.ConnectorKey = (new ConnectorKey("my bundle", "my version", "my connector")); ConfigurationPropertiesImpl configProperties = new ConfigurationPropertiesImpl(); - configProperties.Properties=(new List()); + configProperties.Properties = (new List()); APIConfigurationImpl apiImpl = new APIConfigurationImpl(); - apiImpl.ConfigurationProperties=(configProperties); - v1.DefaultAPIConfiguration=(apiImpl); - v1.ConnectorDisplayNameKey=("mykey"); - + apiImpl.ConfigurationProperties = (configProperties); + v1.DefaultAPIConfiguration = (apiImpl); + v1.ConnectorDisplayNameKey = ("mykey"); + RemoteConnectorInfoImpl v2 = (RemoteConnectorInfoImpl) CloneObject(v1); - + Assert.IsNotNull(v2.Messages); Assert.AreEqual("my bundle", v2.ConnectorKey.BundleName); Assert.AreEqual("my version", v2.ConnectorKey.BundleVersion); Assert.AreEqual("my connector", v2.ConnectorKey.ConnectorName); - Assert.AreEqual("mykey",v2.ConnectorDisplayNameKey); + Assert.AreEqual("mykey", v2.ConnectorDisplayNameKey); Assert.IsNotNull(v2.DefaultAPIConfiguration); } - + [Test] - public void TestAttribute() { + public void TestAttribute() + { ConnectorAttribute v1 = ConnectorAttributeBuilder.Build("foo", "val1", "val2"); ConnectorAttribute v2 = (ConnectorAttribute)CloneObject(v1); - Assert.AreEqual(v1,v2); + Assert.AreEqual(v1, v2); } - + [Test] - public void TestAttributeInfo() { - + public void TestAttributeInfo() + { + ConnectorAttributeInfoBuilder builder = new ConnectorAttributeInfoBuilder(); - builder.Name=("foo"); - builder.ValueType=(typeof(String)); - builder.Required=(true); - builder.Readable=(true); - builder.Creatable=(true); - builder.Updateable=(true); - builder.MultiValued=(true); + builder.Name = ("foo"); + builder.ValueType = (typeof(String)); + builder.Required = (true); + builder.Readable = (true); + builder.Creatable = (true); + builder.Updateable = (true); + builder.MultiValued = (true); builder.ReturnedByDefault = false; ConnectorAttributeInfo v1 = builder.Build(); ConnectorAttributeInfo v2 = (ConnectorAttributeInfo)CloneObject(v1); - Assert.AreEqual(v1,v2); + Assert.AreEqual(v1, v2); Assert.AreEqual("foo", v2.Name); Assert.AreEqual(typeof(String), v2.ValueType); Assert.IsTrue(v2.IsMultiValued); @@ -551,60 +580,65 @@ public void TestAttributeInfo() { Assert.IsTrue(v2.IsUpdateable); Assert.IsTrue(v2.IsCreatable); Assert.IsFalse(v2.IsReturnedByDefault); - + builder.InfoFlags = AllFlags(); v1 = builder.Build(); v2 = (ConnectorAttributeInfo)CloneObject(v1); - Assert.AreEqual(v1,v2); + Assert.AreEqual(v1, v2); } - + private ConnectorAttributeInfo.Flags AllFlags() { ConnectorAttributeInfo.Flags flags = ConnectorAttributeInfo.Flags.NONE; - foreach (Enum e in Enum.GetValues(typeof(ConnectorAttributeInfo.Flags))) { - ConnectorAttributeInfo.Flags f = + foreach (Enum e in Enum.GetValues(typeof(ConnectorAttributeInfo.Flags))) + { + ConnectorAttributeInfo.Flags f = (ConnectorAttributeInfo.Flags)e; flags |= f; } return flags; } - + [Test] - public void TestConnectorObject() { - ConnectorObjectBuilder bld = new ConnectorObjectBuilder(); - bld.SetUid("foo"); - bld.SetName("fooToo"); - - ConnectorObject v1 = bld.Build(); + public void TestConnectorObject() + { + ConnectorObjectBuilder bld = new ConnectorObjectBuilder(); + bld.SetUid("foo"); + bld.SetName("fooToo"); + + ConnectorObject v1 = bld.Build(); ConnectorObject v2 = (ConnectorObject)CloneObject(v1); Assert.AreEqual(v1, v2); } - + [Test] - public void TestName() { + public void TestName() + { Name v1 = new Name("Test"); Name v2 = (Name)CloneObject(v1); Assert.AreEqual(v1, v2); } - + [Test] - public void TestObjectClass() { + public void TestObjectClass() + { ObjectClass v1 = new ObjectClass("test"); ObjectClass v2 = (ObjectClass)CloneObject(v1); Assert.AreEqual(v1, v2); } - + [Test] - public void TestObjectClassInfo() { + public void TestObjectClassInfo() + { ConnectorAttributeInfoBuilder builder = new ConnectorAttributeInfoBuilder(); - builder.Name=("foo"); - builder.ValueType=(typeof(String)); - builder.Required=(true); - builder.Readable=(true); - builder.Updateable=(true); - builder.MultiValued=(true); + builder.Name = ("foo"); + builder.ValueType = (typeof(String)); + builder.Required = (true); + builder.Readable = (true); + builder.Updateable = (true); + builder.MultiValued = (true); ObjectClassInfoBuilder obld = new ObjectClassInfoBuilder(); obld.ObjectType = ObjectClass.ACCOUNT_NAME; obld.IsContainer = true; @@ -614,37 +648,40 @@ public void TestObjectClassInfo() { Assert.AreEqual(v1, v2); Assert.IsTrue(v2.IsContainer); } - + [Test] - public void TestUid() { + public void TestUid() + { Uid v1 = new Uid("test"); Uid v2 = (Uid)CloneObject(v1); Assert.AreEqual(v1, v2); } - + [Test] - public void testOperationOptionInfo() { + public void testOperationOptionInfo() + { OperationOptionInfo v1 = - new OperationOptionInfo("name",typeof(int?)); + new OperationOptionInfo("name", typeof(int?)); OperationOptionInfo v2 = (OperationOptionInfo)CloneObject(v1); Assert.AreEqual(v1, v2); } - + [Test] - public void TestSchema() { + public void TestSchema() + { OperationOptionInfo opInfo = - new OperationOptionInfo("name",typeof(int?)); + new OperationOptionInfo("name", typeof(int?)); ObjectClassInfoBuilder bld = new ObjectClassInfoBuilder(); bld.ObjectType = ObjectClass.ACCOUNT_NAME; ObjectClassInfo info = bld.Build(); ICollection temp = CollectionUtil.NewSet(info); - IDictionary,ICollection> map = - new Dictionary,ICollection>(); + IDictionary, ICollection> map = + new Dictionary, ICollection>(); map[SafeType.Get()] = temp; ICollection temp2 = CollectionUtil.NewSet(opInfo); - IDictionary,ICollection> map2 = - new Dictionary,ICollection>(); + IDictionary, ICollection> map2 = + new Dictionary, ICollection>(); map2[SafeType.Get()] = temp2; Schema v1 = new Schema(CollectionUtil.NewSet(info), CollectionUtil.NewSet(opInfo), @@ -657,183 +694,196 @@ public void TestSchema() { Assert.AreEqual(1, v2.SupportedOptionsByOperation.Count); Assert.AreEqual(1, v2.OperationOptionInfo.Count); } - + [Test] - public void TestContainsFilter() { + public void TestContainsFilter() + { ContainsFilter v1 = new ContainsFilter(ConnectorAttributeBuilder.Build("foo", "bar")); ContainsFilter v2 = (ContainsFilter)CloneObject(v1); Assert.AreEqual(v1.GetAttribute(), v2.GetAttribute()); } - + [Test] - public void TestAndFilter() { + public void TestAndFilter() + { ContainsFilter left1 = new ContainsFilter(ConnectorAttributeBuilder.Build("foo", "bar")); ContainsFilter right1 = new ContainsFilter(ConnectorAttributeBuilder.Build("foo2", "bar2")); - AndFilter v1 = new AndFilter(left1,right1); + AndFilter v1 = new AndFilter(left1, right1); AndFilter v2 = (AndFilter)CloneObject(v1); ContainsFilter left2 = (ContainsFilter)v2.Left; ContainsFilter right2 = (ContainsFilter)v2.Right; - Assert.AreEqual(left1.GetAttribute(),left2.GetAttribute()); - Assert.AreEqual(right1.GetAttribute(),right2.GetAttribute()); + Assert.AreEqual(left1.GetAttribute(), left2.GetAttribute()); + Assert.AreEqual(right1.GetAttribute(), right2.GetAttribute()); } - + [Test] - public void TestEndsWithFilter() { + public void TestEndsWithFilter() + { EndsWithFilter v1 = new EndsWithFilter(ConnectorAttributeBuilder.Build("foo", "bar")); EndsWithFilter v2 = (EndsWithFilter)CloneObject(v1); Assert.AreEqual(v1.GetAttribute(), v2.GetAttribute()); } - + [Test] - public void TestEqualsFilter() { + public void TestEqualsFilter() + { EqualsFilter v1 = new EqualsFilter(ConnectorAttributeBuilder.Build("foo", "bar")); EqualsFilter v2 = (EqualsFilter)CloneObject(v1); Assert.AreEqual(v1.GetAttribute(), v2.GetAttribute()); } - + [Test] - public void TestGreaterThanFilter() { + public void TestGreaterThanFilter() + { GreaterThanFilter v1 = new GreaterThanFilter(ConnectorAttributeBuilder.Build("foo", "bar")); GreaterThanFilter v2 = (GreaterThanFilter)CloneObject(v1); Assert.AreEqual(v1.GetAttribute(), v2.GetAttribute()); } - + [Test] - public void TestGreaterThanOrEqualFilter() { + public void TestGreaterThanOrEqualFilter() + { GreaterThanOrEqualFilter v1 = new GreaterThanOrEqualFilter(ConnectorAttributeBuilder.Build("foo", "bar")); GreaterThanOrEqualFilter v2 = (GreaterThanOrEqualFilter)CloneObject(v1); Assert.AreEqual(v1.GetAttribute(), v2.GetAttribute()); } - + [Test] - public void TestLessThanFilter() { + public void TestLessThanFilter() + { LessThanFilter v1 = new LessThanFilter(ConnectorAttributeBuilder.Build("foo", "bar")); LessThanFilter v2 = (LessThanFilter)CloneObject(v1); Assert.AreEqual(v1.GetAttribute(), v2.GetAttribute()); } - + [Test] - public void TestLessThanOrEqualFilter() { + public void TestLessThanOrEqualFilter() + { LessThanOrEqualFilter v1 = new LessThanOrEqualFilter(ConnectorAttributeBuilder.Build("foo", "bar")); LessThanOrEqualFilter v2 = (LessThanOrEqualFilter)CloneObject(v1); Assert.AreEqual(v1.GetAttribute(), v2.GetAttribute()); } - + [Test] - public void TestNotFilter() { + public void TestNotFilter() + { ContainsFilter left1 = new ContainsFilter(ConnectorAttributeBuilder.Build("foo", "bar")); NotFilter v1 = new NotFilter(left1); NotFilter v2 = (NotFilter)CloneObject(v1); ContainsFilter left2 = (ContainsFilter)v2.Filter; - Assert.AreEqual(left1.GetAttribute(),left2.GetAttribute()); + Assert.AreEqual(left1.GetAttribute(), left2.GetAttribute()); } - + [Test] - public void TestOrFilter() { + public void TestOrFilter() + { ContainsFilter left1 = new ContainsFilter(ConnectorAttributeBuilder.Build("foo", "bar")); ContainsFilter right1 = new ContainsFilter(ConnectorAttributeBuilder.Build("foo2", "bar2")); - OrFilter v1 = new OrFilter(left1,right1); + OrFilter v1 = new OrFilter(left1, right1); OrFilter v2 = (OrFilter)CloneObject(v1); ContainsFilter left2 = (ContainsFilter)v2.Left; ContainsFilter right2 = (ContainsFilter)v2.Right; - Assert.AreEqual(left1.GetAttribute(),left2.GetAttribute()); - Assert.AreEqual(right1.GetAttribute(),right2.GetAttribute()); + Assert.AreEqual(left1.GetAttribute(), left2.GetAttribute()); + Assert.AreEqual(right1.GetAttribute(), right2.GetAttribute()); } - + [Test] - public void TestStartsWithFilter() { + public void TestStartsWithFilter() + { StartsWithFilter v1 = new StartsWithFilter(ConnectorAttributeBuilder.Build("foo", "bar")); StartsWithFilter v2 = (StartsWithFilter)CloneObject(v1); Assert.AreEqual(v1.GetAttribute(), v2.GetAttribute()); } - + [Test] - public void TestContainsAllValuesFilter() { + public void TestContainsAllValuesFilter() + { ContainsAllValuesFilter v1 = new ContainsAllValuesFilter(ConnectorAttributeBuilder.Build("foo", "bar", "foo")); ContainsAllValuesFilter v2 = (ContainsAllValuesFilter)CloneObject(v1); Assert.AreEqual(v1.GetAttribute(), v2.GetAttribute()); } - + [Test] - public void TestExceptions() { + public void TestExceptions() + { { AlreadyExistsException v1 = new AlreadyExistsException("ex"); AlreadyExistsException v2 = (AlreadyExistsException)CloneObject(v1); - Assert.AreEqual("ex",v2.Message); + Assert.AreEqual("ex", v2.Message); } - + { ConfigurationException v1 = new ConfigurationException("ex"); ConfigurationException v2 = (ConfigurationException)CloneObject(v1); - Assert.AreEqual("ex",v2.Message); + Assert.AreEqual("ex", v2.Message); } - + { ConnectionBrokenException v1 = new ConnectionBrokenException("ex"); ConnectionBrokenException v2 = (ConnectionBrokenException)CloneObject(v1); - Assert.AreEqual("ex",v2.Message); + Assert.AreEqual("ex", v2.Message); } - + { ConnectionFailedException v1 = new ConnectionFailedException("ex"); ConnectionFailedException v2 = (ConnectionFailedException)CloneObject(v1); - Assert.AreEqual("ex",v2.Message); + Assert.AreEqual("ex", v2.Message); } - + { ConnectorException v1 = new ConnectorException("ex"); ConnectorException v2 = (ConnectorException)CloneObject(v1); - Assert.AreEqual("ex",v2.Message); + Assert.AreEqual("ex", v2.Message); } { ConnectorIOException v1 = new ConnectorIOException("ex"); ConnectorIOException v2 = (ConnectorIOException)CloneObject(v1); - Assert.AreEqual("ex",v2.Message); + Assert.AreEqual("ex", v2.Message); } { ConnectorSecurityException v1 = new ConnectorSecurityException("ex"); ConnectorSecurityException v2 = (ConnectorSecurityException)CloneObject(v1); - Assert.AreEqual("ex",v2.Message); + Assert.AreEqual("ex", v2.Message); } - + { InvalidCredentialException v1 = new InvalidCredentialException("ex"); InvalidCredentialException v2 = (InvalidCredentialException)CloneObject(v1); - Assert.AreEqual("ex",v2.Message); + Assert.AreEqual("ex", v2.Message); } - + { InvalidPasswordException v1 = new InvalidPasswordException("ex"); InvalidPasswordException v2 = (InvalidPasswordException)CloneObject(v1); - Assert.AreEqual("ex",v2.Message); + Assert.AreEqual("ex", v2.Message); } - + { PasswordExpiredException v1 = new PasswordExpiredException("ex"); - v1.Uid=(new Uid("myuid")); + v1.Uid = (new Uid("myuid")); PasswordExpiredException v2 = (PasswordExpiredException)CloneObject(v1); - Assert.AreEqual("ex",v2.Message); + Assert.AreEqual("ex", v2.Message); Assert.AreEqual("myuid", v2.Uid.GetUidValue()); } - + { OperationTimeoutException v1 = new OperationTimeoutException("ex"); OperationTimeoutException v2 = (OperationTimeoutException)CloneObject(v1); - Assert.AreEqual("ex",v2.Message); + Assert.AreEqual("ex", v2.Message); } - + { PermissionDeniedException v1 = new PermissionDeniedException("ex"); PermissionDeniedException v2 = (PermissionDeniedException)CloneObject(v1); - Assert.AreEqual("ex",v2.Message); + Assert.AreEqual("ex", v2.Message); } - + { UnknownUidException v1 = new UnknownUidException("ex"); UnknownUidException v2 = (UnknownUidException)CloneObject(v1); - Assert.AreEqual("ex",v2.Message); + Assert.AreEqual("ex", v2.Message); } - + { ArgumentException v1 = new ArgumentException("my msg"); ArgumentException v2 = (ArgumentException)CloneObject(v1); @@ -844,51 +894,54 @@ public void TestExceptions() { ArgumentNullException v1 = new ArgumentNullException(null, "my msg 1"); ArgumentException v2 = (ArgumentException)CloneObject(v1); Assert.AreEqual("my msg 1", v2.Message); - } - + } + { Exception v1 = new Exception("my msg2"); Exception v2 = (Exception)CloneObject(v1); Assert.AreEqual("my msg2", v2.Message); } - + } - + [Test] - public void TestHelloRequest() { + public void TestHelloRequest() + { HelloRequest v1 = new HelloRequest(); HelloRequest v2 = (HelloRequest)CloneObject(v1); Assert.IsNotNull(v2); } - + [Test] - public void TestHelloResponse() { + public void TestHelloResponse() + { RemoteConnectorInfoImpl info = new RemoteConnectorInfoImpl(); - info.Messages=(new ConnectorMessagesImpl()); - info.ConnectorKey=(new ConnectorKey("my bundle", + info.Messages = (new ConnectorMessagesImpl()); + info.ConnectorKey = (new ConnectorKey("my bundle", "my version", "my connector")); ConfigurationPropertiesImpl configProperties = new ConfigurationPropertiesImpl(); - configProperties.Properties=(new List()); + configProperties.Properties = (new List()); APIConfigurationImpl apiImpl = new APIConfigurationImpl(); - apiImpl.ConfigurationProperties=(configProperties); - info.DefaultAPIConfiguration=(apiImpl); - info.ConnectorDisplayNameKey=("mykey"); - + apiImpl.ConfigurationProperties = (configProperties); + info.DefaultAPIConfiguration = (apiImpl); + info.ConnectorDisplayNameKey = ("mykey"); + Exception ex = new Exception("foo"); - HelloResponse v1 = new HelloResponse(ex,CollectionUtil.NewReadOnlyList(info)); + HelloResponse v1 = new HelloResponse(ex, CollectionUtil.NewReadOnlyList(info)); HelloResponse v2 = (HelloResponse)CloneObject(v1); Assert.IsNotNull(v2.Exception); Assert.IsNotNull(v2.ConnectorInfos.First()); } - + [Test] - public void TestOperationRequest() { + public void TestOperationRequest() + { ConfigurationPropertiesImpl configProperties = new ConfigurationPropertiesImpl(); - configProperties.Properties=(new List()); + configProperties.Properties = (new List()); APIConfigurationImpl apiImpl = new APIConfigurationImpl(); - apiImpl.ConfigurationProperties=(configProperties); - + apiImpl.ConfigurationProperties = (configProperties); + IList args = new List(); args.Add("my arg"); OperationRequest v1 = new @@ -905,70 +958,78 @@ public void TestOperationRequest() { Assert.AreEqual("my connector", v2.ConnectorKey.ConnectorName); Assert.IsNotNull(v2.Configuration); Assert.AreEqual(SafeType.Get(), v2.Operation); - Assert.AreEqual("mymethodName",v2.OperationMethodName); + Assert.AreEqual("mymethodName", v2.OperationMethodName); Assert.IsTrue( CollectionUtil.Equals( args, v2.Arguments)); } - + [Test] - public void TestOperationResponseEnd() { + public void TestOperationResponseEnd() + { OperationResponseEnd v1 = new OperationResponseEnd(); OperationResponseEnd v2 = (OperationResponseEnd)CloneObject(v1); Assert.IsNotNull(v2); } [Test] - public void TestOperationResponsePart() { + public void TestOperationResponsePart() + { Exception ex = new Exception("foo"); - OperationResponsePart v1 = new OperationResponsePart(ex,"bar"); + OperationResponsePart v1 = new OperationResponsePart(ex, "bar"); OperationResponsePart v2 = (OperationResponsePart)CloneObject(v1); Assert.IsNotNull(v2.Exception); Assert.AreEqual("bar", v2.Result); } - + [Test] - public void TestOperationResponsePause() { + public void TestOperationResponsePause() + { OperationResponsePause v1 = new OperationResponsePause(); OperationResponsePause v2 = (OperationResponsePause)CloneObject(v1); Assert.IsNotNull(v2); } - + [Test] - public void TestOperationRequestMoreData() { + public void TestOperationRequestMoreData() + { OperationRequestMoreData v1 = new OperationRequestMoreData(); OperationRequestMoreData v2 = (OperationRequestMoreData)CloneObject(v1); Assert.IsNotNull(v2); } - + [Test] - public void TestOperationRequestStopData() { + public void TestOperationRequestStopData() + { OperationRequestStopData v1 = new OperationRequestStopData(); OperationRequestStopData v2 = (OperationRequestStopData)CloneObject(v1); Assert.IsNotNull(v2); } - + [Test] - public void TestEchoMessage() { - EchoMessage v1 = new EchoMessage("test","xml"); + public void TestEchoMessage() + { + EchoMessage v1 = new EchoMessage("test", "xml"); EchoMessage v2 = (EchoMessage)CloneObject(v1); Assert.AreEqual("test", v2.Object); Assert.AreEqual("xml", v2.ObjectXml); } - + [Test] - public void TestOperationOptions() { + public void TestOperationOptions() + { OperationOptionsBuilder builder = new OperationOptionsBuilder(); builder.SetOption("foo", "bar"); builder.SetOption("foo2", "bar2"); OperationOptions v1 = builder.Build(); OperationOptions v2 = (OperationOptions)CloneObject(v1); - Assert.AreEqual(2,v2.Options.Count); - Assert.AreEqual("bar",v2.Options["foo"]); - Assert.AreEqual("bar2",v2.Options["foo2"]); + Assert.AreEqual(2, v2.Options.Count); + Assert.AreEqual("bar", v2.Options["foo"]); + Assert.AreEqual("bar2", v2.Options["foo2"]); } [Test] - public void TestScript() { + public void TestScript() + { ScriptBuilder builder = new ScriptBuilder(); builder.ScriptLanguage = "language"; builder.ScriptText = "text"; @@ -979,74 +1040,81 @@ public void TestScript() { } [Test] - public void TestScriptContext() { + public void TestScriptContext() + { ScriptContextBuilder builder = new ScriptContextBuilder(); - builder.ScriptLanguage=("language"); - builder.ScriptText=("text"); + builder.ScriptLanguage = ("language"); + builder.ScriptText = ("text"); builder.AddScriptArgument("foo", "bar"); builder.AddScriptArgument("foo2", "bar2"); ScriptContext v1 = builder.Build(); ScriptContext v2 = (ScriptContext)CloneObject(v1); - Assert.AreEqual(2,v2.ScriptArguments.Count); - Assert.AreEqual("bar",v2.ScriptArguments["foo"]); - Assert.AreEqual("bar2",v2.ScriptArguments["foo2"]); - Assert.AreEqual("language",v2.ScriptLanguage); - Assert.AreEqual("text",v2.ScriptText); + Assert.AreEqual(2, v2.ScriptArguments.Count); + Assert.AreEqual("bar", v2.ScriptArguments["foo"]); + Assert.AreEqual("bar2", v2.ScriptArguments["foo2"]); + Assert.AreEqual("language", v2.ScriptLanguage); + Assert.AreEqual("text", v2.ScriptText); } [Test] - public void TestSyncDeltaType() { + public void TestSyncDeltaType() + { SyncDeltaType v1 = SyncDeltaType.DELETE; SyncDeltaType v2 = (SyncDeltaType)CloneObject(v1); Assert.AreEqual(v1, v2); } - + [Test] - public void TestSyncToken() { + public void TestSyncToken() + { SyncToken v1 = new SyncToken("mytoken"); SyncToken v2 = (SyncToken)CloneObject(v1); - Assert.AreEqual(v1.Value,v2.Value); - Assert.AreEqual(v1,v2); + Assert.AreEqual(v1.Value, v2.Value); + Assert.AreEqual(v1, v2); } - + [Test] - public void TestSyncDelta() { + public void TestSyncDelta() + { ConnectorObjectBuilder bld = new ConnectorObjectBuilder(); bld.SetUid("foo"); bld.SetName("name"); SyncDeltaBuilder builder = new SyncDeltaBuilder(); - builder.PreviousUid=(new Uid("mypreviousuid")); - builder.Uid=(new Uid("myuid")); - builder.DeltaType=(SyncDeltaType.CREATE_OR_UPDATE); - builder.Token=(new SyncToken("mytoken")); - builder.Object=(bld.Build()); + builder.PreviousUid = (new Uid("mypreviousuid")); + builder.Uid = (new Uid("myuid")); + builder.DeltaType = (SyncDeltaType.CREATE_OR_UPDATE); + builder.Token = (new SyncToken("mytoken")); + builder.Object = (bld.Build()); SyncDelta v1 = builder.Build(); SyncDelta v2 = (SyncDelta)CloneObject(v1); - Assert.AreEqual(new Uid("mypreviousuid"),v2.PreviousUid); - Assert.AreEqual(new Uid("foo"),v2.Uid); - Assert.AreEqual(new SyncToken("mytoken"),v2.Token); - Assert.AreEqual(SyncDeltaType.CREATE_OR_UPDATE,v2.DeltaType); - Assert.AreEqual(v1,v2); + Assert.AreEqual(new Uid("mypreviousuid"), v2.PreviousUid); + Assert.AreEqual(new Uid("foo"), v2.Uid); + Assert.AreEqual(new SyncToken("mytoken"), v2.Token); + Assert.AreEqual(SyncDeltaType.CREATE_OR_UPDATE, v2.DeltaType); + Assert.AreEqual(v1, v2); } - + [Test] - public void TestNull() { + public void TestNull() + { Object v1 = null; Object v2 = CloneObject(v1); Assert.IsNull(v2); } - + [Test] - public void TestGuardedByteArray() { + public void TestGuardedByteArray() + { GuardedByteArray v1 = new GuardedByteArray(); v1.AppendByte(0x00); v1.AppendByte(0x01); v1.AppendByte(0x02); GuardedByteArray v2 = (GuardedByteArray)CloneObject(v1); - Assert.AreEqual(new byte[] { 0x00, 0x01, 0x02 },DecryptToByteArray(v2)); + Assert.AreEqual(new byte[] { 0x00, 0x01, 0x02 }, DecryptToByteArray(v2)); } - + [Test] - public void TestGuardedString() { + public void TestGuardedString() + { GuardedString v1 = new GuardedString(); v1.AppendChar('f'); v1.AppendChar('o'); @@ -1055,11 +1123,12 @@ public void TestGuardedString() { v1.AppendChar('a'); v1.AppendChar('r'); GuardedString v2 = (GuardedString)CloneObject(v1); - Assert.AreEqual("foobar",DecryptToString(v2)); + Assert.AreEqual("foobar", DecryptToString(v2)); } - + [Test] - public void TestQualifiedUid() { + public void TestQualifiedUid() + { QualifiedUid v1 = new QualifiedUid(new ObjectClass("myclass"), new Uid("myuid")); QualifiedUid v2 = (QualifiedUid)CloneObject(v1); @@ -1067,21 +1136,25 @@ public void TestQualifiedUid() { Assert.AreEqual("myclass", v2.ObjectClass.GetObjectClassValue()); Assert.AreEqual("myuid", v2.Uid.GetUidValue()); } - - /** - * Highly insecure method! Do not do this in production - * code. This is only for test purposes - */ - private String DecryptToString(GuardedString str) { + + /// + /// Highly insecure method! Do not do this in production + /// code. + /// + /// + /// This is only for test purposes + /// + private String DecryptToString(GuardedString str) + { StringBuilder buf = new StringBuilder(); str.Access( - array=> - { - for ( int i = 0 ; i < array.Length; i++) - { - buf.Append(array[i]); - } - }); + array => + { + for (int i = 0; i < array.Length; i++) + { + buf.Append(array[i]); + } + }); return buf.ToString(); } @@ -1090,13 +1163,13 @@ private byte[] DecryptToByteArray(GuardedByteArray bytes) byte[] result = null; bytes.Access( array => - { - result = new byte[array.Length]; - for (int i = 0; i < array.Length; i++) - { - result[i] = array[i]; - } - }); + { + result = new byte[array.Length]; + for (int i = 0; i < array.Length; i++) + { + result[i] = array[i]; + } + }); return result; } @@ -1107,44 +1180,46 @@ protected virtual Object CloneObject(Object o) } [TestFixture] public class XmlSerializationTests : ObjectSerializationTests - { + { protected override Object CloneObject(Object o) { String xml = SerializerUtil.SerializeXmlObject(o, true); Console.WriteLine(xml); - o = SerializerUtil.DeserializeXmlObject(xml, true); - + o = SerializerUtil.DeserializeXmlObject(xml, true); + //pass through a list to make sure dtd correctly defines all xml objects List list = new List(); list.Add(o); xml = SerializerUtil.SerializeXmlObject(list, true); Console.WriteLine(xml); - IList rv = (IList)SerializerUtil.DeserializeXmlObject(xml, true); + IList rv = (IList)SerializerUtil.DeserializeXmlObject(xml, true); return rv[0]; } - + [Test] - public void TestMultiObject() { + public void TestMultiObject() + { ObjectSerializerFactory factory = ObjectSerializerFactory.GetInstance(); StringWriter sw = new StringWriter(); - XmlObjectSerializer ser = factory.NewXmlSerializer(sw,true,true); + XmlObjectSerializer ser = factory.NewXmlSerializer(sw, true, true); ser.WriteObject("foo"); ser.WriteObject("bar"); ser.Close(true); String xml = sw.ToString(); Console.WriteLine(xml); IList results = new List(); - factory.DeserializeXmlStream(new StringReader(xml), - o=>{ + factory.DeserializeXmlStream(new StringReader(xml), + o => + { results.Add(o); return true; }, - true); - Assert.AreEqual(2,results.Count); - Assert.AreEqual("foo",results[0]); - Assert.AreEqual("bar",results[1]); + true); + Assert.AreEqual(2, results.Count); + Assert.AreEqual("foo", results[0]); + Assert.AreEqual("bar", results[1]); } - + [Test] public void TestWriter() { @@ -1152,7 +1227,7 @@ public void TestWriter() settings.Indent = true; settings.OmitXmlDeclaration = true; XmlWriter writer = XmlWriter.Create(Console.Out, settings); - + // Write the book element. writer.WriteStartElement("book"); writer.WriteEndElement(); @@ -1161,13 +1236,13 @@ public void TestWriter() //writer.WriteStartElement("title"); //writer.WriteString("Pride And Prejudice<<"); //writer.WriteEndElement(); - + // Write the close tag for the root element. //writer.WriteEndElement(); - + // Write the XML and close the writer. //writer.Close(); - + } } -} +} \ No newline at end of file diff --git a/FrameworkTests/ProxyTests.cs b/FrameworkTests/ProxyTests.cs index 8431c563..1714f546 100644 --- a/FrameworkTests/ProxyTests.cs +++ b/FrameworkTests/ProxyTests.cs @@ -32,38 +32,38 @@ namespace FrameworkTests { public interface MyTestInterface { - string TestProperty {get;set;} - + string TestProperty { get; set; } + String TestTwoArgs(string val1, string val2); void TestVoid(IList list); int TestPrimitive(int arg); DateTime TestStruct(DateTime arg); } - + [TestFixture] public class ProxyTests { public class MyHandler : InvocationHandler { private string _testProperty; - - public Object Invoke(Object proxy, MethodInfo method, Object [] args) + + public Object Invoke(Object proxy, MethodInfo method, Object[] args) { if (method.Name.Equals("TestTwoArgs")) { - return ""+method.Name+" "+args[0]+" "+args[1]; + return "" + method.Name + " " + args[0] + " " + args[1]; } - else if ( method.Name.Equals("TestVoid") ) + else if (method.Name.Equals("TestVoid")) { IList arg = (IList)args[0]; arg.Add("my void result"); return null; } - else if ( method.Name.Equals("get_TestProperty") ) + else if (method.Name.Equals("get_TestProperty")) { return _testProperty; } - else if ( method.Name.Equals("set_TestProperty") ) + else if (method.Name.Equals("set_TestProperty")) { _testProperty = (string)args[0]; return null; @@ -74,27 +74,24 @@ public Object Invoke(Object proxy, MethodInfo method, Object [] args) } } } - + [Test] public void TestProxy() { InvocationHandler handler = new MyHandler(); - - MyTestInterface inter = + + MyTestInterface inter = (MyTestInterface)Proxy.NewProxyInstance(typeof(MyTestInterface), handler); Assert.AreEqual("TestTwoArgs foo bar", - inter.TestTwoArgs("foo","bar")); + inter.TestTwoArgs("foo", "bar")); IList temp = new List(); inter.TestVoid(temp); - Assert.AreEqual("my void result",temp[0]); - Assert.AreEqual(10,inter.TestPrimitive(10)); - Assert.AreEqual(1000L,inter.TestStruct(new DateTime(1000)).Ticks); + Assert.AreEqual("my void result", temp[0]); + Assert.AreEqual(10, inter.TestPrimitive(10)); + Assert.AreEqual(1000L, inter.TestStruct(new DateTime(1000)).Ticks); inter.TestProperty = "my property"; - Assert.AreEqual("my property",inter.TestProperty); + Assert.AreEqual("my property", inter.TestProperty); } - } - - -} +} \ No newline at end of file diff --git a/FrameworkTests/SafeTypeTest.cs b/FrameworkTests/SafeTypeTest.cs index 3eafcae7..23f8a5d0 100644 --- a/FrameworkTests/SafeTypeTest.cs +++ b/FrameworkTests/SafeTypeTest.cs @@ -32,15 +32,15 @@ public class SafeTypeTest { [Test] public void TestSafeType() - { - //compile-time type safe - SafeType op = - SafeType.Get(); - Assert.AreEqual(typeof(ScriptOnResourceApiOp),op.RawType); - //runtime type safe. needed for marshalling code, etc - op = - SafeType.ForRawType(typeof(SchemaApiOp)); - Assert.AreEqual(typeof(SchemaApiOp),op.RawType); + { + //compile-time type safe + SafeType op = + SafeType.Get(); + Assert.AreEqual(typeof(ScriptOnResourceApiOp), op.RawType); + //runtime type safe. needed for marshalling code, etc + op = + SafeType.ForRawType(typeof(SchemaApiOp)); + Assert.AreEqual(typeof(SchemaApiOp), op.RawType); } } } diff --git a/FrameworkTests/ScriptTests.cs b/FrameworkTests/ScriptTests.cs index 0f3b3ec8..2d6ef102 100644 --- a/FrameworkTests/ScriptTests.cs +++ b/FrameworkTests/ScriptTests.cs @@ -38,9 +38,10 @@ namespace FrameworkTests public class ScriptTests { [Test] - public void testBooScripting() { + public void testBooScripting() + { ScriptExecutorFactory factory = ScriptExecutorFactory.NewInstance("BOO"); - ScriptExecutor exe = factory.NewScriptExecutor(new Assembly[0],"x", false); + ScriptExecutor exe = factory.NewScriptExecutor(new Assembly[0], "x", false); IDictionary vals = new Dictionary(); vals["x"] = 1; Assert.AreEqual(1, exe.Execute(vals)); @@ -48,20 +49,23 @@ public void testBooScripting() { Assert.AreEqual(2, exe.Execute(vals)); } [Test] - public void testShellScripting() { + public void testShellScripting() + { ScriptExecutorFactory factory = ScriptExecutorFactory.NewInstance("Shell"); - ScriptExecutor exe = factory.NewScriptExecutor(new Assembly[0],"echo bob", false); + ScriptExecutor exe = factory.NewScriptExecutor(new Assembly[0], "echo bob", false); IDictionary vals = new Dictionary(); Assert.AreEqual(0, exe.Execute(vals)); } [Test] [ExpectedException(typeof(ArgumentException))] - public void testUnsupported() { + public void testUnsupported() + { ScriptExecutorFactory.NewInstance("fadsflkj"); } [Test] - public void testBasic() { + public void testBasic() + { ScriptBuilder builder = new ScriptBuilder(); builder.ScriptLanguage = "Groovy"; builder.ScriptText = "print 'foo'"; @@ -75,29 +79,37 @@ public void testBasic() { Assert.AreEqual(s1, s2); Assert.AreEqual(s1.GetHashCode(), s2.GetHashCode()); } - + [Test] - public void testLanguageNotBlank() { - try { + public void testLanguageNotBlank() + { + try + { ScriptBuilder builder = new ScriptBuilder(); builder.ScriptText = "print 'foo'"; builder.Build(); Assert.Fail(); - } catch (ArgumentException) { + } + catch (ArgumentException) + { // OK. } - - try { + + try + { ScriptBuilder builder = new ScriptBuilder(); builder.ScriptText = "print 'foo'"; builder.ScriptLanguage = ""; builder.Build(); Assert.Fail(); - } catch (ArgumentException) { + } + catch (ArgumentException) + { // OK. } - - try { + + try + { ScriptBuilder builder = new ScriptBuilder(); builder.ScriptText = "print 'foo'"; builder.ScriptLanguage = " "; @@ -109,18 +121,22 @@ public void testLanguageNotBlank() { // OK. } } - + [Test] - public void testTextNotNull() { + public void testTextNotNull() + { ScriptBuilder builder = new ScriptBuilder(); - try { + try + { builder.ScriptLanguage = "Groovy"; builder.Build(); Assert.Fail(); - } catch (ArgumentNullException) { + } + catch (ArgumentNullException) + { // OK. } - + // The text can be empty. builder = new ScriptBuilder(); builder.ScriptLanguage = "Groovy"; diff --git a/FrameworkTests/TestHelperTests.cs b/FrameworkTests/TestHelperTests.cs index f83037bd..72225aea 100644 --- a/FrameworkTests/TestHelperTests.cs +++ b/FrameworkTests/TestHelperTests.cs @@ -33,7 +33,7 @@ using Org.IdentityConnectors.Test.Common; namespace FrameworkTests -{ +{ /// /// Description of TestHelperTests. /// @@ -44,7 +44,8 @@ public class TestHelperTests public void testLoadProperties() { string tmpFn = Path.GetTempFileName(); - try { + try + { // create some xml text TextWriter stringWriter = new StringWriter(); XmlTextWriter w = new XmlTextWriter(stringWriter); @@ -60,19 +61,23 @@ public void testLoadProperties() w.Close(); File.WriteAllText(tmpFn, stringWriter.ToString()); // load the properties files - IDictionary dict =TestHelpers.LoadPropertiesFile(tmpFn); + IDictionary dict = TestHelpers.LoadPropertiesFile(tmpFn); Assert.AreEqual(dict["bob"], "bobsValue"); - } finally { + } + finally + { File.Delete(tmpFn); } } [Test] - public void testGetProperties() { + public void testGetProperties() + { Assert.IsTrue(TestHelpers.GetProperty("Help", null).Equals("Me")); } [Test] - public void testFillConfiguration() { + public void testFillConfiguration() + { TestConfiguration testConfig = new TestConfiguration(); // There is no "Foo" property in the config bean. We want to ensure // that TestHelpers.FillConfiguration() does not fail for unknown properties. @@ -86,18 +91,23 @@ public void testFillConfiguration() { Assert.AreEqual(1234, testConfig.Port); } - public class TestConfiguration : Configuration { + public class TestConfiguration : Configuration + { - public ConnectorMessages ConnectorMessages { - get { + public ConnectorMessages ConnectorMessages + { + get + { return null; } - set { + set + { Assert.Fail("Should not set ConnectorMessages"); } } - public void Validate() { + public void Validate() + { Assert.Fail("Should not call Validate()"); } diff --git a/FrameworkTests/TestUtil.cs b/FrameworkTests/TestUtil.cs index 0d413da2..e72737a1 100644 --- a/FrameworkTests/TestUtil.cs +++ b/FrameworkTests/TestUtil.cs @@ -32,18 +32,18 @@ public static class TestUtil { private static readonly object LOCK = new Object(); private static bool _initialized; - - + + public static void InitializeLogging() { - lock(LOCK) + lock (LOCK) { if (!_initialized) { - ConsoleTraceListener listener = + ConsoleTraceListener listener = new ConsoleTraceListener(); - listener.TraceOutputOptions = - TraceOptions.ThreadId|TraceOptions.Timestamp; + listener.TraceOutputOptions = + TraceOptions.ThreadId | TraceOptions.Timestamp; Trace.Listeners.Add(listener); _initialized = true; } diff --git a/FrameworkTests/UpdateImplTests.cs b/FrameworkTests/UpdateImplTests.cs index 79974783..2bce2501 100644 --- a/FrameworkTests/UpdateImplTests.cs +++ b/FrameworkTests/UpdateImplTests.cs @@ -37,50 +37,57 @@ namespace FrameworkTests /// [TestFixture] public class UpdateImplTests - { + { [Test] - [ExpectedException(typeof(ArgumentNullException ))] - public void ValidateUidArg() { - UpdateImpl.ValidateInput(ObjectClass.ACCOUNT, null, new HashSet(),true); + [ExpectedException(typeof(ArgumentNullException))] + public void ValidateUidArg() + { + UpdateImpl.ValidateInput(ObjectClass.ACCOUNT, null, new HashSet(), true); } [Test] - [ExpectedException(typeof(ArgumentNullException ))] - public void ValidateObjectClassArg() { - UpdateImpl.ValidateInput(null, new Uid("foo"), new HashSet(),true); + [ExpectedException(typeof(ArgumentNullException))] + public void ValidateObjectClassArg() + { + UpdateImpl.ValidateInput(null, new Uid("foo"), new HashSet(), true); } - + [Test] - [ExpectedException(typeof(ArgumentNullException ))] - public void ValidateAttrsArg() { - UpdateImpl.ValidateInput(ObjectClass.ACCOUNT, new Uid("foo"),null,true); + [ExpectedException(typeof(ArgumentNullException))] + public void ValidateAttrsArg() + { + UpdateImpl.ValidateInput(ObjectClass.ACCOUNT, new Uid("foo"), null, true); } - + [Test] - [ExpectedException(typeof(ArgumentException ))] - public void ValidateUidAttribute() { - HashSet attrs=new HashSet(); + [ExpectedException(typeof(ArgumentException))] + public void ValidateUidAttribute() + { + HashSet attrs = new HashSet(); attrs.Add(new Uid("foo")); - UpdateImpl.ValidateInput(ObjectClass.ACCOUNT, new Uid("foo"),attrs,true); + UpdateImpl.ValidateInput(ObjectClass.ACCOUNT, new Uid("foo"), attrs, true); } - + [Test] - [ExpectedException(typeof(ArgumentException ))] - public void ValidateAddWithNullAttribute() { + [ExpectedException(typeof(ArgumentException))] + public void ValidateAddWithNullAttribute() + { ICollection attrs = new HashSet(); attrs.Add(ConnectorAttributeBuilder.Build("something")); - UpdateImpl.ValidateInput(ObjectClass.ACCOUNT, new Uid("foo"), attrs, true); + UpdateImpl.ValidateInput(ObjectClass.ACCOUNT, new Uid("foo"), attrs, true); } - + [Test] [ExpectedException(typeof(ArgumentException))] - public void ValidateAttemptToAddName() { + public void ValidateAttemptToAddName() + { ICollection attrs = new HashSet(); attrs.Add(new Name("fadf")); UpdateImpl.ValidateInput(ObjectClass.ACCOUNT, new Uid("foo"), attrs, true); } - + [Test] - public void ValidateAttemptToAddDeleteOperationalAttribute() { + public void ValidateAttemptToAddDeleteOperationalAttribute() + { // list of all the operational attributes.. ICollection list = new List(); list.Add(ConnectorAttributeBuilder.BuildEnabled(false)); @@ -88,43 +95,54 @@ public void ValidateAttemptToAddDeleteOperationalAttribute() { list.Add(ConnectorAttributeBuilder.BuildCurrentPassword(newSecureString("fadsf"))); list.Add(ConnectorAttributeBuilder.BuildPasswordExpirationDate(DateTime.Now)); list.Add(ConnectorAttributeBuilder.BuildPassword(newSecureString("fadsf"))); - foreach (ConnectorAttribute attr in list) { + foreach (ConnectorAttribute attr in list) + { ICollection attrs = new HashSet(); attrs.Add(attr); - try { + try + { UpdateImpl.ValidateInput(ObjectClass.ACCOUNT, new Uid("1"), attrs, true); Assert.Fail("Failed: " + attr.Name); - } catch (ArgumentException) { + } + catch (ArgumentException) + { // this is a good thing.. } } } - - private static SecureString newSecureString(string password) { + + private static SecureString newSecureString(string password) + { SecureString rv = new SecureString(); - foreach (char c in password.ToCharArray()) { + foreach (char c in password.ToCharArray()) + { rv.AppendChar(c); } return rv; } - + /// /// Validate two collections are equal. (Not fast but effective) /// - public static bool AreEqual(ICollection arg1, - ICollection arg2) { - if (arg1.Count != arg2.Count) { + public static bool AreEqual(ICollection arg1, + ICollection arg2) + { + if (arg1.Count != arg2.Count) + { return false; } - foreach (ConnectorAttribute attr in arg1) { - if (!arg2.Contains(attr)) { + foreach (ConnectorAttribute attr in arg1) + { + if (!arg2.Contains(attr)) + { return false; } } return true; } [Test] - public void MergeAddAttribute() { + public void MergeAddAttribute() + { UpdateImpl up = new UpdateImpl(null, null); ICollection actual; ICollection baseAttrs = CollectionUtil.NewSet(); @@ -133,13 +151,14 @@ public void MergeAddAttribute() { // attempt to add a value to an attribute.. ConnectorAttribute cattr = ConnectorAttributeBuilder.Build("abc", 2); changeset.Add(cattr); - expected.Add(ConnectorAttributeBuilder.Build("abc", 2)); - actual = up.Merge(changeset, baseAttrs,true); + expected.Add(ConnectorAttributeBuilder.Build("abc", 2)); + actual = up.Merge(changeset, baseAttrs, true); Assert.IsTrue(AreEqual(expected, actual)); } - + [Test] - public void MergeAddToExistingAttribute() { + public void MergeAddToExistingAttribute() + { UpdateImpl up = new UpdateImpl(null, null); ICollection actual; ICollection baseAttrs = CollectionUtil.NewSet(); @@ -150,13 +169,14 @@ public void MergeAddToExistingAttribute() { ConnectorAttribute cattr = ConnectorAttributeBuilder.Build("abc", 2); baseAttrs.Add(battr); changeset.Add(cattr); - expected.Add(ConnectorAttributeBuilder.Build("abc", 1, 2)); + expected.Add(ConnectorAttributeBuilder.Build("abc", 1, 2)); actual = up.Merge(changeset, baseAttrs, true); Assert.IsTrue(AreEqual(expected, actual)); } - + [Test] - public void MergeDeleteNonExistentAttribute() { + public void MergeDeleteNonExistentAttribute() + { UpdateImpl up = new UpdateImpl(null, null); ICollection actual; ICollection baseAttrs = CollectionUtil.NewSet(); @@ -168,9 +188,10 @@ public void MergeDeleteNonExistentAttribute() { actual = up.Merge(changeset, baseAttrs, false); Assert.IsTrue(AreEqual(expected, actual)); } - + [Test] - public void MergeDeleteToExistingAttribute() { + public void MergeDeleteToExistingAttribute() + { UpdateImpl up = new UpdateImpl(null, null); ICollection actual; ICollection baseAttrs = CollectionUtil.NewSet(); @@ -185,9 +206,10 @@ public void MergeDeleteToExistingAttribute() { actual = up.Merge(changeset, baseAttrs, false); Assert.IsTrue(AreEqual(expected, actual)); } - + [Test] - public void MergeDeleteToExistingAttributeCompletely() { + public void MergeDeleteToExistingAttributeCompletely() + { UpdateImpl up = new UpdateImpl(null, null); ICollection actual; ICollection baseAttrs = CollectionUtil.NewSet(); @@ -202,8 +224,5 @@ public void MergeDeleteToExistingAttributeCompletely() { actual = up.Merge(changeset, baseAttrs, false); Assert.IsTrue(AreEqual(expected, actual)); } - - - } } diff --git a/Service/Program.cs b/Service/Program.cs index 6b465a36..1cb3337c 100644 --- a/Service/Program.cs +++ b/Service/Program.cs @@ -33,198 +33,219 @@ namespace Org.IdentityConnectors.Framework.Service { - - static class Program - { - private const string OPT_SERVICE_NAME="/serviceName"; - - private static void Usage() - { - Console.WriteLine("Usage: ConnectorServer.exe [option], where command is one of the following: "); - Console.WriteLine(" /install [/serviceName ] - Installs the service."); - Console.WriteLine(" /uninstall [/serviceName ] - Uninstalls the service."); - Console.WriteLine(" /run - Runs the service from the console."); - Console.WriteLine(" /setKey [] - Sets the connector server key."); - } - - private static IDictionary ParseOptions(string [] args) - { - IDictionary rv = new Dictionary(); - String serviceName = null; - for ( int i = 1; i < args.Length; i++) { - String opt = args[i].ToLower(); - if (OPT_SERVICE_NAME.ToLower().Equals(opt)) { - i++; - if (i < args.Length) { - serviceName = args[i]; - } - else { - Usage(); - return null; - } - } - else { - Usage(); - return null; - } - } - rv["/serviceName"] = serviceName; - return rv; - } - - /// - /// This method starts the service. - /// - static void Main(string [] args) - { - if ( args.Length == 0 ) - { - //no args - start the service - ServiceBase.Run(new ServiceBase[] { new Service() }); - } - else - { - String cmd = args[0].ToLower(); - if ( cmd.Equals("/setkey") ) { - if ( args.Length > 2 ) { - Usage(); - return; - } - DoSetKey(args.Length>1?args[1]:null); - return; - } - IDictionary options = - ParseOptions(args); - if ( options == null ) { - //there's a parse error in the options, return - return; - } - if ( "/install".Equals(cmd) ) { - DoInstall(options); - } - else if ("/uninstall".Equals(cmd)) { - DoUninstall(options); - } - else if ("/run".Equals(cmd)) { - DoRun(options); - } - else { - Usage(); - return; - } - } - } - - private static void DoInstall(IDictionary options) - { - String serviceName = options[OPT_SERVICE_NAME]; - if ( serviceName != null ) { - ProjectInstaller.ServiceName = serviceName; - } - TransactedInstaller ti = new TransactedInstaller(); - string [] cmdline = + + static class Program + { + private const string OPT_SERVICE_NAME = "/serviceName"; + + private static void Usage() + { + Console.WriteLine("Usage: ConnectorServer.exe [option], where command is one of the following: "); + Console.WriteLine(" /install [/serviceName ] - Installs the service."); + Console.WriteLine(" /uninstall [/serviceName ] - Uninstalls the service."); + Console.WriteLine(" /run - Runs the service from the console."); + Console.WriteLine(" /setKey [] - Sets the connector server key."); + } + + private static IDictionary ParseOptions(string[] args) + { + IDictionary rv = new Dictionary(); + String serviceName = null; + for (int i = 1; i < args.Length; i++) + { + String opt = args[i].ToLower(); + if (OPT_SERVICE_NAME.ToLower().Equals(opt)) + { + i++; + if (i < args.Length) + { + serviceName = args[i]; + } + else + { + Usage(); + return null; + } + } + else + { + Usage(); + return null; + } + } + rv["/serviceName"] = serviceName; + return rv; + } + + /// + /// This method starts the service. + /// + static void Main(string[] args) + { + if (args.Length == 0) + { + //no args - start the service + ServiceBase.Run(new ServiceBase[] { new Service() }); + } + else + { + String cmd = args[0].ToLower(); + if (cmd.Equals("/setkey")) + { + if (args.Length > 2) + { + Usage(); + return; + } + DoSetKey(args.Length > 1 ? args[1] : null); + return; + } + IDictionary options = + ParseOptions(args); + if (options == null) + { + //there's a parse error in the options, return + return; + } + if ("/install".Equals(cmd)) + { + DoInstall(options); + } + else if ("/uninstall".Equals(cmd)) + { + DoUninstall(options); + } + else if ("/run".Equals(cmd)) + { + DoRun(options); + } + else + { + Usage(); + return; + } + } + } + + private static void DoInstall(IDictionary options) + { + String serviceName = options[OPT_SERVICE_NAME]; + if (serviceName != null) + { + ProjectInstaller.ServiceName = serviceName; + } + TransactedInstaller ti = new TransactedInstaller(); + string[] cmdline = { Assembly.GetExecutingAssembly ().Location }; AssemblyInstaller ai = new AssemblyInstaller( - cmdline[0], - new string[0]); - ti.Installers.Add (ai); - InstallContext ctx = new InstallContext ("install.log", - cmdline ); - ti.Context = ctx ; - ti.Install ( new System.Collections.Hashtable()); - } - - private static void DoUninstall(IDictionary options) - { - String serviceName = options[OPT_SERVICE_NAME]; - if ( serviceName != null ) { - ProjectInstaller.ServiceName = serviceName; - } - TransactedInstaller ti = new TransactedInstaller(); - string [] cmdline = + cmdline[0], + new string[0]); + ti.Installers.Add(ai); + InstallContext ctx = new InstallContext("install.log", + cmdline); + ti.Context = ctx; + ti.Install(new System.Collections.Hashtable()); + } + + private static void DoUninstall(IDictionary options) + { + String serviceName = options[OPT_SERVICE_NAME]; + if (serviceName != null) + { + ProjectInstaller.ServiceName = serviceName; + } + TransactedInstaller ti = new TransactedInstaller(); + string[] cmdline = { Assembly.GetExecutingAssembly ().Location }; AssemblyInstaller ai = new AssemblyInstaller( - cmdline[0], - new string[0]); - ti.Installers.Add (ai); - InstallContext ctx = new InstallContext ("uninstall.log", - cmdline ); - ti.Context = ctx ; - ti.Uninstall ( null); - } - - private static void DoRun(IDictionary options) - { - Service svc = new Service(); - - svc.StartService(new String[0]); - - Console.WriteLine("Press q to shutdown."); - Console.WriteLine("Press t for a thread dump."); - - while ( true ) { - ConsoleKeyInfo info = Console.ReadKey(); - if ( info.KeyChar == 'q' ) { - break; - } - else if ( info.KeyChar == 't' ) { - svc.DumpRequests(); - } - } - - svc.StopService(); - } - - private static GuardedString ReadPassword() - { - GuardedString rv = new GuardedString(); - while (true) { - ConsoleKeyInfo info = Console.ReadKey(true); - if ( info.Key == ConsoleKey.Enter ) { - Console.WriteLine(); - rv.MakeReadOnly(); - return rv; - } - else { - Console.Write("*"); - rv.AppendChar(info.KeyChar); - } - } - } - - private static void DoSetKey(string key) - { - GuardedString str; - if ( key == null ) { - Console.Write("Enter Key: "); - GuardedString v1 = ReadPassword(); - Console.Write("Confirm Key: "); - GuardedString v2 = ReadPassword(); - if (!v1.Equals(v2)) { - Console.WriteLine("Error: Key mismatch."); - return; - } - str = v2; - } - else { - str = new GuardedString(); - foreach (char c in key.ToCharArray()) { - str.AppendChar(c); - } - } - Configuration config = - ConfigurationManager.OpenExeConfiguration(ConfigurationUserLevel.None); - config.AppSettings.Settings.Remove(Service.PROP_KEY); - config.AppSettings.Settings.Add(Service.PROP_KEY,str.GetBase64SHA1Hash()); - config.Save(ConfigurationSaveMode.Modified); - Console.WriteLine("Key Updated."); - } - } - + cmdline[0], + new string[0]); + ti.Installers.Add(ai); + InstallContext ctx = new InstallContext("uninstall.log", + cmdline); + ti.Context = ctx; + ti.Uninstall(null); + } + + private static void DoRun(IDictionary options) + { + Service svc = new Service(); + + svc.StartService(new String[0]); + + Console.WriteLine("Press q to shutdown."); + Console.WriteLine("Press t for a thread dump."); + + while (true) + { + ConsoleKeyInfo info = Console.ReadKey(); + if (info.KeyChar == 'q') + { + break; + } + else if (info.KeyChar == 't') + { + svc.DumpRequests(); + } + } + + svc.StopService(); + } + private static GuardedString ReadPassword() + { + GuardedString rv = new GuardedString(); + while (true) + { + ConsoleKeyInfo info = Console.ReadKey(true); + if (info.Key == ConsoleKey.Enter) + { + Console.WriteLine(); + rv.MakeReadOnly(); + return rv; + } + else + { + Console.Write("*"); + rv.AppendChar(info.KeyChar); + } + } + } -} + private static void DoSetKey(string key) + { + GuardedString str; + if (key == null) + { + Console.Write("Enter Key: "); + GuardedString v1 = ReadPassword(); + Console.Write("Confirm Key: "); + GuardedString v2 = ReadPassword(); + if (!v1.Equals(v2)) + { + Console.WriteLine("Error: Key mismatch."); + return; + } + str = v2; + } + else + { + str = new GuardedString(); + foreach (char c in key.ToCharArray()) + { + str.AppendChar(c); + } + } + Configuration config = + ConfigurationManager.OpenExeConfiguration(ConfigurationUserLevel.None); + config.AppSettings.Settings.Remove(Service.PROP_KEY); + config.AppSettings.Settings.Add(Service.PROP_KEY, str.GetBase64SHA1Hash()); + config.Save(ConfigurationSaveMode.Modified); + Console.WriteLine("Key Updated."); + } + } +} \ No newline at end of file diff --git a/ShellScriptExecutorFactory/ShellScriptExecutorFactory.cs b/ShellScriptExecutorFactory/ShellScriptExecutorFactory.cs index 082bcea5..64367a3f 100644 --- a/ShellScriptExecutorFactory/ShellScriptExecutorFactory.cs +++ b/ShellScriptExecutorFactory/ShellScriptExecutorFactory.cs @@ -51,20 +51,24 @@ public class ShellScriptExecutorFactory : ScriptExecutorFactory /// Creates a script executor give the Shell script. /// override - public ScriptExecutor NewScriptExecutor(Assembly [] refs, string script, bool compile) { + public ScriptExecutor NewScriptExecutor(Assembly[] refs, string script, bool compile) + { return new ShellScriptExecutor(script); } - + /// /// Processes the script. /// - class ShellScriptExecutor : ScriptExecutor { + class ShellScriptExecutor : ScriptExecutor + { private readonly string _script; - - public ShellScriptExecutor(string script) { - _script = script; + + public ShellScriptExecutor(string script) + { + _script = script; } - public object Execute(IDictionary arguments) { + public object Execute(IDictionary arguments) + { // create the process info.. Process process = new Process(); // set the defaults.. @@ -75,36 +79,54 @@ public object Execute(IDictionary arguments) { // if there are any environment varibles set to false.. process.StartInfo.UseShellExecute = arguments.Count == 0; // take out username and password if they're in the options. - foreach (KeyValuePair kv in arguments) { - if (kv.Key.ToUpper().Equals("USERNAME")) { + foreach (KeyValuePair kv in arguments) + { + if (kv.Key.ToUpper().Equals("USERNAME")) + { string domainUser = kv.Value.ToString(); - string[] split = domainUser.Split(new char[] {'\\'}); - if (split.Length == 1) { + string[] split = domainUser.Split(new char[] { '\\' }); + if (split.Length == 1) + { process.StartInfo.UserName = split[0]; - } else { + } + else + { process.StartInfo.Domain = split[0]; process.StartInfo.UserName = split[1]; } - } else if (kv.Key.ToUpper().Equals("PASSWORD")) { - if (kv.Value is SecureString) { + } + else if (kv.Key.ToUpper().Equals("PASSWORD")) + { + if (kv.Value is SecureString) + { process.StartInfo.Password = (SecureString)kv.Value; } - else if (kv.Value is GuardedString) { + else if (kv.Value is GuardedString) + { process.StartInfo.Password = ((GuardedString)kv.Value).ToSecureString(); - } else { + } + else + { throw new ArgumentException("Invalid type for password."); } - } else if (kv.Key.ToUpper().Equals("WORKINGDIR")) { + } + else if (kv.Key.ToUpper().Equals("WORKINGDIR")) + { process.StartInfo.WorkingDirectory = kv.Value.ToString(); - } else if (kv.Key.ToUpper().Equals("TIMEOUT")) { + } + else if (kv.Key.ToUpper().Equals("TIMEOUT")) + { timeout = Int32.Parse(kv.Value.ToString()); - } else { + } + else + { process.StartInfo.EnvironmentVariables[kv.Key] = kv.Value.ToString(); } } // write out the script.. string fn = Path.GetTempFileName() + ".cmd"; - using (StreamWriter sw = new StreamWriter(fn)) { + using (StreamWriter sw = new StreamWriter(fn)) + { sw.Write(_script); } // set temp file.. @@ -112,7 +134,8 @@ public object Execute(IDictionary arguments) { // execute script.. process.Start(); // wait for the process to exit.. - if (!process.WaitForExit(timeout)) { + if (!process.WaitForExit(timeout)) + { process.Close(); throw new TimeoutException("Script failed to exit in time!"); } diff --git a/TestBundleV1/TestConnector.cs b/TestBundleV1/TestConnector.cs index da928cf2..21fbbc8d 100644 --- a/TestBundleV1/TestConnector.cs +++ b/TestBundleV1/TestConnector.cs @@ -38,86 +38,101 @@ public class TstConnectorConfig : AbstractConfiguration /// /// keep lower case for consistent unit tests /// - [ConfigurationProperty(OperationTypes=new Type[]{typeof(SyncOp)})] - public string tstField{get;set;} - + [ConfigurationProperty(OperationTypes = new Type[] { typeof(SyncOp) })] + public string tstField { get; set; } + /// /// keep lower case for consistent unit tests /// - public int numResults{get;set;} - + public int numResults { get; set; } + /// /// keep lower case for consistent unit tests /// - public bool failValidation{get;set;} - + public bool failValidation { get; set; } + /// /// keep lower case for consistent unit tests /// - public bool resetConnectionCount{get;set;} - - public override void Validate() { - if (failValidation) { - throw new ConnectorException("validation failed "+CultureInfo.CurrentUICulture.TwoLetterISOLanguageName); + public bool resetConnectionCount { get; set; } + + public override void Validate() + { + if (failValidation) + { + throw new ConnectorException("validation failed " + CultureInfo.CurrentUICulture.TwoLetterISOLanguageName); } - } + } } - - public class MyTstConnection { + + public class MyTstConnection + { private readonly int _connectionNumber; private bool _isGood = true; - - public MyTstConnection(int connectionNumber) { + + public MyTstConnection(int connectionNumber) + { _connectionNumber = connectionNumber; } - - public void Test() { - if (!_isGood) { + + public void Test() + { + if (!_isGood) + { throw new ConnectorException("Connection is bad"); } } - - public void Dispose() { + + public void Dispose() + { _isGood = false; } - - public bool IsGood() { + + public bool IsGood() + { return _isGood; } - - public int GetConnectionNumber() { + + public int GetConnectionNumber() + { return _connectionNumber; } } - + [ConnectorClass("TestConnector", typeof(TstConnectorConfig), - MessageCatalogPaths= new String[]{"TestBundleV1.Messages"} + MessageCatalogPaths = new String[] { "TestBundleV1.Messages" } )] public class TstConnector : CreateOp, PoolableConnector, SchemaOp, SearchOp, SyncOp { private static int _connectionCount = 0; private MyTstConnection _myConnection; private TstConnectorConfig _config; - - public Uid Create(ObjectClass oclass, ICollection attrs, OperationOptions options) { - int? delay = (int?)CollectionUtil.GetValue(options.Options,"delay",null); - if ( delay != null ) { + + public Uid Create(ObjectClass oclass, ICollection attrs, OperationOptions options) + { + int? delay = (int?)CollectionUtil.GetValue(options.Options, "delay", null); + if (delay != null) + { Thread.Sleep((int)delay); } - if ( options.Options.ContainsKey("testPooling")) { + if (options.Options.ContainsKey("testPooling")) + { return new Uid(_myConnection.GetConnectionNumber().ToString()); } - else { + else + { String version = "1.0"; return new Uid(version); } } - public void Init(Configuration cfg) { + public void Init(Configuration cfg) + { _config = (TstConnectorConfig)cfg; - if (_config.resetConnectionCount) { + if (_config.resetConnectionCount) + { _connectionCount = 0; } _myConnection = new MyTstConnection(_connectionCount++); @@ -127,90 +142,107 @@ public static String getVersion() { return "1.0"; } - - public void Dispose() { - if (_myConnection != null) { + + public void Dispose() + { + if (_myConnection != null) + { _myConnection.Dispose(); _myConnection = null; } } - /** - * Used by the script tests - */ - public String concat(String s1, String s2) { - return s1+s2; - } - - public void CheckAlive() { + + /// + /// Used by the script tests + /// + public String concat(String s1, String s2) + { + return s1 + s2; + } + + public void CheckAlive() + { _myConnection.Test(); } - - private class MyTranslator : AbstractFilterTranslator { - + + private class MyTranslator : AbstractFilterTranslator + { + } - public FilterTranslator CreateFilterTranslator(ObjectClass oclass, OperationOptions options) { + public FilterTranslator CreateFilterTranslator(ObjectClass oclass, OperationOptions options) + { return new MyTranslator(); } - public void ExecuteQuery(ObjectClass oclass, String query, ResultsHandler handler, OperationOptions options) { - - for ( int i = 0; i < _config.numResults; i++ ) { - int? delay = (int?)CollectionUtil.GetValue(options.Options,"delay",null); - if ( delay != null ) { + public void ExecuteQuery(ObjectClass oclass, String query, ResultsHandler handler, OperationOptions options) + { + + for (int i = 0; i < _config.numResults; i++) + { + int? delay = (int?)CollectionUtil.GetValue(options.Options, "delay", null); + if (delay != null) + { Thread.Sleep((int)delay); } ConnectorObjectBuilder builder = new ConnectorObjectBuilder(); - builder.SetUid(""+i); + builder.SetUid("" + i); builder.SetName(i.ToString()); builder.ObjectClass = oclass; - for ( int j = 0; j < 50; j++ ) { - builder.AddAttribute("myattribute"+j,"myvaluevaluevalue"+j); + for (int j = 0; j < 50; j++) + { + builder.AddAttribute("myattribute" + j, "myvaluevaluevalue" + j); } ConnectorObject rv = builder.Build(); - if (!handler(rv)) { + if (!handler(rv)) + { break; } } } public void Sync(ObjectClass objClass, SyncToken token, SyncResultsHandler handler, - OperationOptions options) { - for (int i = 0; i < _config.numResults; i++ ) { + OperationOptions options) + { + for (int i = 0; i < _config.numResults; i++) + { ConnectorObjectBuilder obuilder = new ConnectorObjectBuilder(); obuilder.SetUid(i.ToString()); obuilder.SetName(i.ToString()); - obuilder.ObjectClass=(objClass); - + obuilder.ObjectClass = (objClass); + SyncDeltaBuilder builder = new SyncDeltaBuilder(); - builder.Object=(obuilder.Build()); - builder.DeltaType=(SyncDeltaType.CREATE_OR_UPDATE); - builder.Token=(new SyncToken("mytoken")); + builder.Object = (obuilder.Build()); + builder.DeltaType = (SyncDeltaType.CREATE_OR_UPDATE); + builder.Token = (new SyncToken("mytoken")); SyncDelta rv = builder.Build(); - if (!handler(rv)) { + if (!handler(rv)) + { break; } } } - - public SyncToken GetLatestSyncToken(ObjectClass objectClass) + + public SyncToken GetLatestSyncToken(ObjectClass objectClass) { return new SyncToken("mylatest"); } - - public Schema Schema() { + + public Schema Schema() + { SchemaBuilder builder = new SchemaBuilder(SafeType.Get()); - for ( int i = 0 ; i < 2; i++ ) { + for (int i = 0; i < 2; i++) + { ObjectClassInfoBuilder classBuilder = new ObjectClassInfoBuilder(); - classBuilder.ObjectType=("class"+i); - for ( int j = 0; j < 200; j++) { - classBuilder.AddAttributeInfo(ConnectorAttributeInfoBuilder.Build("attributename"+j, typeof(String))); - } + classBuilder.ObjectType = ("class" + i); + for (int j = 0; j < 200; j++) + { + classBuilder.AddAttributeInfo(ConnectorAttributeInfoBuilder.Build("attributename" + j, typeof(String))); + } builder.DefineObjectClass(classBuilder.Build()); } return builder.Build(); } - } } diff --git a/TestBundleV2/TestConnector.cs b/TestBundleV2/TestConnector.cs index 9ed69370..5b74e5d7 100644 --- a/TestBundleV2/TestConnector.cs +++ b/TestBundleV2/TestConnector.cs @@ -30,32 +30,35 @@ namespace org.identityconnectors.testconnector { public class TstConnectorConfig : AbstractConfiguration - { - public override void Validate() { + { + public override void Validate() + { } } - + [ConnectorClass("TestConnector", typeof(TstConnectorConfig), - MessageCatalogPaths=new String[]{"TestBundleV2.Messages"} + MessageCatalogPaths = new String[] { "TestBundleV2.Messages" } )] public class TstConnector : CreateOp, Connector, SchemaOp { - public Uid Create(ObjectClass oclass, ICollection attrs, OperationOptions options) { + public Uid Create(ObjectClass oclass, ICollection attrs, OperationOptions options) + { String version = "2.0"; return new Uid(version); } - public void Init(Configuration cfg) { + public void Init(Configuration cfg) + { } - public void Dispose() { - + public void Dispose() + { + } - - public Schema Schema() { + + public Schema Schema() + { return null; } - - } } diff --git a/TestCommon/FrameworkInternalBridge.cs b/TestCommon/FrameworkInternalBridge.cs index cb661694..17fca333 100644 --- a/TestCommon/FrameworkInternalBridge.cs +++ b/TestCommon/FrameworkInternalBridge.cs @@ -31,7 +31,8 @@ using Org.IdentityConnectors.Framework.Common.Objects; namespace Org.IdentityConnectors.Test.Common { - internal static class FrameworkInternalBridge { + internal static class FrameworkInternalBridge + { private static readonly Object LOCK = new Object(); private static Assembly _assembly = null; /// @@ -39,20 +40,23 @@ internal static class FrameworkInternalBridge { /// /// /// - public static SafeType LoadType(String typeName) where T : class { - + public static SafeType LoadType(String typeName) where T : class + { + Assembly assembly; - lock(LOCK) { - if (_assembly == null) { + lock (LOCK) + { + if (_assembly == null) + { AssemblyName assemName = new AssemblyName(); assemName.Name = "FrameworkInternal"; _assembly = Assembly.Load(assemName); } assembly = _assembly; } - - return SafeType.ForRawType(assembly.GetType(typeName,true)); - + + return SafeType.ForRawType(assembly.GetType(typeName, true)); + } - } + } } diff --git a/TestCommon/Test.cs b/TestCommon/Test.cs index 66483a94..08b66da4 100644 --- a/TestCommon/Test.cs +++ b/TestCommon/Test.cs @@ -38,21 +38,25 @@ using Org.IdentityConnectors.Test.Common.Spi; namespace Org.IdentityConnectors.Test.Common -{ +{ /// /// which stores all connector objects into /// list retrievable with . /// - public sealed class ToListResultsHandler { - private IList _objects + public sealed class ToListResultsHandler + { + private IList _objects = new List(); - public bool Handle(ConnectorObject obj) { + public bool Handle(ConnectorObject obj) + { _objects.Add(obj); return true; } - - public IList Objects { - get { + + public IList Objects + { + get + { return _objects; } } @@ -61,247 +65,294 @@ public IList Objects { /// /// Bag of utility methods useful to connector tests. /// - public sealed class TestHelpers { - - private TestHelpers() { + public sealed class TestHelpers + { + + private TestHelpers() + { } - - /** - * Method for convenient testing of local connectors. - */ + + /// + /// Method for convenient testing of local connectors. + /// public static APIConfiguration CreateTestConfiguration(SafeType clazz, - Configuration config) { + Configuration config) + { return GetSpi().CreateTestConfiguration(clazz, config); } - /** - * Fills a configuration bean with data from the given map. The map - * keys are configuration property names and the values are - * configuration property values. - * - * @param config the configuration bean. - * @param configData the map with configuration data. - */ + /// + /// Fills a configuration bean with data from the given map. + /// + /// + /// The map + /// keys are configuration property names and the values are + /// configuration property values. + /// + /// the configuration bean. + /// the map with configuration data. public static void FillConfiguration(Configuration config, - IDictionary configData) { + IDictionary configData) + { GetSpi().FillConfiguration(config, configData); } - - /** - * Creates an dummy message catalog ideal for unit testing. - * All messages are formatted as follows: - *

- * message-key: arg0.toString(), ..., argn.toString - * @return A dummy message catalog. - */ - public static ConnectorMessages CreateDummyMessages() { + + ///

+ /// Creates an dummy message catalog ideal for unit testing. + /// + /// + /// All messages are formatted as follows: + /// + /// message-key: arg0.toString(), ..., argn.toString + /// + /// + /// A dummy message catalog. + public static ConnectorMessages CreateDummyMessages() + { return GetSpi().CreateDummyMessages(); } - - public static IList SearchToList(SearchApiOp search, - ObjectClass oclass, - Filter filter) { + + public static IList SearchToList(SearchApiOp search, + ObjectClass oclass, + Filter filter) + { return SearchToList(search, oclass, filter, null); } - - public static IList SearchToList(SearchApiOp search, - ObjectClass oclass, + + public static IList SearchToList(SearchApiOp search, + ObjectClass oclass, Filter filter, - OperationOptions options) { + OperationOptions options) + { ToListResultsHandler handler = new ToListResultsHandler(); - search.Search(oclass,filter, handler.Handle,options); + search.Search(oclass, filter, handler.Handle, options); return handler.Objects; } - /** - * Performs a raw, unfiltered search at the SPI level, - * eliminating duplicates from the result set. - * @param search The search SPI - * @param oclass The object class - passed through to - * connector so it may be null if the connecor - * allowing it to be null. (This is convenient for - * unit tests, but will not be the case in general) - * @param filter The filter to search on - * @param options The options - may be null - will - * be cast to an empty OperationOptions - * @return The list of results. - */ - public static IList SearchToList(SearchOp search, - ObjectClass oclass, - Filter filter) where T : class{ - return SearchToList(search,oclass,filter,null); + /// + /// Performs a raw, unfiltered search at the SPI level, + /// eliminating duplicates from the result set. + /// + /// The search SPI + /// The object class - passed through to + /// connector so it may be null if the connecor + /// allowing it to be null. (This is convenient for + /// unit tests, but will not be the case in general) + /// The filter to search on + /// The options - may be null - will + /// be cast to an empty OperationOptions + /// The list of results. + public static IList SearchToList(SearchOp search, + ObjectClass oclass, + Filter filter) where T : class + { + return SearchToList(search, oclass, filter, null); } - /** - * Performs a raw, unfiltered search at the SPI level, - * eliminating duplicates from the result set. - * @param search The search SPI - * @param oclass The object class - passed through to - * connector so it may be null if the connecor - * allowing it to be null. (This is convenient for - * unit tests, but will not be the case in general) - * @param filter The filter to search on - * @param options The options - may be null - will - * be cast to an empty OperationOptions - * @return The list of results. - */ - public static IList SearchToList(SearchOp search, - ObjectClass oclass, + /// + /// Performs a raw, unfiltered search at the SPI level, + /// eliminating duplicates from the result set. + /// + /// The search SPI + /// The object class - passed through to + /// connector so it may be null if the connecor + /// allowing it to be null. (This is convenient for + /// unit tests, but will not be the case in general) + /// The filter to search on + /// The options - may be null - will + /// be cast to an empty OperationOptions + /// The list of results. + public static IList SearchToList(SearchOp search, + ObjectClass oclass, Filter filter, - OperationOptions options) where T : class{ + OperationOptions options) where T : class + { ToListResultsHandler handler = new ToListResultsHandler(); - Search(search,oclass,filter, handler.Handle, options); + Search(search, oclass, filter, handler.Handle, options); return handler.Objects; } - - /** - * Performs a raw, unfiltered search at the SPI level, - * eliminating duplicates from the result set. - * @param search The search SPI - * @param oclass The object class - passed through to - * connector so it may be null if the connecor - * allowing it to be null. (This is convenient for - * unit tests, but will not be the case in general) - * @param filter The filter to search on - * @param handler The result handler - * @param options The options - may be null - will - * be cast to an empty OperationOptions - */ + + /// + /// Performs a raw, unfiltered search at the SPI level, + /// eliminating duplicates from the result set. + /// + /// The search SPI + /// The object class - passed through to + /// connector so it may be null if the connecor + /// allowing it to be null. (This is convenient for + /// unit tests, but will not be the case in general) + /// The filter to search on + /// The result handler + /// The options - may be null - will + /// be cast to an empty OperationOptions public static void Search(SearchOp search, - ObjectClass oclass, - Filter filter, + ObjectClass oclass, + Filter filter, ResultsHandler handler, - OperationOptions options) where T : class { + OperationOptions options) where T : class + { GetSpi().Search(search, oclass, filter, handler, options); } - + //At some point we might make this pluggable, but for now, hard-code private const String IMPL_NAME = "Org.IdentityConnectors.Framework.Impl.Test.TestHelpersImpl"; private static readonly object LOCK = new object(); private static TestHelpersSpi _instance; - - /** - * Returns the instance of this factory. - * @return The instance of this factory - */ - private static TestHelpersSpi GetSpi() { - lock(LOCK) { - if (_instance == null) { + + /// + /// Returns the instance of this factory. + /// + /// The instance of this factory + private static TestHelpersSpi GetSpi() + { + lock (LOCK) + { + if (_instance == null) + { SafeType type = FrameworkInternalBridge.LoadType(IMPL_NAME); _instance = type.CreateInstance(); } return _instance; } } - + private static IDictionary _properties = null; private static readonly string PREFIX = Environment.GetEnvironmentVariable("USERPROFILE") + "/.connectors/"; public static readonly string GLOBAL_PROPS = "connectors.xml"; - + [MethodImpl(MethodImplOptions.NoInlining)] - public static string GetProperty(string key, string def) { - Assembly asm = Assembly.GetCallingAssembly(); - return CollectionUtil.GetValue(GetProperties(asm), key, def); + public static string GetProperty(string key, string def) + { + Assembly asm = Assembly.GetCallingAssembly(); + return CollectionUtil.GetValue(GetProperties(asm), key, def); } - - private static IDictionary GetProperties(Assembly asm) { - lock(LOCK) { - if (_properties == null) { + + private static IDictionary GetProperties(Assembly asm) + { + lock (LOCK) + { + if (_properties == null) + { _properties = LoadProperties(asm); } } // create a new instance so its not mutable return CollectionUtil.NewReadOnlyDictionary(_properties); } - - private static IDictionary LoadProperties(Assembly asm) { - const string ERR = "TestHelpers: Unable to load optional XML properties file \"{0}\""; + + private static IDictionary LoadProperties(Assembly asm) + { + const string ERR = "TestHelpers: Unable to load optional XML properties file \"{0}\""; string fn = null; IDictionary props = null; IDictionary ret = new Dictionary(); - + //load global properties file - try { - fn = Path.Combine(PREFIX, GLOBAL_PROPS); - props = LoadPropertiesFile(fn); - CollectionUtil.AddOrReplaceAll(ret, props); - } catch (Exception) { - Trace.TraceInformation(ERR, fn); - } - + try + { + fn = Path.Combine(PREFIX, GLOBAL_PROPS); + props = LoadPropertiesFile(fn); + CollectionUtil.AddOrReplaceAll(ret, props); + } + catch (Exception) + { + Trace.TraceInformation(ERR, fn); + } + // load the project properties file - try { + try + { fn = Path.Combine(Environment.CurrentDirectory, "project.xml"); props = LoadPropertiesFile(fn); CollectionUtil.AddAll(ret, props); - } catch (Exception) { + } + catch (Exception) + { Trace.TraceInformation(ERR, fn); } - + // private settings are in the "assembly name" folder, as defined in the assembly string prjName = asm.GetName().Name; - if (!StringUtil.IsBlank(prjName)) { - //load private project properties file - try { - fn = Path.Combine(PREFIX, prjName + "/project.xml"); + if (!StringUtil.IsBlank(prjName)) + { + //load private project properties file + try + { + fn = Path.Combine(PREFIX, prjName + "/project.xml"); props = LoadPropertiesFile(fn); CollectionUtil.AddOrReplaceAll(ret, props); - } catch (IOException) { + } + catch (IOException) + { Trace.TraceInformation(ERR, fn); } - - string cfg = Environment.GetEnvironmentVariable("configuration"); - if(!StringUtil.IsBlank(cfg)) { - try { + + string cfg = Environment.GetEnvironmentVariable("configuration"); + if (!StringUtil.IsBlank(cfg)) + { + try + { // load a config-specific properties file fn = Path.Combine(PREFIX, prjName + "/" + cfg + "/project.xml"); props = LoadPropertiesFile(fn); CollectionUtil.AddOrReplaceAll(ret, props); - } catch (IOException) { + } + catch (IOException) + { Trace.TraceInformation(ERR, fn); } - } - } else { - TraceUtil.TraceException("Could not infer assembly name.", new Exception()); + } + } + else + { + TraceUtil.TraceException("Could not infer assembly name.", new Exception()); } - + // load the environment variables - foreach (DictionaryEntry entry in Environment.GetEnvironmentVariables()) { + foreach (DictionaryEntry entry in Environment.GetEnvironmentVariables()) + { ret[entry.Key.ToString()] = entry.Value.ToString(); } return ret; } - + /// /// Format for the xml file is simple <property name='' value=''/> /// /// /// - public static IDictionary LoadPropertiesFile(string filename) { + public static IDictionary LoadPropertiesFile(string filename) + { IDictionary ret = new Dictionary(); //Environment. XmlTextReader reader = null; - try { + try + { // Load the reader with the data file and ignore all white space nodes. reader = new XmlTextReader(filename); reader.WhitespaceHandling = WhitespaceHandling.None; // Parse the file and display each of the nodes. - while (reader.Read()) { - if (reader.NodeType == XmlNodeType.Element && - reader.Name.Equals("property")) { + while (reader.Read()) + { + if (reader.NodeType == XmlNodeType.Element && + reader.Name.Equals("property")) + { string name = reader.GetAttribute("name"); string xmlValue = reader.GetAttribute("value"); - if (!StringUtil.IsBlank(name) && xmlValue != null) { + if (!StringUtil.IsBlank(name) && xmlValue != null) + { ret[name] = xmlValue; } } - } - } finally { - if (reader!=null) - reader.Close(); + } + } + finally + { + if (reader != null) + reader.Close(); } return ret; - } + } } } diff --git a/TestCommon/TestHelpersSpi.cs b/TestCommon/TestHelpersSpi.cs index 692c300f..c618dc58 100644 --- a/TestCommon/TestHelpersSpi.cs +++ b/TestCommon/TestHelpersSpi.cs @@ -35,20 +35,20 @@ namespace Org.IdentityConnectors.Test.Common.Spi /// Private use only, do not implement! Use the methods in /// instead. /// - public interface TestHelpersSpi { - + public interface TestHelpersSpi + { APIConfiguration CreateTestConfiguration(SafeType clazz, Configuration config); - + void FillConfiguration(Configuration config, IDictionary configData); void Search(SearchOp search, - ObjectClass oclass, - Filter filter, + ObjectClass oclass, + Filter filter, ResultsHandler handler, OperationOptions options) where T : class; - + ConnectorMessages CreateDummyMessages(); } } From 6c53d4acc91a14f49cfaff546b5df49ca6b0929c Mon Sep 17 00:00:00 2001 From: abadea Date: Tue, 13 Oct 2009 10:22:42 +0000 Subject: [PATCH 252/342] Issue 581: Framework should return default catalog message if not found in specific catalog --- FrameworkInternal/Api.cs | 23 ++++++++++++----------- 1 file changed, 12 insertions(+), 11 deletions(-) diff --git a/FrameworkInternal/Api.cs b/FrameworkInternal/Api.cs index 8b66f069..4334a57b 100644 --- a/FrameworkInternal/Api.cs +++ b/FrameworkInternal/Api.cs @@ -402,25 +402,20 @@ public String Format(String key, String dflt, params object[] args) dflt = key; } CultureInfo foundCulture = locale; - IDictionary - catalog = CollectionUtil.GetValue(_catalogs, foundCulture, null); + String message = GetCatalogMessage(foundCulture, key); //check neutral culture - if (catalog == null) + if (message == null) { foundCulture = foundCulture.Parent; - catalog = CollectionUtil.GetValue(_catalogs, foundCulture, null); + message = GetCatalogMessage(foundCulture, key); } //check invariant culture - if (catalog == null) + if (message == null) { foundCulture = foundCulture.Parent; - catalog = CollectionUtil.GetValue(_catalogs, foundCulture, null); - } - String message = null; - if (catalog != null) - { - message = CollectionUtil.GetValue(catalog, key, null); + message = GetCatalogMessage(foundCulture, key); } + //and default to framework if (message == null) { message = GetFrameworkMessage(locale, key); @@ -437,6 +432,12 @@ public String Format(String key, String dflt, params object[] args) } } + private String GetCatalogMessage(CultureInfo culture, String key) + { + IDictionary catalog = CollectionUtil.GetValue(_catalogs, culture, null); + return catalog != null ? CollectionUtil.GetValue(catalog, key, null) : null; + } + private String GetFrameworkMessage(CultureInfo culture, String key) { ResourceManager manager = From 45bc245a0f4c4a165d646fdd0d36de63e534c57c Mon Sep 17 00:00:00 2001 From: abadea Date: Tue, 20 Oct 2009 15:25:44 +0000 Subject: [PATCH 253/342] Issue 444: Clarify Configuration.validate(), TestOp.test(), and PoolableConnector.checkAlive() --- Framework/ApiOperations.cs | 37 ++++++++++++++++---- Framework/Spi.cs | 72 +++++++++++++++++++++++++++++++++----- Framework/SpiOperations.cs | 23 +++++++++--- 3 files changed, 114 insertions(+), 18 deletions(-) diff --git a/Framework/ApiOperations.cs b/Framework/ApiOperations.cs index 6b594395..2d320777 100644 --- a/Framework/ApiOperations.cs +++ b/Framework/ApiOperations.cs @@ -423,23 +423,48 @@ Uid RemoveAttributeValues(ObjectClass objclass, } + /// + /// Validates the . + /// + /// A valid configuration is one that is ready to be used by the connector: + /// it is complete (all the required properties have been given values) + /// and the property values are well-formed (are in the expected range, + /// have the expected format, etc.) + /// public interface ValidateApiOp : APIOperation { /// - /// Tests connectivity and validity of the . + /// Validates the . /// - /// iff the is not valid or a - /// to the resource could not be established. + /// iff the configuration is not valid. void Validate(); } + /// + /// Tests the with the connector. + /// + /// Unlike validation performed by , testing a configuration should + /// check that any pieces of environment referred by the configuration are available. + /// For example the connector could make a physical connection to a host specified + /// in the configuration to check that it exists and that the credentials + /// specified in the configuration are usable. + /// + /// + /// Since this operation may connect to the resource, it may be slow. Clients are + /// advised not to invoke this operation often, such as before every provisioning operation. + /// This operation is not intended to check that the connector is alive + /// (its physical connection to the resource has not timed out). + /// + /// + /// This operation may be invoked before the configuration has been validated. + /// + /// public interface TestApiOp : APIOperation { /// - /// Tests connectivity and validity of the . + /// Tests the current with the connector. /// - /// iff the is not valid or a - /// to the resource could not be established. + /// iff the configuration is not valid or the test failed. void Test(); } } \ No newline at end of file diff --git a/Framework/Spi.cs b/Framework/Spi.cs index efa80ebf..55c8f958 100644 --- a/Framework/Spi.cs +++ b/Framework/Spi.cs @@ -92,17 +92,56 @@ public interface AttributeNormalizer #endregion #region Configuration - /// - /// Configuration information for the Connector. - /// + /// + /// Encapsulates the configuration of a connector. + /// + /// Implementations of the Configuration interface must have a default + /// constructor. All properties are considered configuration for the connector. + /// The initial value of the property is + /// considered the default value of the property. The types of the properties + /// can be only those returned by and + /// multi-dimensional arrays thereof. The properties are not required by default, + /// but a property can be marked as required through use of the . + /// + /// + /// Each property corresponds to two entries in a resource named Messages: + /// [Property].display and [Property].help. For example, + /// hostname.help and hostname.display would be the keys + /// corresponding to a hostname property. The display message is the display + /// name of the property and can be used to display the property in a view. The help + /// message holds the description of the property. The names of the two keys can be overridden + /// through the ConfigurationProperty annotation. + /// + /// public interface Configuration { - + /// + /// Gets or sets the {@link ConnectorMessages message catalog} instance that allows the Connector + /// to localize messages. The setter is called before any bean property setter, + /// the method or this property getter. + /// ConnectorMessages ConnectorMessages { get; set; } /// - /// Determine if the configuration is valid based on the values set. + /// Determines if the configuration is valid. + /// + /// A valid configuration is one that is ready to be used by the connector: + /// it is complete (all the required properties have been given values) + /// and the property values are well-formed (are in the expected range, + /// have the expected format, etc.) + /// + /// + /// Implementations of this method should not connect to the resource + /// in an attempt to validate the configuration. For example, implementations + /// should not attempt to check that a host of the specified name exists + /// by making a connection to it. Such checks can be performed in the implementation + /// of the method. + /// /// + /// iff the configuration is not valid. Implementations + /// are encouraged to throw the most specific exception available. + /// When no specific exception is available, implementations can throw + /// . void Validate(); } #endregion @@ -275,9 +314,26 @@ public interface Connector : IDisposable public interface PoolableConnector : Connector { /// - /// Checks to see if the connector is still alive. - /// - /// If no longer alive. + /// Checks if the connector is still alive. + /// + /// A connector can spend a large amount of time in the pool before + /// being used. This method is intended to check if the connector is + /// alive and operations can be invoked on it (for instance, an implementation + /// would check that the connector's physical connection to the resource + /// has not timed out). + /// + /// + /// The major difference between this method and is that + /// this method must do only the minimum that is necessary to check that the + /// connector is still alive. TestOp.Test() does a more thorough + /// check of the environment specified in the Configuration, and can therefore + /// be much slower. + /// + /// + /// This method can be called often. Implementations should do their + /// best to keep this method fast. + /// + /// if the connector is no longer alive. void CheckAlive(); } #endregion diff --git a/Framework/SpiOperations.cs b/Framework/SpiOperations.cs index 53d4cd89..9698a3f4 100644 --- a/Framework/SpiOperations.cs +++ b/Framework/SpiOperations.cs @@ -458,13 +458,28 @@ Uid RemoveAttributeValues(ObjectClass objclass, OperationOptions options); } + /// + /// Tests the connector . + /// + /// Unlike validation performed by , testing a configuration + /// checks that any pieces of environment referred by the configuration are available. + /// For example, the connector could make a physical connection to a host specified + /// in the configuration to check that it exists and that the credentials + /// specified in the configuration are usable. + /// + /// + /// This operation may be invoked before the configuration has been validated. + /// An implementation is free to validate the configuration before testing it. + /// public interface TestOp : SPIOperation { /// - /// Tests connectivity and validity of the . - /// - /// iff the is not valid or a - /// to the resource could not be established. + /// Tests the with the connector. + /// + /// iff the configuration is not valid or the test failed. Implementations + /// are encouraged to throw the most specific exception available. + /// When no specific exception is available, implementations can throw + /// . void Test(); } From 6f59aad33e67ab903a0e7dd9f920a8123adc86fd Mon Sep 17 00:00:00 2001 From: abadea Date: Fri, 23 Oct 2009 10:19:48 +0000 Subject: [PATCH 254/342] Trivial, better wording --- Framework/ApiOperations.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Framework/ApiOperations.cs b/Framework/ApiOperations.cs index 2d320777..0af94b63 100644 --- a/Framework/ApiOperations.cs +++ b/Framework/ApiOperations.cs @@ -453,7 +453,7 @@ public interface ValidateApiOp : APIOperation /// Since this operation may connect to the resource, it may be slow. Clients are /// advised not to invoke this operation often, such as before every provisioning operation. /// This operation is not intended to check that the connector is alive - /// (its physical connection to the resource has not timed out). + /// (i.e., its physical connection to the resource has not timed out). /// /// /// This operation may be invoked before the configuration has been validated. From 1a587a924d125caa19455e9f8425a57f73aa6a7d Mon Sep 17 00:00:00 2001 From: abadea Date: Tue, 27 Oct 2009 17:44:35 +0000 Subject: [PATCH 255/342] Trivial, improving documentation --- Common/Assertions.cs | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/Common/Assertions.cs b/Common/Assertions.cs index 2b933300..7f94013b 100644 --- a/Common/Assertions.cs +++ b/Common/Assertions.cs @@ -30,12 +30,12 @@ namespace Org.IdentityConnectors.Common public static class Assertions { /// - /// Throws if the parameter o + /// Throws if the parameter /// is null. /// /// check if the object is null. /// name of the parameter to check for null. - /// if o is null and constructs a + /// if is null and constructs a /// message with the name of the parameter. public static void NullCheck(Object o, String param) { @@ -47,11 +47,13 @@ public static void NullCheck(Object o, String param) } /// - /// Throws if the parameter o + /// Throws if the parameter /// is null or blank. /// /// value to test for blank. /// name of the parameter to check. + /// if is null or blank and constructs a + /// message with the name of the parameter. public static void BlankCheck(String o, String param) { String FORMAT = "Parameter '{0}' must not be blank."; From 3e239e45667a3ca7b8cc16e8b8f455c4288ebe3e Mon Sep 17 00:00:00 2001 From: abadea Date: Thu, 29 Oct 2009 14:48:02 +0000 Subject: [PATCH 256/342] Trivial, documenting correct exception --- Common/Assertions.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Common/Assertions.cs b/Common/Assertions.cs index 7f94013b..ea53710c 100644 --- a/Common/Assertions.cs +++ b/Common/Assertions.cs @@ -35,7 +35,7 @@ public static class Assertions /// /// check if the object is null. /// name of the parameter to check for null. - /// if is null and constructs a + /// if is null and constructs a /// message with the name of the parameter. public static void NullCheck(Object o, String param) { From 50985dbe50a90a9bd801dcd7640e31ec344b5361 Mon Sep 17 00:00:00 2001 From: abadea Date: Thu, 29 Oct 2009 14:49:54 +0000 Subject: [PATCH 257/342] Trivial, documenting correct exception --- Common/Assertions.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Common/Assertions.cs b/Common/Assertions.cs index ea53710c..b13aafc2 100644 --- a/Common/Assertions.cs +++ b/Common/Assertions.cs @@ -30,7 +30,7 @@ namespace Org.IdentityConnectors.Common public static class Assertions { /// - /// Throws if the parameter + /// Throws if the parameter /// is null. /// /// check if the object is null. From 6c51f103147796113bb328275bd809231e3e0f71 Mon Sep 17 00:00:00 2001 From: lajos_nagy Date: Fri, 30 Oct 2009 12:48:07 +0000 Subject: [PATCH 258/342] Issue #531: Read configuration files for tests from custom path As the configuration property loading mechanism is slightly different from the one on Java side, see the doc of TestHelpers.GetProperties(Type type, Assembly assembly) for details. In addition, the DotNetCommonBuild.Targets has been modified to add InternalsVisibleTo attribute to the generated AssemblyInfo.cs with the value of InternalsVisibleTo *project property*, that must be set in the *.csproj file in question (see TestCommon.csproj for an example). --- .../ActiveDirectoryConnectorTest.cs | 19 +- .../ActiveDirectoryConnectorTests.csproj | 6 +- .../config/config.xml} | 22 ++ DotNetCommonBuild.Targets | 3 +- FrameworkTests/FrameworkTests.csproj | 12 +- .../config/config.xml | 28 ++ .../config/myconfig/config.xml | 27 ++ FrameworkTests/PropertyBagTests.cs | 135 +++++++ FrameworkTests/TestHelperTests.cs | 175 +++++++-- FrameworkTests/project.xml | 5 - TestCommon/PropertyBag.cs | 154 ++++++++ TestCommon/Test.cs | 339 +++++++++++++----- TestCommon/TestCommon.csproj | 9 +- TestCommon/config.xsd | 38 ++ 14 files changed, 845 insertions(+), 127 deletions(-) rename ActiveDirectoryConnectorTests/{project.xml => Org.IdentityConnectors.ActiveDirectory.ActiveDirectoryConnector/config/config.xml} (50%) create mode 100644 FrameworkTests/Org.IdentityConnectors.TestConnector.FakeConnector/config/config.xml create mode 100644 FrameworkTests/Org.IdentityConnectors.TestConnector.FakeConnector/config/myconfig/config.xml create mode 100644 FrameworkTests/PropertyBagTests.cs delete mode 100644 FrameworkTests/project.xml create mode 100644 TestCommon/PropertyBag.cs create mode 100644 TestCommon/config.xsd diff --git a/ActiveDirectoryConnectorTests/ActiveDirectoryConnectorTest.cs b/ActiveDirectoryConnectorTests/ActiveDirectoryConnectorTest.cs index 3a0c8f70..eaa15c35 100644 --- a/ActiveDirectoryConnectorTests/ActiveDirectoryConnectorTest.cs +++ b/ActiveDirectoryConnectorTests/ActiveDirectoryConnectorTest.cs @@ -1310,8 +1310,8 @@ public void TestDisableDate() public void TestSyncGC() { // test with searchChildDomain (uses GC) - TestSync(true, GetProperty(config_PROPERTY_SYNC_CONTAINER_ROOT)); - TestSync(true, GetProperty(config_PROPERTY_SYNC_CONTAINER_CHILD)); + TestSync(true, GetProperty(config_PROPERTY_SYNC_CONTAINER_ROOT, null)); + TestSync(true, GetProperty(config_PROPERTY_SYNC_CONTAINER_CHILD, null)); } // test sync @@ -1319,8 +1319,8 @@ public void TestSyncGC() public void TestSyncDC() { // test withouth searchChildDomains (uses DC) - TestSync(false, GetProperty(config_PROPERTY_SYNC_CONTAINER_ROOT)); - TestSync(false, GetProperty(config_PROPERTY_SYNC_CONTAINER_CHILD)); + TestSync(false, GetProperty(config_PROPERTY_SYNC_CONTAINER_ROOT, null)); + TestSync(false, GetProperty(config_PROPERTY_SYNC_CONTAINER_CHILD, null)); } [Test] @@ -2738,7 +2738,16 @@ private static void VerifyObject(ICollection requestedAttrib // this needs to be replaced by the real one. public string GetProperty(string propertyName) { - string propertyValue = TestHelpers.GetProperty(propertyName, null); + var propertyValue = + TestHelpers.GetProperties(typeof(ActiveDirectoryConnector)).GetProperty(propertyName); + //Trace.WriteLine(String.Format("GetProperty: {0} = {1}", propertyName, propertyValue)); + return propertyValue; + } + + public string GetProperty(string propertyName, string def) + { + var propertyValue = + TestHelpers.GetProperties(typeof(ActiveDirectoryConnector)).GetProperty(propertyName, def); //Trace.WriteLine(String.Format("GetProperty: {0} = {1}", propertyName, propertyValue)); return propertyValue; } diff --git a/ActiveDirectoryConnectorTests/ActiveDirectoryConnectorTests.csproj b/ActiveDirectoryConnectorTests/ActiveDirectoryConnectorTests.csproj index 3c12f81b..1271b7db 100644 --- a/ActiveDirectoryConnectorTests/ActiveDirectoryConnectorTests.csproj +++ b/ActiveDirectoryConnectorTests/ActiveDirectoryConnectorTests.csproj @@ -105,12 +105,12 @@ - + Always - + if EXIST "$(SolutionDir)\ShellScriptExecutorFactory\$(OutDir)\Shell.ScriptExecutorFactory.dll". (copy "$(SolutionDir)\ShellScriptExecutorFactory\$(OutDir)\Shell.ScriptExecutorFactory.dll" "$(TargetDir)") else (echo yuck > .\delete.me.ad) - + \ No newline at end of file diff --git a/ActiveDirectoryConnectorTests/project.xml b/ActiveDirectoryConnectorTests/Org.IdentityConnectors.ActiveDirectory.ActiveDirectoryConnector/config/config.xml similarity index 50% rename from ActiveDirectoryConnectorTests/project.xml rename to ActiveDirectoryConnectorTests/Org.IdentityConnectors.ActiveDirectory.ActiveDirectoryConnector/config/config.xml index 748bfeaf..ede50e9f 100644 --- a/ActiveDirectoryConnectorTests/project.xml +++ b/ActiveDirectoryConnectorTests/Org.IdentityConnectors.ActiveDirectory.ActiveDirectoryConnector/config/config.xml @@ -1,4 +1,26 @@  + diff --git a/DotNetCommonBuild.Targets b/DotNetCommonBuild.Targets index 5cfd154a..df2777fd 100644 --- a/DotNetCommonBuild.Targets +++ b/DotNetCommonBuild.Targets @@ -55,7 +55,8 @@ AssemblyCopyright="$(Copyright)" AssemblyCompany="$(Company)" AssemblyVersion="$(Major).$(Minor).$(Build).$(SVN_Revision)" - AssemblyFileVersion="$(Major).$(Minor).$(Build).$(SVN_Revision)" /> + AssemblyFileVersion="$(Major).$(Minor).$(Build).$(SVN_Revision)" + InternalsVisibleTo="$(InternalsVisibleTo)" /> False True DEBUG;TRACE + + bin\Release\ @@ -81,6 +83,7 @@ + @@ -91,9 +94,6 @@ Always - - Always - @@ -163,4 +163,10 @@ + + + + + + \ No newline at end of file diff --git a/FrameworkTests/Org.IdentityConnectors.TestConnector.FakeConnector/config/config.xml b/FrameworkTests/Org.IdentityConnectors.TestConnector.FakeConnector/config/config.xml new file mode 100644 index 00000000..f2546a2e --- /dev/null +++ b/FrameworkTests/Org.IdentityConnectors.TestConnector.FakeConnector/config/config.xml @@ -0,0 +1,28 @@ + + + + + + + \ No newline at end of file diff --git a/FrameworkTests/Org.IdentityConnectors.TestConnector.FakeConnector/config/myconfig/config.xml b/FrameworkTests/Org.IdentityConnectors.TestConnector.FakeConnector/config/myconfig/config.xml new file mode 100644 index 00000000..5df51e6c --- /dev/null +++ b/FrameworkTests/Org.IdentityConnectors.TestConnector.FakeConnector/config/myconfig/config.xml @@ -0,0 +1,27 @@ + + + + + + \ No newline at end of file diff --git a/FrameworkTests/PropertyBagTests.cs b/FrameworkTests/PropertyBagTests.cs new file mode 100644 index 00000000..1358b25c --- /dev/null +++ b/FrameworkTests/PropertyBagTests.cs @@ -0,0 +1,135 @@ +/* + * ==================== + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER. + * + * Copyright 2008-2009 Sun Microsystems, Inc. All rights reserved. + * + * The contents of this file are subject to the terms of the Common Development + * and Distribution License("CDDL") (the "License"). You may not use this file + * except in compliance with the License. + * + * You can obtain a copy of the License at + * http://IdentityConnectors.dev.java.net/legal/license.txt + * See the License for the specific language governing permissions and limitations + * under the License. + * + * When distributing the Covered Code, include this CDDL Header Notice in each file + * and include the License file at identityconnectors/legal/license.txt. + * If applicable, add the following below this CDDL Header, with the fields + * enclosed by brackets [] replaced by your own identifying information: + * "Portions Copyrighted [year] [name of copyright owner]" + * ==================== + */ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using NUnit.Framework; +using Org.IdentityConnectors.Test.Common; +using Org.IdentityConnectors.Common; + +namespace FrameworkTests +{ + [TestFixture] + public class PropertyBagTests + { + private PropertyBag bag = null; + + [SetUp] + public void SetUp() + { + bag = CreateBag(); + } + + [Test] + public void TestGetProperty() + { + Assert.AreEqual("value1", bag.GetProperty("key1")); + Assert.IsNull(bag.GetProperty("key2")); + + Assert.AreEqual((int?)1, bag.GetProperty("key3")); + Assert.AreEqual((long?)1, bag.GetProperty("key5")); + + // try not existing + try + { + bag.GetProperty("key4"); + Assert.Fail("Get Property must fail for unexisting property"); + } + catch (ArgumentException) + { + } + + // Try cast + try + { + bag.GetProperty("key3"); + Assert.Fail("Get Property with incompatible type must fail on InvalidCastException"); + } + catch (InvalidCastException) + { + } + } + + [Test] + public void TestGetPropertyWithDef() + { + Assert.AreEqual("value1", bag.GetProperty("key1", "def")); + Assert.IsNull(bag.GetProperty("key2", "def")); + Assert.AreEqual("def", bag.GetProperty("key4", "def")); + Assert.IsNull(bag.GetProperty("key4", null)); + } + + [Test] + public void TestGetStringProperty() + { + Assert.AreEqual("value1", bag.GetStringProperty("key1")); + Assert.IsNull(bag.GetStringProperty("key2")); + // Try cast + try + { + bag.GetStringProperty("key3"); + Assert.Fail("Get Property with incompatible type must fail on InvalidCastException"); + } + catch (InvalidCastException) + { + } + + // try not existing + try + { + bag.GetStringProperty("key4"); + Assert.Fail("Get String Property must fail for unexisting property"); + } + catch (ArgumentException) + { + } + } + + [Test] + public void TestToDictionary() + { + var properties = bag.ToDictionary(); + Assert.That(CollectionUtil.DictionariesEqual(properties, CreateDictionary()), + "ToDictionary must return the same properties as it was created with" ); + } + + private static PropertyBag CreateBag() + { + var properties = CreateDictionary(); + return new PropertyBag(properties); + } + + private static Dictionary CreateDictionary() + { + var properties = new Dictionary + { + {"key1", "value1"}, + {"key2", null}, + {"key3", (int?) 1}, + {"key5", (long?) 1} + }; + return properties; + } + } +} diff --git a/FrameworkTests/TestHelperTests.cs b/FrameworkTests/TestHelperTests.cs index 72225aea..dcef674f 100644 --- a/FrameworkTests/TestHelperTests.cs +++ b/FrameworkTests/TestHelperTests.cs @@ -24,13 +24,16 @@ using System.IO; using System.Xml; using System.Collections.Generic; +using System.Linq; using NUnit.Framework; -using NUnit.Framework.SyntaxHelpers; using Org.IdentityConnectors.Framework.Common.Objects; using Org.IdentityConnectors.Framework.Spi; using Org.IdentityConnectors.Test.Common; +using System.Text; +using System.Diagnostics; +using Org.IdentityConnectors.Common; namespace FrameworkTests { @@ -41,38 +44,142 @@ namespace FrameworkTests public class TestHelperTests { [Test] - public void testLoadProperties() + public void TestReadConfiguration() { - string tmpFn = Path.GetTempFileName(); - try + using (var memoryStream = new MemoryStream()) { - // create some xml text - TextWriter stringWriter = new StringWriter(); - XmlTextWriter w = new XmlTextWriter(stringWriter); - w.WriteStartElement("configuration"); - w.WriteStartElement("property"); - w.WriteAttributeString("name", "bob"); - w.WriteAttributeString("value", "bobsValue"); - w.WriteEndElement(); - w.WriteStartElement("property"); - w.WriteAttributeString("name", "bob2"); - w.WriteAttributeString("value", "bob2sValue"); - w.WriteEndElement(); - w.Close(); - File.WriteAllText(tmpFn, stringWriter.ToString()); + var properties = new Dictionary() + { + {"bob", "bobsValue"}, + {"bob2", "bob2sValue"} + }; + + CreateXmlConfiguration(memoryStream, properties); + + memoryStream.Seek(0, SeekOrigin.Begin); // load the properties files - IDictionary dict = TestHelpers.LoadPropertiesFile(tmpFn); - Assert.AreEqual(dict["bob"], "bobsValue"); + var dict = TestHelpers.ReadConfiguration(memoryStream); + foreach (var property in properties) + { + Assert.AreEqual(dict[property.Key], property.Value); + } } - finally + } + + /// + /// Creates an XML configuration in the specified stream based on the . + /// + /// The output stream. + /// The properties to be stored. + /// The caller is responsible for closing the stream. + private static void CreateXmlConfiguration(Stream stream, IDictionary properties) + { + var settings = new XmlWriterSettings + { + Encoding = Encoding.UTF8, + CloseOutput = false + }; + using (var writer = XmlWriter.Create(stream, settings)) { - File.Delete(tmpFn); + writer.WriteStartDocument(); + writer.WriteStartElement("config"); + foreach (var property in properties) + { + writer.WriteStartElement("property"); + writer.WriteAttributeString("name", property.Key); + writer.WriteAttributeString("value", property.Value); + writer.WriteEndElement(); + } + writer.WriteEndDocument(); + writer.Flush(); } } + [Test] - public void testGetProperties() + public void TestGetProperties() + { + const string testConfigName = "myconfig"; + Type connectorType = typeof(Org.IdentityConnectors.TestConnector.FakeConnector); + + //store environment variables, they must be restored at the end + var oldTestConfig = Environment.GetEnvironmentVariable(TestHelpers.TestConfigEVName); + var oldPrivateConfigRoot = Environment.GetEnvironmentVariable(TestHelpers.PrivateConfigRootEVName); + Environment.SetEnvironmentVariable(TestHelpers.TestConfigEVName, testConfigName); + + var privateConfigRoot = Path.GetTempPath(); + + try + { + //set the TestHelpers.PrivateConfigRootEVName environment variable used by this test + Environment.SetEnvironmentVariable(TestHelpers.PrivateConfigRootEVName, privateConfigRoot); + + //at the end the created dir structure must be deleted, hence we need to store this + privateConfigRoot = Path.Combine(privateConfigRoot, "config"); + + var privateConfigPath = Path.Combine(Path.Combine(privateConfigRoot, connectorType.FullName), + "config-private"); + //create the directory structure for the general and the specific "testConfigName" private config + Directory.CreateDirectory(Path.Combine(privateConfigPath, testConfigName)); + + //create general private config file + using (var configFile = File.Create(Path.Combine(privateConfigPath, "config.xml"))) + { + CreateXmlConfiguration(configFile, new Dictionary() {{"privatekey", "value"}}); + } + + //create specific private config file + using (var configFile = File.Create(Path.Combine(Path.Combine(privateConfigPath, testConfigName), "config.xml"))) + { + CreateXmlConfiguration(configFile, new Dictionary() {{"myconfig.privatekey", "value"}}); + } + + PropertyBag bag1 = TestHelpers.GetProperties(connectorType); + CheckProperties(bag1); + PropertyBag bag2 = TestHelpers.GetProperties(connectorType); + Assert.AreSame(bag1, bag2, "TestHepers must create the same PropertyBag for the same connector"); + } + finally + { + if (oldTestConfig != null) + { + Environment.SetEnvironmentVariable(TestHelpers.TestConfigEVName, oldTestConfig); + } + if (oldPrivateConfigRoot != null) + { + Environment.SetEnvironmentVariable(TestHelpers.PrivateConfigRootEVName, oldPrivateConfigRoot); + } + + try + { + if (Directory.Exists(privateConfigRoot)) + { + Directory.Delete(privateConfigRoot, true); + } + } + catch (Exception ex) + { + //although, something bad happened there is no need to fail the test since the next time it will overwrite + //the temporary files if any exists + Trace.TraceWarning(ex.ToString()); + } + } + } + + private static void CheckProperties(PropertyBag bag) { - Assert.IsTrue(TestHelpers.GetProperty("Help", null).Equals("Me")); + var properties = new Dictionary + { + {"Help", "Me"}, + {"publickey", "value"}, + {"myconfig.publickey", "value"}, + {"privatekey", "value"}, + {"myconfig.privatekey", "value"} + }; + + var bagElements = bag.ToDictionary(); + Assert.That( + CollectionUtil.DictionariesEqual(properties, bagElements) && CollectionUtil.DictionariesEqual(bagElements, properties), + "Expected test properties not equal"); } [Test] @@ -119,3 +226,23 @@ public void Validate() } } } + +namespace Org.IdentityConnectors.TestConnector +{ + public class FakeConnector : Connector + { + #region Connector Members + public void Init(Configuration configuration) + { + throw new NotImplementedException(); + } + #endregion + + #region IDisposable Members + public void Dispose() + { + throw new NotImplementedException(); + } + #endregion + } +} diff --git a/FrameworkTests/project.xml b/FrameworkTests/project.xml deleted file mode 100644 index 6f40613f..00000000 --- a/FrameworkTests/project.xml +++ /dev/null @@ -1,5 +0,0 @@ - - - - - \ No newline at end of file diff --git a/TestCommon/PropertyBag.cs b/TestCommon/PropertyBag.cs new file mode 100644 index 00000000..bcb0ec94 --- /dev/null +++ b/TestCommon/PropertyBag.cs @@ -0,0 +1,154 @@ +/* + * ==================== + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER. + * + * Copyright 2008-2009 Sun Microsystems, Inc. All rights reserved. + * + * The contents of this file are subject to the terms of the Common Development + * and Distribution License("CDDL") (the "License"). You may not use this file + * except in compliance with the License. + * + * You can obtain a copy of the License at + * http://IdentityConnectors.dev.java.net/legal/license.txt + * See the License for the specific language governing permissions and limitations + * under the License. + * + * When distributing the Covered Code, include this CDDL Header Notice in each file + * and include the License file at identityconnectors/legal/license.txt. + * If applicable, add the following below this CDDL Header, with the fields + * enclosed by brackets [] replaced by your own identifying information: + * "Portions Copyrighted [year] [name of copyright owner]" + * ==================== + */ +using System; +using System.Collections.Generic; +using System.Globalization; +using Org.IdentityConnectors.Common; + +namespace Org.IdentityConnectors.Test.Common +{ + /// + /// Encapsulates a read-only bag of properties, which can be accessed in a type-safe manner. + /// + /// + /// The simplest way to obtain a required (i.e., the property must be in the bag, otherwise an exception is thrown) + /// property value is . + /// If the property is not a required one, the method can be used, + /// which also takes a default value which is returned when the property is not present in the bag. + /// + public sealed class PropertyBag + { + #region Member variables + private readonly Dictionary _bag; + #endregion + + #region Constructors + /// + /// Initializes a new instance of the class with the properties. + /// + /// The properties contained in the bag. + internal PropertyBag(IDictionary bag) + { + _bag = new Dictionary(bag); + } + #endregion + + #region Methods + /// + /// Gets the value of a required property defined by in a type-safe manner. + /// + /// The type of the property to get. + /// The name of the property. + /// The value of the property in bag; the value might be null. + /// Thrown when no property found defined by + /// Thrown when the property exists, but its value is not of type . + /// See for details on the types that can be defined in . + public T GetProperty(string name) + { + if (!_bag.ContainsKey(name)) + { + throw new ArgumentException(string.Format(CultureInfo.InvariantCulture, @"Property named ""{0}"" not found in bag.", name)); + } + + return CastValue(name); + } + + /// + /// Casts the value of a property defined by to the type . + /// + /// The type, the property must be cast to. + /// The name of the property. + /// The value of the property in bag cast to type ; the value might be null. + /// Thrown when the value of the property is not of type . + /// The property must be in the bag! + /// + /// The .Net BCL does not provide wrappers for primitive classes, hence to make sure that a property containing a null value + /// cannot break your code use a nullable primitive type. + /// For example: + /// + /// PropertyBag bag = TestHelpers.GetProperties(typeof(yourConnector)); + /// int? mightBeNull = bag.GetProperty("property name"); + /// + /// + /// + /// + private T CastValue(string name) + { + object value = _bag[name]; + + //if the value of the property is null then just return null, there is no need for type checking + if ((value != null) && + !(value is T)) //otherwise check if value is an instance of T + { + throw new InvalidCastException(string.Format(CultureInfo.InvariantCulture, + @"Property named ""{0}"" is of type ""{1}"" but expected type was ""{2}""", + name, value.GetType(), typeof (T))); + } + + return (T)value; + } + + /// + /// Gets a property value, returning a default value when no property with the specified name exists in the bag. + /// + /// The type of the property to get. + /// The name of the property. + /// The default value returned when no property with the specified name exists in the bag. + /// The value of the property in bag cast to type or the default value ; + /// the value might be null. + /// Thrown when the property exists, but its value is not of type . + /// See for details on the types that can be defined in . + public T GetProperty(string name, T def) + { + if (!_bag.ContainsKey(name)) + { + return def; + } + return CastValue(name); + } + + /// + /// Gets a required property value known to be of type . + /// + /// The name of the property. + /// The value of the property in bag; the value might be null. + /// Thrown when no property found defined by + /// Thrown when the property exists, but its value is not of type . + /// The method expects that the value is an instance of . It does not attempt to + /// call on the value. + public string GetStringProperty(string name) + { + return GetProperty(name); + } + + /// + /// Returns a key-value pair collection that represents the current . + /// + /// A instance that represents the current . + internal IDictionary ToDictionary() + { + return CollectionUtil.NewDictionary(_bag); + } + #endregion + } +} diff --git a/TestCommon/Test.cs b/TestCommon/Test.cs index 08b66da4..ee506f53 100644 --- a/TestCommon/Test.cs +++ b/TestCommon/Test.cs @@ -25,6 +25,7 @@ using System.Collections.Generic; using System.Diagnostics; using System.IO; +using System.Linq; using System.Reflection; using System.Runtime.CompilerServices; using System.Xml; @@ -36,6 +37,8 @@ using Org.IdentityConnectors.Framework.Spi; using Org.IdentityConnectors.Framework.Spi.Operations; using Org.IdentityConnectors.Test.Common.Spi; +using System.Globalization; +using System.Xml.Schema; namespace Org.IdentityConnectors.Test.Common { @@ -217,122 +220,295 @@ private static TestHelpersSpi GetSpi() } } - private static IDictionary _properties = null; - private static readonly string PREFIX = Environment.GetEnvironmentVariable("USERPROFILE") + "/.connectors/"; - public static readonly string GLOBAL_PROPS = "connectors.xml"; + private static Dictionary _propertyBags = new Dictionary(); + private const string ConfigFileName = "config.xml"; + private static readonly object ConfigurationLock = new object(); + private const string LoadConfigErrorMessage = @"TestHelpers: Unable to load optional XML configuration file ""{0}"". Exception: {1}"; - [MethodImpl(MethodImplOptions.NoInlining)] - public static string GetProperty(string key, string def) - { - Assembly asm = Assembly.GetCallingAssembly(); - return CollectionUtil.GetValue(GetProperties(asm), key, def); - } + /// + /// The name of the environment variable that contains the name of a certain configuration from which the + /// test configuration properties to be loaded besides the general properties. + /// + internal const string TestConfigEVName = "TEST_CONFIG"; + + /// + /// The name of the environment variable that contains the path of the root directory to the private configurations. + /// + internal const string PrivateConfigRootEVName = "PRIVATE_CONFIG_ROOT"; + + /// + /// The name of the environment variable, the value of which is the location of the current user's profile directory. + /// + internal const string UserProfileEVName = "USERPROFILE"; - private static IDictionary GetProperties(Assembly asm) + /// + /// Gets the configuration properties for a specified type. + /// + /// + /// + /// Gets the configuration properties for the specified from the provided . + /// + /// The type, the fully qualified name (FQN) of which to be used to identify the configuration. + /// The assembly, that contains the public configuration properties. Recommended to be the test project. + /// Bag of properties for the specified and optionally set configuration. + /// The properties are loaded from public and private configuration files, the former as manifest resources, the + /// latter as file system entries by using the specified as root prefix. Optionally, a certain test setup + /// can be used by defining the "TEST_CONFIG" environment variable that can be used to override the general configuration properties. Both + /// public and private configurations can be overridden with a certain test setup. + /// + /// Public configuration properties are loaded as manifest resources from the specified according + /// to the following: + /// + /// + /// Load the general properties from a resource, the name of which is constructed as follows: + /// type.FullName + ".config.config.xml". To achieve this, you need to create a new folder + /// in your test project named as the FQN of the specified , then create a folder called + /// "config", at last add the configuration file called "config.xml" to this folder and set its "Build Action" + /// property to "Embedded Resource". + /// + /// + /// + /// Load the configuration specific properties from a resource, the name of which is constructed + /// as follows: + /// type.FullName + ".config."+ Environment.GetEnvironmentVariable("TEST_CONFIG") + ".config.xml". + /// To achieve this, you need to create a new folder underneath the previously created "config" folder, its name + /// is defined by the configuration name and add the "config.xml" to this particular folder with "Build Action" + /// property set to "Embedded Resource". + /// + /// + /// + /// + /// + /// The private configuration properties are loaded from the file system as follows: + /// + /// + /// Load the general properties from a file, the path of which is constructed as follows: + /// Environment.GetEnvironmentVariable("PRIVATE_CONFIG_ROOT") + "\config\" + type.FullName + "\config-private\config.xml" + /// + /// + /// + /// Load the configuration specific properties from a file, the path of which is constructed as follows: + /// Environment.GetEnvironmentVariable("PRIVATE_CONFIG_ROOT") + "\config\" + type.FullName + "\config-private\" + + /// Environment.GetEnvironmentVariable("TEST_CONFIG") + "\config.xml" + /// + /// + /// + /// NOTE that if the "PRIVATE_CONFIG_ROOT" environment variable is not defined, it will be replaced in the path with the default root + /// which points to a directory of the current user's profile container, the path of which is constructed as follows: + /// Environment.GetEnvironmentVariable("USERPROFILE") + "\.connectors\" + type.Assembly.GetName().Name + /// For example: c:\Users\Administrator\.connectors\CONNECTOR_CONTAINER_ASSEMBLY_NAME\ + /// + /// + /// Thrown when the root directory of the private configuration cannot be determined. + public static PropertyBag GetProperties(Type type, Assembly assembly) { - lock (LOCK) + lock (ConfigurationLock) { - if (_properties == null) + PropertyBag bag; + if (_propertyBags.ContainsKey(type.FullName)) { - _properties = LoadProperties(asm); + bag = _propertyBags[type.FullName]; } + else + { + bag = LoadProperties(type, assembly); + _propertyBags.Add(type.FullName, bag); + } + return bag; } - // create a new instance so its not mutable - return CollectionUtil.NewReadOnlyDictionary(_properties); } - private static IDictionary LoadProperties(Assembly asm) + /// + /// Gets the configuration properties for the specified from the calling assembly. + /// + /// The type, the fully qualified name (FQN) of which to be used to identify the configuration. + /// Bag of properties for the specified and optionally set configuration. + /// See for details of the property loading mechanism. + public static PropertyBag GetProperties(Type type) { - const string ERR = "TestHelpers: Unable to load optional XML properties file \"{0}\""; - string fn = null; - IDictionary props = null; - IDictionary ret = new Dictionary(); + return GetProperties(type, Assembly.GetCallingAssembly()); + } - //load global properties file - try + /// + /// Loads the properties from public and private configurations, optionally from a certain configuration defined by + /// "TEST_CONFIG" environment variable. + /// + /// The type, the fully qualified name (FQN) of which to be used to identify the configuration. + /// The assembly, that contains the configuration resources. + /// Bag of properties for the specified . + /// Thrown when the root directory of the private configuration cannot be determined. + private static PropertyBag LoadProperties(Type type, Assembly assembly) + { + string bagName = type.FullName; + string configFilePath = string.Empty; + IDictionary properties = null; + var ret = new Dictionary(); + + //load the general public properties file + configFilePath = string.Format(CultureInfo.InvariantCulture, "{0}.config.{1}", bagName, ConfigFileName); + properties = LoadConfigurationFromResource(assembly, configFilePath); + CollectionUtil.AddOrReplaceAll(ret, properties); + + //load the configuration specific public properties file + string configurationName = Environment.GetEnvironmentVariable(TestConfigEVName); + if (!StringUtil.IsBlank(configurationName)) { - fn = Path.Combine(PREFIX, GLOBAL_PROPS); - props = LoadPropertiesFile(fn); - CollectionUtil.AddOrReplaceAll(ret, props); + configFilePath = string.Format(CultureInfo.InvariantCulture, "{0}.config.{1}.{2}", bagName, + configurationName, ConfigFileName); + properties = LoadConfigurationFromResource(assembly, configFilePath); + CollectionUtil.AddOrReplaceAll(ret, properties); } - catch (Exception) + + //determine the root directory of the private properties files + string privateConfigRoot = string.Empty; + if (Environment.GetEnvironmentVariable(PrivateConfigRootEVName) != null) { - Trace.TraceInformation(ERR, fn); + privateConfigRoot = Environment.GetEnvironmentVariable(PrivateConfigRootEVName); } - - // load the project properties file - try + else { - fn = Path.Combine(Environment.CurrentDirectory, "project.xml"); - props = LoadPropertiesFile(fn); - CollectionUtil.AddAll(ret, props); + if (Environment.GetEnvironmentVariable(UserProfileEVName) != null) + { + privateConfigRoot = Path.Combine(Environment.GetEnvironmentVariable(UserProfileEVName), + Path.Combine(".connectors", type.Assembly.GetName().Name)); + } + else + throw new InvalidOperationException( + @"Neither the ""PRIVATE_CONFIG_ROOT"" nor the ""USERPROFILE"" environment variable is defined."); } - catch (Exception) + + privateConfigRoot = Path.Combine(privateConfigRoot, "config"); + + //load the general private properties file + configFilePath = Path.Combine(Path.Combine(Path.Combine(privateConfigRoot, bagName), "config-private"), ConfigFileName); + properties = LoadConfigurationFromFile(configFilePath); + CollectionUtil.AddOrReplaceAll(ret, properties); + + // load the configuration specific private properties file + if (!StringUtil.IsBlank(configurationName)) { - Trace.TraceInformation(ERR, fn); + configFilePath = Path.Combine(Path.Combine(Path.Combine(Path.Combine(privateConfigRoot, bagName), + "config-private"), configurationName), ConfigFileName); + properties = LoadConfigurationFromFile(configFilePath); + CollectionUtil.AddOrReplaceAll(ret, properties); } + return new PropertyBag(ret); + } - // private settings are in the "assembly name" folder, as defined in the assembly - string prjName = asm.GetName().Name; - if (!StringUtil.IsBlank(prjName)) + /// + /// Loads the configuration properties from the specified . + /// + /// The config file path. + /// A property name-value pair collection representing the configuration. + private static IDictionary LoadConfigurationFromFile(string filePath) + { + IDictionary properties = null; + try { - //load private project properties file - try + if (File.Exists(filePath)) { - fn = Path.Combine(PREFIX, prjName + "/project.xml"); - props = LoadPropertiesFile(fn); - CollectionUtil.AddOrReplaceAll(ret, props); + using (var stream = new FileStream(filePath, FileMode.Open, FileAccess.Read, FileShare.Read)) + { + properties = ReadConfiguration(stream); + } } - catch (IOException) + else { - Trace.TraceInformation(ERR, fn); + Trace.TraceWarning(@"The configuration file on path ""{0}"" is not found.", filePath); } + } + catch (Exception e) + { + Trace.TraceInformation(LoadConfigErrorMessage, filePath, e); + } + return properties; + } - string cfg = Environment.GetEnvironmentVariable("configuration"); - if (!StringUtil.IsBlank(cfg)) + /// + /// Loads the configuration properties from a resource defined by . + /// + /// The assembly, that contains the resource. + /// The name of the resource. + /// A property name-value pair collection representing the configuration. + private static IDictionary LoadConfigurationFromResource(Assembly assembly, string configResName) + { + IDictionary properties = null; + try + { + //the default namespace with which the resource name starts is not known, therefore + //it must be looked up in the manifest resource list + var resourceName = (from name in assembly.GetManifestResourceNames() + where name.EndsWith(configResName, StringComparison.InvariantCultureIgnoreCase) + select name).FirstOrDefault(); + if (!StringUtil.IsBlank(resourceName)) { - try + using (var stream = assembly.GetManifestResourceStream(resourceName)) { - // load a config-specific properties file - fn = Path.Combine(PREFIX, prjName + "/" + cfg + "/project.xml"); - props = LoadPropertiesFile(fn); - CollectionUtil.AddOrReplaceAll(ret, props); - } - catch (IOException) - { - Trace.TraceInformation(ERR, fn); + if (stream == null) + { + throw new InvalidOperationException(string.Format(CultureInfo.InvariantCulture, + @"Although, the configuration file called ""{0}"" exists, it cannot be accessed.", + configResName)); + } + properties = ReadConfiguration(stream); } } + else + { + Trace.TraceWarning(@"The configuration resource called ""{0}"" is not found.", configResName); + } } - else - { - TraceUtil.TraceException("Could not infer assembly name.", new Exception()); - } - - // load the environment variables - foreach (DictionaryEntry entry in Environment.GetEnvironmentVariables()) + catch (Exception e) { - ret[entry.Key.ToString()] = entry.Value.ToString(); + Trace.TraceInformation(LoadConfigErrorMessage, configResName, e); } - return ret; + return properties; } /// - /// Format for the xml file is simple <property name='' value=''/> + /// Reads the configuration properties from the provided stream. /// - /// - /// - public static IDictionary LoadPropertiesFile(string filename) + /// The stream containing the configuration properties. + /// A property name-value pair collection representing the configuration. + /// The configuration properties are stored in XML format. The stream, that is opened for reading or it can be read, + /// has to contain the configuration properties in XML that adheres to the config.xsd schema embedded to + /// the assembly of this project. + /// + /// For example: + /// + /// + /// + /// + /// ]]> + /// + /// + /// Thrown when the XSD used for validating the configuration is not found in the manifest. + /// Thrown when the contains XML that does not adhere to the schema. + internal static IDictionary ReadConfiguration(Stream configStream) { - IDictionary ret = new Dictionary(); - //Environment. - XmlTextReader reader = null; - try + var properties = new Dictionary(); + //validate the XML configuration against the XSD + var schemaSet = new XmlSchemaSet(); + var schemaStream = Assembly.GetExecutingAssembly().GetManifestResourceStream("Org.IdentityConnectors.Test.Common.config.xsd"); + if (schemaStream == null) { - // Load the reader with the data file and ignore all white space nodes. - reader = new XmlTextReader(filename); - reader.WhitespaceHandling = WhitespaceHandling.None; - // Parse the file and display each of the nodes. + throw new InvalidOperationException(@"The schema used for validating of the configuration file is not found."); + } + var schemaReader = XmlReader.Create(schemaStream); + schemaSet.Add(null, schemaReader); + + //load the reader with the data stream and ignore all white space nodes + var readerSettings = new XmlReaderSettings + { + IgnoreWhitespace = true, + ValidationType = ValidationType.Schema, + Schemas = schemaSet + }; + + using (var reader = XmlReader.Create(configStream, readerSettings)) + { + //read all the nodes and extract the ones that contain properties while (reader.Read()) { if (reader.NodeType == XmlNodeType.Element && @@ -342,17 +518,12 @@ public static IDictionary LoadPropertiesFile(string filename) string xmlValue = reader.GetAttribute("value"); if (!StringUtil.IsBlank(name) && xmlValue != null) { - ret[name] = xmlValue; + properties[name] = xmlValue; } } } } - finally - { - if (reader != null) - reader.Close(); - } - return ret; + return properties; } } } diff --git a/TestCommon/TestCommon.csproj b/TestCommon/TestCommon.csproj index 593b0dc4..1abcaa6e 100644 --- a/TestCommon/TestCommon.csproj +++ b/TestCommon/TestCommon.csproj @@ -1,4 +1,4 @@ - + Debug @@ -12,6 +12,7 @@ TestCommon v3.5 512 + FrameworkTests true @@ -47,6 +48,7 @@ + @@ -60,5 +62,8 @@ {8B24461B-456A-4032-89A1-CD418F7B5B62} Framework - + + + + \ No newline at end of file diff --git a/TestCommon/config.xsd b/TestCommon/config.xsd new file mode 100644 index 00000000..968ccb70 --- /dev/null +++ b/TestCommon/config.xsd @@ -0,0 +1,38 @@ + + + + + + + + + + + + + + + + + From aaf803996befbf86751359a3c31c2da4606c4f20 Mon Sep 17 00:00:00 2001 From: abadea Date: Mon, 2 Nov 2009 16:08:02 +0000 Subject: [PATCH 259/342] Increasing framework version to 1.2 --- Common/version.template | 2 +- Framework/version.template | 2 +- FrameworkInternal/version.template | 2 +- FrameworkTests/version.template | 2 +- Service/version.template | 2 +- ServiceInstall/Setup.wxs | 2 +- TestCommon/version.template | 2 +- 7 files changed, 7 insertions(+), 7 deletions(-) diff --git a/Common/version.template b/Common/version.template index bd2666ab..db700576 100644 --- a/Common/version.template +++ b/Common/version.template @@ -1 +1 @@ -1.0.0.0 \ No newline at end of file +1.2.0.0 diff --git a/Framework/version.template b/Framework/version.template index bd2666ab..db700576 100644 --- a/Framework/version.template +++ b/Framework/version.template @@ -1 +1 @@ -1.0.0.0 \ No newline at end of file +1.2.0.0 diff --git a/FrameworkInternal/version.template b/FrameworkInternal/version.template index bd2666ab..db700576 100644 --- a/FrameworkInternal/version.template +++ b/FrameworkInternal/version.template @@ -1 +1 @@ -1.0.0.0 \ No newline at end of file +1.2.0.0 diff --git a/FrameworkTests/version.template b/FrameworkTests/version.template index bd2666ab..db700576 100644 --- a/FrameworkTests/version.template +++ b/FrameworkTests/version.template @@ -1 +1 @@ -1.0.0.0 \ No newline at end of file +1.2.0.0 diff --git a/Service/version.template b/Service/version.template index bd2666ab..db700576 100644 --- a/Service/version.template +++ b/Service/version.template @@ -1 +1 @@ -1.0.0.0 \ No newline at end of file +1.2.0.0 diff --git a/ServiceInstall/Setup.wxs b/ServiceInstall/Setup.wxs index 54724a64..f9e70b89 100644 --- a/ServiceInstall/Setup.wxs +++ b/ServiceInstall/Setup.wxs @@ -3,7 +3,7 @@ Date: Fri, 13 Nov 2009 10:56:39 +0000 Subject: [PATCH 260/342] Issue 593: Move framework to a single directory - .NET part --- .../ActiveDirectoryConnector.csproj | 38 +++--- .../ActiveDirectoryConnectorTests.csproj | 44 +++---- .../BooScriptExecutorFactory.csproj | 2 +- Common/Common.csproj | 3 +- Common/version.template | 2 +- ConnectorFramework.sln | 79 ++++++++++++ Console/Console.csproj | 91 ------------- Console/MainForm.Designer.cs | 47 ------- Console/MainForm.cs | 47 ------- Console/Program.cs | 45 ------- Console/version.template | 1 - DotNetConnectors.sln | 73 ----------- ExchangeConnector/ExchangeConnector.csproj | 21 +-- Framework.targets | 122 ++++++++++++++++++ Framework/Framework.csproj | 3 +- Framework/version.template | 2 +- FrameworkInternal/FrameworkInternal.csproj | 3 +- FrameworkInternal/version.template | 2 +- FrameworkTests/FrameworkTests.csproj | 23 ++-- FrameworkTests/version.template | 2 +- Service/Service.csproj | 2 +- Service/version.template | 2 +- ServiceInstall/ExtBuild.proj | 7 - ServiceInstall/ServiceInstall.wixproj | 31 ++++- .../ShellScriptExecutorFactory.csproj | 3 +- .../TestBundleV1}/AssemblyInfo.cs | 0 .../TestBundleV1}/Messages.es-ES.resx | 0 .../TestBundleV1}/Messages.es.resx | 0 .../TestBundleV1}/Messages.resx | 0 .../TestBundleV1}/TestBundleV1.csproj | 4 +- .../TestBundleV1}/TestConnector.cs | 0 .../TestBundleV2}/AssemblyInfo.cs | 0 .../TestBundleV2}/TestBundleV2.csproj | 4 +- .../TestBundleV2}/TestConnector.cs | 0 TestCommon/TestCommon.csproj | 7 +- TestCommon/version.template | 2 +- 36 files changed, 317 insertions(+), 395 deletions(-) create mode 100755 ConnectorFramework.sln delete mode 100644 Console/Console.csproj delete mode 100644 Console/MainForm.Designer.cs delete mode 100644 Console/MainForm.cs delete mode 100644 Console/Program.cs delete mode 100644 Console/version.template create mode 100755 Framework.targets rename {TestBundleV1 => TestBundles/TestBundleV1}/AssemblyInfo.cs (100%) rename {TestBundleV1 => TestBundles/TestBundleV1}/Messages.es-ES.resx (100%) rename {TestBundleV1 => TestBundles/TestBundleV1}/Messages.es.resx (100%) rename {TestBundleV1 => TestBundles/TestBundleV1}/Messages.resx (100%) rename {TestBundleV1 => TestBundles/TestBundleV1}/TestBundleV1.csproj (94%) rename {TestBundleV1 => TestBundles/TestBundleV1}/TestConnector.cs (100%) rename {TestBundleV2 => TestBundles/TestBundleV2}/AssemblyInfo.cs (100%) rename {TestBundleV2 => TestBundles/TestBundleV2}/TestBundleV2.csproj (94%) rename {TestBundleV2 => TestBundles/TestBundleV2}/TestConnector.cs (100%) diff --git a/ActiveDirectoryConnector/ActiveDirectoryConnector.csproj b/ActiveDirectoryConnector/ActiveDirectoryConnector.csproj index 517bb05d..71c1ce4f 100644 --- a/ActiveDirectoryConnector/ActiveDirectoryConnector.csproj +++ b/ActiveDirectoryConnector/ActiveDirectoryConnector.csproj @@ -1,4 +1,4 @@ - - - - {D09E6A1F-55F6-4661-8E5A-C485E4B08BB3} - Debug - AnyCPU - WinExe - Console - Console - Console - v3.5 - - - prompt - AnyCPU - ./bin/Debug/ - True - Full - False - True - DEBUG;TRACE - WinExe - Console - False - 4 - - - pdbonly - AnyCPU - ./bin/Release/ - False - True - False - TRACE - WinExe - Console - False - 4 - - - - - ..\..\..\..\..\..\..\Program Files\SharpDevelop\3.0\AddIns\AddIns\BackendBindings\BooBinding\Boo.Lang.dll - - - - 3.5 - - - - - - - - - - Form - - - MainForm.cs - - - - - - {8B24461B-456A-4032-89A1-CD418F7B5B62} - Framework - - - diff --git a/Console/MainForm.Designer.cs b/Console/MainForm.Designer.cs deleted file mode 100644 index 1782f06d..00000000 --- a/Console/MainForm.Designer.cs +++ /dev/null @@ -1,47 +0,0 @@ -/* - * Created by SharpDevelop. - * User: Administrator - * Date: 3/18/2008 - * Time: 4:30 PM - * - * To change this template use Tools | Options | Coding | Edit Standard Headers. - */ -namespace Console -{ - partial class MainForm - { - /// - /// Designer variable used to keep track of non-visual components. - /// - private System.ComponentModel.IContainer components = null; - - /// - /// Disposes resources used by the form. - /// - /// true if managed resources should be disposed; otherwise, false. - protected override void Dispose(bool disposing) - { - if (disposing) { - if (components != null) { - components.Dispose(); - } - } - base.Dispose(disposing); - } - - /// - /// This method is required for Windows Forms designer support. - /// Do not change the method contents inside the source code editor. The Forms designer might - /// not be able to load this method if it was changed manually. - /// - private void InitializeComponent() - { - // - // MainForm - // - this.AutoScaleMode = System.Windows.Forms.AutoScaleMode.Font; - this.Text = "Console"; - this.Name = "MainForm"; - } - } -} diff --git a/Console/MainForm.cs b/Console/MainForm.cs deleted file mode 100644 index ac181ad5..00000000 --- a/Console/MainForm.cs +++ /dev/null @@ -1,47 +0,0 @@ -/* - * ==================== - * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER. - * - * Copyright 2008-2009 Sun Microsystems, Inc. All rights reserved. - * - * The contents of this file are subject to the terms of the Common Development - * and Distribution License("CDDL") (the "License"). You may not use this file - * except in compliance with the License. - * - * You can obtain a copy of the License at - * http://IdentityConnectors.dev.java.net/legal/license.txt - * See the License for the specific language governing permissions and limitations - * under the License. - * - * When distributing the Covered Code, include this CDDL Header Notice in each file - * and include the License file at identityconnectors/legal/license.txt. - * If applicable, add the following below this CDDL Header, with the fields - * enclosed by brackets [] replaced by your own identifying information: - * "Portions Copyrighted [year] [name of copyright owner]" - * ==================== - */ -using System; -using System.Collections.Generic; -using System.Drawing; -using System.Windows.Forms; - -namespace Console -{ - /// - /// Description of MainForm. - /// - public partial class MainForm : Form - { - public MainForm() - { - // - // The InitializeComponent() call is required for Windows Forms designer support. - // - InitializeComponent(); - - // - // TODO: Add constructor code after the InitializeComponent() call. - // - } - } -} diff --git a/Console/Program.cs b/Console/Program.cs deleted file mode 100644 index 6bf701ac..00000000 --- a/Console/Program.cs +++ /dev/null @@ -1,45 +0,0 @@ -/* - * ==================== - * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER. - * - * Copyright 2008-2009 Sun Microsystems, Inc. All rights reserved. - * - * The contents of this file are subject to the terms of the Common Development - * and Distribution License("CDDL") (the "License"). You may not use this file - * except in compliance with the License. - * - * You can obtain a copy of the License at - * http://IdentityConnectors.dev.java.net/legal/license.txt - * See the License for the specific language governing permissions and limitations - * under the License. - * - * When distributing the Covered Code, include this CDDL Header Notice in each file - * and include the License file at identityconnectors/legal/license.txt. - * If applicable, add the following below this CDDL Header, with the fields - * enclosed by brackets [] replaced by your own identifying information: - * "Portions Copyrighted [year] [name of copyright owner]" - * ==================== - */ -using System; -using System.Windows.Forms; - -namespace Console -{ - /// - /// Class with program entry point. - /// - internal sealed class Program - { - /// - /// Program entry point. - /// - [STAThread] - private static void Main(string[] args) - { - Application.EnableVisualStyles(); - Application.SetCompatibleTextRenderingDefault(false); - Application.Run(new MainForm()); - } - - } -} diff --git a/Console/version.template b/Console/version.template deleted file mode 100644 index bd2666ab..00000000 --- a/Console/version.template +++ /dev/null @@ -1 +0,0 @@ -1.0.0.0 \ No newline at end of file diff --git a/DotNetConnectors.sln b/DotNetConnectors.sln index b3627839..ceddedc8 100644 --- a/DotNetConnectors.sln +++ b/DotNetConnectors.sln @@ -1,84 +1,15 @@ - Microsoft Visual Studio Solution File, Format Version 10.00 # Visual Studio 2008 -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Service", "Service\Service.csproj", "{A9D6374A-D51F-4FA3-8C02-5B1D23FAA82E}" -EndProject -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Framework", "Framework\Framework.csproj", "{8B24461B-456A-4032-89A1-CD418F7B5B62}" -EndProject -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Common", "Common\Common.csproj", "{F140E8DA-52B4-4159-992A-9DA10EA8EEFB}" -EndProject -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "FrameworkTests", "FrameworkTests\FrameworkTests.csproj", "{32804F5A-812C-4FA6-835C-BDAE5B24D355}" -EndProject -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "TestBundleV1", "TestBundleV1\TestBundleV1.csproj", "{0BC2A013-56FE-46DD-90FC-2D2D57748FB6}" -EndProject -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "TestBundleV2", "TestBundleV2\TestBundleV2.csproj", "{3E737796-3A83-4924-9FF1-DC542F21F59E}" -EndProject -Project("{CFEE4113-1246-4D54-95CB-156813CB8593}") = "ServiceInstall", "ServiceInstall\ServiceInstall.wixproj", "{1CBA8F74-050C-432B-8437-08BD13BDC684}" -EndProject -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Console", "Console\Console.csproj", "{D09E6A1F-55F6-4661-8E5A-C485E4B08BB3}" -EndProject -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "FrameworkInternal", "FrameworkInternal\FrameworkInternal.csproj", "{5B011775-B121-4EEE-A410-BA2D2F5BFB8B}" -EndProject -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "BooScriptExecutorFactory", "BooScriptExecutorFactory\BooScriptExecutorFactory.csproj", "{0747C440-70E4-4E63-9F9D-03B3A010C991}" -EndProject -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "ShellScriptExecutorFactory", "ShellScriptExecutorFactory\ShellScriptExecutorFactory.csproj", "{4700690A-2D29-40A0-86AC-E5A9F71A479A}" -EndProject Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "ActiveDirectoryConnector", "ActiveDirectoryConnector\ActiveDirectoryConnector.csproj", "{BDF495CA-0FCD-4E51-A871-D467CDE3B43E}" EndProject Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "ActiveDirectoryConnectorTests", "ActiveDirectoryConnectorTests\ActiveDirectoryConnectorTests.csproj", "{DB8F7DA9-11A2-4BEB-8C14-25D8229EBE7E}" EndProject -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "TestCommon", "TestCommon\TestCommon.csproj", "{E6A207D2-E083-41BF-B522-D9D3EC09323E}" -EndProject Global GlobalSection(SolutionConfigurationPlatforms) = preSolution Debug|Any CPU = Debug|Any CPU Release|Any CPU = Release|Any CPU EndGlobalSection GlobalSection(ProjectConfigurationPlatforms) = postSolution - {A9D6374A-D51F-4FA3-8C02-5B1D23FAA82E}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {A9D6374A-D51F-4FA3-8C02-5B1D23FAA82E}.Debug|Any CPU.Build.0 = Debug|Any CPU - {A9D6374A-D51F-4FA3-8C02-5B1D23FAA82E}.Release|Any CPU.ActiveCfg = Release|Any CPU - {A9D6374A-D51F-4FA3-8C02-5B1D23FAA82E}.Release|Any CPU.Build.0 = Release|Any CPU - {8B24461B-456A-4032-89A1-CD418F7B5B62}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {8B24461B-456A-4032-89A1-CD418F7B5B62}.Debug|Any CPU.Build.0 = Debug|Any CPU - {8B24461B-456A-4032-89A1-CD418F7B5B62}.Release|Any CPU.ActiveCfg = Release|Any CPU - {8B24461B-456A-4032-89A1-CD418F7B5B62}.Release|Any CPU.Build.0 = Release|Any CPU - {F140E8DA-52B4-4159-992A-9DA10EA8EEFB}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {F140E8DA-52B4-4159-992A-9DA10EA8EEFB}.Debug|Any CPU.Build.0 = Debug|Any CPU - {F140E8DA-52B4-4159-992A-9DA10EA8EEFB}.Release|Any CPU.ActiveCfg = Release|Any CPU - {F140E8DA-52B4-4159-992A-9DA10EA8EEFB}.Release|Any CPU.Build.0 = Release|Any CPU - {32804F5A-812C-4FA6-835C-BDAE5B24D355}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {32804F5A-812C-4FA6-835C-BDAE5B24D355}.Debug|Any CPU.Build.0 = Debug|Any CPU - {32804F5A-812C-4FA6-835C-BDAE5B24D355}.Release|Any CPU.ActiveCfg = Release|Any CPU - {32804F5A-812C-4FA6-835C-BDAE5B24D355}.Release|Any CPU.Build.0 = Release|Any CPU - {0BC2A013-56FE-46DD-90FC-2D2D57748FB6}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {0BC2A013-56FE-46DD-90FC-2D2D57748FB6}.Debug|Any CPU.Build.0 = Debug|Any CPU - {0BC2A013-56FE-46DD-90FC-2D2D57748FB6}.Release|Any CPU.ActiveCfg = Release|Any CPU - {0BC2A013-56FE-46DD-90FC-2D2D57748FB6}.Release|Any CPU.Build.0 = Release|Any CPU - {3E737796-3A83-4924-9FF1-DC542F21F59E}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {3E737796-3A83-4924-9FF1-DC542F21F59E}.Debug|Any CPU.Build.0 = Debug|Any CPU - {3E737796-3A83-4924-9FF1-DC542F21F59E}.Release|Any CPU.ActiveCfg = Release|Any CPU - {3E737796-3A83-4924-9FF1-DC542F21F59E}.Release|Any CPU.Build.0 = Release|Any CPU - {1CBA8F74-050C-432B-8437-08BD13BDC684}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {1CBA8F74-050C-432B-8437-08BD13BDC684}.Debug|Any CPU.Build.0 = Debug|Any CPU - {1CBA8F74-050C-432B-8437-08BD13BDC684}.Release|Any CPU.ActiveCfg = Release|Any CPU - {1CBA8F74-050C-432B-8437-08BD13BDC684}.Release|Any CPU.Build.0 = Release|Any CPU - {D09E6A1F-55F6-4661-8E5A-C485E4B08BB3}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {D09E6A1F-55F6-4661-8E5A-C485E4B08BB3}.Debug|Any CPU.Build.0 = Debug|Any CPU - {D09E6A1F-55F6-4661-8E5A-C485E4B08BB3}.Release|Any CPU.ActiveCfg = Release|Any CPU - {D09E6A1F-55F6-4661-8E5A-C485E4B08BB3}.Release|Any CPU.Build.0 = Release|Any CPU - {5B011775-B121-4EEE-A410-BA2D2F5BFB8B}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {5B011775-B121-4EEE-A410-BA2D2F5BFB8B}.Debug|Any CPU.Build.0 = Debug|Any CPU - {5B011775-B121-4EEE-A410-BA2D2F5BFB8B}.Release|Any CPU.ActiveCfg = Release|Any CPU - {5B011775-B121-4EEE-A410-BA2D2F5BFB8B}.Release|Any CPU.Build.0 = Release|Any CPU - {0747C440-70E4-4E63-9F9D-03B3A010C991}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {0747C440-70E4-4E63-9F9D-03B3A010C991}.Debug|Any CPU.Build.0 = Debug|Any CPU - {0747C440-70E4-4E63-9F9D-03B3A010C991}.Release|Any CPU.ActiveCfg = Release|Any CPU - {0747C440-70E4-4E63-9F9D-03B3A010C991}.Release|Any CPU.Build.0 = Release|Any CPU - {4700690A-2D29-40A0-86AC-E5A9F71A479A}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {4700690A-2D29-40A0-86AC-E5A9F71A479A}.Debug|Any CPU.Build.0 = Debug|Any CPU - {4700690A-2D29-40A0-86AC-E5A9F71A479A}.Release|Any CPU.ActiveCfg = Release|Any CPU - {4700690A-2D29-40A0-86AC-E5A9F71A479A}.Release|Any CPU.Build.0 = Release|Any CPU {BDF495CA-0FCD-4E51-A871-D467CDE3B43E}.Debug|Any CPU.ActiveCfg = Debug|Any CPU {BDF495CA-0FCD-4E51-A871-D467CDE3B43E}.Debug|Any CPU.Build.0 = Debug|Any CPU {BDF495CA-0FCD-4E51-A871-D467CDE3B43E}.Release|Any CPU.ActiveCfg = Release|Any CPU @@ -87,10 +18,6 @@ Global {DB8F7DA9-11A2-4BEB-8C14-25D8229EBE7E}.Debug|Any CPU.Build.0 = Debug|Any CPU {DB8F7DA9-11A2-4BEB-8C14-25D8229EBE7E}.Release|Any CPU.ActiveCfg = Release|Any CPU {DB8F7DA9-11A2-4BEB-8C14-25D8229EBE7E}.Release|Any CPU.Build.0 = Release|Any CPU - {E6A207D2-E083-41BF-B522-D9D3EC09323E}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {E6A207D2-E083-41BF-B522-D9D3EC09323E}.Debug|Any CPU.Build.0 = Debug|Any CPU - {E6A207D2-E083-41BF-B522-D9D3EC09323E}.Release|Any CPU.ActiveCfg = Release|Any CPU - {E6A207D2-E083-41BF-B522-D9D3EC09323E}.Release|Any CPU.Build.0 = Release|Any CPU EndGlobalSection GlobalSection(SolutionProperties) = preSolution HideSolutionNode = FALSE diff --git a/ExchangeConnector/ExchangeConnector.csproj b/ExchangeConnector/ExchangeConnector.csproj index 4af39ff1..7fa4ab2c 100644 --- a/ExchangeConnector/ExchangeConnector.csproj +++ b/ExchangeConnector/ExchangeConnector.csproj @@ -35,6 +35,7 @@ v3.5 512 true + ..\..\framework\dotnet false ExchangeConnectorTests @@ -57,6 +58,16 @@ 4 + + False + $(ConnectorFrameworkDir)\Dist\Common.dll + False + + + False + $(ConnectorFrameworkDir)\Dist\Framework.dll + False + False @@ -80,16 +91,6 @@ ActiveDirectoryConnector True - - {F140E8DA-52B4-4159-992A-9DA10EA8EEFB} - Common - False - - - {8B24461B-456A-4032-89A1-CD418F7B5B62} - Framework - False - diff --git a/Framework.targets b/Framework.targets new file mode 100755 index 00000000..789be1bf --- /dev/null +++ b/Framework.targets @@ -0,0 +1,122 @@ + + + + $(MSBuildProjectDirectory)\version.template + $(MSBuildProjectDirectory)\version.txt + Sun Microsystems, Inc. + Copyright 2008-2009 Sun Microsystems, Inc. All rights reserved. + $(MSBuildProjectDirectory)\..\Dist + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + FrameworkBeforeBuild; + $(BuildDependsOn); + FrameworkAfterBuild + + + $(CleanDependsOn); + FrameworkClean + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/Framework/Framework.csproj b/Framework/Framework.csproj index 3a2e05f4..2a73b670 100644 --- a/Framework/Framework.csproj +++ b/Framework/Framework.csproj @@ -34,6 +34,7 @@ False 4 false + true prompt @@ -63,7 +64,7 @@ AnyCPU 4096 - + diff --git a/Framework/version.template b/Framework/version.template index db700576..4e0321ef 100644 --- a/Framework/version.template +++ b/Framework/version.template @@ -1 +1 @@ -1.2.0.0 +1.2.0.0 \ No newline at end of file diff --git a/FrameworkInternal/FrameworkInternal.csproj b/FrameworkInternal/FrameworkInternal.csproj index 7ab7282d..d47ebb75 100644 --- a/FrameworkInternal/FrameworkInternal.csproj +++ b/FrameworkInternal/FrameworkInternal.csproj @@ -34,6 +34,7 @@ False 4 false + true prompt @@ -62,7 +63,7 @@ AnyCPU 4096 - + diff --git a/FrameworkInternal/version.template b/FrameworkInternal/version.template index db700576..4e0321ef 100644 --- a/FrameworkInternal/version.template +++ b/FrameworkInternal/version.template @@ -1 +1 @@ -1.2.0.0 +1.2.0.0 \ No newline at end of file diff --git a/FrameworkTests/FrameworkTests.csproj b/FrameworkTests/FrameworkTests.csproj index ff902a8c..886fa52f 100644 --- a/FrameworkTests/FrameworkTests.csproj +++ b/FrameworkTests/FrameworkTests.csproj @@ -1,4 +1,4 @@ - - - + + - + - - + + @@ -154,14 +155,6 @@ - - - - - - - - diff --git a/FrameworkTests/version.template b/FrameworkTests/version.template index db700576..4e0321ef 100644 --- a/FrameworkTests/version.template +++ b/FrameworkTests/version.template @@ -1 +1 @@ -1.2.0.0 +1.2.0.0 \ No newline at end of file diff --git a/Service/Service.csproj b/Service/Service.csproj index 6e1f939c..b6a67c92 100644 --- a/Service/Service.csproj +++ b/Service/Service.csproj @@ -67,7 +67,7 @@ AnyCPU 4096 - + diff --git a/Service/version.template b/Service/version.template index db700576..4e0321ef 100644 --- a/Service/version.template +++ b/Service/version.template @@ -1 +1 @@ -1.2.0.0 +1.2.0.0 \ No newline at end of file diff --git a/ServiceInstall/ExtBuild.proj b/ServiceInstall/ExtBuild.proj index d8d676bb..dcd5ce0f 100644 --- a/ServiceInstall/ExtBuild.proj +++ b/ServiceInstall/ExtBuild.proj @@ -25,12 +25,6 @@ Debug - - - - - - @@ -58,7 +52,6 @@ - diff --git a/ServiceInstall/ServiceInstall.wixproj b/ServiceInstall/ServiceInstall.wixproj index 45e20954..6cac177c 100644 --- a/ServiceInstall/ServiceInstall.wixproj +++ b/ServiceInstall/ServiceInstall.wixproj @@ -59,7 +59,7 @@ - $(WixToolPath)\WixUIExtension.dll + $(WixToolPath)\WixUIExtension.dll @@ -69,4 +69,31 @@ - + + + Common + {f140e8da-52b4-4159-992a-9da10ea8eefb} + True + + + FrameworkInternal + {5b011775-b121-4eee-a410-ba2d2f5bfb8b} + True + + + Framework + {8b24461b-456a-4032-89a1-cd418f7b5b62} + True + + + Service + {a9d6374a-d51f-4fa3-8c02-5b1d23faa82e} + True + + + TestCommon + {e6a207d2-e083-41bf-b522-d9d3ec09323e} + True + + + \ No newline at end of file diff --git a/ShellScriptExecutorFactory/ShellScriptExecutorFactory.csproj b/ShellScriptExecutorFactory/ShellScriptExecutorFactory.csproj index b914956d..ef613f29 100644 --- a/ShellScriptExecutorFactory/ShellScriptExecutorFactory.csproj +++ b/ShellScriptExecutorFactory/ShellScriptExecutorFactory.csproj @@ -30,6 +30,7 @@ Shell.ScriptExecutorFactory ShellScriptExecutorFactory v3.5 + true prompt @@ -54,7 +55,7 @@ False TRACE - + diff --git a/TestBundleV1/AssemblyInfo.cs b/TestBundles/TestBundleV1/AssemblyInfo.cs similarity index 100% rename from TestBundleV1/AssemblyInfo.cs rename to TestBundles/TestBundleV1/AssemblyInfo.cs diff --git a/TestBundleV1/Messages.es-ES.resx b/TestBundles/TestBundleV1/Messages.es-ES.resx similarity index 100% rename from TestBundleV1/Messages.es-ES.resx rename to TestBundles/TestBundleV1/Messages.es-ES.resx diff --git a/TestBundleV1/Messages.es.resx b/TestBundles/TestBundleV1/Messages.es.resx similarity index 100% rename from TestBundleV1/Messages.es.resx rename to TestBundles/TestBundleV1/Messages.es.resx diff --git a/TestBundleV1/Messages.resx b/TestBundles/TestBundleV1/Messages.resx similarity index 100% rename from TestBundleV1/Messages.resx rename to TestBundles/TestBundleV1/Messages.resx diff --git a/TestBundleV1/TestBundleV1.csproj b/TestBundles/TestBundleV1/TestBundleV1.csproj similarity index 94% rename from TestBundleV1/TestBundleV1.csproj rename to TestBundles/TestBundleV1/TestBundleV1.csproj index 271ff4ed..972d3da3 100644 --- a/TestBundleV1/TestBundleV1.csproj +++ b/TestBundles/TestBundleV1/TestBundleV1.csproj @@ -75,11 +75,11 @@ - + {F140E8DA-52B4-4159-992A-9DA10EA8EEFB} Common - + {8B24461B-456A-4032-89A1-CD418F7B5B62} Framework diff --git a/TestBundleV1/TestConnector.cs b/TestBundles/TestBundleV1/TestConnector.cs similarity index 100% rename from TestBundleV1/TestConnector.cs rename to TestBundles/TestBundleV1/TestConnector.cs diff --git a/TestBundleV2/AssemblyInfo.cs b/TestBundles/TestBundleV2/AssemblyInfo.cs similarity index 100% rename from TestBundleV2/AssemblyInfo.cs rename to TestBundles/TestBundleV2/AssemblyInfo.cs diff --git a/TestBundleV2/TestBundleV2.csproj b/TestBundles/TestBundleV2/TestBundleV2.csproj similarity index 94% rename from TestBundleV2/TestBundleV2.csproj rename to TestBundles/TestBundleV2/TestBundleV2.csproj index b2b913ef..ac224405 100644 --- a/TestBundleV2/TestBundleV2.csproj +++ b/TestBundles/TestBundleV2/TestBundleV2.csproj @@ -66,11 +66,11 @@ - + {F140E8DA-52B4-4159-992A-9DA10EA8EEFB} Common - + {8B24461B-456A-4032-89A1-CD418F7B5B62} Framework diff --git a/TestBundleV2/TestConnector.cs b/TestBundles/TestBundleV2/TestConnector.cs similarity index 100% rename from TestBundleV2/TestConnector.cs rename to TestBundles/TestBundleV2/TestConnector.cs diff --git a/TestCommon/TestCommon.csproj b/TestCommon/TestCommon.csproj index 1abcaa6e..453a47c3 100644 --- a/TestCommon/TestCommon.csproj +++ b/TestCommon/TestCommon.csproj @@ -13,8 +13,9 @@ v3.5 512 FrameworkTests + true - + true full false @@ -23,7 +24,7 @@ prompt 4 - + pdbonly true bin\Release\ @@ -31,7 +32,7 @@ prompt 4 - + diff --git a/TestCommon/version.template b/TestCommon/version.template index db700576..4e0321ef 100644 --- a/TestCommon/version.template +++ b/TestCommon/version.template @@ -1 +1 @@ -1.2.0.0 +1.2.0.0 \ No newline at end of file From 0056fe8c33ad59ef428fa8824139c1ad9a16cb3c Mon Sep 17 00:00:00 2001 From: abadea Date: Fri, 13 Nov 2009 14:54:13 +0000 Subject: [PATCH 261/342] Issue 593: Move framework to a single directory - .NET part --- build.xml | 15 ++++++++++----- 1 file changed, 10 insertions(+), 5 deletions(-) diff --git a/build.xml b/build.xml index 00dbfaeb..c17d8829 100644 --- a/build.xml +++ b/build.xml @@ -23,8 +23,6 @@ - - @@ -33,21 +31,29 @@ - + + + + + - + + + + + @@ -78,4 +84,3 @@ - From 03372e7538c9698ede47e9cb197003aaff6bd899 Mon Sep 17 00:00:00 2001 From: abadea Date: Fri, 13 Nov 2009 16:26:05 +0000 Subject: [PATCH 262/342] Removing the executable property from files that do not need it --- ConnectorFramework.sln | 0 Framework.targets | 0 FrameworkTests/ConnectorFacadeTests.cs | 0 FrameworkTests/GuardedByteArrayTests.cs | 0 FrameworkTests/MockConnector.cs | 0 FrameworkTests/ObjectClassUtilTests.cs | 0 6 files changed, 0 insertions(+), 0 deletions(-) mode change 100755 => 100644 ConnectorFramework.sln mode change 100755 => 100644 Framework.targets mode change 100755 => 100644 FrameworkTests/ConnectorFacadeTests.cs mode change 100755 => 100644 FrameworkTests/GuardedByteArrayTests.cs mode change 100755 => 100644 FrameworkTests/MockConnector.cs mode change 100755 => 100644 FrameworkTests/ObjectClassUtilTests.cs diff --git a/ConnectorFramework.sln b/ConnectorFramework.sln old mode 100755 new mode 100644 diff --git a/Framework.targets b/Framework.targets old mode 100755 new mode 100644 diff --git a/FrameworkTests/ConnectorFacadeTests.cs b/FrameworkTests/ConnectorFacadeTests.cs old mode 100755 new mode 100644 diff --git a/FrameworkTests/GuardedByteArrayTests.cs b/FrameworkTests/GuardedByteArrayTests.cs old mode 100755 new mode 100644 diff --git a/FrameworkTests/MockConnector.cs b/FrameworkTests/MockConnector.cs old mode 100755 new mode 100644 diff --git a/FrameworkTests/ObjectClassUtilTests.cs b/FrameworkTests/ObjectClassUtilTests.cs old mode 100755 new mode 100644 From 776da9b4d3d133420c223a10982747a3807d3833 Mon Sep 17 00:00:00 2001 From: abadea Date: Fri, 13 Nov 2009 17:16:19 +0000 Subject: [PATCH 263/342] Issue 593: Move framework to a single directory - Java part --- ActiveDirectoryConnector/build.xml | 2 +- ExchangeConnector/build.xml | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/ActiveDirectoryConnector/build.xml b/ActiveDirectoryConnector/build.xml index 54c99e46..1e98d751 100644 --- a/ActiveDirectoryConnector/build.xml +++ b/ActiveDirectoryConnector/build.xml @@ -22,7 +22,7 @@ --> - + diff --git a/ExchangeConnector/build.xml b/ExchangeConnector/build.xml index 3c4eba44..fa50e663 100644 --- a/ExchangeConnector/build.xml +++ b/ExchangeConnector/build.xml @@ -22,7 +22,7 @@ --> - + From 79c42178665e39f635f67f5e78a25af9a955d04b Mon Sep 17 00:00:00 2001 From: abadea Date: Fri, 13 Nov 2009 19:42:06 +0000 Subject: [PATCH 264/342] Using a non-default port for the connector server to avoid port conflicts --- FrameworkTests/ConnectorInfoManagerTests.cs | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/FrameworkTests/ConnectorInfoManagerTests.cs b/FrameworkTests/ConnectorInfoManagerTests.cs index 1e97ed42..67bfb711 100644 --- a/FrameworkTests/ConnectorInfoManagerTests.cs +++ b/FrameworkTests/ConnectorInfoManagerTests.cs @@ -500,9 +500,9 @@ protected override ConnectorInfoManager GetConnectorInfoManager() str.AppendChar('t'); #if DEBUG - const int PORT = 8758; + const int PORT = 58758; #else - const int PORT = 8759; + const int PORT = 58759; #endif _server = ConnectorServer.NewInstance(); _server.Port = PORT; @@ -570,9 +570,9 @@ protected override ConnectorInfoManager GetConnectorInfoManager() str.AppendChar('t'); #if DEBUG - const int PORT = 8762; + const int PORT = 58762; #else - const int PORT = 8761; + const int PORT = 58761; #endif /*X509Store store = new X509Store("TestCertificateStore", From 821d95710d1cd612f14f6105d8062ae19ade89ac Mon Sep 17 00:00:00 2001 From: roman_kitko Date: Mon, 14 Dec 2009 10:07:30 +0000 Subject: [PATCH 265/342] Use 'Last changed Rev' for revision part of version in dotnet builds. This will be in sync with java builds --- DotNetCommonBuild.Targets | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/DotNetCommonBuild.Targets b/DotNetCommonBuild.Targets index df2777fd..ff4eac84 100644 --- a/DotNetCommonBuild.Targets +++ b/DotNetCommonBuild.Targets @@ -32,7 +32,7 @@ - + From d5387545faf9c8d560f299935197d47e25020c9e Mon Sep 17 00:00:00 2001 From: abadea Date: Tue, 15 Dec 2009 13:43:45 +0000 Subject: [PATCH 266/342] Using 'Last changed Rev' for revision part of version in .NET framework --- Framework.targets | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) diff --git a/Framework.targets b/Framework.targets index 789be1bf..2bc058fb 100644 --- a/Framework.targets +++ b/Framework.targets @@ -27,12 +27,19 @@ Sun Microsystems, Inc. Copyright 2008-2009 Sun Microsystems, Inc. All rights reserved. $(MSBuildProjectDirectory)\..\Dist + $(MSBuildProjectDirectory)\..\..\ - + + From 7179e343f2d9af10f8e0ba09668e859db514d9f2 Mon Sep 17 00:00:00 2001 From: lajos_nagy Date: Thu, 17 Dec 2009 11:25:23 +0000 Subject: [PATCH 267/342] Issue 606: Test operation fails with bad error message when period is used To check for syntax errors in a dn the ADSI lib is used by creating a PathnameClass out of the text representation of the dn. Added new exception with a friendly error message if the existence check of the Container config. prop. throws an exception. Configuration validation has been added to the initialization sequence of the connector, hence any time a new instance of the connector is created and initialized the configuration will be validated. Configuration related unit tests have been moved to ActiveDirectoryConfigurationTests to separate from connector tests. --- .../ActiveDirectoryConfiguration.cs | 44 +++-- .../ActiveDirectoryConnector.cs | 43 ++--- .../ActiveDirectoryUtils.cs | 34 ++++ ActiveDirectoryConnector/Messages.resx | 9 + .../ActiveDirectoryConfigurationTests.cs | 173 +++++++++++++++++ .../ActiveDirectoryConnectorTest.cs | 182 ++++++++---------- .../ActiveDirectoryConnectorTests.csproj | 5 +- ActiveDirectoryConnectorTests/ConfigHelper.cs | 92 +++++++++ 8 files changed, 441 insertions(+), 141 deletions(-) create mode 100644 ActiveDirectoryConnectorTests/ActiveDirectoryConfigurationTests.cs create mode 100644 ActiveDirectoryConnectorTests/ConfigHelper.cs diff --git a/ActiveDirectoryConnector/ActiveDirectoryConfiguration.cs b/ActiveDirectoryConnector/ActiveDirectoryConfiguration.cs index 6fa201dd..444c41c4 100644 --- a/ActiveDirectoryConnector/ActiveDirectoryConfiguration.cs +++ b/ActiveDirectoryConnector/ActiveDirectoryConfiguration.cs @@ -104,50 +104,68 @@ public ActiveDirectoryConfiguration() LDAPHostName = ""; } + /// + /// Determines if the configuration is valid. + /// + /// See for the definition of a valid + /// configuration. + /// + /// Thrown when the configuration is not valid. public override void Validate() { - String message = "Configuration errors: "; + var message = new StringBuilder(); + Boolean foundError = false; // can't lookup the schema without the domain name if ((DomainName == null) || (DomainName.Length == 0)) { - message += ConnectorMessages.Format( - "confReqParam_domainName", "Domain name not supplied "); + message.Append(ConnectorMessages.Format( + "confReqParam_domainName", "Domain name not supplied ")); foundError = true; } if ((DirectoryAdminName == null) || (DirectoryAdminName.Length == 0)) { - message += ConnectorMessages.Format( - "confReqParam_adminName", "Directory administrator name not supplied "); + message.Append(ConnectorMessages.Format( + "confReqParam_adminName", "Directory administrator name not supplied ")); foundError = true; } if ((DirectoryAdminPassword == null) || (DirectoryAdminPassword.Length == 0)) { - message += ConnectorMessages.Format( - "confReqParam_adminPass", "Directory administrator password not supplied "); + message.Append(ConnectorMessages.Format( + "confReqParam_adminPass", "Directory administrator password not supplied ")); foundError = true; } if ((ObjectClass == null) || (ObjectClass.Length == 0)) { - message += ConnectorMessages.Format( - "confReqParam_objClass", "ObjectClass was not supplied "); + message.Append(ConnectorMessages.Format( + "confReqParam_objClass", "ObjectClass was not supplied ")); foundError = true; } - if ((Container == null) || (Container.Length == 0)) + if (string.IsNullOrEmpty(Container)) { - message += ConnectorMessages.Format( - "confReqParam_Container", "Container was not supplied "); + message.Append(ConnectorMessages.Format( + "confReqParam_Container", "Container was not supplied ")); foundError = true; } + else + { + if (!ActiveDirectoryUtils.IsValidDn(Container)) + { + message.Append( ConnectorMessages.Format( + "confParam_Container_invalid_path", @"Container '{0}' could not be recognized as a distinguished name (DN) ", Container ) ); + foundError = true; + } + } if (foundError) { - throw new ConfigurationException(message); + throw new ConfigurationException( ConnectorMessages.Format( + "ex_ConfigErrors", "Configuration errors: {0}", message.ToString() ) ); } } } diff --git a/ActiveDirectoryConnector/ActiveDirectoryConnector.cs b/ActiveDirectoryConnector/ActiveDirectoryConnector.cs index dbe5eb11..e97a2ce9 100644 --- a/ActiveDirectoryConnector/ActiveDirectoryConnector.cs +++ b/ActiveDirectoryConnector/ActiveDirectoryConnector.cs @@ -37,6 +37,7 @@ using Org.IdentityConnectors.Framework.Common; using System.Text; using Org.IdentityConnectors.Common.Script; +using System.Globalization; namespace Org.IdentityConnectors.ActiveDirectory { @@ -246,6 +247,7 @@ public virtual Uid Create(ObjectClass oclass, public virtual void Init(Configuration configuration) { Trace.TraceInformation("Active Directory Init method"); + configuration.Validate(); _configuration = (ActiveDirectoryConfiguration)configuration; _utils = new ActiveDirectoryUtils(_configuration); } @@ -678,12 +680,6 @@ private void ExecuteQuery(ObjectClass oclass, string query, } } } - - // this is the path that all searches come from unless otherwise directed - private string GetSearchContainerPath() - { - return GetSearchContainerPath(UseGlobalCatalog(), _configuration.LDAPHostName, _configuration.Container); - } private string GetSearchContainerPath(bool useGC, string hostname, string searchContainer) { @@ -759,26 +755,29 @@ public virtual void Test() _configuration.ObjectClass)); } - // see if SearchContainer is valid - if (!DirectoryEntry.Exists(GetSearchContainerPath())) + try { - throw new ConnectorException( - _configuration.ConnectorMessages.Format( - "ex_InvalidSearchContainerInConfiguration", - "An invalid search container was supplied: {0}", - _configuration.Container)); + // see if the Container exists + if (!DirectoryEntry.Exists( GetSearchContainerPath( UseGlobalCatalog(), + _configuration.LDAPHostName, _configuration.Container ) )) + { + throw new ConnectorException( + _configuration.ConnectorMessages.Format( + "ex_InvalidContainerInConfiguration", + "An invalid container was supplied: {0}", + _configuration.Container ) ); + } } - - // see if the Context exists - - if (!DirectoryEntry.Exists(GetSearchContainerPath(UseGlobalCatalog(), - _configuration.LDAPHostName, _configuration.Container))) + catch (DirectoryServicesCOMException dscex) { + Trace.TraceError( string.Format( CultureInfo.InvariantCulture, + "Failed to determine whether the Container '{0}' exists. Exception: {1}", _configuration.Container, dscex ) ); + throw new ConnectorException( - _configuration.ConnectorMessages.Format( - "ex_InvalidContainerInConfiguration", - "An invalid container was supplied: {0}", - _configuration.Container)); + _configuration.ConnectorMessages.Format( + "ex_ContainerNotFound", + "Could not find the Container '{0}', the following message was returned from the server: {1}", + _configuration.Container, dscex.Message ), dscex ); } } diff --git a/ActiveDirectoryConnector/ActiveDirectoryUtils.cs b/ActiveDirectoryConnector/ActiveDirectoryUtils.cs index 76eee0f1..70ea06ee 100644 --- a/ActiveDirectoryConnector/ActiveDirectoryUtils.cs +++ b/ActiveDirectoryConnector/ActiveDirectoryUtils.cs @@ -34,6 +34,7 @@ using ActiveDs; using Org.IdentityConnectors.Common.Security; using System.DirectoryServices.ActiveDirectory; +using System.Runtime.InteropServices; namespace Org.IdentityConnectors.ActiveDirectory { @@ -769,6 +770,39 @@ static internal LargeInteger GetLargeIntegerFromLong(Int64 int64Value) largeInteger.LowPart = (int)(int64Value & 0xFFFFFFFF); return largeInteger; } + + /// + /// Determines whether is a valid distinguished name. + /// + /// The string representation of the distinguished name to validate. + /// + /// true if is valid; otherwise, false. + /// + /// A DN is valid if it can be processed by the AD API. This method does not test RFC 2253 compliance, + /// but only basic syntactical check. + internal static bool IsValidDn(string dn) + { + var result = false; + try + { + if (getADSPathname( null, null, dn ) != null) + { + result = true; + } + } + catch (COMException comex) + { + if (comex.ErrorCode == -2147463168) //E_ADS_BAD_PATHNAME + { + result = false; + } + else + { + throw; + } + } + return result; + } } } diff --git a/ActiveDirectoryConnector/Messages.resx b/ActiveDirectoryConnector/Messages.resx index 61cebd2b..ab15935b 100644 --- a/ActiveDirectoryConnector/Messages.resx +++ b/ActiveDirectoryConnector/Messages.resx @@ -279,4 +279,13 @@ An invalid search context was supplied: {0} + + Container '{0}' could not be recognized as a distinguished name (DN) + + + Configuration errors: {0} + + + Could not find the Container '{0}', the following message was returned from the server: {1} + \ No newline at end of file diff --git a/ActiveDirectoryConnectorTests/ActiveDirectoryConfigurationTests.cs b/ActiveDirectoryConnectorTests/ActiveDirectoryConfigurationTests.cs new file mode 100644 index 00000000..de953c8d --- /dev/null +++ b/ActiveDirectoryConnectorTests/ActiveDirectoryConfigurationTests.cs @@ -0,0 +1,173 @@ +/* + * ==================== + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER. + * + * Copyright 2008-2009 Sun Microsystems, Inc. All rights reserved. + * + * The contents of this file are subject to the terms of the Common Development + * and Distribution License("CDDL") (the "License"). You may not use this file + * except in compliance with the License. + * + * You can obtain a copy of the License at + * http://IdentityConnectors.dev.java.net/legal/license.txt + * See the License for the specific language governing permissions and limitations + * under the License. + * + * When distributing the Covered Code, include this CDDL Header Notice in each file + * and include the License file at identityconnectors/legal/license.txt. + * If applicable, add the following below this CDDL Header, with the fields + * enclosed by brackets [] replaced by your own identifying information: + * "Portions Copyrighted [year] [name of copyright owner]" + * ==================== + */ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using NUnit.Framework; +using Org.IdentityConnectors.Test.Common; +using Org.IdentityConnectors.Framework.Common.Exceptions; + +namespace Org.IdentityConnectors.ActiveDirectory +{ + [TestFixture] + public class ActiveDirectoryConfigurationTests + { + [Test] + public void TestProperties() + { + var sut = new ActiveDirectoryConfiguration + { + ConnectorMessages = TestHelpers.CreateDummyMessages() + }; + + var container = TestHelpers.GetProperties( + typeof( ActiveDirectoryConnector ) ).GetProperty( ConfigHelper.CONFIG_PROPERTY_CONTAINER ); + sut.Container = container; + Assert.AreEqual( container, sut.Container, "Container" ); + + //test with the negate of the default value + var createHomeDirectory = !sut.CreateHomeDirectory; + sut.CreateHomeDirectory = createHomeDirectory; + Assert.AreEqual( createHomeDirectory, sut.CreateHomeDirectory, "CreateHomeDirectory" ); + + var directoryAdminName = TestHelpers.GetProperties( + typeof( ActiveDirectoryConnector ) ).GetProperty( ConfigHelper.CONFIG_PROPERTY_USER ); + sut.DirectoryAdminName = directoryAdminName; + Assert.AreEqual( directoryAdminName, sut.DirectoryAdminName, "DirectoryAdminName" ); + + var directoryAdminPassword = TestHelpers.GetProperties( + typeof( ActiveDirectoryConnector ) ).GetProperty( ConfigHelper.CONFIG_PROPERTY_PASSWORD ); + sut.DirectoryAdminPassword = directoryAdminPassword; + Assert.AreEqual( directoryAdminPassword, sut.DirectoryAdminPassword, "DirectoryAdminPassword" ); + + var domainName = TestHelpers.GetProperties( + typeof( ActiveDirectoryConnector ) ).GetProperty( ConfigHelper.CONFIG_PROPERTY_DOMAIN_NAME ); + sut.DomainName = domainName; + Assert.AreEqual( domainName, sut.DomainName, "DomainName" ); + + var ldapHostName = TestHelpers.GetProperties( + typeof( ActiveDirectoryConnector ) ).GetProperty( ConfigHelper.CONFIG_PROPERTY_LDAPHOSTNAME ); + sut.LDAPHostName = ldapHostName; + Assert.AreEqual( ldapHostName, sut.LDAPHostName, "LDAPHostName" ); + + const string objectClassName = "DOES NOT MATTER"; + sut.ObjectClass = objectClassName; + Assert.AreEqual( objectClassName, sut.ObjectClass, "ObjectClass" ); + + //test with the negate of the default value + var searchChildDomains = !sut.SearchChildDomains; + sut.SearchChildDomains = searchChildDomains; + Assert.AreEqual( searchChildDomains, sut.SearchChildDomains, "SearchChildDomains" ); + + var searchContext = TestHelpers.GetProperties( + typeof( ActiveDirectoryConnector ) ).GetProperty( ConfigHelper.CONFIG_PROPERTY_SEARCH_CONTEXT ); + sut.SearchContext = searchContext; + Assert.AreEqual( searchContext, sut.SearchContext, "SearchContext" ); + + var syncDomainController = TestHelpers.GetProperties( + typeof( ActiveDirectoryConnector ) ).GetProperty( ConfigHelper.CONFIG_PROPERTY_SYNC_DOMAIN_CONTROLLER ); + sut.SyncDomainController = syncDomainController; + Assert.AreEqual( syncDomainController, sut.SyncDomainController, "SyncDomainController" ); + + var syncGlobalCatalogServer = TestHelpers.GetProperties( + typeof( ActiveDirectoryConnector ) ).GetProperty( ConfigHelper.CONFIG_PROPERTY_GC_DOMAIN_CONTROLLER ); + sut.SyncGlobalCatalogServer = syncGlobalCatalogServer; + Assert.AreEqual( syncGlobalCatalogServer, sut.SyncGlobalCatalogServer, "SyncGlobalCatalogServer" ); + } + + [Test] + public void TestValidate() + { + var sut = (ActiveDirectoryConfiguration)ConfigHelper.GetConfiguration(); + //test if the default configuration is valid, hence the following tests will fail only if the + //changed property is incorrect + sut.Validate(); + + var domainName = sut.DomainName; + try + { + sut.DomainName = string.Empty; + sut.Validate(); + Assert.Fail( "Exception was not thrown for empty DomainName" ); + } + catch(ConfigurationException) + { + sut.DomainName = domainName; + } + + var directoryAdminName = sut.DirectoryAdminName; + try + { + sut.DirectoryAdminName = string.Empty; + sut.Validate(); + Assert.Fail( "Exception was not thrown for empty DirectoryAdminName" ); + } + catch (ConfigurationException) + { + sut.DirectoryAdminName = directoryAdminName; + } + + var directoryAdminPassword = sut.DirectoryAdminPassword; + try + { + sut.DirectoryAdminPassword = string.Empty; + sut.Validate(); + Assert.Fail( "Exception was not thrown for empty DirectoryAdminPassword" ); + } + catch (ConfigurationException) + { + sut.DirectoryAdminPassword = directoryAdminPassword; + } + + var objectClass = sut.ObjectClass; + try + { + sut.ObjectClass = string.Empty; + sut.Validate(); + Assert.Fail( "Exception was not thrown for empty ObjectClass" ); + } + catch (ConfigurationException) + { + sut.ObjectClass = objectClass; + } + + + var container = sut.Container; + try + { + sut.Container = string.Empty; + sut.Validate(); + Assert.Fail( "Exception was not thrown for empty Container" ); + + sut.Container = "CN=ClaytonFarlow.DC=NotMyCompany.DC=com"; + sut.Validate(); + Assert.Fail( "Exception was not thrown for Container containing periods" ); + } + catch (ConfigurationException) + { + sut.Container = container; + } + } + } +} diff --git a/ActiveDirectoryConnectorTests/ActiveDirectoryConnectorTest.cs b/ActiveDirectoryConnectorTests/ActiveDirectoryConnectorTest.cs index eaa15c35..e91769db 100644 --- a/ActiveDirectoryConnectorTests/ActiveDirectoryConnectorTest.cs +++ b/ActiveDirectoryConnectorTests/ActiveDirectoryConnectorTest.cs @@ -43,65 +43,55 @@ namespace Org.IdentityConnectors.ActiveDirectory public class ActiveDirectoryConnectorTest { Random _rand = new Random(); - public static readonly string CONFIG_PROPERTY_USER = "config_user"; - public static readonly string CONFIG_PROPERTY_PASSWORD = "config_password"; - public static readonly string CONFIG_PROPERTY_HOST = "config_host"; - public static readonly string CONFIG_PROPERTY_PORT = "config_port"; - public static readonly string CONFIG_PROPERTY_CONTAINER = "config_container"; - public static readonly string CONFIG_PROPERTY_SCRIPT_USER_LOCAL = "config_script_user_local"; - public static readonly string CONFIG_PROPERTY_SCRIPT_PASSWORD_LOCAL = "config_script_password_local"; - public static readonly string CONFIG_PROPERTY_SCRIPT_USER_DOMAIN = "config_script_user_domain"; - public static readonly string CONFIG_PROPERTY_SCRIPT_PASSWORD_DOMAIN = "config_script_password_domain"; - public static readonly string CONFIG_PROPERTY_LDAPHOSTNAME = "config_ldap_hostname"; - public static readonly string CONFIG_PROPERTY_SEARCH_CONTEXT = "config_search_context"; - public static readonly string config_PROPERTY_SYNC_CONTAINER_ROOT = "config_sync_container_root"; - public static readonly string config_PROPERTY_SYNC_CONTAINER_CHILD = "config_sync_container_child"; - public static readonly string CONFIG_PROPERTY_DOMAIN_NAME = "config_domain_name"; - public static readonly string CONFIG_PROPERTY_SYNC_DOMAIN_CONTROLLER = "config_sync_domain_controller"; - public static readonly string CONFIG_PROPERTY_GC_DOMAIN_CONTROLLER = "config_sync_gc_domain_controller"; - public static readonly string TEST_PARAM_SHARED_HOME_FOLDER = "test_param_shared_home_folder"; + // having troubles with duplicate random numbers public static List randomList = new List(); - [Test] - public void TestConfiguration() { - ActiveDirectoryConfiguration config = new ActiveDirectoryConfiguration(); - config.ConnectorMessages = TestHelpers.CreateDummyMessages(); - String directoryAdminName = GetProperty(CONFIG_PROPERTY_USER); - config.DirectoryAdminName = directoryAdminName; - Assert.AreEqual(directoryAdminName, config.DirectoryAdminName); - } - [Test] public void TestTest() { ActiveDirectoryConnector connectorGood = new ActiveDirectoryConnector(); - ActiveDirectoryConfiguration config = (ActiveDirectoryConfiguration)GetConfiguration(); + ActiveDirectoryConfiguration config = (ActiveDirectoryConfiguration)ConfigHelper.GetConfiguration(); connectorGood.Init(config); connectorGood.Test(); - bool threwException = false; + var objectClass = config.ObjectClass; try { config.ObjectClass = "BadObjectClass"; ActiveDirectoryConnector connectorBad = new ActiveDirectoryConnector(); connectorBad.Init(config); connectorBad.Test(); + + Assert.Fail( "Bad configuration should have caused an exception" ); } catch (ConnectorException e) { - threwException = true; + config.ObjectClass = objectClass; } - Assert.IsTrue(threwException, "Bad configuration should have caused an exception"); + var container = config.Container; + try + { + config.Container += ",DC=BadDC"; + ActiveDirectoryConnector connectorBad = new ActiveDirectoryConnector(); + connectorBad.Init( config ); + connectorBad.Test(); + + Assert.Fail( "Configuration with bad DC in Container should have caused an exception" ); + } + catch (ConnectorException e) + { + config.Container = container; + } } [Test] public void TestSchema() { ActiveDirectoryConnector connector = new ActiveDirectoryConnector(); - connector.Init(GetConfiguration()); + connector.Init( ConfigHelper.GetConfiguration() ); Schema schema = connector.Schema(); Boolean foundOptionalAttributes = false; Boolean foundOperationalAttributes = false; @@ -152,7 +142,7 @@ public void TestBasics_Account() { //Initialize Connector ActiveDirectoryConnector connector = new ActiveDirectoryConnector(); - connector.Init(GetConfiguration()); + connector.Init( ConfigHelper.GetConfiguration() ); // create user ICollection createAttributes = GetNormalAttributes_Account(); @@ -164,7 +154,7 @@ public void TestBasics_Account() new List(); Name oldName = ConnectorAttributeUtil.GetNameFromAttributes(createAttributes); String newName = ActiveDirectoryUtils.GetRelativeName(oldName); - newName = newName.Trim() + "_new, " + GetProperty(CONFIG_PROPERTY_CONTAINER); + newName = newName.Trim() + "_new, " + GetProperty( ConfigHelper.CONFIG_PROPERTY_CONTAINER ); updateReplaceAttrs.Add(ConnectorAttributeBuilder.Build( Name.NAME, newName)); updateReplaceAttrs.Add(ConnectorAttributeBuilder.Build( @@ -192,7 +182,7 @@ public void TestBasics_Group() { //Initialize Connector ActiveDirectoryConnector connector = new ActiveDirectoryConnector(); - connector.Init(GetConfiguration()); + connector.Init( ConfigHelper.GetConfiguration() ); Uid uidToDelete = null; try @@ -221,7 +211,7 @@ public void TestBasics_Group() new List(); Name oldName = ConnectorAttributeUtil.GetNameFromAttributes(createAttributes); String newName = ActiveDirectoryUtils.GetRelativeName(oldName); - newName = newName.Trim() + "_new, " + GetProperty(CONFIG_PROPERTY_CONTAINER); + newName = newName.Trim() + "_new, " + GetProperty( ConfigHelper.CONFIG_PROPERTY_CONTAINER ); updateReplaceAttrs.Add(createUid); updateReplaceAttrs.Add(ConnectorAttributeBuilder.Build( @@ -256,7 +246,7 @@ public void TestCreate_Account() { //Initialize Connector ActiveDirectoryConnector connector = new ActiveDirectoryConnector(); - connector.Init(GetConfiguration()); + connector.Init( ConfigHelper.GetConfiguration() ); Uid createUid = null; try { @@ -279,7 +269,7 @@ public void TestCreate_Group() { //Initialize Connector ActiveDirectoryConnector connector = new ActiveDirectoryConnector(); - connector.Init(GetConfiguration()); + connector.Init( ConfigHelper.GetConfiguration() ); Uid createUid = null; try { @@ -302,7 +292,7 @@ public void TestCreate_OrganizationalUnit() { //Initialize Connector ActiveDirectoryConnector connector = new ActiveDirectoryConnector(); - connector.Init(GetConfiguration()); + connector.Init( ConfigHelper.GetConfiguration() ); Uid createUid = null; try { @@ -327,7 +317,7 @@ public void TestCreateWithHomeDirectory_Account() { //Initialize Connector ActiveDirectoryConnector connector = new ActiveDirectoryConnector(); - ActiveDirectoryConfiguration config = (ActiveDirectoryConfiguration)GetConfiguration(); + ActiveDirectoryConfiguration config = (ActiveDirectoryConfiguration)ConfigHelper.GetConfiguration(); config.CreateHomeDirectory = true; connector.Init(config); Uid userUid = null; @@ -337,7 +327,7 @@ public void TestCreateWithHomeDirectory_Account() ICollection createAttributes = GetNormalAttributes_Account(); // read the homedir path, and append the samaccountname - StringBuilder homeDirPathBuilder = new StringBuilder(GetProperty(TEST_PARAM_SHARED_HOME_FOLDER)); + StringBuilder homeDirPathBuilder = new StringBuilder( GetProperty( ConfigHelper.TEST_PARAM_SHARED_HOME_FOLDER ) ); if (!homeDirPathBuilder.ToString().EndsWith("\\")) { homeDirPathBuilder.Append('\\'); @@ -419,7 +409,7 @@ public void TestCreateWithHomeDirectoryNoCreateConfig_Account() { //Initialize Connector ActiveDirectoryConnector connector = new ActiveDirectoryConnector(); - ActiveDirectoryConfiguration config = (ActiveDirectoryConfiguration)GetConfiguration(); + ActiveDirectoryConfiguration config = (ActiveDirectoryConfiguration)ConfigHelper.GetConfiguration(); config.CreateHomeDirectory = false; connector.Init(config); Uid userUid = null; @@ -429,7 +419,7 @@ public void TestCreateWithHomeDirectoryNoCreateConfig_Account() ICollection createAttributes = GetNormalAttributes_Account(); // read the homedir path, and append the samaccountname - StringBuilder homeDirPathBuilder = new StringBuilder(GetProperty(TEST_PARAM_SHARED_HOME_FOLDER)); + StringBuilder homeDirPathBuilder = new StringBuilder( GetProperty( ConfigHelper.TEST_PARAM_SHARED_HOME_FOLDER ) ); if (!homeDirPathBuilder.ToString().EndsWith("\\")) { homeDirPathBuilder.Append('\\'); @@ -468,7 +458,7 @@ public void TestCreateWithHomeDirectoryNoCreateConfig_Account() public void TestSearchNoFilter_Account() { ActiveDirectoryConnector connector = new ActiveDirectoryConnector(); - Configuration config = GetConfiguration(); + Configuration config = ConfigHelper.GetConfiguration(); config.ConnectorMessages = TestHelpers.CreateDummyMessages(); connector.Init(config); @@ -527,7 +517,7 @@ public void TestSearchNoFilter_Account() public void TestSearchNoFilter_Group() { ActiveDirectoryConnector connector = new ActiveDirectoryConnector(); - connector.Init(GetConfiguration()); + connector.Init( ConfigHelper.GetConfiguration() ); ICollection createdUids = new HashSet(); try @@ -584,7 +574,7 @@ public void TestSearchNoFilter_Group() public void TestSearchByName_account() { ActiveDirectoryConnector connector = new ActiveDirectoryConnector(); - connector.Init(GetConfiguration()); + connector.Init( ConfigHelper.GetConfiguration() ); Uid createUid = null; @@ -655,7 +645,7 @@ public void TestSearchByName_account() public void TestSearchByName_group() { ActiveDirectoryConnector connector = new ActiveDirectoryConnector(); - connector.Init(GetConfiguration()); + connector.Init( ConfigHelper.GetConfiguration() ); Uid createUid = null; @@ -726,7 +716,7 @@ public void TestSearchByName_group() public void TestSearchByCNWithWildcard_account() { ActiveDirectoryConnector connector = new ActiveDirectoryConnector(); - connector.Init(GetConfiguration()); + connector.Init( ConfigHelper.GetConfiguration() ); Uid uid1 = null; Uid uid2 = null; @@ -774,7 +764,7 @@ public void TestSearchByCNWithWildcard_account() public void TestSearchByRegularAttribute_account() { ActiveDirectoryConnector connector = new ActiveDirectoryConnector(); - connector.Init(GetConfiguration()); + connector.Init( ConfigHelper.GetConfiguration() ); Uid createUid = null; @@ -828,7 +818,7 @@ public void TestSearchByRegularAttribute_account() public void TestSearchByRegularAttributeWithWildcard_account() { ActiveDirectoryConnector connector = new ActiveDirectoryConnector(); - connector.Init(GetConfiguration()); + connector.Init( ConfigHelper.GetConfiguration() ); ICollection uids = new HashSet(); @@ -893,7 +883,7 @@ public void TestCreateWithAllAttributes_Account() { //Initialize Connector ActiveDirectoryConnector connector = new ActiveDirectoryConnector(); - connector.Init(GetConfiguration()); + connector.Init( ConfigHelper.GetConfiguration() ); Uid createUid = null; try @@ -920,7 +910,7 @@ public void Test_OpAtt_Enabled_Account() { //Initialize Connector ActiveDirectoryConnector connector = new ActiveDirectoryConnector(); - connector.Init(GetConfiguration()); + connector.Init( ConfigHelper.GetConfiguration() ); Uid createUid = null; try @@ -954,18 +944,18 @@ public void TestScriptOnResource() { //Initialize Connector ActiveDirectoryConnector connector = new ActiveDirectoryConnector(); - connector.Init(GetConfiguration()); + connector.Init( ConfigHelper.GetConfiguration() ); try { RunScript(connector, "", "", ""); - RunScript(connector, GetProperty(CONFIG_PROPERTY_SCRIPT_USER_LOCAL), - GetProperty(CONFIG_PROPERTY_SCRIPT_PASSWORD_LOCAL), ""); - RunScript(connector, GetProperty(CONFIG_PROPERTY_SCRIPT_USER_DOMAIN), - GetProperty(CONFIG_PROPERTY_SCRIPT_PASSWORD_DOMAIN), ""); + RunScript( connector, GetProperty( ConfigHelper.CONFIG_PROPERTY_SCRIPT_USER_LOCAL ), + GetProperty( ConfigHelper.CONFIG_PROPERTY_SCRIPT_PASSWORD_LOCAL ), "" ); + RunScript( connector, GetProperty( ConfigHelper.CONFIG_PROPERTY_SCRIPT_USER_DOMAIN ), + GetProperty( ConfigHelper.CONFIG_PROPERTY_SCRIPT_PASSWORD_DOMAIN ), "" ); // now try one with the prefix set - ActiveDirectoryConfiguration prefixConfig = (ActiveDirectoryConfiguration)GetConfiguration(); + ActiveDirectoryConfiguration prefixConfig = (ActiveDirectoryConfiguration)ConfigHelper.GetConfiguration(); string prefix = "UnitTest_"; connector.Dispose(); connector.Init(prefixConfig); @@ -976,7 +966,7 @@ public void TestScriptOnResource() bool scriptFailed = false; try { - RunScript(connector, GetProperty(CONFIG_PROPERTY_USER), "bogus", ""); + RunScript( connector, GetProperty( ConfigHelper.CONFIG_PROPERTY_USER ), "bogus", "" ); } catch (Exception e) { @@ -994,7 +984,7 @@ public void TestAddGroup_Account() { //Initialize Connector ActiveDirectoryConnector connector = new ActiveDirectoryConnector(); - connector.Init(GetConfiguration()); + connector.Init( ConfigHelper.GetConfiguration() ); Uid groupUid = null; Uid userUid = null; try @@ -1033,7 +1023,7 @@ public void TestAddGroup_Account() public void TestRemoveAttributeValue() { ActiveDirectoryConnector connector = new ActiveDirectoryConnector(); - connector.Init(GetConfiguration()); + connector.Init( ConfigHelper.GetConfiguration() ); Uid createUid = null; @@ -1043,7 +1033,7 @@ public void TestRemoveAttributeValue() ICollection attributes = new HashSet(); attributes.Add(ConnectorAttributeBuilder.Build( - "ad_container", GetProperty(CONFIG_PROPERTY_CONTAINER))); + "ad_container", GetProperty( ConfigHelper.CONFIG_PROPERTY_CONTAINER ) ) ); attributes.Add(ConnectorAttributeBuilder.Build( "userPassword", "secret")); attributes.Add(ConnectorAttributeBuilder.Build( @@ -1056,7 +1046,7 @@ public void TestRemoveAttributeValue() "displayName", "nunit test user " + randomNumber)); attributes.Add(ConnectorAttributeBuilder.Build( Name.NAME, "cn=nunit" + randomNumber + "," + - GetProperty(CONFIG_PROPERTY_CONTAINER))); + GetProperty( ConfigHelper.CONFIG_PROPERTY_CONTAINER ) ) ); attributes.Add(ConnectorAttributeBuilder.Build( "mail", "nunitUser" + randomNumber + "@some.com")); attributes.Add(ConnectorAttributeBuilder.Build( @@ -1096,7 +1086,7 @@ public void TestRemoveAttributeValue() public void TestContainerChange_account() { ActiveDirectoryConnector connector = new ActiveDirectoryConnector(); - connector.Init(GetConfiguration()); + connector.Init( ConfigHelper.GetConfiguration() ); Uid createOuUid = null; Uid createUserUid = null; @@ -1162,7 +1152,7 @@ public void TestContainerChange_account() public void TestContainerChange_group() { ActiveDirectoryConnector connector = new ActiveDirectoryConnector(); - connector.Init(GetConfiguration()); + connector.Init( ConfigHelper.GetConfiguration() ); Uid createOuUid = null; Uid createGroupUid = null; @@ -1237,7 +1227,7 @@ public void TestEnableDate() { //Initialize Connector ActiveDirectoryConnector connector = new ActiveDirectoryConnector(); - connector.Init(GetConfiguration()); + connector.Init( ConfigHelper.GetConfiguration() ); Uid createUid = null; try @@ -1272,7 +1262,7 @@ public void TestDisableDate() { //Initialize Connector ActiveDirectoryConnector connector = new ActiveDirectoryConnector(); - connector.Init(GetConfiguration()); + connector.Init( ConfigHelper.GetConfiguration() ); Uid createUid = null; try @@ -1310,8 +1300,8 @@ public void TestDisableDate() public void TestSyncGC() { // test with searchChildDomain (uses GC) - TestSync(true, GetProperty(config_PROPERTY_SYNC_CONTAINER_ROOT, null)); - TestSync(true, GetProperty(config_PROPERTY_SYNC_CONTAINER_CHILD, null)); + TestSync( true, GetProperty( ConfigHelper.config_PROPERTY_SYNC_CONTAINER_ROOT, null ) ); + TestSync( true, GetProperty( ConfigHelper.config_PROPERTY_SYNC_CONTAINER_CHILD, null ) ); } // test sync @@ -1319,8 +1309,8 @@ public void TestSyncGC() public void TestSyncDC() { // test withouth searchChildDomains (uses DC) - TestSync(false, GetProperty(config_PROPERTY_SYNC_CONTAINER_ROOT, null)); - TestSync(false, GetProperty(config_PROPERTY_SYNC_CONTAINER_CHILD, null)); + TestSync( false, GetProperty( ConfigHelper.config_PROPERTY_SYNC_CONTAINER_ROOT, null ) ); + TestSync( false, GetProperty( ConfigHelper.config_PROPERTY_SYNC_CONTAINER_CHILD, null ) ); } [Test] @@ -1328,7 +1318,7 @@ public void TestUserPasswordChange() { //Initialize Connector ActiveDirectoryConnector connector = new ActiveDirectoryConnector(); - connector.Init(GetConfiguration()); + connector.Init( ConfigHelper.GetConfiguration() ); Uid createUid = null; try @@ -1416,7 +1406,7 @@ public void TestAuthenticateUser() { //Initialize Connector ActiveDirectoryConnector connector = new ActiveDirectoryConnector(); - connector.Init(GetConfiguration()); + connector.Init( ConfigHelper.GetConfiguration() ); Uid createUid = null; try @@ -1534,7 +1524,7 @@ public void TestShortnameAndDescription() { //Initialize Connector ActiveDirectoryConnector connector = new ActiveDirectoryConnector(); - connector.Init(GetConfiguration()); + connector.Init( ConfigHelper.GetConfiguration() ); Uid uidAccount = null; Uid uidGroup = null; Uid uidOu = null; @@ -1664,7 +1654,7 @@ public void TestPasswordExpiration() { //Initialize Connector ActiveDirectoryConnector connector = new ActiveDirectoryConnector(); - connector.Init(GetConfiguration()); + connector.Init( ConfigHelper.GetConfiguration() ); Uid createUid = null; try @@ -1746,7 +1736,7 @@ public void TestAccountLocked() { //Initialize Connector ActiveDirectoryConnector connector = new ActiveDirectoryConnector(); - connector.Init(GetConfiguration()); + connector.Init( ConfigHelper.GetConfiguration() ); Uid createUid = null; try @@ -1859,8 +1849,8 @@ public void TestSync(bool searchChildDomains, String container) { //Initialize Connector ActiveDirectoryConnector connector = new ActiveDirectoryConnector(); - ActiveDirectoryConfiguration configuration = - (ActiveDirectoryConfiguration)GetConfiguration(); + ActiveDirectoryConfiguration configuration = + (ActiveDirectoryConfiguration)ConfigHelper.GetConfiguration(); configuration.SearchContext = container; configuration.SearchChildDomains = searchChildDomains; connector.Init(configuration); @@ -1959,8 +1949,8 @@ public void TestGetLastSyncToken() { //Initialize Connector ActiveDirectoryConnector connector = new ActiveDirectoryConnector(); - ActiveDirectoryConfiguration configuration = - (ActiveDirectoryConfiguration)GetConfiguration(); + ActiveDirectoryConfiguration configuration = + (ActiveDirectoryConfiguration)ConfigHelper.GetConfiguration(); configuration.SearchChildDomains = false; connector.Init(configuration); SyncToken noGCToken = connector.GetLatestSyncToken(ObjectClass.ACCOUNT); @@ -2280,7 +2270,7 @@ public void DeleteAndVerifyObject(ActiveDirectoryConnector connector, public void TestOuSearch() { ActiveDirectoryConnector connector = new ActiveDirectoryConnector(); - ActiveDirectoryConfiguration config = (ActiveDirectoryConfiguration)GetConfiguration(); + ActiveDirectoryConfiguration config = (ActiveDirectoryConfiguration)ConfigHelper.GetConfiguration(); connector.Init(config); ObjectClass OUObjectClass = ActiveDirectoryConnector.ouObjectClass; @@ -2294,7 +2284,7 @@ public void TestUnmatchedCaseGUIDSearch() { //Initialize Connector ActiveDirectoryConnector connector = new ActiveDirectoryConnector(); - connector.Init(GetConfiguration()); + connector.Init( ConfigHelper.GetConfiguration() ); Uid userUid = null; try { @@ -2363,22 +2353,6 @@ public void VerifyObject(ActiveDirectoryConnector connector, Uid uid, VerifyObject(attributes, GetConnectorObjectFromUid(connector, oclass, uid)); } - - public Configuration GetConfiguration() - { - ActiveDirectoryConfiguration config = new ActiveDirectoryConfiguration(); - config.DomainName = GetProperty(CONFIG_PROPERTY_DOMAIN_NAME); - config.LDAPHostName = GetProperty(CONFIG_PROPERTY_LDAPHOSTNAME); - config.DirectoryAdminName = GetProperty(CONFIG_PROPERTY_USER); - config.DirectoryAdminPassword = GetProperty(CONFIG_PROPERTY_PASSWORD); - config.Container = GetProperty(CONFIG_PROPERTY_CONTAINER); - config.SearchContext = GetProperty(CONFIG_PROPERTY_SEARCH_CONTEXT); - config.SyncDomainController = GetProperty(CONFIG_PROPERTY_SYNC_DOMAIN_CONTROLLER); - config.SyncGlobalCatalogServer = GetProperty(CONFIG_PROPERTY_GC_DOMAIN_CONTROLLER); - config.ConnectorMessages = TestHelpers.CreateDummyMessages(); - return config; - } - /** * NOTES: * - cn and __NAME__ should be the same. Test if they are not @@ -2396,7 +2370,7 @@ public ICollection GetNormalAttributes_Account() // the container ... is a fabricated attribute attributes.Add(ConnectorAttributeBuilder.Build( - "ad_container", GetProperty(CONFIG_PROPERTY_CONTAINER))); + "ad_container", GetProperty( ConfigHelper.CONFIG_PROPERTY_CONTAINER ) ) ); attributes.Add(ConnectorAttributeBuilder.Build( "userPassword", "secret")); attributes.Add(ConnectorAttributeBuilder.Build( @@ -2409,7 +2383,7 @@ public ICollection GetNormalAttributes_Account() "displayName", "nunit test user " + randomNumber)); attributes.Add(ConnectorAttributeBuilder.Build( Name.NAME, "cn=nunit" + randomNumber + "," + - GetProperty(CONFIG_PROPERTY_CONTAINER))); + GetProperty( ConfigHelper.CONFIG_PROPERTY_CONTAINER ) ) ); attributes.Add(ConnectorAttributeBuilder.Build( "mail", "nunitUser" + randomNumber + "@some.com")); attributes.Add(ConnectorAttributeBuilder.Build( @@ -2424,7 +2398,7 @@ public ICollection GetAllAttributes_Account() // the container ... is a fabricated attribute attributes.Add(ConnectorAttributeBuilder.Build( - "ad_container", GetProperty(CONFIG_PROPERTY_CONTAINER))); + "ad_container", GetProperty( ConfigHelper.CONFIG_PROPERTY_CONTAINER ) ) ); GuardedString password = new GuardedString(); foreach (char c in "secret") { @@ -2581,7 +2555,7 @@ public ICollection GetAllAttributes_Account() // now set name operational attribute attributes.Add(ConnectorAttributeBuilder.Build( Name.NAME, "cn=nunit" + randomNumber + "," + - GetProperty(CONFIG_PROPERTY_CONTAINER))); + GetProperty( ConfigHelper.CONFIG_PROPERTY_CONTAINER ) ) ); /* @@ -2606,7 +2580,7 @@ public ICollection GetNormalAttributes_Group() "description", "Original Description" + randomNumber)); attributes.Add(ConnectorAttributeBuilder.Build( Name.NAME, "cn=nunitGroup" + randomNumber + "," + - GetProperty(CONFIG_PROPERTY_CONTAINER))); + GetProperty( ConfigHelper.CONFIG_PROPERTY_CONTAINER ) ) ); attributes.Add(ConnectorAttributeBuilder.Build( "groupType", 4)); return attributes; @@ -2619,7 +2593,7 @@ public ICollection GetNormalAttributes_OrganizationalUnit() attributes.Add(ConnectorAttributeBuilder.Build( Name.NAME, "ou=nunitOU" + randomNumber + "," + - GetProperty(CONFIG_PROPERTY_CONTAINER))); + GetProperty( ConfigHelper.CONFIG_PROPERTY_CONTAINER ) ) ); return attributes; } @@ -2775,7 +2749,7 @@ public ICollection GetDefaultAttributesToGet(ObjectClass oclass) ICollection attributesToGet = new HashSet(); ActiveDirectoryConnector connector = new ActiveDirectoryConnector(); - connector.Init(GetConfiguration()); + connector.Init( ConfigHelper.GetConfiguration() ); Schema schema = connector.Schema(); ObjectClassInfo ocInfo = schema.FindObjectClassInfo(oclass.GetObjectClassValue()); Assert.IsNotNull(ocInfo); diff --git a/ActiveDirectoryConnectorTests/ActiveDirectoryConnectorTests.csproj b/ActiveDirectoryConnectorTests/ActiveDirectoryConnectorTests.csproj index ba08f85c..559a922d 100644 --- a/ActiveDirectoryConnectorTests/ActiveDirectoryConnectorTests.csproj +++ b/ActiveDirectoryConnectorTests/ActiveDirectoryConnectorTests.csproj @@ -1,4 +1,4 @@ - Create Home Directory @@ -301,5 +300,10 @@ An invalid search context was supplied: {0} - + + There are no registered PowerShell snap-ins. + + + There is no supported Exchange PowerShell snap-in registered. + \ No newline at end of file diff --git a/ExchangeConnector/PSExchangeConnector.cs b/ExchangeConnector/PSExchangeConnector.cs index 71e46a4c..fa01ce87 100644 --- a/ExchangeConnector/PSExchangeConnector.cs +++ b/ExchangeConnector/PSExchangeConnector.cs @@ -231,7 +231,7 @@ public override void Init(Configuration configuration) this.configuration = (ExchangeConfiguration)configuration; // create runspace instance, will be alive as long as the connector instance is alive - this.runspace = new RunSpaceInstance(RunSpaceInstance.SnapIn.Exchange); + this.runspace = new RunSpaceInstance(RunSpaceInstance.SnapIn.Exchange, configuration.ConnectorMessages); // read the object class info definitions this.mapOcInfo = ExchangeUtility.GetOCInfo(); diff --git a/ExchangeConnector/RunSpaceInstance.cs b/ExchangeConnector/RunSpaceInstance.cs index c64b0c78..a9cfb9d1 100644 --- a/ExchangeConnector/RunSpaceInstance.cs +++ b/ExchangeConnector/RunSpaceInstance.cs @@ -33,8 +33,11 @@ namespace Org.IdentityConnectors.Exchange using System.Management.Automation; using System.Management.Automation.Runspaces; using System.Text; + using System.Linq; using Org.IdentityConnectors.Framework.Common.Exceptions; + using Org.IdentityConnectors.Framework.Common.Objects; + using Org.IdentityConnectors.Common; /// /// @@ -62,9 +65,14 @@ internal sealed class RunSpaceInstance : IDisposable private static readonly string ClassName = typeof(RunSpaceInstance).ToString(); /// - /// The exchange snap in name which needs to be loaded + /// The Exchange 2007 snap in name which needs to be loaded /// - private const string ExchangeSnapIn = "Microsoft.Exchange.Management.PowerShell.Admin"; + private const string Exchange2007SnapIn = "Microsoft.Exchange.Management.PowerShell.Admin"; + + /// + /// The Exchange 2010 snap in name which needs to be loaded + /// + private const string Exchange2010SnapIn = "Microsoft.Exchange.Management.PowerShell.E2010"; /// /// Instance variable keeping the @@ -80,13 +88,22 @@ internal sealed class RunSpaceInstance : IDisposable /// instance, managed resource /// private RunspaceInvoke runSpaceInvoke; + + /// + /// The catalog of localized messages. + /// + private ConnectorMessages _messageCatalog; /// /// Initializes a new instance of the class. /// /// Type of snapin to be loaded - public RunSpaceInstance(SnapIn snapin) + /// The message catalog used for conveying localized messages. + /// Thrown when is null. + public RunSpaceInstance(SnapIn snapin, ConnectorMessages messageCatalog) { + _messageCatalog = Assertions.NullChecked(messageCatalog, "messageCatalog"); + // initialize this this.InitRunSpace(snapin); } @@ -106,6 +123,15 @@ internal enum SnapIn /// Exchange } + + /// + /// Defines the various supported Exchange versions. + /// + private enum ExchangeVersion + { + E2007, + E2010 + } /// /// Test the state of this , throws if in incorrect state @@ -305,6 +331,7 @@ private void Dispose(bool disposing) } } } + _messageCatalog = null; } /// @@ -323,11 +350,24 @@ private void InitRunSpace(SnapIn snapin) switch (snapin) { case SnapIn.Exchange: - // used for force load of the exchange dll's - AppDomain.CurrentDomain.AssemblyResolve += - new ResolveEventHandler(ExchangeUtility.AssemblyResolver); - - this.runSpaceConfig.AddPSSnapIn(ExchangeSnapIn, out snapOutput); + var serverVersion = GetExchangeServerVersion(); + switch (serverVersion) + { + case ExchangeVersion.E2007: + // used for force load of the exchange dll's + AppDomain.CurrentDomain.AssemblyResolve += + new ResolveEventHandler(ExchangeUtility.AssemblyResolver2007); + + this.runSpaceConfig.AddPSSnapIn(Exchange2007SnapIn, out snapOutput); + break; + case ExchangeVersion.E2010: + // used for force load of the exchange dll's + AppDomain.CurrentDomain.AssemblyResolve += + new ResolveEventHandler(ExchangeUtility.AssemblyResolver2010); + + this.runSpaceConfig.AddPSSnapIn(Exchange2010SnapIn, out snapOutput); + break; + } break; } @@ -343,6 +383,81 @@ private void InitRunSpace(SnapIn snapin) this.runSpaceInvoke = new RunspaceInvoke(this.runSpace); Debug.WriteLine(MethodName + ":exit", ClassName); + } + + /// + /// Determines the version of the Exchange server. + /// + /// As the remote management functionality is not utilized, the Exchange powershell snap-in must be registered + /// on the local computer. Different snap-in is used to manage Exchange 2007 and 2010, hence the server version is determined by the + /// registered snap-in. + /// + /// The version of the Exchange server to manage. + /// Thrown when the version cannot be determined. + private ExchangeVersion GetExchangeServerVersion() + { + const string MethodName = "GetServerVersion"; + Debug.WriteLine(MethodName + ":entry", ClassName); + + const string ExchangeSnapinNamePrefix = "Microsoft.Exchange.Management.PowerShell."; + + ExchangeVersion? version = null; + using (var runspace = RunspaceFactory.CreateRunspace()) + { + runspace.Open(); + + using (var pipeline = runspace.CreatePipeline()) + { + var getSnapinsCommand = new Command("Get-PSSnapin"); + getSnapinsCommand.Parameters.Add("Registered"); + + pipeline.Commands.Add(getSnapinsCommand); + + var snapinList = pipeline.Invoke(); + + PipelineReader reader = pipeline.Error; + CheckErrors((IList)reader.ReadToEnd()); + + runspace.Close(); + + if ((snapinList == null) || (snapinList.Count == 0)) + { + Debug.WriteLine("No snap-in returned"); + throw new ConnectorException(_messageCatalog.Format("ex_NoPowerShellSnapins", "There are no registered PowerShell snap-ins.")); + } + + foreach (var snapin in snapinList) + { + if ((snapin.Properties["Name"] != null) && + (snapin.Properties["Name"].Value != null) && + (snapin.Properties["Name"].Value.ToString().StartsWith(ExchangeSnapinNamePrefix, + StringComparison.InvariantCultureIgnoreCase))) + { + var snapinName = snapin.Properties["Name"].Value.ToString(); + switch (snapinName.Substring(ExchangeSnapinNamePrefix.Length)) + { + case "Admin": + //Microsoft.Exchange.Management.PowerShell.Admin snap-in is used to manage Exchange 2007 + version = ExchangeVersion.E2007; + break; + case "E2010": + //Microsoft.Exchange.Management.PowerShell.E2010 snap-in is used to manage Exchange 2010 + version = ExchangeVersion.E2010; + break; + } + } + } + } + } + + if (!version.HasValue) + { + throw new ConnectorException(_messageCatalog.Format("ex_NoSupportedExchangeSnapin", + "There is no supported Exchange PowerShell snap-in registered.")); + } + + Debug.WriteLine(MethodName + ":exit", ClassName); + return version.Value; } } } From c156005fe1980b13417454d00dc8cf8d2d8cdc25 Mon Sep 17 00:00:00 2001 From: lajos_nagy Date: Wed, 31 Mar 2010 21:26:39 +0000 Subject: [PATCH 275/342] Issue #633: Method not found error while initializing Exchange connector --- ExchangeConnector/RunSpaceInstance.cs | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/ExchangeConnector/RunSpaceInstance.cs b/ExchangeConnector/RunSpaceInstance.cs index a9cfb9d1..ec0b1102 100644 --- a/ExchangeConnector/RunSpaceInstance.cs +++ b/ExchangeConnector/RunSpaceInstance.cs @@ -102,7 +102,8 @@ internal sealed class RunSpaceInstance : IDisposable /// Thrown when is null. public RunSpaceInstance(SnapIn snapin, ConnectorMessages messageCatalog) { - _messageCatalog = Assertions.NullChecked(messageCatalog, "messageCatalog"); + Assertions.NullCheck( messageCatalog, "messageCatalog" ); + _messageCatalog = messageCatalog; // initialize this this.InitRunSpace(snapin); From 30d1de071bd840f7082c8be8bd6631950a68f50f Mon Sep 17 00:00:00 2001 From: tknappek Date: Fri, 7 May 2010 11:33:09 +0000 Subject: [PATCH 276/342] Merge from framework 1.1: License update for 100d release - license.rtf removed, first installer screen updated to include oracle copyright and trademark, license agreement skipped in the installer --- ServiceInstall/File.top | 1 - ServiceInstall/ServiceInstall.wixproj | 4 +- ServiceInstall/Setup.wxs | 68 +++++++++++++++++++++++++-- ServiceInstall/license.rtf | 30 ------------ 4 files changed, 65 insertions(+), 38 deletions(-) delete mode 100644 ServiceInstall/license.rtf diff --git a/ServiceInstall/File.top b/ServiceInstall/File.top index c046613c..59c63874 100644 --- a/ServiceInstall/File.top +++ b/ServiceInstall/File.top @@ -19,4 +19,3 @@ - diff --git a/ServiceInstall/ServiceInstall.wixproj b/ServiceInstall/ServiceInstall.wixproj index df22d27f..a189c588 100644 --- a/ServiceInstall/ServiceInstall.wixproj +++ b/ServiceInstall/ServiceInstall.wixproj @@ -1,4 +1,4 @@ - - + + + + + + + + + + + + + + + + + + + 1 + + 1 + + 1 + 1 + 1 + 1 + + WixUI_InstallMode = "Change" + WixUI_InstallMode = "InstallCustom" + 1 + + WixUI_InstallMode = "InstallCustom" + WixUI_InstallMode = "InstallTypical" OR WixUI_InstallMode = "InstallComplete" + WixUI_InstallMode = "Change" + WixUI_InstallMode = "Repair" OR WixUI_InstallMode = "Remove" + + 1 + + 1 + 1 + 1 + 1 + + + + + 1 + + + + + + + + + + + + NOT Installed + + diff --git a/ServiceInstall/license.rtf b/ServiceInstall/license.rtf deleted file mode 100644 index 5ecc7cf2..00000000 --- a/ServiceInstall/license.rtf +++ /dev/null @@ -1,30 +0,0 @@ -{\rtf1\ansi\deff0\adeflang1025 -{\fonttbl{\f0\froman\fprq2\fcharset0 Thorndale{\*\falt Times New Roman};}{\f1\froman\fprq2\fcharset0 Thorndale{\*\falt Times New Roman};}{\f2\fswiss\fprq2\fcharset0 Albany{\*\falt Arial};}{\f3\fnil\fprq2\fcharset0 Andale Sans UI{\*\falt Arial Unicode MS};}{\f4\fnil\fprq2\fcharset0 Tahoma;}{\f5\fnil\fprq0\fcharset0 Tahoma;}} -{\colortbl;\red0\green0\blue0;\red128\green128\blue128;} -{\stylesheet{\s1\cf0{\*\hyphen2\hyphlead2\hyphtrail2\hyphmax0}\rtlch\af4\afs24\lang255\ltrch\dbch\af3\langfe255\hich\f0\fs24\lang1033\loch\f0\fs24\lang1033\snext1 Normal;} -{\s2\sb240\sa120\keepn\cf0{\*\hyphen2\hyphlead2\hyphtrail2\hyphmax0}\rtlch\afs28\lang255\ltrch\dbch\langfe255\hich\f2\fs28\lang1033\loch\f2\fs28\lang1033\sbasedon1\snext3 Heading;} -{\s3\sa120\cf0{\*\hyphen2\hyphlead2\hyphtrail2\hyphmax0}\rtlch\af4\afs24\lang255\ltrch\dbch\af3\langfe255\hich\f0\fs24\lang1033\loch\f0\fs24\lang1033\sbasedon1\snext3 Body Text;} -{\s4\sa120\cf0{\*\hyphen2\hyphlead2\hyphtrail2\hyphmax0}\rtlch\af5\afs24\lang255\ltrch\dbch\af3\langfe255\hich\f0\fs24\lang1033\loch\f0\fs24\lang1033\sbasedon3\snext4 List;} -{\s5\sb120\sa120\cf0{\*\hyphen2\hyphlead2\hyphtrail2\hyphmax0}\rtlch\af5\afs24\lang255\ai\ltrch\dbch\af3\langfe255\hich\f0\fs24\lang1033\i\loch\f0\fs24\lang1033\i\sbasedon1\snext5 caption;} -{\s6\cf0{\*\hyphen2\hyphlead2\hyphtrail2\hyphmax0}\rtlch\af5\afs24\lang255\ltrch\dbch\af3\langfe255\hich\f0\fs24\lang1033\loch\f0\fs24\lang1033\sbasedon1\snext6 Index;} -} -{\info{\author William Droste}{\creatim\yr2008\mo8\dy7\hr10\min50}{\revtim\yr0\mo0\dy0\hr0\min0}{\printim\yr0\mo0\dy0\hr0\min0}{\comment StarWriter}{\vern6800}}\deftab709 -{\*\pgdsctbl -{\pgdsc0\pgdscuse195\pgwsxn12240\pghsxn15840\marglsxn1134\margrsxn1134\margtsxn1134\margbsxn1134\pgdscnxt0 Standard;}} -\paperh15840\paperw12240\margl1134\margr1134\margt1134\margb1134\sectd\sbknone\pgwsxn12240\pghsxn15840\marglsxn1134\margrsxn1134\margtsxn1134\margbsxn1134\ftnbj\ftnstart1\ftnrstcont\ftnnar\aenddoc\aftnrstcont\aftnstart1\aftnnrlc -\pard\plain \ltrpar\s1\cf0{\*\hyphen2\hyphlead2\hyphtrail2\hyphmax0}\rtlch\af4\afs24\lang255\ltrch\dbch\af3\langfe255\hich\f0\fs24\lang1033\loch\f0\fs24\lang1033 {\rtlch \ltrch\loch\f0\fs24\lang1033\i0\b0 Copyright \'a9 2008 Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, California 95054, U.S.A. All rights reserved.} -\par \pard\plain \ltrpar\s1\cf0{\*\hyphen2\hyphlead2\hyphtrail2\hyphmax0}\rtlch\af4\afs24\lang255\ltrch\dbch\af3\langfe255\hich\f0\fs24\lang1033\loch\f0\fs24\lang1033 -\par \pard\plain \ltrpar\s1\cf0{\*\hyphen2\hyphlead2\hyphtrail2\hyphmax0}\rtlch\af4\afs24\lang255\ltrch\dbch\af3\langfe255\hich\f0\fs24\lang1033\loch\f0\fs24\lang1033 {\rtlch \ltrch\loch }{\rtlch \ltrch\loch\f0\fs24\lang1033\i0\b0 U.S. Government Rights - Commercial software. Government users are subject to the Sun Microsystems, Inc. standard license agreement and applicable provisions of the FAR and its supplements.} -\par \pard\plain \ltrpar\s1\cf0{\*\hyphen2\hyphlead2\hyphtrail2\hyphmax0}\rtlch\af4\afs24\lang255\ltrch\dbch\af3\langfe255\hich\f0\fs24\lang1033\loch\f0\fs24\lang1033 -\par \pard\plain \ltrpar\s1\cf0{\*\hyphen2\hyphlead2\hyphtrail2\hyphmax0}\rtlch\af4\afs24\lang255\ltrch\dbch\af3\langfe255\hich\f0\fs24\lang1033\loch\f0\fs24\lang1033 {\rtlch \ltrch\loch }{\rtlch \ltrch\loch\f0\fs24\lang1033\i0\b0 Use is subject to license terms.} -\par \pard\plain \ltrpar\s1\cf0{\*\hyphen2\hyphlead2\hyphtrail2\hyphmax0}\rtlch\af4\afs24\lang255\ltrch\dbch\af3\langfe255\hich\f0\fs24\lang1033\loch\f0\fs24\lang1033 -\par \pard\plain \ltrpar\s1\cf0{\*\hyphen2\hyphlead2\hyphtrail2\hyphmax0}\rtlch\af4\afs24\lang255\ltrch\dbch\af3\langfe255\hich\f0\fs24\lang1033\loch\f0\fs24\lang1033 {\rtlch \ltrch\loch }{\rtlch \ltrch\loch\f0\fs24\lang1033\i0\b0 This distribution may include materials developed by third parties.} -\par \pard\plain \ltrpar\s1\cf0{\*\hyphen2\hyphlead2\hyphtrail2\hyphmax0}\rtlch\af4\afs24\lang255\ltrch\dbch\af3\langfe255\hich\f0\fs24\lang1033\loch\f0\fs24\lang1033 -\par \pard\plain \ltrpar\s1\cf0{\*\hyphen2\hyphlead2\hyphtrail2\hyphmax0}\rtlch\af4\afs24\lang255\ltrch\dbch\af3\langfe255\hich\f0\fs24\lang1033\loch\f0\fs24\lang1033 {\rtlch \ltrch\loch }{\rtlch \ltrch\loch\f0\fs24\lang1033\i0\b0 Sun, Sun Microsystems, the Sun logo, Java and Project Identity Connectors are trademarks or registered trademarks of Sun Microsystems, Inc. in the U.S. and other countries.} -\par \pard\plain \ltrpar\s1\cf0{\*\hyphen2\hyphlead2\hyphtrail2\hyphmax0}\rtlch\af4\afs24\lang255\ltrch\dbch\af3\langfe255\hich\f0\fs24\lang1033\loch\f0\fs24\lang1033 -\par \pard\plain \ltrpar\s1\cf0{\*\hyphen2\hyphlead2\hyphtrail2\hyphmax0}\rtlch\af4\afs24\lang255\ltrch\dbch\af3\langfe255\hich\f0\fs24\lang1033\loch\f0\fs24\lang1033 {\rtlch \ltrch\loch }{\rtlch \ltrch\loch\f0\fs24\lang1033\i0\b0 UNIX is a registered trademark in the U.S. and other countries, exclusively licensed through X/Open Company, Ltd.} -\par \pard\plain \ltrpar\s1\cf0{\*\hyphen2\hyphlead2\hyphtrail2\hyphmax0}\rtlch\af4\afs24\lang255\ltrch\dbch\af3\langfe255\hich\f0\fs24\lang1033\loch\f0\fs24\lang1033 -\par \pard\plain \ltrpar\s1\cf0{\*\hyphen2\hyphlead2\hyphtrail2\hyphmax0}\rtlch\af4\afs24\lang255\ltrch\dbch\af3\langfe255\hich\f0\fs24\lang1033\loch\f0\fs24\lang1033 {\rtlch \ltrch\loch }{\rtlch \ltrch\loch\f0\fs24\lang1033\i0\b0 This product is covered and controlled by U.S. Export Control laws and may be subject to the export or import laws in other countries. Nuclear, missile, chemical biological weapons or nuclear maritime end uses or end users, whether direct or indirec -t, are strictly prohibited. Export or reexport to countries subject to U.S. embargo or to entities identified on U.S. export exclusion lists, including, but not limited to, the denied persons and specially designated nationals lists is strictly prohibited. - } -\par } \ No newline at end of file From 8c94ba8f2a8889fbb6f6a66c0ae2642b052f1807 Mon Sep 17 00:00:00 2001 From: mikro Date: Fri, 15 Apr 2011 10:12:46 +0000 Subject: [PATCH 277/342] - Remove NUnit.Framework.SytaxHelpers nammespace usings --- FrameworkTests/CollectionUtilTests.cs | 1 - FrameworkTests/ConnectorInfoManagerTests.cs | 1 - FrameworkTests/FilterTranslatorTests.cs | 1 - FrameworkTests/GuardedByteArrayTests.cs | 1 - FrameworkTests/GuardedStringTests.cs | 1 - FrameworkTests/LocaleTests.cs | 1 - FrameworkTests/ObjectNormalizerFacadeTests.cs | 1 - FrameworkTests/ObjectPoolTests.cs | 1 - FrameworkTests/ObjectSerializationTests.cs | 1 - FrameworkTests/ProxyTests.cs | 1 - FrameworkTests/SafeTypeTest.cs | 1 - FrameworkTests/ScriptTests.cs | 1 - FrameworkTests/UpdateImplTests.cs | 1 - ServiceInstall/ServiceInstall.wixproj | 9 ++++++--- 14 files changed, 6 insertions(+), 16 deletions(-) diff --git a/FrameworkTests/CollectionUtilTests.cs b/FrameworkTests/CollectionUtilTests.cs index 9f4c74ae..4fd40478 100644 --- a/FrameworkTests/CollectionUtilTests.cs +++ b/FrameworkTests/CollectionUtilTests.cs @@ -23,7 +23,6 @@ using System; using System.Collections.Generic; using NUnit.Framework; -using NUnit.Framework.SyntaxHelpers; using Org.IdentityConnectors.Common; namespace FrameworkTests { diff --git a/FrameworkTests/ConnectorInfoManagerTests.cs b/FrameworkTests/ConnectorInfoManagerTests.cs index 67bfb711..5d6095bd 100644 --- a/FrameworkTests/ConnectorInfoManagerTests.cs +++ b/FrameworkTests/ConnectorInfoManagerTests.cs @@ -22,7 +22,6 @@ */ using System; using NUnit.Framework; -using NUnit.Framework.SyntaxHelpers; using System.Collections.Generic; using System.Diagnostics; using Org.IdentityConnectors.Common; diff --git a/FrameworkTests/FilterTranslatorTests.cs b/FrameworkTests/FilterTranslatorTests.cs index da942314..9cd7e45d 100644 --- a/FrameworkTests/FilterTranslatorTests.cs +++ b/FrameworkTests/FilterTranslatorTests.cs @@ -23,7 +23,6 @@ using System; using System.Collections.Generic; using NUnit.Framework; -using NUnit.Framework.SyntaxHelpers; using Org.IdentityConnectors.Common; using Org.IdentityConnectors.Framework.Common.Objects; using Org.IdentityConnectors.Framework.Common.Objects.Filters; diff --git a/FrameworkTests/GuardedByteArrayTests.cs b/FrameworkTests/GuardedByteArrayTests.cs index 17f05c48..18498d25 100644 --- a/FrameworkTests/GuardedByteArrayTests.cs +++ b/FrameworkTests/GuardedByteArrayTests.cs @@ -27,7 +27,6 @@ using Org.IdentityConnectors.Common.Security; using Org.IdentityConnectors.Framework.Common.Serializer; using NUnit.Framework; -using NUnit.Framework.SyntaxHelpers; namespace FrameworkTests { diff --git a/FrameworkTests/GuardedStringTests.cs b/FrameworkTests/GuardedStringTests.cs index 75cd8a41..34c66231 100644 --- a/FrameworkTests/GuardedStringTests.cs +++ b/FrameworkTests/GuardedStringTests.cs @@ -27,7 +27,6 @@ using Org.IdentityConnectors.Common.Security; using Org.IdentityConnectors.Framework.Common.Serializer; using NUnit.Framework; -using NUnit.Framework.SyntaxHelpers; namespace FrameworkTests { diff --git a/FrameworkTests/LocaleTests.cs b/FrameworkTests/LocaleTests.cs index 3de09b88..f352bb2e 100644 --- a/FrameworkTests/LocaleTests.cs +++ b/FrameworkTests/LocaleTests.cs @@ -22,7 +22,6 @@ */ using System; using NUnit.Framework; -using NUnit.Framework.SyntaxHelpers; using System.Globalization; using System.Collections.Generic; using Org.IdentityConnectors.Common; diff --git a/FrameworkTests/ObjectNormalizerFacadeTests.cs b/FrameworkTests/ObjectNormalizerFacadeTests.cs index f9a75d10..d92f5282 100644 --- a/FrameworkTests/ObjectNormalizerFacadeTests.cs +++ b/FrameworkTests/ObjectNormalizerFacadeTests.cs @@ -22,7 +22,6 @@ */ using System; using NUnit.Framework; -using NUnit.Framework.SyntaxHelpers; using Org.IdentityConnectors.Framework.Common.Objects; using Org.IdentityConnectors.Framework.Common.Objects.Filters; using Org.IdentityConnectors.Framework.Common.Serializer; diff --git a/FrameworkTests/ObjectPoolTests.cs b/FrameworkTests/ObjectPoolTests.cs index a4003051..b4832be5 100644 --- a/FrameworkTests/ObjectPoolTests.cs +++ b/FrameworkTests/ObjectPoolTests.cs @@ -23,7 +23,6 @@ using System; using System.Threading; using NUnit.Framework; -using NUnit.Framework.SyntaxHelpers; using Org.IdentityConnectors.Common.Pooling; using Org.IdentityConnectors.Framework.Common.Exceptions; diff --git a/FrameworkTests/ObjectSerializationTests.cs b/FrameworkTests/ObjectSerializationTests.cs index 02746c14..0985f6a3 100644 --- a/FrameworkTests/ObjectSerializationTests.cs +++ b/FrameworkTests/ObjectSerializationTests.cs @@ -24,7 +24,6 @@ using System.IO; using System.Text; using NUnit.Framework; -using NUnit.Framework.SyntaxHelpers; using System.Collections.Generic; using System.Globalization; using System.Security; diff --git a/FrameworkTests/ProxyTests.cs b/FrameworkTests/ProxyTests.cs index 1714f546..568a7af5 100644 --- a/FrameworkTests/ProxyTests.cs +++ b/FrameworkTests/ProxyTests.cs @@ -22,7 +22,6 @@ */ using System; using NUnit.Framework; -using NUnit.Framework.SyntaxHelpers; using System.Reflection; using System.Reflection.Emit; using Org.IdentityConnectors.Common.Proxy; diff --git a/FrameworkTests/SafeTypeTest.cs b/FrameworkTests/SafeTypeTest.cs index 23f8a5d0..91a4f0ae 100644 --- a/FrameworkTests/SafeTypeTest.cs +++ b/FrameworkTests/SafeTypeTest.cs @@ -22,7 +22,6 @@ */ using System; using NUnit.Framework; -using NUnit.Framework.SyntaxHelpers; using Org.IdentityConnectors.Common; using Org.IdentityConnectors.Framework.Api.Operations; namespace FrameworkTests diff --git a/FrameworkTests/ScriptTests.cs b/FrameworkTests/ScriptTests.cs index 2d6ef102..0fe07410 100644 --- a/FrameworkTests/ScriptTests.cs +++ b/FrameworkTests/ScriptTests.cs @@ -27,7 +27,6 @@ using Org.IdentityConnectors.Common.Script; using Org.IdentityConnectors.Framework.Common.Objects; using NUnit.Framework; -using NUnit.Framework.SyntaxHelpers; namespace FrameworkTests { diff --git a/FrameworkTests/UpdateImplTests.cs b/FrameworkTests/UpdateImplTests.cs index 2bce2501..d64b1b5c 100644 --- a/FrameworkTests/UpdateImplTests.cs +++ b/FrameworkTests/UpdateImplTests.cs @@ -23,7 +23,6 @@ using System; using System.Security; using NUnit.Framework; -using NUnit.Framework.SyntaxHelpers; using System.Collections.Generic; using Org.IdentityConnectors.Common; using Org.IdentityConnectors.Framework.Api; diff --git a/ServiceInstall/ServiceInstall.wixproj b/ServiceInstall/ServiceInstall.wixproj index a189c588..562d11d2 100644 --- a/ServiceInstall/ServiceInstall.wixproj +++ b/ServiceInstall/ServiceInstall.wixproj @@ -1,4 +1,4 @@ - + $(MSBuildProjectDirectory) @@ -73,11 +74,11 @@ diff --git a/legal/CDDLv1.txt b/legal/CDDLv1.txt new file mode 100644 index 00000000..1717cf28 --- /dev/null +++ b/legal/CDDLv1.txt @@ -0,0 +1,383 @@ + + +COMMON DEVELOPMENT AND DISTRIBUTION LICENSE (CDDL) Version 1.0 + +1. Definitions. + +1.1. "Contributor" means each individual or entity that +creates or contributes to the creation of Modifications. + +1.2. "Contributor Version" means the combination of the +Original Software, prior Modifications used by a +Contributor (if any), and the Modifications made by that +particular Contributor. + +1.3. "Covered Software" means (a) the Original Software, or +(b) Modifications, or (c) the combination of files +containing Original Software with files containing +Modifications, in each case including portions thereof. + +1.4. "Executable" means the Covered Software in any form +other than Source Code. + +1.5. "Initial Developer" means the individual or entity +that first makes Original Software available under this +License. + +1.6. "Larger Work" means a work which combines Covered +Software or portions thereof with code not governed by the +terms of this License. + +1.7. "License" means this document. + +1.8. "Licensable" means having the right to grant, to the +maximum extent possible, whether at the time of the initial +grant or subsequently acquired, any and all of the rights +conveyed herein. + +1.9. "Modifications" means the Source Code and Executable +form of any of the following: + +A. Any file that results from an addition to, +deletion from or modification of the contents of a +file containing Original Software or previous +Modifications; + +B. Any new file that contains any part of the +Original Software or previous Modification; or + +C. Any new file that is contributed or otherwise made +available under the terms of this License. + +1.10. "Original Software" means the Source Code and +Executable form of computer software code that is +originally released under this License. + +1.11. "Patent Claims" means any patent claim(s), now owned +or hereafter acquired, including without limitation, +method, process, and apparatus claims, in any patent +Licensable by grantor. + +1.12. "Source Code" means (a) the common form of computer +software code in which modifications are made and (b) +associated documentation included in or with such code. + +1.13. "You" (or "Your") means an individual or a legal +entity exercising rights under, and complying with all of +the terms of, this License. For legal entities, "You" +includes any entity which controls, is controlled by, or is +under common control with You. For purposes of this +definition, "control" means (a) the power, direct or +indirect, to cause the direction or management of such +entity, whether by contract or otherwise, or (b) ownership +of more than fifty percent (50%) of the outstanding shares +or beneficial ownership of such entity. + +2. License Grants. + +2.1. The Initial Developer Grant. + +Conditioned upon Your compliance with Section 3.1 below and +subject to third party intellectual property claims, the +Initial Developer hereby grants You a world-wide, +royalty-free, non-exclusive license: + +(a) under intellectual property rights (other than +patent or trademark) Licensable by Initial Developer, +to use, reproduce, modify, display, perform, +sublicense and distribute the Original Software (or +portions thereof), with or without Modifications, +and/or as part of a Larger Work; and + +(b) under Patent Claims infringed by the making, +using or selling of Original Software, to make, have +made, use, practice, sell, and offer for sale, and/or +otherwise dispose of the Original Software (or +portions thereof). + +(c) The licenses granted in Sections 2.1(a) and (b) +are effective on the date Initial Developer first +distributes or otherwise makes the Original Software +available to a third party under the terms of this +License. + +(d) Notwithstanding Section 2.1(b) above, no patent +license is granted: (1) for code that You delete from +the Original Software, or (2) for infringements +caused by: (i) the modification of the Original +Software, or (ii) the combination of the Original +Software with other software or devices. + +2.2. Contributor Grant. + +Conditioned upon Your compliance with Section 3.1 below and +subject to third party intellectual property claims, each +Contributor hereby grants You a world-wide, royalty-free, +non-exclusive license: + +(a) under intellectual property rights (other than +patent or trademark) Licensable by Contributor to +use, reproduce, modify, display, perform, sublicense +and distribute the Modifications created by such +Contributor (or portions thereof), either on an +unmodified basis, with other Modifications, as +Covered Software and/or as part of a Larger Work; and + +(b) under Patent Claims infringed by the making, +using, or selling of Modifications made by that +Contributor either alone and/or in combination with +its Contributor Version (or portions of such +combination), to make, use, sell, offer for sale, +have made, and/or otherwise dispose of: (1) +Modifications made by that Contributor (or portions +thereof); and (2) the combination of Modifications +made by that Contributor with its Contributor Version +(or portions of such combination). + +(c) The licenses granted in Sections 2.2(a) and +2.2(b) are effective on the date Contributor first +distributes or otherwise makes the Modifications +available to a third party. + +(d) Notwithstanding Section 2.2(b) above, no patent +license is granted: (1) for any code that Contributor +has deleted from the Contributor Version; (2) for +infringements caused by: (i) third party +modifications of Contributor Version, or (ii) the +combination of Modifications made by that Contributor +with other software (except as part of the +Contributor Version) or other devices; or (3) under +Patent Claims infringed by Covered Software in the +absence of Modifications made by that Contributor. + +3. Distribution Obligations. + +3.1. Availability of Source Code. + +Any Covered Software that You distribute or otherwise make +available in Executable form must also be made available in +Source Code form and that Source Code form must be +distributed only under the terms of this License. You must +include a copy of this License with every copy of the +Source Code form of the Covered Software You distribute or +otherwise make available. You must inform recipients of any +such Covered Software in Executable form as to how they can +obtain such Covered Software in Source Code form in a +reasonable manner on or through a medium customarily used +for software exchange. + +3.2. Modifications. + +The Modifications that You create or to which You +contribute are governed by the terms of this License. You +represent that You believe Your Modifications are Your +original creation(s) and/or You have sufficient rights to +grant the rights conveyed by this License. + +3.3. Required Notices. + +You must include a notice in each of Your Modifications +that identifies You as the Contributor of the Modification. +You may not remove or alter any copyright, patent or +trademark notices contained within the Covered Software, or +any notices of licensing or any descriptive text giving +attribution to any Contributor or the Initial Developer. + +3.4. Application of Additional Terms. + +You may not offer or impose any terms on any Covered +Software in Source Code form that alters or restricts the +applicable version of this License or the recipients' +rights hereunder. You may choose to offer, and to charge a +fee for, warranty, support, indemnity or liability +obligations to one or more recipients of Covered Software. +However, you may do so only on Your own behalf, and not on +behalf of the Initial Developer or any Contributor. You +must make it absolutely clear that any such warranty, +support, indemnity or liability obligation is offered by +You alone, and You hereby agree to indemnify the Initial +Developer and every Contributor for any liability incurred +by the Initial Developer or such Contributor as a result of +warranty, support, indemnity or liability terms You offer. + +3.5. Distribution of Executable Versions. + +You may distribute the Executable form of the Covered +Software under the terms of this License or under the terms +of a license of Your choice, which may contain terms +different from this License, provided that You are in +compliance with the terms of this License and that the +license for the Executable form does not attempt to limit +or alter the recipient's rights in the Source Code form +from the rights set forth in this License. If You +distribute the Covered Software in Executable form under a +different license, You must make it absolutely clear that +any terms which differ from this License are offered by You +alone, not by the Initial Developer or Contributor. You +hereby agree to indemnify the Initial Developer and every +Contributor for any liability incurred by the Initial +Developer or such Contributor as a result of any such terms +You offer. + +3.6. Larger Works. + +You may create a Larger Work by combining Covered Software +with other code not governed by the terms of this License +and distribute the Larger Work as a single product. In such +a case, You must make sure the requirements of this License +are fulfilled for the Covered Software. + +4. Versions of the License. + +4.1. New Versions. + +Sun Microsystems, Inc. is the initial license steward and +may publish revised and/or new versions of this License +from time to time. Each version will be given a +distinguishing version number. Except as provided in +Section 4.3, no one other than the license steward has the +right to modify this License. + +4.2. Effect of New Versions. + +You may always continue to use, distribute or otherwise +make the Covered Software available under the terms of the +version of the License under which You originally received +the Covered Software. If the Initial Developer includes a +notice in the Original Software prohibiting it from being +distributed or otherwise made available under any +subsequent version of the License, You must distribute and +make the Covered Software available under the terms of the +version of the License under which You originally received +the Covered Software. Otherwise, You may also choose to +use, distribute or otherwise make the Covered Software +available under the terms of any subsequent version of the +License published by the license steward. + +4.3. Modified Versions. + +When You are an Initial Developer and You want to create a +new license for Your Original Software, You may create and +use a modified version of this License if You: (a) rename +the license and remove any references to the name of the +license steward (except to note that the license differs +from this License); and (b) otherwise make it clear that +the license contains terms which differ from this License. + +5. DISCLAIMER OF WARRANTY. + +COVERED SOFTWARE IS PROVIDED UNDER THIS LICENSE ON AN "AS IS" +BASIS, WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED OR IMPLIED, +INCLUDING, WITHOUT LIMITATION, WARRANTIES THAT THE COVERED +SOFTWARE IS FREE OF DEFECTS, MERCHANTABLE, FIT FOR A PARTICULAR +PURPOSE OR NON-INFRINGING. THE ENTIRE RISK AS TO THE QUALITY AND +PERFORMANCE OF THE COVERED SOFTWARE IS WITH YOU. SHOULD ANY +COVERED SOFTWARE PROVE DEFECTIVE IN ANY RESPECT, YOU (NOT THE +INITIAL DEVELOPER OR ANY OTHER CONTRIBUTOR) ASSUME THE COST OF +ANY NECESSARY SERVICING, REPAIR OR CORRECTION. THIS DISCLAIMER OF +WARRANTY CONSTITUTES AN ESSENTIAL PART OF THIS LICENSE. NO USE OF +ANY COVERED SOFTWARE IS AUTHORIZED HEREUNDER EXCEPT UNDER THIS +DISCLAIMER. + +6. TERMINATION. + +6.1. This License and the rights granted hereunder will +terminate automatically if You fail to comply with terms +herein and fail to cure such breach within 30 days of +becoming aware of the breach. Provisions which, by their +nature, must remain in effect beyond the termination of +this License shall survive. + +6.2. If You assert a patent infringement claim (excluding +declaratory judgment actions) against Initial Developer or +a Contributor (the Initial Developer or Contributor against +whom You assert such claim is referred to as "Participant") +alleging that the Participant Software (meaning the +Contributor Version where the Participant is a Contributor +or the Original Software where the Participant is the +Initial Developer) directly or indirectly infringes any +patent, then any and all rights granted directly or +indirectly to You by such Participant, the Initial +Developer (if the Initial Developer is not the Participant) +and all Contributors under Sections 2.1 and/or 2.2 of this +License shall, upon 60 days notice from Participant +terminate prospectively and automatically at the expiration +of such 60 day notice period, unless if within such 60 day +period You withdraw Your claim with respect to the +Participant Software against such Participant either +unilaterally or pursuant to a written agreement with +Participant. + +6.3. In the event of termination under Sections 6.1 or 6.2 +above, all end user licenses that have been validly granted +by You or any distributor hereunder prior to termination +(excluding licenses granted to You by any distributor) +shall survive termination. + +7. LIMITATION OF LIABILITY. + +UNDER NO CIRCUMSTANCES AND UNDER NO LEGAL THEORY, WHETHER TORT +(INCLUDING NEGLIGENCE), CONTRACT, OR OTHERWISE, SHALL YOU, THE +INITIAL DEVELOPER, ANY OTHER CONTRIBUTOR, OR ANY DISTRIBUTOR OF +COVERED SOFTWARE, OR ANY SUPPLIER OF ANY OF SUCH PARTIES, BE +LIABLE TO ANY PERSON FOR ANY INDIRECT, SPECIAL, INCIDENTAL, OR +CONSEQUENTIAL DAMAGES OF ANY CHARACTER INCLUDING, WITHOUT +LIMITATION, DAMAGES FOR LOST PROFITS, LOSS OF GOODWILL, WORK +STOPPAGE, COMPUTER FAILURE OR MALFUNCTION, OR ANY AND ALL OTHER +COMMERCIAL DAMAGES OR LOSSES, EVEN IF SUCH PARTY SHALL HAVE BEEN +INFORMED OF THE POSSIBILITY OF SUCH DAMAGES. THIS LIMITATION OF +LIABILITY SHALL NOT APPLY TO LIABILITY FOR DEATH OR PERSONAL +INJURY RESULTING FROM SUCH PARTY'S NEGLIGENCE TO THE EXTENT +APPLICABLE LAW PROHIBITS SUCH LIMITATION. SOME JURISDICTIONS DO +NOT ALLOW THE EXCLUSION OR LIMITATION OF INCIDENTAL OR +CONSEQUENTIAL DAMAGES, SO THIS EXCLUSION AND LIMITATION MAY NOT +APPLY TO YOU. + +8. U.S. GOVERNMENT END USERS. + +The Covered Software is a "commercial item," as that term is +defined in 48 C.F.R. 2.101 (Oct. 1995), consisting of "commercial +computer software" (as that term is defined at 48 C.F.R. $ +252.227-7014(a)(1)) and "commercial computer software +documentation" as such terms are used in 48 C.F.R. 12.212 (Sept. +1995). Consistent with 48 C.F.R. 12.212 and 48 C.F.R. 227.7202-1 +through 227.7202-4 (June 1995), all U.S. Government End Users +acquire Covered Software with only those rights set forth herein. +This U.S. Government Rights clause is in lieu of, and supersedes, +any other FAR, DFAR, or other clause or provision that addresses +Government rights in computer software under this License. + +9. MISCELLANEOUS. + +This License represents the complete agreement concerning subject +matter hereof. If any provision of this License is held to be +unenforceable, such provision shall be reformed only to the +extent necessary to make it enforceable. This License shall be +governed by the law of the jurisdiction specified in a notice +contained within the Original Software (except to the extent +applicable law, if any, provides otherwise), excluding such +jurisdiction's conflict-of-law provisions. Any litigation +relating to this License shall be subject to the jurisdiction of +the courts located in the jurisdiction and venue specified in a +notice contained within the Original Software, with the losing +party responsible for costs, including, without limitation, court +costs and reasonable attorneys' fees and expenses. The +application of the United Nations Convention on Contracts for the +International Sale of Goods is expressly excluded. Any law or +regulation which provides that the language of a contract shall +be construed against the drafter shall not apply to this License. +You agree that You alone are responsible for compliance with the +United States export administration regulations (and the export +control laws and regulation of any other countries) when You use, +distribute or otherwise make available any Covered Software. + +10. RESPONSIBILITY FOR CLAIMS. + +As between Initial Developer and the Contributors, each party is +responsible for claims and damages arising, directly or +indirectly, out of its utilization of rights under this License +and You agree to work with Initial Developer and Contributors to +distribute such responsibility on an equitable basis. Nothing +herein is intended or shall be deemed to constitute any admission +of liability. + diff --git a/legal/THIRDPARTYREADME.txt b/legal/THIRDPARTYREADME.txt new file mode 100644 index 00000000..797aa123 --- /dev/null +++ b/legal/THIRDPARTYREADME.txt @@ -0,0 +1,3591 @@ +DO NOT TRANSLATE OR LOCALIZE +*************************************************************************** +%%The following software may be included in this product: +Groovy Scripting Language +Use of any of this software is governed by the terms of the license below: +Apache License +Version 2.0, January 2004 +http://www.apache.org/licenses/ + +TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION + +1. Definitions. + +"License" shall mean the terms and conditions for use, reproduction, and +distribution as defined by Sections 1 through 9 of this document. + +"Licensor" shall mean the copyright owner or entity authorized by the copyright +owner that is granting the License. + +"Legal Entity" shall mean the union of the acting entity and all other entities +that control, are controlled by, or are under common control with that entity. +For the purposes of this definition, "control" means (i) the power, direct or +indirect, to cause the direction or management of such entity, whether by +contract or otherwise, or (ii) ownership of fifty percent (50%) or more of the +outstanding shares, or (iii) beneficial ownership of such entity. + +"You" (or "Your") shall mean an individual or Legal Entity exercising +permissions granted by this License. + +"Source" form shall mean the preferred form for making modifications, including +but not limited to software source code, documentation source, and configuration +files. + +"Object" form shall mean any form resulting from mechanical transformation or +translation of a Source form, including but not limited to compiled object code, +generated documentation, and conversions to other media types. + +"Work" shall mean the work of authorship, whether in Source or Object form, made +available under the License, as indicated by a copyright notice that is included +in or attached to the work (an example is provided in the Appendix below). + +"Derivative Works" shall mean any work, whether in Source or Object form, that +is based on (or derived from) the Work and for which the editorial revisions, +annotations, elaborations, or other modifications represent, as a whole, an +original work of authorship. For the purposes of this License, Derivative Works +shall not include works that remain separable from, or merely link (or bind by +name) to the interfaces of, the Work and Derivative Works thereof. + +"Contribution" shall mean any work of authorship, including the original version +of the Work and any modifications or additions to that Work or Derivative Works +thereof, that is intentionally submitted to Licensor for inclusion in the Work +by the copyright owner or by an individual or Legal Entity authorized to submit +on behalf of the copyright owner. For the purposes of this definition, +"submitted" means any form of electronic, verbal, or written communication sent +to the Licensor or its representatives, including but not limited to +communication on electronic mailing lists, source code control systems, and +issue tracking systems that are managed by, or on behalf of, the Licensor for +the purpose of discussing and improving the Work, but excluding communication +that is conspicuously marked or otherwise designated in writing by the copyright +owner as "Not a Contribution." + +"Contributor" shall mean Licensor and any individual or Legal Entity on behalf +of whom a Contribution has been received by Licensor and subsequently +incorporated within the Work. + +2. Grant of Copyright License. Subject to the terms and conditions of this +License, each Contributor hereby grants to You a perpetual, worldwide, +non-exclusive, no-charge, royalty-free, irrevocable copyright license to +reproduce, prepare Derivative Works of, publicly display, publicly perform, +sublicense, and distribute the Work and such Derivative Works in Source or +Object form. + +3. Grant of Patent License. Subject to the terms and conditions of this License, +each Contributor hereby grants to You a perpetual, worldwide, non-exclusive, +no-charge, royalty-free, irrevocable (except as stated in this section) patent +license to make, have made, use, offer to sell, sell, import, and otherwise +transfer the Work, where such license applies only to those patent claims +licensable by such Contributor that are necessarily infringed by their +Contribution(s) alone or by combination of their Contribution(s) with the Work +to which such Contribution(s) was submitted. If You institute patent litigation +against any entity (including a cross-claim or counterclaim in a lawsuit) +alleging that the Work or a Contribution incorporated within the Work +constitutes direct or contributory patent infringement, then any patent licenses +granted to You under this License for that Work shall terminate as of the date +such litigation is filed. + +4. Redistribution. You may reproduce and distribute copies of the Work or +Derivative Works thereof in any medium, with or without modifications, and in +Source or Object form, provided that You meet the following conditions: + +1. You must give any other recipients of the Work or Derivative Works a copy +of this License; and + +2. You must cause any modified files to carry prominent notices stating that +You changed the files; and + +3. You must retain, in the Source form of any Derivative Works that You +distribute, all copyright, patent, trademark, and attribution notices from the +Source form of the Work, excluding those notices that do not pertain to any part +of the Derivative Works; and + +4. If the Work includes a "NOTICE" text file as part of its distribution, +then any Derivative Works that You distribute must include a readable copy of +the attribution notices contained within such NOTICE file, excluding those +notices that do not pertain to any part of the Derivative Works, in at least one +of the following places: within a NOTICE text file distributed as part of the +Derivative Works; within the Source form or documentation, if provided along +with the Derivative Works; or, within a display generated by the Derivative +Works, if and wherever such third-party notices normally appear. The contents of +the NOTICE file are for informational purposes only and do not modify the +License. You may add Your own attribution notices within Derivative Works that +You distribute, alongside or as an addendum to the NOTICE text from the Work, +provided that such additional attribution notices cannot be construed as +modifying the License. + +You may add Your own copyright statement to Your modifications and may provide +additional or different license terms and conditions for use, reproduction, or +distribution of Your modifications, or for any such Derivative Works as a whole, +provided Your use, reproduction, and distribution of the Work otherwise complies +with the conditions stated in this License. + +5. Submission of Contributions. Unless You explicitly state otherwise, any +Contribution intentionally submitted for inclusion in the Work by You to the +Licensor shall be under the terms and conditions of this License, without any +additional terms or conditions. Notwithstanding the above, nothing herein shall +supersede or modify the terms of any separate license agreement you may have +executed with Licensor regarding such Contributions. + +6. Trademarks. This License does not grant permission to use the trade names, +trademarks, service marks, or product names of the Licensor, except as required +for reasonable and customary use in describing the origin of the Work and +reproducing the content of the NOTICE file. + +7. Disclaimer of Warranty. Unless required by applicable law or agreed to in +writing, Licensor provides the Work (and each Contributor provides its +Contributions) on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +KIND, either express or implied, including, without limitation, any warranties +or conditions of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A +PARTICULAR PURPOSE. You are solely responsible for determining the +appropriateness of using or redistributing the Work and assume any risks +associated with Your exercise of permissions under this License. + +8. Limitation of Liability. In no event and under no legal theory, whether in +tort (including negligence), contract, or otherwise, unless required by +applicable law (such as deliberate and grossly negligent acts) or agreed to in +writing, shall any Contributor be liable to You for damages, including any +direct, indirect, special, incidental, or consequential damages of any character +arising as a result of this License or out of the use or inability to use the +Work (including but not limited to damages for loss of goodwill, work stoppage, +computer failure or malfunction, or any and all other commercial damages or +losses), even if such Contributor has been advised of the possibility of such +damages. + +9. Accepting Warranty or Additional Liability. While redistributing the Work or +Derivative Works thereof, You may choose to offer, and charge a fee for, +acceptance of support, warranty, indemnity, or other liability obligations +and/or rights consistent with this License. However, in accepting such +obligations, You may act only on Your own behalf and on Your sole +responsibility, not on behalf of any other Contributor, and only if You agree to +indemnify, defend, and hold each Contributor harmless for any liability incurred +by, or claims asserted against, such Contributor by reason of your accepting any +such warranty or additional liability. + +END OF TERMS AND CONDITIONS +APPENDIX: How to apply the Apache License to your work + +To apply the Apache License to your work, attach the following boilerplate +notice, with the fields enclosed by brackets "[]" replaced with your own +identifying information. (Don't include the brackets!) The text should be +enclosed in the appropriate comment syntax for the file format. We also +recommend that a file or class name and description of purpose be included on +the same "printed page" as the copyright notice for easier identification within +third-party archives. +Copyright [yyyy] [name of copyright owner] 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. +*************************************************************************** +%%The following software may be included in this product: +Apache Commons Pool +Use of any of this software is governed by the terms of the license below: +Apache License +Version 2.0, January 2004 +http://www.apache.org/licenses/ + +TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION + +1. Definitions. + +"License" shall mean the terms and conditions for use, reproduction, +and distribution as defined by Sections 1 through 9 of this document. + +"Licensor" shall mean the copyright owner or entity authorized by +the copyright owner that is granting the License. + +"Legal Entity" shall mean the union of the acting entity and all +other entities that control, are controlled by, or are under common +control with that entity. For the purposes of this definition, +"control" means (i) the power, direct or indirect, to cause the +direction or management of such entity, whether by contract or +otherwise, or (ii) ownership of fifty percent (50%) or more of the +outstanding shares, or (iii) beneficial ownership of such entity. + +"You" (or "Your") shall mean an individual or Legal Entity +exercising permissions granted by this License. + +"Source" form shall mean the preferred form for making modifications, +including but not limited to software source code, documentation +source, and configuration files. + +"Object" form shall mean any form resulting from mechanical +transformation or translation of a Source form, including but +not limited to compiled object code, generated documentation, +and conversions to other media types. + +"Work" shall mean the work of authorship, whether in Source or +Object form, made available under the License, as indicated by a +copyright notice that is included in or attached to the work +(an example is provided in the Appendix below). + +"Derivative Works" shall mean any work, whether in Source or Object +form, that is based on (or derived from) the Work and for which the +editorial revisions, annotations, elaborations, or other modifications +represent, as a whole, an original work of authorship. For the purposes +of this License, Derivative Works shall not include works that remain +separable from, or merely link (or bind by name) to the interfaces of, +the Work and Derivative Works thereof. + +"Contribution" shall mean any work of authorship, including +the original version of the Work and any modifications or additions +to that Work or Derivative Works thereof, that is intentionally +submitted to Licensor for inclusion in the Work by the copyright owner +or by an individual or Legal Entity authorized to submit on behalf of +the copyright owner. For the purposes of this definition, "submitted" +means any form of electronic, verbal, or written communication sent +to the Licensor or its representatives, including but not limited to +communication on electronic mailing lists, source code control systems, +and issue tracking systems that are managed by, or on behalf of, the +Licensor for the purpose of discussing and improving the Work, but +excluding communication that is conspicuously marked or otherwise +designated in writing by the copyright owner as "Not a Contribution." + +"Contributor" shall mean Licensor and any individual or Legal Entity +on behalf of whom a Contribution has been received by Licensor and +subsequently incorporated within the Work. + +2. Grant of Copyright License. Subject to the terms and conditions of +this License, each Contributor hereby grants to You a perpetual, +worldwide, non-exclusive, no-charge, royalty-free, irrevocable +copyright license to reproduce, prepare Derivative Works of, +publicly display, publicly perform, sublicense, and distribute the +Work and such Derivative Works in Source or Object form. + +3. Grant of Patent License. Subject to the terms and conditions of +this License, each Contributor hereby grants to You a perpetual, +worldwide, non-exclusive, no-charge, royalty-free, irrevocable +(except as stated in this section) patent license to make, have made, +use, offer to sell, sell, import, and otherwise transfer the Work, +where such license applies only to those patent claims licensable +by such Contributor that are necessarily infringed by their +Contribution(s) alone or by combination of their Contribution(s) +with the Work to which such Contribution(s) was submitted. If You +institute patent litigation against any entity (including a +cross-claim or counterclaim in a lawsuit) alleging that the Work +or a Contribution incorporated within the Work constitutes direct +or contributory patent infringement, then any patent licenses +granted to You under this License for that Work shall terminate +as of the date such litigation is filed. + +4. Redistribution. You may reproduce and distribute copies of the +Work or Derivative Works thereof in any medium, with or without +modifications, and in Source or Object form, provided that You +meet the following conditions: + +(a) You must give any other recipients of the Work or +Derivative Works a copy of this License; and + +(b) You must cause any modified files to carry prominent notices +stating that You changed the files; and + +(c) You must retain, in the Source form of any Derivative Works +that You distribute, all copyright, patent, trademark, and +attribution notices from the Source form of the Work, +excluding those notices that do not pertain to any part of +the Derivative Works; and + +(d) If the Work includes a "NOTICE" text file as part of its +distribution, then any Derivative Works that You distribute must +include a readable copy of the attribution notices contained +within such NOTICE file, excluding those notices that do not +pertain to any part of the Derivative Works, in at least one +of the following places: within a NOTICE text file distributed +as part of the Derivative Works; within the Source form or +documentation, if provided along with the Derivative Works; or, +within a display generated by the Derivative Works, if and +wherever such third-party notices normally appear. The contents +of the NOTICE file are for informational purposes only and +do not modify the License. You may add Your own attribution +notices within Derivative Works that You distribute, alongside +or as an addendum to the NOTICE text from the Work, provided +that such additional attribution notices cannot be construed +as modifying the License. + +You may add Your own copyright statement to Your modifications and +may provide additional or different license terms and conditions +for use, reproduction, or distribution of Your modifications, or +for any such Derivative Works as a whole, provided Your use, +reproduction, and distribution of the Work otherwise complies with +the conditions stated in this License. + +5. Submission of Contributions. Unless You explicitly state otherwise, +any Contribution intentionally submitted for inclusion in the Work +by You to the Licensor shall be under the terms and conditions of +this License, without any additional terms or conditions. +Notwithstanding the above, nothing herein shall supersede or modify +the terms of any separate license agreement you may have executed +with Licensor regarding such Contributions. + +6. Trademarks. This License does not grant permission to use the trade +names, trademarks, service marks, or product names of the Licensor, +except as required for reasonable and customary use in describing the +origin of the Work and reproducing the content of the NOTICE file. + +7. Disclaimer of Warranty. Unless required by applicable law or +agreed to in writing, Licensor provides the Work (and each +Contributor provides its Contributions) on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or +implied, including, without limitation, any warranties or conditions +of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A +PARTICULAR PURPOSE. You are solely responsible for determining the +appropriateness of using or redistributing the Work and assume any +risks associated with Your exercise of permissions under this License. + +8. Limitation of Liability. In no event and under no legal theory, +whether in tort (including negligence), contract, or otherwise, +unless required by applicable law (such as deliberate and grossly +negligent acts) or agreed to in writing, shall any Contributor be +liable to You for damages, including any direct, indirect, special, +incidental, or consequential damages of any character arising as a +result of this License or out of the use or inability to use the +Work (including but not limited to damages for loss of goodwill, +work stoppage, computer failure or malfunction, or any and all +other commercial damages or losses), even if such Contributor +has been advised of the possibility of such damages. + +9. Accepting Warranty or Additional Liability. While redistributing +the Work or Derivative Works thereof, You may choose to offer, +and charge a fee for, acceptance of support, warranty, indemnity, +or other liability obligations and/or rights consistent with this +License. However, in accepting such obligations, You may act only +on Your own behalf and on Your sole responsibility, not on behalf +of any other Contributor, and only if You agree to indemnify, +defend, and hold each Contributor harmless for any liability +incurred by, or claims asserted against, such Contributor by reason +of your accepting any such warranty or additional liability. + +END OF TERMS AND CONDITIONS + +APPENDIX: How to apply the Apache License to your work. + +To apply the Apache License to your work, attach the following +boilerplate notice, with the fields enclosed by brackets "[]" +replaced with your own identifying information. (Don't include +the brackets!) The text should be enclosed in the appropriate +comment syntax for the file format. We also recommend that a +file or class name and description of purpose be included on the +same "printed page" as the copyright notice for easier +identification within third-party archives. + +Copyright [yyyy] [name of copyright owner] + +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. +*************************************************************************** +%%The following software may be included in this product: +HSQL Database Engine +Use of any of this software is governed by the terms of the license below: +COPYRIGHTS AND LICENSES + +ORIGINAL LICENSE (a.k.a. "hypersonic_lic.txt") + +For content, code, and products originally developed by Thomas Mueller and the +Hypersonic SQL Group: + +Copyright (c) 1995-2000 by the Hypersonic SQL Group. +All rights reserved. + + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are met: + +Redistributions of source code must retain the above copyright notice, this +list of conditions and the following disclaimer. + +Redistributions in binary form must reproduce the above copyright notice, +this list of conditions and the following disclaimer in the documentation +and/or other materials provided with the distribution. + +Neither the name of the Hypersonic SQL Group nor the names of its +contributors may be used to endorse or promote products derived from this +software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" +AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE +ARE DISCLAIMED. IN NO EVENT SHALL THE HYPERSONIC SQL GROUP, +OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; +LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND +ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS +SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +This software consists of voluntary contributions made by many individuals on +behalf of the +Hypersonic SQL Group. + + + +For work added by the HSQL Development Group (a.k.a. hsqldb_lic.txt): +Copyright (c) 2001-2005, The HSQL Development Group +All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are met: + +Redistributions of source code must retain the above copyright notice, this +list of conditions and the following disclaimer. + +Redistributions in binary form must reproduce the above copyright notice, +this list of conditions and the following disclaimer in the documentation +and/or other materials provided with the distribution. + +Neither the name of the HSQL Development Group nor the names of its +contributors may be used to endorse or promote products derived from this +software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" +AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE +ARE DISCLAIMED. IN NO EVENT SHALL HSQL DEVELOPMENT GROUP, HSQLDB.ORG, +OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; +LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND +ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS +SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +*************************************************************************** +%%The following software may be included in this product: +Derby +Use of any of this software is governed by the terms of the license below: +Apache License, Version 2.0 + +Apache License +Version 2.0, January 2004 +http://www.apache.org/licenses/ + +TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION + +1. Definitions. + +"License" shall mean the terms and conditions for use, reproduction, and +distribution as defined by Sections 1 through 9 of this document. + +"Licensor" shall mean the copyright owner or entity authorized by the copyright +owner that is granting the License. + +"Legal Entity" shall mean the union of the acting entity and all other entities +that control, are controlled by, or are under common control with that entity. +For the purposes of this definition, "control" means (i) the power, direct or +indirect, to cause the direction or management of such entity, whether by +contract or otherwise, or (ii) ownership of fifty percent (50%) or more of the +outstanding shares, or (iii) beneficial ownership of such entity. + +"You" (or "Your") shall mean an individual or Legal Entity exercising +permissions granted by this License. + +"Source" form shall mean the preferred form for making modifications, including +but not limited to software source code, documentation source, and configuration +files. + +"Object" form shall mean any form resulting from mechanical transformation or +translation of a Source form, including but not limited to compiled object code, +generated documentation, and conversions to other media types. + +"Work" shall mean the work of authorship, whether in Source or Object form, made +available under the License, as indicated by a copyright notice that is included +in or attached to the work (an example is provided in the Appendix below). + +"Derivative Works" shall mean any work, whether in Source or Object form, that +is based on (or derived from) the Work and for which the editorial revisions, +annotations, elaborations, or other modifications represent, as a whole, an +original work of authorship. For the purposes of this License, Derivative Works +shall not include works that remain separable from, or merely link (or bind by +name) to the interfaces of, the Work and Derivative Works thereof. + +"Contribution" shall mean any work of authorship, including the original version +of the Work and any modifications or additions to that Work or Derivative Works +thereof, that is intentionally submitted to Licensor for inclusion in the Work +by the copyright owner or by an individual or Legal Entity authorized to submit +on behalf of the copyright owner. For the purposes of this definition, +"submitted" means any form of electronic, verbal, or written communication sent +to the Licensor or its representatives, including but not limited to +communication on electronic mailing lists, source code control systems, and +issue tracking systems that are managed by, or on behalf of, the Licensor for +the purpose of discussing and improving the Work, but excluding communication +that is conspicuously marked or otherwise designated in writing by the copyright +owner as "Not a Contribution." + +"Contributor" shall mean Licensor and any individual or Legal Entity on behalf +of whom a Contribution has been received by Licensor and subsequently +incorporated within the Work. + +2. Grant of Copyright License. Subject to the terms and conditions of this +License, each Contributor hereby grants to You a perpetual, worldwide, +non-exclusive, no-charge, royalty-free, irrevocable copyright license to +reproduce, prepare Derivative Works of, publicly display, publicly perform, +sublicense, and distribute the Work and such Derivative Works in Source or +Object form. + +3. Grant of Patent License. Subject to the terms and conditions of this License, +each Contributor hereby grants to You a perpetual, worldwide, non-exclusive, +no-charge, royalty-free, irrevocable (except as stated in this section) patent +license to make, have made, use, offer to sell, sell, import, and otherwise +transfer the Work, where such license applies only to those patent claims +licensable by such Contributor that are necessarily infringed by their +Contribution(s) alone or by combination of their Contribution(s) with the Work +to which such Contribution(s) was submitted. If You institute patent litigation +against any entity (including a cross-claim or counterclaim in a lawsuit) +alleging that the Work or a Contribution incorporated within the Work +constitutes direct or contributory patent infringement, then any patent licenses +granted to You under this License for that Work shall terminate as of the date +such litigation is filed. + +4. Redistribution. You may reproduce and distribute copies of the Work or +Derivative Works thereof in any medium, with or without modifications, and in +Source or Object form, provided that You meet the following conditions: + +1. You must give any other recipients of the Work or Derivative Works a copy +of this License; and + +2. You must cause any modified files to carry prominent notices stating that +You changed the files; and + +3. You must retain, in the Source form of any Derivative Works that You +distribute, all copyright, patent, trademark, and attribution notices from the +Source form of the Work, excluding those notices that do not pertain to any part +of the Derivative Works; and + +4. If the Work includes a "NOTICE" text file as part of its distribution, +then any Derivative Works that You distribute must include a readable copy of +the attribution notices contained within such NOTICE file, excluding those +notices that do not pertain to any part of the Derivative Works, in at least one +of the following places: within a NOTICE text file distributed as part of the +Derivative Works; within the Source form or documentation, if provided along +with the Derivative Works; or, within a display generated by the Derivative +Works, if and wherever such third-party notices normally appear. The contents of +the NOTICE file are for informational purposes only and do not modify the +License. You may add Your own attribution notices within Derivative Works that +You distribute, alongside or as an addendum to the NOTICE text from the Work, +provided that such additional attribution notices cannot be construed as +modifying the License. + +You may add Your own copyright statement to Your modifications and may provide +additional or different license terms and conditions for use, reproduction, or +distribution of Your modifications, or for any such Derivative Works as a whole, +provided Your use, reproduction, and distribution of the Work otherwise complies +with the conditions stated in this License. + +5. Submission of Contributions. Unless You explicitly state otherwise, any +Contribution intentionally submitted for inclusion in the Work by You to the +Licensor shall be under the terms and conditions of this License, without any +additional terms or conditions. Notwithstanding the above, nothing herein shall +supersede or modify the terms of any separate license agreement you may have +executed with Licensor regarding such Contributions. + +6. Trademarks. This License does not grant permission to use the trade names, +trademarks, service marks, or product names of the Licensor, except as required +for reasonable and customary use in describing the origin of the Work and +reproducing the content of the NOTICE file. + +7. Disclaimer of Warranty. Unless required by applicable law or agreed to in +writing, Licensor provides the Work (and each Contributor provides its +Contributions) on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +KIND, either express or implied, including, without limitation, any warranties +or conditions of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A +PARTICULAR PURPOSE. You are solely responsible for determining the +appropriateness of using or redistributing the Work and assume any risks +associated with Your exercise of permissions under this License. + +8. Limitation of Liability. In no event and under no legal theory, whether in +tort (including negligence), contract, or otherwise, unless required by +applicable law (such as deliberate and grossly negligent acts) or agreed to in +writing, shall any Contributor be liable to You for damages, including any +direct, indirect, special, incidental, or consequential damages of any character +arising as a result of this License or out of the use or inability to use the +Work (including but not limited to damages for loss of goodwill, work stoppage, +computer failure or malfunction, or any and all other commercial damages or +losses), even if such Contributor has been advised of the possibility of such +damages. + +9. Accepting Warranty or Additional Liability. While redistributing the Work or +Derivative Works thereof, You may choose to offer, and charge a fee for, +acceptance of support, warranty, indemnity, or other liability obligations +and/or rights consistent with this License. However, in accepting such +obligations, You may act only on Your own behalf and on Your sole +responsibility, not on behalf of any other Contributor, and only if You agree to +indemnify, defend, and hold each Contributor harmless for any liability incurred +by, or claims asserted against, such Contributor by reason of your accepting any +such warranty or additional liability. + +END OF TERMS AND CONDITIONS +APPENDIX: How to apply the Apache License to your work + +To apply the Apache License to your work, attach the following boilerplate +notice, with the fields enclosed by brackets "[]" replaced with your own +identifying information. (Don't include the brackets!) The text should be +enclosed in the appropriate comment syntax for the file format. We also +recommend that a file or class name and description of purpose be included on +the same "printed page" as the copyright notice for easier identification within +third-party archives. +Copyright [yyyy] [name of copyright owner] 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. +*************************************************************************** +%%The following software may be included in this product: +Apache Commons - commons-net-1.4.1.jar +Use of any of this software is governed by the terms of the license below: +Apache License +Version 2.0, January 2004 +http://www.apache.org/licenses/ + +TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION + +1. Definitions. + +"License" shall mean the terms and conditions for use, reproduction, +and distribution as defined by Sections 1 through 9 of this document. + +"Licensor" shall mean the copyright owner or entity authorized by +the copyright owner that is granting the License. + +"Legal Entity" shall mean the union of the acting entity and all +other entities that control, are controlled by, or are under common +control with that entity. For the purposes of this definition, +"control" means (i) the power, direct or indirect, to cause the +direction or management of such entity, whether by contract or +otherwise, or (ii) ownership of fifty percent (50%) or more of the +outstanding shares, or (iii) beneficial ownership of such entity. + +"You" (or "Your") shall mean an individual or Legal Entity +exercising permissions granted by this License. + +"Source" form shall mean the preferred form for making modifications, +including but not limited to software source code, documentation +source, and configuration files. + +"Object" form shall mean any form resulting from mechanical +transformation or translation of a Source form, including but +not limited to compiled object code, generated documentation, +and conversions to other media types. + +"Work" shall mean the work of authorship, whether in Source or +Object form, made available under the License, as indicated by a +copyright notice that is included in or attached to the work +(an example is provided in the Appendix below). + +"Derivative Works" shall mean any work, whether in Source or Object +form, that is based on (or derived from) the Work and for which the +editorial revisions, annotations, elaborations, or other modifications +represent, as a whole, an original work of authorship. For the purposes +of this License, Derivative Works shall not include works that remain +separable from, or merely link (or bind by name) to the interfaces of, +the Work and Derivative Works thereof. + +"Contribution" shall mean any work of authorship, including +the original version of the Work and any modifications or additions +to that Work or Derivative Works thereof, that is intentionally +submitted to Licensor for inclusion in the Work by the copyright owner +or by an individual or Legal Entity authorized to submit on behalf of +the copyright owner. For the purposes of this definition, "submitted" +means any form of electronic, verbal, or written communication sent +to the Licensor or its representatives, including but not limited to +communication on electronic mailing lists, source code control systems, +and issue tracking systems that are managed by, or on behalf of, the +Licensor for the purpose of discussing and improving the Work, but +excluding communication that is conspicuously marked or otherwise +designated in writing by the copyright owner as "Not a Contribution." + +"Contributor" shall mean Licensor and any individual or Legal Entity +on behalf of whom a Contribution has been received by Licensor and +subsequently incorporated within the Work. + +2. Grant of Copyright License. Subject to the terms and conditions of +this License, each Contributor hereby grants to You a perpetual, +worldwide, non-exclusive, no-charge, royalty-free, irrevocable +copyright license to reproduce, prepare Derivative Works of, +publicly display, publicly perform, sublicense, and distribute the +Work and such Derivative Works in Source or Object form. + +3. Grant of Patent License. Subject to the terms and conditions of +this License, each Contributor hereby grants to You a perpetual, +worldwide, non-exclusive, no-charge, royalty-free, irrevocable +(except as stated in this section) patent license to make, have made, +use, offer to sell, sell, import, and otherwise transfer the Work, +where such license applies only to those patent claims licensable +by such Contributor that are necessarily infringed by their +Contribution(s) alone or by combination of their Contribution(s) +with the Work to which such Contribution(s) was submitted. If You +institute patent litigation against any entity (including a +cross-claim or counterclaim in a lawsuit) alleging that the Work +or a Contribution incorporated within the Work constitutes direct +or contributory patent infringement, then any patent licenses +granted to You under this License for that Work shall terminate +as of the date such litigation is filed. + +4. Redistribution. You may reproduce and distribute copies of the +Work or Derivative Works thereof in any medium, with or without +modifications, and in Source or Object form, provided that You +meet the following conditions: + +(a) You must give any other recipients of the Work or +Derivative Works a copy of this License; and + +(b) You must cause any modified files to carry prominent notices +stating that You changed the files; and + +(c) You must retain, in the Source form of any Derivative Works +that You distribute, all copyright, patent, trademark, and +attribution notices from the Source form of the Work, +excluding those notices that do not pertain to any part of +the Derivative Works; and + +(d) If the Work includes a "NOTICE" text file as part of its +distribution, then any Derivative Works that You distribute must +include a readable copy of the attribution notices contained +within such NOTICE file, excluding those notices that do not +pertain to any part of the Derivative Works, in at least one +of the following places: within a NOTICE text file distributed +as part of the Derivative Works; within the Source form or +documentation, if provided along with the Derivative Works; or, +within a display generated by the Derivative Works, if and +wherever such third-party notices normally appear. The contents +of the NOTICE file are for informational purposes only and +do not modify the License. You may add Your own attribution +notices within Derivative Works that You distribute, alongside +or as an addendum to the NOTICE text from the Work, provided +that such additional attribution notices cannot be construed +as modifying the License. + +You may add Your own copyright statement to Your modifications and +may provide additional or different license terms and conditions +for use, reproduction, or distribution of Your modifications, or +for any such Derivative Works as a whole, provided Your use, +reproduction, and distribution of the Work otherwise complies with +the conditions stated in this License. + +5. Submission of Contributions. Unless You explicitly state otherwise, +any Contribution intentionally submitted for inclusion in the Work +by You to the Licensor shall be under the terms and conditions of +this License, without any additional terms or conditions. +Notwithstanding the above, nothing herein shall supersede or modify +the terms of any separate license agreement you may have executed +with Licensor regarding such Contributions. + +6. Trademarks. This License does not grant permission to use the trade +names, trademarks, service marks, or product names of the Licensor, +except as required for reasonable and customary use in describing the +origin of the Work and reproducing the content of the NOTICE file. + +7. Disclaimer of Warranty. Unless required by applicable law or +agreed to in writing, Licensor provides the Work (and each +Contributor provides its Contributions) on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or +implied, including, without limitation, any warranties or conditions +of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A +PARTICULAR PURPOSE. You are solely responsible for determining the +appropriateness of using or redistributing the Work and assume any +risks associated with Your exercise of permissions under this License. + +8. Limitation of Liability. In no event and under no legal theory, +whether in tort (including negligence), contract, or otherwise, +unless required by applicable law (such as deliberate and grossly +negligent acts) or agreed to in writing, shall any Contributor be +liable to You for damages, including any direct, indirect, special, +incidental, or consequential damages of any character arising as a +result of this License or out of the use or inability to use the +Work (including but not limited to damages for loss of goodwill, +work stoppage, computer failure or malfunction, or any and all +other commercial damages or losses), even if such Contributor +has been advised of the possibility of such damages. + +9. Accepting Warranty or Additional Liability. While redistributing +the Work or Derivative Works thereof, You may choose to offer, +and charge a fee for, acceptance of support, warranty, indemnity, +or other liability obligations and/or rights consistent with this +License. However, in accepting such obligations, You may act only +on Your own behalf and on Your sole responsibility, not on behalf +of any other Contributor, and only if You agree to indemnify, +defend, and hold each Contributor harmless for any liability +incurred by, or claims asserted against, such Contributor by reason +of your accepting any such warranty or additional liability. + +END OF TERMS AND CONDITIONS + +APPENDIX: How to apply the Apache License to your work. + +To apply the Apache License to your work, attach the following +boilerplate notice, with the fields enclosed by brackets "[]" +replaced with your own identifying information. (Don't include +the brackets!) The text should be enclosed in the appropriate +comment syntax for the file format. We also recommend that a +file or class name and description of purpose be included on the +same "printed page" as the copyright notice for easier +identification within third-party archives. + +Copyright [yyyy] [name of copyright owner] + +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. +*************************************************************************** +%%The following software may be included in this product: +Jakarta Oro +Use of any of this software is governed by the terms of the license below: +Apache License +Version 2.0, January 2004 +http://www.apache.org/licenses/ + +TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION + +1. Definitions. + +"License" shall mean the terms and conditions for use, reproduction, +and distribution as defined by Sections 1 through 9 of this document. + +"Licensor" shall mean the copyright owner or entity authorized by +the copyright owner that is granting the License. + +"Legal Entity" shall mean the union of the acting entity and all +other entities that control, are controlled by, or are under common +control with that entity. For the purposes of this definition, +"control" means (i) the power, direct or indirect, to cause the +direction or management of such entity, whether by contract or +otherwise, or (ii) ownership of fifty percent (50%) or more of the +outstanding shares, or (iii) beneficial ownership of such entity. + +"You" (or "Your") shall mean an individual or Legal Entity +exercising permissions granted by this License. + +"Source" form shall mean the preferred form for making modifications, +including but not limited to software source code, documentation +source, and configuration files. + +"Object" form shall mean any form resulting from mechanical +transformation or translation of a Source form, including but +not limited to compiled object code, generated documentation, +and conversions to other media types. + +"Work" shall mean the work of authorship, whether in Source or +Object form, made available under the License, as indicated by a +copyright notice that is included in or attached to the work +(an example is provided in the Appendix below). + +"Derivative Works" shall mean any work, whether in Source or Object +form, that is based on (or derived from) the Work and for which the +editorial revisions, annotations, elaborations, or other modifications +represent, as a whole, an original work of authorship. For the purposes +of this License, Derivative Works shall not include works that remain +separable from, or merely link (or bind by name) to the interfaces of, +the Work and Derivative Works thereof. + +"Contribution" shall mean any work of authorship, including +the original version of the Work and any modifications or additions +to that Work or Derivative Works thereof, that is intentionally +submitted to Licensor for inclusion in the Work by the copyright owner +or by an individual or Legal Entity authorized to submit on behalf of +the copyright owner. For the purposes of this definition, "submitted" +means any form of electronic, verbal, or written communication sent +to the Licensor or its representatives, including but not limited to +communication on electronic mailing lists, source code control systems, +and issue tracking systems that are managed by, or on behalf of, the +Licensor for the purpose of discussing and improving the Work, but +excluding communication that is conspicuously marked or otherwise +designated in writing by the copyright owner as "Not a Contribution." + +"Contributor" shall mean Licensor and any individual or Legal Entity +on behalf of whom a Contribution has been received by Licensor and +subsequently incorporated within the Work. + +2. Grant of Copyright License. Subject to the terms and conditions of +this License, each Contributor hereby grants to You a perpetual, +worldwide, non-exclusive, no-charge, royalty-free, irrevocable +copyright license to reproduce, prepare Derivative Works of, +publicly display, publicly perform, sublicense, and distribute the +Work and such Derivative Works in Source or Object form. + +3. Grant of Patent License. Subject to the terms and conditions of +this License, each Contributor hereby grants to You a perpetual, +worldwide, non-exclusive, no-charge, royalty-free, irrevocable +(except as stated in this section) patent license to make, have made, +use, offer to sell, sell, import, and otherwise transfer the Work, +where such license applies only to those patent claims licensable +by such Contributor that are necessarily infringed by their +Contribution(s) alone or by combination of their Contribution(s) +with the Work to which such Contribution(s) was submitted. If You +institute patent litigation against any entity (including a +cross-claim or counterclaim in a lawsuit) alleging that the Work +or a Contribution incorporated within the Work constitutes direct +or contributory patent infringement, then any patent licenses +granted to You under this License for that Work shall terminate +as of the date such litigation is filed. + +4. Redistribution. You may reproduce and distribute copies of the +Work or Derivative Works thereof in any medium, with or without +modifications, and in Source or Object form, provided that You +meet the following conditions: + +(a) You must give any other recipients of the Work or +Derivative Works a copy of this License; and + +(b) You must cause any modified files to carry prominent notices +stating that You changed the files; and + +(c) You must retain, in the Source form of any Derivative Works +that You distribute, all copyright, patent, trademark, and +attribution notices from the Source form of the Work, +excluding those notices that do not pertain to any part of +the Derivative Works; and + +(d) If the Work includes a "NOTICE" text file as part of its +distribution, then any Derivative Works that You distribute must +include a readable copy of the attribution notices contained +within such NOTICE file, excluding those notices that do not +pertain to any part of the Derivative Works, in at least one +of the following places: within a NOTICE text file distributed +as part of the Derivative Works; within the Source form or +documentation, if provided along with the Derivative Works; or, +within a display generated by the Derivative Works, if and +wherever such third-party notices normally appear. The contents +of the NOTICE file are for informational purposes only and +do not modify the License. You may add Your own attribution +notices within Derivative Works that You distribute, alongside +or as an addendum to the NOTICE text from the Work, provided +that such additional attribution notices cannot be construed +as modifying the License. + +You may add Your own copyright statement to Your modifications and +may provide additional or different license terms and conditions +for use, reproduction, or distribution of Your modifications, or +for any such Derivative Works as a whole, provided Your use, +reproduction, and distribution of the Work otherwise complies with +the conditions stated in this License. + +5. Submission of Contributions. Unless You explicitly state otherwise, +any Contribution intentionally submitted for inclusion in the Work +by You to the Licensor shall be under the terms and conditions of +this License, without any additional terms or conditions. +Notwithstanding the above, nothing herein shall supersede or modify +the terms of any separate license agreement you may have executed +with Licensor regarding such Contributions. + +6. Trademarks. This License does not grant permission to use the trade +names, trademarks, service marks, or product names of the Licensor, +except as required for reasonable and customary use in describing the +origin of the Work and reproducing the content of the NOTICE file. + +7. Disclaimer of Warranty. Unless required by applicable law or +agreed to in writing, Licensor provides the Work (and each +Contributor provides its Contributions) on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or +implied, including, without limitation, any warranties or conditions +of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A +PARTICULAR PURPOSE. You are solely responsible for determining the +appropriateness of using or redistributing the Work and assume any +risks associated with Your exercise of permissions under this License. + +8. Limitation of Liability. In no event and under no legal theory, +whether in tort (including negligence), contract, or otherwise, +unless required by applicable law (such as deliberate and grossly +negligent acts) or agreed to in writing, shall any Contributor be +liable to You for damages, including any direct, indirect, special, +incidental, or consequential damages of any character arising as a +result of this License or out of the use or inability to use the +Work (including but not limited to damages for loss of goodwill, +work stoppage, computer failure or malfunction, or any and all +other commercial damages or losses), even if such Contributor +has been advised of the possibility of such damages. + +9. Accepting Warranty or Additional Liability. While redistributing +the Work or Derivative Works thereof, You may choose to offer, +and charge a fee for, acceptance of support, warranty, indemnity, +or other liability obligations and/or rights consistent with this +License. However, in accepting such obligations, You may act only +on Your own behalf and on Your sole responsibility, not on behalf +of any other Contributor, and only if You agree to indemnify, +defend, and hold each Contributor harmless for any liability +incurred by, or claims asserted against, such Contributor by reason +of your accepting any such warranty or additional liability. + +END OF TERMS AND CONDITIONS + +APPENDIX: How to apply the Apache License to your work. + +To apply the Apache License to your work, attach the following +boilerplate notice, with the fields enclosed by brackets "[]" +replaced with your own identifying information. (Don't include +the brackets!) The text should be enclosed in the appropriate +comment syntax for the file format. We also recommend that a +file or class name and description of purpose be included on the +same "printed page" as the copyright notice for easier +identification within third-party archives. + +Copyright [yyyy] [name of copyright owner] + +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. +*************************************************************************** +%%The following software may be included in this product: +Jsch +Use of any of this software is governed by the terms of the license below: +JSch 0.0.* was released under the GNU LGPL license. Later, we have switched +over to a BSD-style license. + +------------------------------------------------------------------------------ +Copyright (c) 2002,2003,2004,2005,2006,2007,2008 Atsuhiko Yamanaka, JCraft,Inc. +All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are met: + +1. Redistributions of source code must retain the above copyright notice, +this list of conditions and the following disclaimer. + +2. Redistributions in binary form must reproduce the above copyright +notice, this list of conditions and the following disclaimer in +the documentation and/or other materials provided with the distribution. + +3. The names of the authors may not be used to endorse or promote products +derived from this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED OR IMPLIED WARRANTIES, +INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND +FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL JCRAFT, +INC. OR ANY CONTRIBUTORS TO THIS SOFTWARE BE LIABLE FOR ANY DIRECT, INDIRECT, +INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, +OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF +LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING +NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, +EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +Additional License(s) +LICENSE.txt:JSch 0.0.* was released under the GNU LGPL license. +Later, we have switched +LICENSE.txt:over to a BSD-style license. +*************************************************************************** +%%The following software may be included in this product: +Expect4j +Use of any of this software is governed by the terms of the license below: +Apache License, Version 2.0 + +Apache License +Version 2.0, January 2004 +http://www.apache.org/licenses/ + +TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION + +1. Definitions. + +"License" shall mean the terms and conditions for use, reproduction, and +distribution as defined by Sections 1 through 9 of this document. + +"Licensor" shall mean the copyright owner or entity authorized by the copyright +owner that is granting the License. + +"Legal Entity" shall mean the union of the acting entity and all other entities +that control, are controlled by, or are under common control with that entity. +For the purposes of this definition, "control" means (i) the power, direct or +indirect, to cause the direction or management of such entity, whether by +contract or otherwise, or (ii) ownership of fifty percent (50%) or more of the +outstanding shares, or (iii) beneficial ownership of such entity. + +"You" (or "Your") shall mean an individual or Legal Entity exercising +permissions granted by this License. + +"Source" form shall mean the preferred form for making modifications, including +but not limited to software source code, documentation source, and configuration +files. + +"Object" form shall mean any form resulting from mechanical transformation or +translation of a Source form, including but not limited to compiled object code, +generated documentation, and conversions to other media types. + +"Work" shall mean the work of authorship, whether in Source or Object form, made +available under the License, as indicated by a copyright notice that is included +in or attached to the work (an example is provided in the Appendix below). + +"Derivative Works" shall mean any work, whether in Source or Object form, that +is based on (or derived from) the Work and for which the editorial revisions, +annotations, elaborations, or other modifications represent, as a whole, an +original work of authorship. For the purposes of this License, Derivative Works +shall not include works that remain separable from, or merely link (or bind by +name) to the interfaces of, the Work and Derivative Works thereof. + +"Contribution" shall mean any work of authorship, including the original version +of the Work and any modifications or additions to that Work or Derivative Works +thereof, that is intentionally submitted to Licensor for inclusion in the Work +by the copyright owner or by an individual or Legal Entity authorized to submit +on behalf of the copyright owner. For the purposes of this definition, +"submitted" means any form of electronic, verbal, or written communication sent +to the Licensor or its representatives, including but not limited to +communication on electronic mailing lists, source code control systems, and +issue tracking systems that are managed by, or on behalf of, the Licensor for +the purpose of discussing and improving the Work, but excluding communication +that is conspicuously marked or otherwise designated in writing by the copyright +owner as "Not a Contribution." + +"Contributor" shall mean Licensor and any individual or Legal Entity on behalf +of whom a Contribution has been received by Licensor and subsequently +incorporated within the Work. + +2. Grant of Copyright License. Subject to the terms and conditions of this +License, each Contributor hereby grants to You a perpetual, worldwide, +non-exclusive, no-charge, royalty-free, irrevocable copyright license to +reproduce, prepare Derivative Works of, publicly display, publicly perform, +sublicense, and distribute the Work and such Derivative Works in Source or +Object form. + +3. Grant of Patent License. Subject to the terms and conditions of this License, +each Contributor hereby grants to You a perpetual, worldwide, non-exclusive, +no-charge, royalty-free, irrevocable (except as stated in this section) patent +license to make, have made, use, offer to sell, sell, import, and otherwise +transfer the Work, where such license applies only to those patent claims +licensable by such Contributor that are necessarily infringed by their +Contribution(s) alone or by combination of their Contribution(s) with the Work +to which such Contribution(s) was submitted. If You institute patent litigation +against any entity (including a cross-claim or counterclaim in a lawsuit) +alleging that the Work or a Contribution incorporated within the Work +constitutes direct or contributory patent infringement, then any patent licenses +granted to You under this License for that Work shall terminate as of the date +such litigation is filed. + +4. Redistribution. You may reproduce and distribute copies of the Work or +Derivative Works thereof in any medium, with or without modifications, and in +Source or Object form, provided that You meet the following conditions: + +1. You must give any other recipients of the Work or Derivative Works a copy +of this License; and + +2. You must cause any modified files to carry prominent notices stating that +You changed the files; and + +3. You must retain, in the Source form of any Derivative Works that You +distribute, all copyright, patent, trademark, and attribution notices from the +Source form of the Work, excluding those notices that do not pertain to any part +of the Derivative Works; and + +4. If the Work includes a "NOTICE" text file as part of its distribution, +then any Derivative Works that You distribute must include a readable copy of +the attribution notices contained within such NOTICE file, excluding those +notices that do not pertain to any part of the Derivative Works, in at least one +of the following places: within a NOTICE text file distributed as part of the +Derivative Works; within the Source form or documentation, if provided along +with the Derivative Works; or, within a display generated by the Derivative +Works, if and wherever such third-party notices normally appear. The contents of +the NOTICE file are for informational purposes only and do not modify the +License. You may add Your own attribution notices within Derivative Works that +You distribute, alongside or as an addendum to the NOTICE text from the Work, +provided that such additional attribution notices cannot be construed as +modifying the License. + +You may add Your own copyright statement to Your modifications and may provide +additional or different license terms and conditions for use, reproduction, or +distribution of Your modifications, or for any such Derivative Works as a whole, +provided Your use, reproduction, and distribution of the Work otherwise complies +with the conditions stated in this License. + +5. Submission of Contributions. Unless You explicitly state otherwise, any +Contribution intentionally submitted for inclusion in the Work by You to the +Licensor shall be under the terms and conditions of this License, without any +additional terms or conditions. Notwithstanding the above, nothing herein shall +supersede or modify the terms of any separate license agreement you may have +executed with Licensor regarding such Contributions. + +6. Trademarks. This License does not grant permission to use the trade names, +trademarks, service marks, or product names of the Licensor, except as required +for reasonable and customary use in describing the origin of the Work and +reproducing the content of the NOTICE file. + +7. Disclaimer of Warranty. Unless required by applicable law or agreed to in +writing, Licensor provides the Work (and each Contributor provides its +Contributions) on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +KIND, either express or implied, including, without limitation, any warranties +or conditions of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A +PARTICULAR PURPOSE. You are solely responsible for determining the +appropriateness of using or redistributing the Work and assume any risks +associated with Your exercise of permissions under this License. + +8. Limitation of Liability. In no event and under no legal theory, whether in +tort (including negligence), contract, or otherwise, unless required by +applicable law (such as deliberate and grossly negligent acts) or agreed to in +writing, shall any Contributor be liable to You for damages, including any +direct, indirect, special, incidental, or consequential damages of any character +arising as a result of this License or out of the use or inability to use the +Work (including but not limited to damages for loss of goodwill, work stoppage, +computer failure or malfunction, or any and all other commercial damages or +losses), even if such Contributor has been advised of the possibility of such +damages. + +9. Accepting Warranty or Additional Liability. While redistributing the Work or +Derivative Works thereof, You may choose to offer, and charge a fee for, +acceptance of support, warranty, indemnity, or other liability obligations +and/or rights consistent with this License. However, in accepting such +obligations, You may act only on Your own behalf and on Your sole +responsibility, not on behalf of any other Contributor, and only if You agree to +indemnify, defend, and hold each Contributor harmless for any liability incurred +by, or claims asserted against, such Contributor by reason of your accepting any +such warranty or additional liability. + +END OF TERMS AND CONDITIONS +APPENDIX: How to apply the Apache License to your work + +To apply the Apache License to your work, attach the following boilerplate +notice, with the fields enclosed by brackets "[]" replaced with your own +identifying information. (Don't include the brackets!) The text should be +enclosed in the appropriate comment syntax for the file format. We also +recommend that a file or class name and description of purpose be included on +the same "printed page" as the copyright notice for easier identification within +third-party archives. +Copyright [yyyy] [name of copyright owner] 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. +Apache Projects + +* HTTP Server +* ActiveMQ +* Ant +* APR +* Archiva +* Beehive +* Cayenne +* Cocoon +* Commons +* Continuum +* CXF +* DB +* Directory +* Excalibur +* Felix +* Forrest +* Geronimo +* Gump +* Hadoop +* Harmony +* HiveMind +* HttpComponents +* iBATIS +* Incubator +* Jackrabbit +* Jakarta +* James +* Labs +* Lenya +* Logging +* Lucene +* Maven +* Mina +* MyFaces +* ODE +* OFBiz +* OpenEJB +* OpenJPA +* Perl +* POI +* Portals +* Roller +* Santuario +* ServiceMix +* Shale +* SpamAssassin +* STDCXX +* Struts +* Synapse +* Tapestry +* TCL +* Tiles +* Tomcat +* Turbine +* Velocity +* Wicket +* Web Services +* Xalan +* Xerces +* XML +* XMLBeans +* XML Graphics + +Foundation + +* FAQ +* Licenses +* News +* Public Records +* Sponsorship +* Donations +* Thanks +* Contact + +Foundation Projects + +* Conferences +* Infrastructure +* JCP + +How it works + +* Introduction +* Meritocracy +* Structure +* Roles +* Collaboration +* Infrastructure +* Incubator +* Other entities +* Glossary +* Voting + +Get Involved + +* Mailing Lists +* Version Control +* Developer Info + +Download + +* from a mirror + +Related Sites + +* ApacheCon +* Bookstore +* Feathercast +* PlanetApache + +Copyright © 2008 The Apache Software Foundation, Licensed under the Apache +License, Version 2.0. +*************************************************************************** +%%The following software may be included in this product: +FreeHost3270 +Use of any of this software is governed by the terms of the license below: +GNU LESSER GENERAL PUBLIC LICENSE + +Version 2.1, February 1999 + +Copyright (C) 1991, 1999 Free Software Foundation, Inc. +51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA +Everyone is permitted to copy and distribute verbatim copies +of this license document, but changing it is not allowed. + +[This is the first released version of the Lesser GPL. It also counts +as the successor of the GNU Library Public License, version 2, hence +the version number 2.1.] +Preamble + +The licenses for most software are designed to take away your freedom to share and change it. By +contrast, the GNU General Public Licenses are intended to guarantee your freedom to share and change +free software--to make sure the software is free for all its users. + +This license, the Lesser General Public License, applies to some specially designated software +packages--typically libraries--of the Free Software Foundation and other authors who decide to use it. +You can use it too, but we suggest you first think carefully about whether this license or the ordinary +General Public License is the better strategy to use in any particular case, based on the explanations +below. + +When we speak of free software, we are referring to freedom of use, not price. Our General Public +Licenses are designed to make sure that you have the freedom to distribute copies of free software (and +charge for this service if you wish); that you receive source code or can get it if you want it; that you +can change the software and use pieces of it in new free programs; and that you are informed that you +can do these things. + +To protect your rights, we need to make restrictions that forbid distributors to deny you these rights or +to ask you to surrender these rights. These restrictions translate to certain responsibilities for you if +you distribute copies of the library or if you modify it. + +For example, if you distribute copies of the library, whether gratis or for a fee, you must give the +recipients all the rights that we gave you. You must make sure that they, too, receive or can get the +source code. If you link other code with the library, you must provide complete object files to the +recipients, so that they can relink them with the library after making changes to the library and +recompiling it. And you must show them these terms so they know their rights. + +We protect your rights with a two-step method: (1) we copyright the library, and (2) we offer you this +license, which gives you legal permission to copy, distribute and/or modify the library. + +To protect each distributor, we want to make it very clear that there is no warranty for the free library. +Also, if the library is modified by someone else and passed on, the recipients should know that what +they have is not the original version, so that the original author's reputation will not be affected by +problems that might be introduced by others. + +Finally, software patents pose a constant threat to the existence of any free program. We wish to make +sure that a company cannot effectively restrict the users of a free program by obtaining a restrictive +license from a patent holder. Therefore, we insist that any patent license obtained for a version of the +library must be consistent with the full freedom of use specified in this license. + +Most GNU software, including some libraries, is covered by the ordinary GNU General Public License. +This license, the GNU Lesser General Public License, applies to certain designated libraries, and is quite +different from the ordinary General Public License. We use this license for certain libraries in order to +permit linking those libraries into non-free programs. + +When a program is linked with a library, whether statically or using a shared library, the combination of +the two is legally speaking a combined work, a derivative of the original library. The ordinary General +Public License therefore permits such linking only if the entire combination fits its criteria of freedom. +The Lesser General Public License permits more lax criteria for linking other code with the library. + +We call this license the "Lesser" General Public License because it does Less to protect the user's +freedom than the ordinary General Public License. It also provides other free software developers Less +of an advantage over competing non-free programs. These disadvantages are the reason we use the +ordinary General Public License for many libraries. However, the Lesser license provides advantages in +certain special circumstances. + +For example, on rare occasions, there may be a special need to encourage the widest possible use of a +certain library, so that it becomes a de-facto standard. To achieve this, non-free programs must be +allowed to use the library. A more frequent case is that a free library does the same job as widely used +non-free libraries. In this case, there is little to gain by limiting the free library to free software only, so +we use the Lesser General Public License. + +In other cases, permission to use a particular library in non-free programs enables a greater number of +people to use a large body of free software. For example, permission to use the GNU C Library in non- +free programs enables many more people to use the whole GNU operating system, as well as its variant, +the GNU/Linux operating system. + +Although the Lesser General Public License is Less protective of the users' freedom, it does ensure that +the user of a program that is linked with the Library has the freedom and the wherewithal to run that +program using a modified version of the Library. + +The precise terms and conditions for copying, distribution and modification follow. Pay close attention +to the difference between a "work based on the library" and a "work that uses the library". The former +contains code derived from the library, whereas the latter must be combined with the library in order to +run. + +TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION + +0. This License Agreement applies to any software library or other program which contains a notice +placed by the copyright holder or other authorized party saying it may be distributed under the terms +of this Lesser General Public License (also called "this License"). Each licensee is addressed as "you". + +A "library" means a collection of software functions and/or data prepared so as to be conveniently +linked with application programs (which use some of those functions and data) to form executables. + +The "Library", below, refers to any such software library or work which has been distributed under these +terms. A "work based on the Library" means either the Library or any derivative work under copyright +law: that is to say, a work containing the Library or a portion of it, either verbatim or with modifications +and/or translated straightforwardly into another language. (Hereinafter, translation is included without +limitation in the term "modification".) + +"Source code" for a work means the preferred form of the work for making modifications to it. For a +library, complete source code means all the source code for all modules it contains, plus any associated +interface definition files, plus the scripts used to control compilation and installation of the library. + +Activities other than copying, distribution and modification are not covered by this License; they are +outside its scope. The act of running a program using the Library is not restricted, and output from +such a program is covered only if its contents constitute a work based on the Library (independent of +the use of the Library in a tool for writing it). Whether that is true depends on what the Library does +and what the program that uses the Library does. + +1. You may copy and distribute verbatim copies of the Library's complete source code as you receive it, +in any medium, provided that you conspicuously and appropriately publish on each copy an +appropriate copyright notice and disclaimer of warranty; keep intact all the notices that refer to this +License and to the absence of any warranty; and distribute a copy of this License along with the Library. + +You may charge a fee for the physical act of transferring a copy, and you may at your option offer +warranty protection in exchange for a fee. + +2. You may modify your copy or copies of the Library or any portion of it, thus forming a work based +on the Library, and copy and distribute such modifications or work under the terms of Section 1 above, +provided that you also meet all of these conditions: + +a) The modified work must itself be a software library. +b) You must cause the files modified to carry prominent notices stating that you changed the files and +the date of any change. +c) You must cause the whole of the work to be licensed at no charge to all third parties under the terms +of this License. +d) If a facility in the modified Library refers to a function or a table of data to be supplied by an +application program that uses the facility, other than as an argument passed when the facility is +invoked, then you must make a good faith effort to ensure that, in the event an application does not +supply such function or table, the facility still operates, and performs whatever part of its purpose +remains meaningful. +(For example, a function in a library to compute square roots has a purpose that is entirely well- +defined independent of the application. Therefore, Subsection 2d requires that any application- +supplied function or table used by this function must be optional: if the application does not supply it, +the square root function must still compute square roots.) + +These requirements apply to the modified work as a whole. If identifiable sections of that work are not +derived from the Library, and can be reasonably considered independent and separate works in +themselves, then this License, and its terms, do not apply to those sections when you distribute them as +separate works. But when you distribute the same sections as part of a whole which is a work based on +the Library, the distribution of the whole must be on the terms of this License, whose permissions for +other licensees extend to the entire whole, and thus to each and every part regardless of who wrote it. +Thus, it is not the intent of this section to claim rights or contest your rights to work written entirely by +you; rather, the intent is to exercise the right to control the distribution of derivative or collective works +based on the Library. + +In addition, mere aggregation of another work not based on the Library with the Library (or with a work +based on the Library) on a volume of a storage or distribution medium does not bring the other work +under the scope of this License. + +3. You may opt to apply the terms of the ordinary GNU General Public License instead of this License to +a given copy of the Library. To do this, you must alter all the notices that refer to this License, so that +they refer to the ordinary GNU General Public License, version 2, instead of to this License. (If a newer +version than version 2 of the ordinary GNU General Public License has appeared, then you can specify +that version instead if you wish.) Do not make any other change in these notices. + +Once this change is made in a given copy, it is irreversible for that copy, so the ordinary GNU General +Public License applies to all subsequent copies and derivative works made from that copy. + +This option is useful when you wish to copy part of the code of the Library into a program that is not a +library. + +4. You may copy and distribute the Library (or a portion or derivative of it, under Section 2) in object +code or executable form under the terms of Sections 1 and 2 above provided that you accompany it +with the complete corresponding machine-readable source code, which must be distributed under the +terms of Sections 1 and 2 above on a medium customarily used for software interchange. + +If distribution of object code is made by offering access to copy from a designated place, then offering +equivalent access to copy the source code from the same place satisfies the requirement to distribute +the source code, even though third parties are not compelled to copy the source along with the object +code. + +5. A program that contains no derivative of any portion of the Library, but is designed to work with the +Library by being compiled or linked with it, is called a "work that uses the Library". Such a work, in +isolation, is not a derivative work of the Library, and therefore falls outside the scope of this License. + +However, linking a "work that uses the Library" with the Library creates an executable that is a +derivative of the Library (because it contains portions of the Library), rather than a "work that uses the +library". The executable is therefore covered by this License. Section 6 states terms for distribution of +such executables. + +When a "work that uses the Library" uses material from a header file that is part of the Library, the +object code for the work may be a derivative work of the Library even though the source code is not. +Whether this is true is especially significant if the work can be linked without the Library, or if the work +is itself a library. The threshold for this to be true is not precisely defined by law. + +If such an object file uses only numerical parameters, data structure layouts and accessors, and small +macros and small inline functions (ten lines or less in length), then the use of the object file is +unrestricted, regardless of whether it is legally a derivative work. (Executables containing this object +code plus portions of the Library will still fall under Section 6.) + +Otherwise, if the work is a derivative of the Library, you may distribute the object code for the work +under the terms of Section 6. Any executables containing that work also fall under Section 6, whether +or not they are linked directly with the Library itself. + +6. As an exception to the Sections above, you may also combine or link a "work that uses the Library" +with the Library to produce a work containing portions of the Library, and distribute that work under +terms of your choice, provided that the terms permit modification of the work for the customer's own +use and reverse engineering for debugging such modifications. + +You must give prominent notice with each copy of the work that the Library is used in it and that the +Library and its use are covered by this License. You must supply a copy of this License. If the work +during execution displays copyright notices, you must include the copyright notice for the Library +among them, as well as a reference directing the user to the copy of this License. Also, you must do +one of these things: + +a) Accompany the work with the complete corresponding machine-readable source code for the Library +including whatever changes were used in the work (which must be distributed under Sections 1 and 2 +above); and, if the work is an executable linked with the Library, with the complete machine-readable +"work that uses the Library", as object code and/or source code, so that the user can modify the Library +and then relink to produce a modified executable containing the modified Library. (It is understood that +the user who changes the contents of definitions files in the Library will not necessarily be able to +recompile the application to use the modified definitions.) +b) Use a suitable shared library mechanism for linking with the Library. A suitable mechanism is one +that (1) uses at run time a copy of the library already present on the user's computer system, rather +than copying library functions into the executable, and (2) will operate properly with a modified version +of the library, if the user installs one, as long as the modified version is interface-compatible with the +version that the work was made with. +c) Accompany the work with a written offer, valid for at least three years, to give the same user the +materials specified in Subsection 6a, above, for a charge no more than the cost of performing this +distribution. +d) If distribution of the work is made by offering access to copy from a designated place, offer +equivalent access to copy the above specified materials from the same place. +e) Verify that the user has already received a copy of these materials or that you have already sent this +user a copy. +For an executable, the required form of the "work that uses the Library" must include any data and +utility programs needed for reproducing the executable from it. However, as a special exception, the +materials to be distributed need not include anything that is normally distributed (in either source or +binary form) with the major components (compiler, kernel, and so on) of the operating system on which +the executable runs, unless that component itself accompanies the executable. + +It may happen that this requirement contradicts the license restrictions of other proprietary libraries +that do not normally accompany the operating system. Such a contradiction means you cannot use both +them and the Library together in an executable that you distribute. + +7. You may place library facilities that are a work based on the Library side-by-side in a single library +together with other library facilities not covered by this License, and distribute such a combined library, +provided that the separate distribution of the work based on the Library and of the other library +facilities is otherwise permitted, and provided that you do these two things: + +a) Accompany the combined library with a copy of the same work based on the Library, uncombined +with any other library facilities. This must be distributed under the terms of the Sections above. +b) Give prominent notice with the combined library of the fact that part of it is a work based on the +Library, and explaining where to find the accompanying uncombined form of the same work. +8. You may not copy, modify, sublicense, link with, or distribute the Library except as expressly +provided under this License. Any attempt otherwise to copy, modify, sublicense, link with, or distribute +the Library is void, and will automatically terminate your rights under this License. However, parties +who have received copies, or rights, from you under this License will not have their licenses terminated +so long as such parties remain in full compliance. + +9. You are not required to accept this License, since you have not signed it. However, nothing else +grants you permission to modify or distribute the Library or its derivative works. These actions are +prohibited by law if you do not accept this License. Therefore, by modifying or distributing the Library +(or any work based on the Library), you indicate your acceptance of this License to do so, and all its +terms and conditions for copying, distributing or modifying the Library or works based on it. + +10. Each time you redistribute the Library (or any work based on the Library), the recipient +automatically receives a license from the original licensor to copy, distribute, link with or modify the +Library subject to these terms and conditions. You may not impose any further restrictions on the +recipients' exercise of the rights granted herein. You are not responsible for enforcing compliance by +third parties with this License. + +11. If, as a consequence of a court judgment or allegation of patent infringement or for any other +reason (not limited to patent issues), conditions are imposed on you (whether by court order, +agreement or otherwise) that contradict the conditions of this License, they do not excuse you from the +conditions of this License. If you cannot distribute so as to satisfy simultaneously your obligations +under this License and any other pertinent obligations, then as a consequence you may not distribute +the Library at all. For example, if a patent license would not permit royalty-free redistribution of the +Library by all those who receive copies directly or indirectly through you, then the only way you could +satisfy both it and this License would be to refrain entirely from distribution of the Library. + +If any portion of this section is held invalid or unenforceable under any particular circumstance, the +balance of the section is intended to apply, and the section as a whole is intended to apply in other +circumstances. + +It is not the purpose of this section to induce you to infringe any patents or other property right claims +or to contest validity of any such claims; this section has the sole purpose of protecting the integrity of +the free software distribution system which is implemented by public license practices. Many people +have made generous contributions to the wide range of software distributed through that system in +reliance on consistent application of that system; it is up to the author/donor to decide if he or she is +willing to distribute software through any other system and a licensee cannot impose that choice. + +This section is intended to make thoroughly clear what is believed to be a consequence of the rest of +this License. + +12. If the distribution and/or use of the Library is restricted in certain countries either by patents or by +copyrighted interfaces, the original copyright holder who places the Library under this License may add +an explicit geographical distribution limitation excluding those countries, so that distribution is +permitted only in or among countries not thus excluded. In such case, this License incorporates the +limitation as if written in the body of this License. + +13. The Free Software Foundation may publish revised and/or new versions of the Lesser General Public +License from time to time. Such new versions will be similar in spirit to the present version, but may +differ in detail to address new problems or concerns. + +Each version is given a distinguishing version number. If the Library specifies a version number of this +License which applies to it and "any later version", you have the option of following the terms and +conditions either of that version or of any later version published by the Free Software Foundation. If +the Library does not specify a license version number, you may choose any version ever published by +the Free Software Foundation. + +14. If you wish to incorporate parts of the Library into other free programs whose distribution +conditions are incompatible with these, write to the author to ask for permission. For software which is +copyrighted by the Free Software Foundation, write to the Free Software Foundation; we sometimes +make exceptions for this. Our decision will be guided by the two goals of preserving the free status of +all derivatives of our free software and of promoting the sharing and reuse of software generally. + +NO WARRANTY + +15. BECAUSE THE LIBRARY IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY FOR THE LIBRARY, +TO THE EXTENT PERMITTED BY APPLICABLE LAW. EXCEPT WHEN OTHERWISE STATED IN WRITING THE +COPYRIGHT HOLDERS AND/OR OTHER PARTIES PROVIDE THE LIBRARY "AS IS" WITHOUT WARRANTY OF +ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES +OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE ENTIRE RISK AS TO THE QUALITY +AND PERFORMANCE OF THE LIBRARY IS WITH YOU. SHOULD THE LIBRARY PROVE DEFECTIVE, YOU +ASSUME THE COST OF ALL NECESSARY SERVICING, REPAIR OR CORRECTION. + +16. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING WILL ANY +COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY AND/OR REDISTRIBUTE THE LIBRARY AS +PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL +OR CONSEQUENTIAL DAMAGES ARISING OUT OF THE USE OR INABILITY TO USE THE LIBRARY +(INCLUDING BUT NOT LIMITED TO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR LOSSES +SUSTAINED BY YOU OR THIRD PARTIES OR A FAILURE OF THE LIBRARY TO OPERATE WITH ANY OTHER +SOFTWARE), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH +DAMAGES. + +END OF TERMS AND CONDITIONS + +How to Apply These Terms to Your New Libraries + +If you develop a new library, and you want it to be of the greatest possible use to the public, we +recommend making it free software that everyone can redistribute and change. You can do so by +permitting redistribution under these terms (or, alternatively, under the terms of the ordinary General +Public License). + +To apply these terms, attach the following notices to the library. It is safest to attach them to the start +of each source file to most effectively convey the exclusion of warranty; and each file should have at +least the "copyright" line and a pointer to where the full notice is found. + +one line to give the library's name and an idea of what it does. +Copyright (C) year name of author + +This library is free software; you can redistribute it and/or +modify it under the terms of the GNU Lesser General Public +License as published by the Free Software Foundation; either +version 2.1 of the License, or (at your option) any later version. + +This library is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +Lesser General Public License for more details. + +You should have received a copy of the GNU Lesser General Public +License along with this library; if not, write to the Free Software +Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA +Also add information on how to contact you by electronic and paper mail. + +You should also get your employer (if you work as a programmer) or your school, if any, to sign a +"copyright disclaimer" for the library, if necessary. Here is a sample; alter the names: + +Yoyodyne, Inc., hereby disclaims all copyright interest in +the library `Frob' (a library for tweaking knobs) written +by James Random Hacker. + +signature of Ty Coon, 1 April 1990 +Ty Coon, President of Vice +That's all there is to it! +*************************************************************************** +%%The following software may be included in this product: +Boo +Use of any of this software is governed by the terms of the license below: +Copyright (c) 2003, 2004, Rodrigo B. de Oliveira (rbo@acm.org) +All rights reserved. + +Redistribution and use in source and binary forms, with or without modification, +are permitted provided that the following conditions are met: + +* Redistributions of source code must retain the above copyright notice, +this list of conditions and the following disclaimer. +* Redistributions in binary form must reproduce the above copyright notice, +this list of conditions and the following disclaimer in the documentation +and/or other materials provided with the distribution. +* Neither the name of Rodrigo B. de Oliveira nor the names of its +contributors may be used to endorse or promote products derived from this +software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND +ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED +WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE +FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL +DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR +SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER +CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, +OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF +THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +*************************************************************************** + + +DO NOT TRANSLATE OR LOCALIZE +*************************************************************************** +%%The following software may be included in this product: +Groovy Scripting Language +Use of any of this software is governed by the terms of the license below: +Apache License +Version 2.0, January 2004 +http://www.apache.org/licenses/ + +TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION + +1. Definitions. + +"License" shall mean the terms and conditions for use, reproduction, and +distribution as defined by Sections 1 through 9 of this document. + +"Licensor" shall mean the copyright owner or entity authorized by the copyright +owner that is granting the License. + +"Legal Entity" shall mean the union of the acting entity and all other entities +that control, are controlled by, or are under common control with that entity. +For the purposes of this definition, "control" means (i) the power, direct or +indirect, to cause the direction or management of such entity, whether by +contract or otherwise, or (ii) ownership of fifty percent (50%) or more of the +outstanding shares, or (iii) beneficial ownership of such entity. + +"You" (or "Your") shall mean an individual or Legal Entity exercising +permissions granted by this License. + +"Source" form shall mean the preferred form for making modifications, including +but not limited to software source code, documentation source, and configuration +files. + +"Object" form shall mean any form resulting from mechanical transformation or +translation of a Source form, including but not limited to compiled object code, +generated documentation, and conversions to other media types. + +"Work" shall mean the work of authorship, whether in Source or Object form, made +available under the License, as indicated by a copyright notice that is included +in or attached to the work (an example is provided in the Appendix below). + +"Derivative Works" shall mean any work, whether in Source or Object form, that +is based on (or derived from) the Work and for which the editorial revisions, +annotations, elaborations, or other modifications represent, as a whole, an +original work of authorship. For the purposes of this License, Derivative Works +shall not include works that remain separable from, or merely link (or bind by +name) to the interfaces of, the Work and Derivative Works thereof. + +"Contribution" shall mean any work of authorship, including the original version +of the Work and any modifications or additions to that Work or Derivative Works +thereof, that is intentionally submitted to Licensor for inclusion in the Work +by the copyright owner or by an individual or Legal Entity authorized to submit +on behalf of the copyright owner. For the purposes of this definition, +"submitted" means any form of electronic, verbal, or written communication sent +to the Licensor or its representatives, including but not limited to +communication on electronic mailing lists, source code control systems, and +issue tracking systems that are managed by, or on behalf of, the Licensor for +the purpose of discussing and improving the Work, but excluding communication +that is conspicuously marked or otherwise designated in writing by the copyright +owner as "Not a Contribution." + +"Contributor" shall mean Licensor and any individual or Legal Entity on behalf +of whom a Contribution has been received by Licensor and subsequently +incorporated within the Work. + +2. Grant of Copyright License. Subject to the terms and conditions of this +License, each Contributor hereby grants to You a perpetual, worldwide, +non-exclusive, no-charge, royalty-free, irrevocable copyright license to +reproduce, prepare Derivative Works of, publicly display, publicly perform, +sublicense, and distribute the Work and such Derivative Works in Source or +Object form. + +3. Grant of Patent License. Subject to the terms and conditions of this License, +each Contributor hereby grants to You a perpetual, worldwide, non-exclusive, +no-charge, royalty-free, irrevocable (except as stated in this section) patent +license to make, have made, use, offer to sell, sell, import, and otherwise +transfer the Work, where such license applies only to those patent claims +licensable by such Contributor that are necessarily infringed by their +Contribution(s) alone or by combination of their Contribution(s) with the Work +to which such Contribution(s) was submitted. If You institute patent litigation +against any entity (including a cross-claim or counterclaim in a lawsuit) +alleging that the Work or a Contribution incorporated within the Work +constitutes direct or contributory patent infringement, then any patent licenses +granted to You under this License for that Work shall terminate as of the date +such litigation is filed. + +4. Redistribution. You may reproduce and distribute copies of the Work or +Derivative Works thereof in any medium, with or without modifications, and in +Source or Object form, provided that You meet the following conditions: + +1. You must give any other recipients of the Work or Derivative Works a copy +of this License; and + +2. You must cause any modified files to carry prominent notices stating that +You changed the files; and + +3. You must retain, in the Source form of any Derivative Works that You +distribute, all copyright, patent, trademark, and attribution notices from the +Source form of the Work, excluding those notices that do not pertain to any part +of the Derivative Works; and + +4. If the Work includes a "NOTICE" text file as part of its distribution, +then any Derivative Works that You distribute must include a readable copy of +the attribution notices contained within such NOTICE file, excluding those +notices that do not pertain to any part of the Derivative Works, in at least one +of the following places: within a NOTICE text file distributed as part of the +Derivative Works; within the Source form or documentation, if provided along +with the Derivative Works; or, within a display generated by the Derivative +Works, if and wherever such third-party notices normally appear. The contents of +the NOTICE file are for informational purposes only and do not modify the +License. You may add Your own attribution notices within Derivative Works that +You distribute, alongside or as an addendum to the NOTICE text from the Work, +provided that such additional attribution notices cannot be construed as +modifying the License. + +You may add Your own copyright statement to Your modifications and may provide +additional or different license terms and conditions for use, reproduction, or +distribution of Your modifications, or for any such Derivative Works as a whole, +prov ided Your use, reproduction, and distribution of the Work otherwise complies +with the conditions stated in this License. + +5. Submission of Contributions. Unless You explicitly state otherwise, any +Contribution intentionally submitted for inclusion in the Work by You to the +Licensor shall be under the terms and conditions of this License, without any +additional terms or conditions. Notwithstanding the above, nothing herein shall +supersede or modify the terms of any separate license agreement you may have +executed with Licensor regarding such Contributions. + +6. Trademarks. This License does not grant permission to use the trade names, +trademarks, service marks, or product names of the Licensor, except as required +for reasonable and customary use in describing the origin of the Work and +reproducing the content of the NOTICE file. + +7. Disclaimer of Warranty. Unless required by applicable law or agreed to in +writing, Licensor provides the Work (and each Contributor provides its +Contributions) on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +KIND, either express or implied, including, without limitation, any warranties +or conditions of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A +PARTICULAR PURPOSE. You are solely responsible for determining the +appropriateness of using or redistributing the Work and assume any risks +associated with Your exercise of permissions under this License. + +8. Limitation of Liability. In no event and under no legal theory, whether in +tort (including negligence), contract, or otherwise, unless required by +applicable law (such as deliberate and grossly negligent acts) or agreed to in +writing, shall any Contributor be liable to You for damages, including any +direct, indirect, special, incidental, or consequential damages of any character +arising as a result of this License or out of the use or inability to use the +Work (including but not limited to damages for loss of goodwill, work stoppage, +computer failure or malfunction, or any and all other commercial damages or +losses), even if such Contributor has been advised of the possibility of such +damages. + +9. Accepting Warranty or Additional Liability. While redistributing the Work or +Derivative Works thereof, You may choose to offer, and charge a fee for, +acceptance of support, warranty, indemnity, or other liability obligations +and/or rights consistent with this License. However, in accepting such +obligations, You may act only on Your own behalf and on Your sole +responsibility, not on behalf of any other Contributor, and only if You agree to +indemnify, defend, and hold each Contributor harmless for any liability incurred +by, or claims asserted against, such Contributor by reason of your accepting any +such warranty or additional liability. + +END OF TERMS AND CONDITIONS +APPENDIX: How to apply the Apache License to your work + +To apply the Apache License to your work, attach the following boilerplate +notice, with the fields enclosed by brackets "[]" replaced with your own +identifying information. (Don't include the brackets!) The text should be +enclosed in the appropriate comment syntax for the file format. We also +recommend that a file or class name and description of purpose be included on +the same "printed page" as the copyright notice for easier identification within +third-party archives. +Copyright [yyyy] [name of copyright owner] 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. +*************************************************************************** +%%The following software may be included in this product: +Apache Commons Pool +Use of any of this software is governed by the terms of the license below: +Apache License +Version 2.0, January 2004 +http://www.apache.org/licenses/ + +TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION + +1. Definitions. + +"License" shall mean the terms and conditions for use, reproduction, +and distribution as defined by Sections 1 through 9 of this document. + +"Licensor" shall mean the copyright owner or entity authorized by +the copyright owner that is granting the License. + +"Legal Entity" shall mean the union of the acting entity and all +other entities that control, are controlled by, or are under common +control with that entity. For the purposes of this definition, +"control" means (i) the power, direct or indirect, to cause the +direction or management of such entity, whether by contract or +otherwise, or (ii) ownership of fifty percent (50%) or more of the +outstanding shares, or (iii) beneficial ownership of such entity. + +"You" (or "Your") shall mean an individual or Legal Entity +exercising permissions granted by this License. + +"Source" form shall mean the preferred form for making modifications, +including but not limited to software source code, documentation +source, and configuration files. + +"Object" form shall mean any form resulting from mechanical +transformation or translation of a Source form, including but +not limited to compiled object code, generated documentation, +and conversions to other media types. + +"Work" shall mean the work of authorship, whether in Source or +Object form, made available under the License, as indicated by a +copyright notice that is included in or attached to the work +(an example is provided in the Appendix below). + +"Derivative Works" shall mean any work, whether in Source or Object +form, that is based on (or derived from) the Work and for which the +editorial revisions, annotations, elaborations, or other modifications +represent, as a whole, an original work of authorship. For the purposes +of this License, Derivative Works shall not include works that remain +separable from, or merely link (or bind by name) to the interfaces of, +the Work and Derivative Works thereof. + +"Contribution" shall mean any work of authorship, including +the original version of the Work and any modifications or additions +to that Work or Derivative Works thereof, that is intentionally +submitted to Licensor for inclusion in the Work by the copyright owner +or by an individual or Legal Entity authorized to submit on behalf of +the copyright owner. For the purposes of this definition, "submitted" +means any form of electronic, verbal, or written communication sent +to the Licensor or its representatives, including but not limited to +communication on electronic mailing lists, source code control systems, +and issue tracking systems that are managed by, or on behalf of, the +Licensor for the purpose of discussing and improving the Work, but +excluding communication that is conspicuously marked or otherwise +designated in writing by the copyright owner as "Not a Contribution." + +"Contributor" shall mean Licensor and any individual or Legal Entity +on behalf of whom a Contribution has been received by Licensor and +subsequently incorporated within the Work. + +2. Grant of Copyright License. Subject to the terms and conditions of +this License, each Contributor hereby grants to You a perpetual, +worldwide, non-exclusive, no-charge, royalty-free, irrevocable +copyright license to reproduce, prepare Derivative Works of, +publicly display, publicly perform, sublicense, and distribute the +Work and such Derivative Works in Source or Object form. + +3. Grant of Patent License. Subject to the terms and conditions of +this License, each Contributor hereby grants to You a perpetual, +worldwide, non-exclusive, no-charge, royalty-free, irrevocable +(except as stated in this section) patent license to make, have made, +use, offer to sell, sell, import, and otherwise transfer the Work, +where such license applies only to those patent claims licensable +by such Contributor that are necessarily infringed by their +Contribution(s) alone or by combination of their Contribution(s) +with the Work to which such Contribution(s) was submitted. If You +institute patent litigation against any entity (including a +cross-claim or counterclaim in a lawsuit) alleging that the Work +or a Contribution incorporated within the Work constitutes direct +or contributory patent infringement, then any patent licenses +granted to You under this License for that Work shall terminate +as of the date such litigation is filed. + +4. Redistribution. You may reproduce and distribute copies of the +Work or Derivative Works thereof in any medium, with or without +modifications, and in Source or Object form, provided that You +meet the following conditions: + +(a) You must give any other recipients of the Work or +Derivative Works a copy of this License; and + +(b) You must cause any modified files to carry prominent notices +stating that You changed the files; and + +(c) You must retain, in the Source form of any Derivative Works +that You distribute, all copyright, patent, trademark, and +attribution notices from the Source form of the Work, +excluding those notices that do not pertain to any part of +the Derivative Works; and + +(d) If the Work includes a "NOTICE" text file as part of its +distribution, then any Derivative Works that You distribute must +include a readable copy of the attribution notices contained +within such NOTICE file, excluding those notices that do not +pertain to any part of the Derivative Works, in at least one +of the following places: within a NOTICE text file distributed +as part of the Derivative Works; within the Source form or +documentation, if provided along with the Derivative Works; or, +within a display generated by the Derivative Works, if and +wherever such third-party notices normally appear. The contents +of the NOTICE file are for informational purposes only and +do not modify the License. You may add Your own attribution +notices within Derivative Works that You distribute, alongside +or as an addendum to the NOTICE text from the Work, provided +that such additional attribution notices cannot be construed +as modifying the License. + +You may add Your own copyright statement to Your modifications and +may provide additional or different license terms and conditions +for use, reproduction, or distribution of Your modifications, or +for any such Derivative Works as a whole, provided Your use, +reproduction, and distribution of the Work otherwise complies with +the conditions stated in this License. + +5. Submission of Contributions. Unless You explicitly state otherwise, +any Contribution intentionally submitted for inclusion in the Work +by You to the Licensor shall be under the terms and conditions of +this License, without any additional terms or conditions. +Notwithstanding the above, nothing herein shall supersede or modify +the terms of any separate license agreement you may have executed +with Licensor regarding such Contributions. + +6. Trademarks. This License does not grant permission to use the trade +names, trademarks, service marks, or product names of the Licensor, +except as required for reasonable and customary use in describing the +origin of the Work and reproducing the content of the NOTICE file. + +7. Disclaimer of Warranty. Unless required by applicable law or +agreed to in writing, Licensor provides the Work (and each +Contributor provides its Contributions) on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or +implied, including, without limitation, any warranties or conditions +of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A +PARTICULAR PURPOSE. You are solely responsible for determining the +appropriateness of using or redistributing the Work and assume any +risks associated with Your exercise of permissions under this License. + +8. Limitation of Liability. In no event and under no legal theory, +whether in tort (including negligence), contract, or otherwise, +unless required by applicable law (such as deliberate and grossly +negligent acts) or agreed to in writing, shall any Contributor be +liable to You for damages, including any direct, indirect, special, +incidental, or consequential damages of any character arising as a +result of this License or out of the use or inability to use the +Work (including but not limited to damages for loss of goodwill, +work stoppage, computer failure or malfunction, or any and all +other commercial damages or losses), even if such Contributor +has been advised of the possibility of such damages. + +9. Accepting Warranty or Additional Liability. While redistributing +the Work or Derivative Works thereof, You may choose to offer, +and charge a fee for, acceptance of support, warranty, indemnity, +or other liability obligations and/or rights consistent with this +License. However, in accepting such obligations, You may act only +on Your own behalf and on Your sole responsibility, not on behalf +of any other Contributor, and only if You agree to indemnify, +defend, and hold each Contributor harmless for any liability +incurred by, or claims asserted against, such Contributor by reason +of your accepting any such warranty or additional liability. + +END OF TERMS AND CONDITIONS + +APPENDIX: How to apply the Apache License to your work. + +To apply the Apache License to your work, attach the following +boilerplate notice, with the fields enclosed by brackets "[]" +replaced with your own identifying information. (Don't include +the brackets!) The text should be enclosed in the appropriate +comment syntax for the file format. We also recommend that a +file or class name and description of purpose be included on the +same "printed page" as the copyright notice for easier +identification within third-party archives. + +Copyright [yyyy] [name of copyright owner] + +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. +*************************************************************************** +%%The following software may be included in this product: +HSQL Database Engine +Use of any of this software is governed by the terms of the license below: +COPYRIGHTS AND LICENSES + +ORIGINAL LICENSE (a.k.a. "hypersonic_lic.txt") + +For content, code, and products originally developed by Thomas Mueller and the +Hypersonic SQL Group: + +Copyright (c) 1995-2000 by the Hypersonic SQL Group. +All rights reserved. + + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are met: + +Redistributions of source code must retain the above copyright notice, this +list of conditions and the following disclaimer. + +Redistributions in binary form must reproduce the above copyright notice, +this list of conditions and the following disclaimer in the documentation +and/or other materials provided with the distribution. + +Neither the name of the Hypersonic SQL Group nor the names of its +contributors may be used to endorse or promote products derived from this +software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" +AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE +ARE DISCLAIMED. IN NO EVENT SHALL THE HYPERSONIC SQL GROUP, +OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; +LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND +ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS +SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +This software consists of voluntary contributions made by many individuals on +behalf of the +Hypersonic SQL Group. + + + +For work added by the HSQL Development Group (a.k.a. hsqldb_lic.txt): +Copyright (c) 2001-2005, The HSQL Development Group +All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are met: + +Redistributions of source code must retain the above copyright notice, this +list of conditions and the following disclaimer. + +Redistributions in binary form must reproduce the above copyright notice, +this list of conditions and the following disclaimer in the documentation +and/or other materials provided with the distribution. + +Neither the name of the HSQL Development Group nor the names of its +contributors may be used to endorse or promote products derived from this +software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" +AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE +ARE DISCLAIMED. IN NO EVENT SHALL HSQL DEVELOPMENT GROUP, HSQLDB.ORG, +OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; +LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND +ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS +SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +*************************************************************************** +%%The following software may be included in this product: +Derby +Use of any of this software is governed by the terms of the license below: +Apache License, Version 2.0 + +Apache License +Version 2.0, January 2004 +http://www.apache.org/licenses/ + +TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION + +1. Definitions. + +"License" shall mean the terms and conditions for use, reproduction, and +distribution as defined by Sections 1 through 9 of this document. + +"Licensor" shall mean the copyright owner or entity authorized by the copyright +owner that is granting the License. + +"Legal Entity" shall mean the union of the acting entity and all other entities +that control, are controlled by, or are under common control with that entity. +For the purposes of this definition, "control" means (i) the power, direct or +indirect, to cause the direction or management of such entity, whether by +contract or otherwise, or (ii) ownership of fifty percent (50%) or more of the +outstanding shares, or (iii) beneficial ownership of such entity. + +"You" (or "Your") shall mean an individual or Legal Entity exercising +permissions granted by this License. + +"Source" form shall mean the preferred form for making modifications, including +but not limited to software source code, documentation source, and configuration +files. + +"Object" form shall mean any form resulting from mechanical transformation or +translation of a Source form, including but not limited to compiled object code, +generated documentation, and conversions to other media types. + +"Work" shall mean the work of authorship, whether in Source or Object form, made +available under the License, as indicated by a copyright notice that is included +in or attached to the work (an example is provided in the Appendix below). + +"Derivative Works" shall mean any work, whether in Source or Object form, that +is based on (or derived from) the Work and for which the editorial revisions, +annotations, elaborations, or other modifications represent, as a whole, an +original work of authorship. For the purposes of this License, Derivative Works +shall not include works that remain separable from, or merely link (or bind by +name) to the interfaces of, the Work and Derivative Works thereof. + +"Contribution" shall mean any work of authorship, including the original version +of the Work and any modifications or additions to that Work or Derivative Works +thereof, that is intentionally submitted to Licensor for inclusion in the Work +by the copyright owner or by an individual or Legal Entity authorized to submit +on behalf of the copyright owner. For the purposes of this definition, +"submitted" means any form of electronic, verbal, or written communication sent +to the Licensor or its representatives, including but not limited to +communication on electronic mailing lists, source code control systems, and +issue tracking systems that are managed by, or on behalf of, the Licensor for +the purpose of discussing and improving the Work, but excluding communication +that is conspicuously marked or otherwise designated in writing by the copyright +owner as "Not a Contribution." + +"Contributor" shall mean Licensor and any individual or Legal Entity on behalf +of whom a Contribution has been received by Licensor and subsequently +incorporated within the Work. + +2. Grant of Copyright License. Subject to the terms and conditions of this +License, each Contributor hereby grants to You a perpetual, worldwide, +non-exclusive, no-charge, royalty-free, irrevocable copyright license to +reproduce, prepare Derivative Works of, publicly display, publicly perform, +sublicense, and distribute the Work and such Derivative Works in Source or +Object form. + +3. Grant of Patent License. Subject to the terms and conditions of this License, +each Contributor hereby grants to You a perpetual, worldwide, non-exclusive, +no-charge, royalty-free, irrevocable (except as stated in this section) patent +license to make, have made, use, offer to sell, sell, import, and otherwise +transfer the Work, where such license applies only to those patent claims +licensable by such Contributor that are necessarily infringed by their +Contribution(s) alone or by combination of their Contribution(s) with the Work +to which such Contribution(s) was submitted. If You institute patent litigation +against any entity (including a cross-claim or counterclaim in a lawsuit) +alleging that the Work or a Contribution incorporated within the Work +constitutes direct or contributory patent infringement, then any patent licenses +granted to You under this License for that Work shall terminate as of the date +such litigation is filed. + +4. Redistribution. You may reproduce and distribute copies of the Work or +Derivative Works thereof in any medium, with or without modifications, and in +Source or Object form, provided that You meet the following conditions: + +1. You must give any other recipients of the Work or Derivative Works a copy +of this License; and + +2. You must cause any modified files to carry prominent notices stating that +You changed the files; and + +3. You must retain, in the Source form of any Derivative Works that You +distribute, all copyright, patent, trademark, and attribution notices from the +Source form of the Work, excluding those notices that do not pertain to any part +of the Derivative Works; and + +4. If the Work includes a "NOTICE" text file as part of its distribution, +then any Derivative Works that You distribute must include a readable copy of +the attribution notices contained within such NOTICE file, excluding those +notices that do not pertain to any part of the Derivative Works, in at least one +of the following places: within a NOTICE text file distributed as part of the +Derivative Works; within the Source form or documentation, if provided along +with the Derivative Works; or, within a display generated by the Derivative +Works, if and wherever such third-party notices normally appear. The contents of +the NOTICE file are for informational purposes only and do not modify the +License. You may add Your own attribution notices within Derivative Works that +You distribute, alongside or as an addendum to the NOTICE text from the Work, +provided that such additional attribution notices cannot be construed as +modifying the License. + +You may add Your own copyright statement to Your modifications and may provide +additional or different license terms and conditions for use, reproduction, or +distribution of Your modifications, or for any such Derivative Works as a whole, +provided Your use, reproduction, and distribution of the Work otherwise complies +with the conditions stated in this License. + +5. Submission of Contributions. Unless You explicitly state otherwise, any +Contribution intentionally submitted for inclusion in the Work by You to the +Licensor shall be under the terms and conditions of this License, without any +additional terms or conditions. Notwithstanding the above, nothing herein shall +supersede or modify the terms of any separate license agreement you may have +executed with Licensor regarding such Contributions. + +6. Trademarks. This License does not grant permission to use the trade names, +trademarks, service marks, or product names of the Licensor, except as required +for reasonable and customary use in describing the origin of the Work and +reproducing the content of the NOTICE file. + +7. Disclaimer of Warranty. Unless required by applicable law or agreed to in +writing, Licensor provides the Work (and each Contributor provides its +Contributions) on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +KIND, either express or implied, including, without limitation, any warranties +or conditions of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A +PARTICULAR PURPOSE. You are solely responsible for determining the +appropriateness of using or redistributing the Work and assume any risks +associated with Your exercise of permissions under this License. + +8. Limitation of Liability. In no event and under no legal theory, whether in +tort (including negligence), contract, or otherwise, unless required by +applicable law (such as deliberate and grossly negligent acts) or agreed to in +writing, shall any Contributor be liable to You for damages, including any +direct, indirect, special, incidental, or consequential damages of any character +arising as a result of this License or out of the use or inability to use the +Work (including but not limited to damages for loss of goodwill, work stoppage, +computer failure or malfunction, or any and all other commercial damages or +losses), even if such Contributor has been advised of the possibility of such +damages. + +9. Accepting Warranty or Additional Liability. While redistributing the Work or +Derivative Works thereof, You may choose to offer, and charge a fee for, +acceptance of support, warranty, indemnity, or other liability obligations +and/or rights consistent with this License. However, in accepting such +obligations, You may act only on Your own behalf and on Your sole +responsibility, not on behalf of any other Contributor, and only if You agree to +indemnify, defend, and hold each Contributor harmless for any liability incurred +by, or claims asserted against, such Contributor by reason of your accepting any +such warranty or additional liability. + +END OF TERMS AND CONDITIONS +APPENDIX: How to apply the Apache License to your work + +To apply the Apache License to your work, attach the following boilerplate +notice, with the fields enclosed by brackets "[]" replaced with your own +identifying information. (Don't include the brackets!) The text should be +enclosed in the appropriate comment syntax for the file format. We also +recommend that a file or class name and description of purpose be included on +the same "printed page" as the copyright notice for easier identification within +third-party archives. +Copyright [yyyy] [name of copyright owner] 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. +*************************************************************************** +%%The following software may be included in this product: +Apache Commons - commons-net-1.4.1.jar +Use of any of this software is governed by the terms of the license below: +Apache License +Version 2.0, January 2004 +http://www.apache.org/licenses/ + +TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION + +1. Definitions. + +"License" shall mean the terms and conditions for use, reproduction, +and distribution as defined by Sections 1 through 9 of this document. + +"Licensor" shall mean the copyright owner or entity authorized by +the copyright owner that is granting the License. + +"Legal Entity" shall mean the union of the acting entity and all +other entities that control, are controlled by, or are under common +control with that entity. For the purposes of this definition, +"control" means (i) the power, direct or indirect, to cause the +direction or management of such entity, whether by contract or +otherwise, or (ii) ownership of fifty percent (50%) or more of the +outstanding shares, or (iii) beneficial ownership of such entity. + +"You" (or "Your") shall mean an individual or Legal Entity +exercising permissions granted by this License. + +"Source" form shall mean the preferred form for making modifications, +including but not limited to software source code, documentation +source, and configuration files. + +"Object" form shall mean any form resulting from mechanical +transformation or translation of a Source form, including but +not limited to compiled object code, generated documentation, +and conversions to other media types. + +"Work" shall mean the work of authorship, whether in Source or +Object form, made available under the License, as indicated by a +copyright notice that is included in or attached to the work +(an example is provided in the Appendix below). + +"Derivative Works" shall mean any work, whether in Source or Object +form, that is based on (or derived from) the Work and for which the +editorial revisions, annotations, elaborations, or other modifications +represent, as a whole, an original work of authorship. For the purposes +of this License, Derivative Works shall not include works that remain +separable from, or merely link (or bind by name) to the interfaces of, +the Work and Derivative Works thereof. + +"Contribution" shall mean any work of authorship, including +the original version of the Work and any modifications or additions +to that Work or Derivative Works thereof, that is intentionally +submitted to Licensor for inclusion in the Work by the copyright owner +or by an individual or Legal Entity authorized to submit on behalf of +the copyright owner. For the purposes of this definition, "submitted" +means any form of electronic, verbal, or written communication sent +to the Licensor or its representatives, including but not limited to +communication on electronic mailing lists, source code control systems, +and issue tracking systems that are managed by, or on behalf of, the +Licensor for the purpose of discussing and improving the Work, but +excluding communication that is conspicuously marked or otherwise +designated in writing by the copyright owner as "Not a Contribution." + +"Contributor" shall mean Licensor and any individual or Legal Entity +on behalf of whom a Contribution has been received by Licensor and +subsequently incorporated within the Work. + +2. Grant of Copyright License. Subject to the terms and conditions of +this License, each Contributor hereby grants to You a perpetual, +worldwide, non-exclusive, no-charge, royalty-free, irrevocable +copyright license to reproduce, prepare Derivative Works of, +publicly display, publicly perform, sublicense, and distribute the +Work and such Derivative Works in Source or Object form. + +3. Grant of Patent License. Subject to the terms and conditions of +this License, each Contributor hereby grants to You a perpetual, +worldwide, non-exclusive, no-charge, royalty-free, irrevocable +(except as stated in this section) patent license to make, have made, +use, offer to sell, sell, import, and otherwise transfer the Work, +where such license applies only to those patent claims licensable +by such Contributor that are necessarily infringed by their +Contribution(s) alone or by combination of their Contribution(s) +with the Work to which such Contribution(s) was submitted. If You +institute patent litigation against any entity (including a +cross-claim or counterclaim in a lawsuit) alleging that the Work +or a Contribution incorporated within the Work constitutes direct +or contributory patent infringement, then any patent licenses +granted to You under this License for that Work shall terminate +as of the date such litigation is filed. + +4. Redistribution. You may reproduce and distribute copies of the +Work or Derivative Works thereof in any medium, with or without +modifications, and in Source or Object form, provided that You +meet the following conditions: + +(a) You must give any other recipients of the Work or +Derivative Works a copy of this License; and + +(b) You must cause any modified files to carry prominent notices +stating that You changed the files; and + +(c) You must retain, in the Source form of any Derivative Works +that You distribute, all copyright, patent, trademark, and +attribution notices from the Source form of the Work, +excluding those notices that do not pertain to any part of +the Derivative Works; and + +(d) If the Work includes a "NOTICE" text file as part of its +distribution, then any Derivative Works that You distribute must +include a readable copy of the attribution notices contained +within such NOTICE file, excluding those notices that do not +pertain to any part of the Derivative Works, in at least one +of the following places: within a NOTICE text file distributed +as part of the Derivative Works; within the Source form or +documentation, if provided along with the Derivative Works; or, +within a display generated by the Derivative Works, if and +wherever such third-party notices normally appear. The contents +of the NOTICE file are for informational purposes only and +do not modify the License. You may add Your own attribution +notices within Derivative Works that You distribute, alongside +or as an addendum to the NOTICE text from the Work, provided +that such additional attribution notices cannot be construed +as modifying the License. + +You may add Your own copyright statement to Your modifications and +may provide additional or different license terms and conditions +for use, reproduction, or distribution of Your modifications, or +for any such Derivative Works as a whole, provided Your use, +reproduction, and distribution of the Work otherwise complies with +the conditions stated in this License. + +5. Submission of Contributions. Unless You explicitly state otherwise, +any Contribution intentionally submitted for inclusion in the Work +by You to the Licensor shall be under the terms and conditions of +this License, without any additional terms or conditions. +Notwithstanding the above, nothing herein shall supersede or modify +the terms of any separate license agreement you may have executed +with Licensor regarding such Contributions. + +6. Trademarks. This License does not grant permission to use the trade +names, trademarks, service marks, or product names of the Licensor, +except as required for reasonable and customary use in describing the +origin of the Work and reproducing the content of the NOTICE file. + +7. Disclaimer of Warranty. Unless required by applicable law or +agreed to in writing, Licensor provides the Work (and each +Contributor provides its Contributions) on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or +implied, including, without limitation, any warranties or conditions +of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A +PARTICULAR PURPOSE. You are solely responsible for determining the +appropriateness of using or redistributing the Work and assume any +risks associated with Your exercise of permissions under this License. + +8. Limitation of Liability. In no event and under no legal theory, +whether in tort (including negligence), contract, or otherwise, +unless required by applicable law (such as deliberate and grossly +negligent acts) or agreed to in writing, shall any Contributor be +liable to You for damages, including any direct, indirect, special, +incidental, or consequential damages of any character arising as a +result of this License or out of the use or inability to use the +Work (including but not limited to damages for loss of goodwill, +work stoppage, computer failure or malfunction, or any and all +other commercial damages or losses), even if such Contributor +has been advised of the possibility of such damages. + +9. Accepting Warranty or Additional Liability. While redistributing +the Work or Derivative Works thereof, You may choose to offer, +and charge a fee for, acceptance of support, warranty, indemnity, +or other liability obligations and/or rights consistent with this +License. However, in accepting such obligations, You may act only +on Your own behalf and on Your sole responsibility, not on behalf +of any other Contributor, and only if You agree to indemnify, +defend, and hold each Contributor harmless for any liability +incurred by, or claims asserted against, such Contributor by reason +of your accepting any such warranty or additional liability. + +END OF TERMS AND CONDITIONS + +APPENDIX: How to apply the Apache License to your work. + +To apply the Apache License to your work, attach the following +boilerplate notice, with the fields enclosed by brackets "[]" +replaced with your own identifying information. (Don't include +the brackets!) The text should be enclosed in the appropriate +comment syntax for the file format. We also recommend that a +file or class name and description of purpose be included on the +same "printed page" as the copyright notice for easier +identification within third-party archives. + +Copyright [yyyy] [name of copyright owner] + +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. +*************************************************************************** +%%The following software may be included in this product: +Jakarta Oro +Use of any of this software is governed by the terms of the license below: +Apache License +Version 2.0, January 2004 +http://www.apache.org/licenses/ + +TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION + +1. Definitions. + +"License" shall mean the terms and conditions for use, reproduction, +and distribution as defined by Sections 1 through 9 of this document. + +"Licensor" shall mean the copyright owner or entity authorized by +the copyright owner that is granting the License. + +"Legal Entity" shall mean the union of the acting entity and all +other entities that control, are controlled by, or are under common +control with that entity. For the purposes of this definition, +"control" means (i) the power, direct or indirect, to cause the +direction or management of such entity, whether by contract or +otherwise, or (ii) ownership of fifty percent (50%) or more of the +outstanding shares, or (iii) beneficial ownership of such entity. + +"You" (or "Your") shall mean an individual or Legal Entity +exercising permissions granted by this License. + +"Source" form shall mean the preferred form for making modifications, +including but not limited to software source code, documentation +source, and configuration files. + +"Object" form shall mean any form resulting from mechanical +transformation or translation of a Source form, including but +not limited to compiled object code, generated documentation, +and conversions to other media types. + +"Work" shall mean the work of authorship, whether in Source or +Object form, made available under the License, as indicated by a +copyright notice that is included in or attached to the work +(an example is provided in the Appendix below). + +"Derivative Works" shall mean any work, whether in Source or Object +form, that is based on (or derived from) the Work and for which the +editorial revisions, annotations, elaborations, or other modifications +represent, as a whole, an original work of authorship. For the purposes +of this License, Derivative Works shall not include works that remain +separable from, or merely link (or bind by name) to the interfaces of, +the Work and Derivative Works thereof. + +"Contribution" shall mean any work of authorship, including +the original version of the Work and any modifications or additions +to that Work or Derivative Works thereof, that is intentionally +submitted to Licensor for inclusion in the Work by the copyright owner +or by an individual or Legal Entity authorized to submit on behalf of +the copyright owner. For the purposes of this definition, "submitted" +means any form of electronic, verbal, or written communication sent +to the Licensor or its representatives, including but not limited to +communication on electronic mailing lists, source code control systems, +and issue tracking systems that are managed by, or on behalf of, the +Licensor for the purpose of discussing and improving the Work, but +excluding communication that is conspicuously marked or otherwise +designated in writing by the copyright owner as "Not a Contribution." + +"Contributor" shall mean Licensor and any individual or Legal Entity +on behalf of whom a Contribution has been received by Licensor and +subsequently incorporated within the Work. + +2. Grant of Copyright License. Subject to the terms and conditions of +this License, each Contributor hereby grants to You a perpetual, +worldwide, non-exclusive, no-charge, royalty-free, irrevocable +copyright license to reproduce, prepare Derivative Works of, +publicly display, publicly perform, sublicense, and distribute the +Work and such Derivative Works in Source or Object form. + +3. Grant of Patent License. Subject to the terms and conditions of +this License, each Contributor hereby grants to You a perpetual, +worldwide, non-exclusive, no-charge, royalty-free, irrevocable +(except as stated in this section) patent license to make, have made, +use, offer to sell, sell, import, and otherwise transfer the Work, +where such license applies only to those patent claims licensable +by such Contributor that are necessarily infringed by their +Contribution(s) alone or by combination of their Contribution(s) +with the Work to which such Contribution(s) was submitted. If You +institute patent litigation against any entity (including a +cross-claim or counterclaim in a lawsuit) alleging that the Work +or a Contribution incorporated within the Work constitutes direct +or contributory patent infringement, then any patent licenses +granted to You under this License for that Work shall terminate +as of the date such litigation is filed. + +4. Redistribution. You may reproduce and distribute copies of the +Work or Derivative Works thereof in any medium, with or without +modifications, and in Source or Object form, provided that You +meet the following conditions: + +(a) You must give any other recipients of the Work or +Derivative Works a copy of this License; and + +(b) You must cause any modified files to carry prominent notices +stating that You changed the files; and + +(c) You must retain, in the Source form of any Derivative Works +that You distribute, all copyright, patent, trademark, and +attribution notices from the Source form of the Work, +excluding those notices that do not pertain to any part of +the Derivative Works; and + +(d) If the Work includes a "NOTICE" text file as part of its +distribution, then any Derivative Works that You distribute must +include a readable copy of the attribution notices contained +within such NOTICE file, excluding those notices that do not +pertain to any part of the Derivative Works, in at least one +of the following places: within a NOTICE text file distributed +as part of the Derivative Works; within the Source form or +documentation, if provided along with the Derivative Works; or, +within a display generated by the Derivative Works, if and +wherever such third-party notices normally appear. The contents +of the NOTICE file are for informational purposes only and +do not modify the License. You may add Your own attribution +notices within Derivative Works that You distribute, alongside +or as an addendum to the NOTICE text from the Work, provided +that such additional attribution notices cannot be construed +as modifying the License. + +You may add Your own copyright statement to Your modifications and +may provide additional or different license terms and conditions +for use, reproduction, or distribution of Your modifications, or +for any such Derivative Works as a whole, provided Your use, +reproduction, and distribution of the Work otherwise complies with +the conditions stated in this License. + +5. Submission of Contributions. Unless You explicitly state otherwise, +any Contribution intentionally submitted for inclusion in the Work +by You to the Licensor shall be under the terms and conditions of +this License, without any additional terms or conditions. +Notwithstanding the above, nothing herein shall supersede or modify +the terms of any separate license agreement you may have executed +with Licensor regarding such Contributions. + +6. Trademarks. This License does not grant permission to use the trade +names, trademarks, service marks, or product names of the Licensor, +except as required for reasonable and customary use in describing the +origin of the Work and reproducing the content of the NOTICE file. + +7. Disclaimer of Warranty. Unless required by applicable law or +agreed to in writing, Licensor provides the Work (and each +Contributor provides its Contributions) on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or +implied, including, without limitation, any warranties or conditions +of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A +PARTICULAR PURPOSE. You are solely responsible for determining the +appropriateness of using or redistributing the Work and assume any +risks associated with Your exercise of permissions under this License. + +8. Limitation of Liability. In no event and under no legal theory, +whether in tort (including ne gligence), contract, or otherwise, +unless required by applicable law (such as deliberate and grossly +negligent acts) or agreed to in writing, shall any Contributor be +liable to You for damages, including any direct, indirect, special, +incidental, or consequential damages of any character arising as a +result of this License or out of the use or inability to use the +Work (including but not limited to damages for loss of goodwill, +work stoppage, computer failure or malfunction, or any and all +other commercial damages or losses), even if such Contributor +has been advised of the possibility of such damages. + +9. Accepting Warranty or Additional Liability. While redistributing +the Work or Derivative Works thereof, You may choose to offer, +and charge a fee for, acceptance of support, warranty, indemnity, +or other liability obligations and/or rights consistent with this +License. However, in accepting such obligations, You may act only +on Your own behalf and on Your sole responsibility, not on behalf +of any other Contributor, and only if You agree to indemnify, +defend, and hold each Contributor harmless for any liability +incurred by, or claims asserted against, such Contributor by reason +of your accepting any such warranty or additional liability. + +END OF TERMS AND CONDITIONS + +APPENDIX: How to apply the Apache License to your work. + +To apply the Apache License to your work, attach the following +boilerplate notice, with the fields enclosed by brackets "[]" +replaced with your own identifying information. (Don't include +the brackets!) The text should be enclosed in the appropriate +comment syntax for the file format. We also recommend that a +file or class name and description of purpose be included on the +same "printed page" as the copyright notice for easier +identification within third-party archives. + +Copyright [yyyy] [name of copyright owner] + +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. +*************************************************************************** +%%The following software may be included in this product: +Jsch +Use of any of this software is governed by the terms of the license below: +JSch 0.0.* was released under the GNU LGPL license. Later, we have switched +over to a BSD-style license. + +------------------------------------------------------------------------------ +Copyright (c) 2002,2003,2004,2005,2006,2007,2008 Atsuhiko Yamanaka, JCraft,Inc. +All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are met: + +1. Redistributions of source code must retain the above copyright notice, +this list of conditions and the following disclaimer. + +2. Redistributions in binary form must reproduce the above copyright +notice, this list of conditions and the following disclaimer in +the documentation and/or other materials provided with the distribution. + +3. The names of the authors may not be used to endorse or promote products +derived from this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED OR IMPLIED WARRANTIES, +INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND +FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL JCRAFT, +INC. OR ANY CONTRIBUTORS TO THIS SOFTWARE BE LIABLE FOR ANY DIRECT, INDIRECT, +INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, +OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF +LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING +NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, +EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +Additional License(s) +LICENSE.txt:JSch 0.0.* was released under the GNU LGPL license. +Later, we have switched +LICENSE.txt:over to a BSD-style license. +*************************************************************************** +%%The following software may be included in this product: +Expect4j +Use of any of this software is governed by the terms of the license below: +Apache License, Version 2.0 + +Apache License +Version 2.0, January 2004 +http://www.apache.org/licenses/ + +TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION + +1. Definitions. + +"License" shall mean the terms and conditions for use, reproduction, and +distribution as defined by Sections 1 through 9 of this document. + +"Licensor" shall mean the copyright owner or entity authorized by the copyright +owner that is granting the License. + +"Legal Entity" shall mean the union of the acting entity and all other entities +that control, are controlled by, or are under common control with that entity. +For the purposes of this definition, "control" means (i) the power, direct or +indirect, to cause the direction or management of such entity, whether by +contract or otherwise, or (ii) ownership of fifty percent (50%) or more of the +outstanding shares, or (iii) beneficial ownership of such entity. + +"You" (or "Your") shall mean an individual or Legal Entity exercising +permissions granted by this License. + +"Source" form shall mean the preferred form for making modifications, including +but not limited to software source code, documentation source, and configuration +files. + +"Object" form shall mean any form resulting from mechanical transformation or +translation of a Source form, including but not limited to compiled object code, +generated documentation, and conversions to other media types. + +"Work" shall mean the work of authorship, whether in Source or Object form, made +available under the License, as indicated by a copyright notice that is included +in or attached to the work (an example is provided in the Appendix below). + +"Derivative Works" shall mean any work, whether in Source or Object form, that +is based on (or derived from) the Work and for which the editorial revisions, +annotations, elaborations, or other modifications represent, as a whole, an +original work of authorship. For the purposes of this License, Derivative Works +shall not include works that remain separable from, or merely link (or bind by +name) to the interfaces of, the Work and Derivative Works thereof. + +"Contribution" shall mean any work of authorship, including the original version +of the Work and any modifications or additions to that Work or Derivative Works +thereof, that is intentionally submitted to Licensor for inclusion in the Work +by the copyright owner or by an individual or Legal Entity authorized to submit +on behalf of the copyright owner. For the purposes of this definition, +"submitted" means any form of electronic, verbal, or written communication sent +to the Licensor or its representatives, including but not limited to +communication on electronic mailing lists, source code control systems, and +issue tracking systems that are managed by, or on behalf of, the Licensor for +the purpose of discussing and improving the Work, but excluding communication +that is conspicuously marked or otherwise designated in writing by the copyright +owner as "Not a Contribution." + +"Contributor" shall mean Licensor and any individual or Legal Entity on behalf +of whom a Contribution has been received by Licensor and subsequently +incorporated within the Work. + +2. Grant of Copyright License. Subject to the terms and conditions of this +License, each Contributor hereby grants to You a perpetual, worldwide, +non-exclusive, no-charge, royalty-free, irrevocable copyright license to +reproduce, prepare Derivative Works of, publicly display, publicly perform, +sublicense, and distribute the Work and such Derivative Works in Source or +Object form. + +3. Grant of Patent License. Subject to the terms and conditions of this License, +each Contributor hereby grants to You a perpetual, worldwide, non-exclusive, +no-charge, royalty-free, irrevocable (except as stated in this section) patent +license to make, have made, use, offer to sell, sell, import, and otherwise +transfer the Work, where such license applies only to those patent claims +licensable by such Contributor that are necessarily infringed by their +Contribution(s) alone or by combination of their Contribution(s) with the Work +to which such Contribution(s) was submitted. If You institute patent litigation +against any entity (including a cross-claim or counterclaim in a lawsuit) +alleging that the Work or a Contribution incorporated within the Work +constitutes direct or contributory patent infringement, then any patent licenses +granted to You under this License for that Work shall terminate as of the date +such litigation is filed. + +4. Redistribution. You may reproduce and distribute copies of the Work or +Derivative Works thereof in any medium, with or without modifications, and in +Source or Object form, provided that You meet the following conditions: + +1. You must give any other recipients of the Work or Derivative Works a copy +of this License; and + +2. You must cause any modified files to carry prominent notices stating that +You changed the files; and + +3. You must retain, in the Source form of any Derivative Works that You +distribute, all copyright, patent, trademark, and attribution notices from the +Source form of the Work, excluding those notices that do not pertain to any part +of the Derivative Works; and + +4. If the Work includes a "NOTICE" text file as part of its distribution, +then any Derivative Works that You distribute must include a readable copy of +the attribution notices contained within such NOTICE file, excluding those +notices that do not pertain to any part of the Derivative Works, in at least one +of the following places: within a NOTICE text file distributed as part of the +Derivative Works; within the Source form or documentation, if provided along +with the Derivative Works; or, within a display generated by the Derivative +Works, if and wherever such third-party notices normally appear. The contents of +the NOTICE file are for informational purposes only and do not modify the +License. You may add Your own attribution notices within Derivative Works that +You distribute, alongside or as an addendum to the NOTICE text from the Work, +provided that such additional attribution notices cannot be construed as +modifying the License. + +You may add Your own copyright statement to Your modifications and may provide +additional or different license terms and conditions for use, reproduction, or +distribution of Your modifications, or for any such Derivative Works as a whole, +provided Your use, reproduction, and distribution of the Work otherwise complies +with the conditions stated in this License. + +5. Submission of Contributions. Unless You explicitly state otherwise, any +Contribution intentionally submitted for inclusion in the Work by You to the +Licensor shall be under the terms and conditions of this License, without any +additional terms or conditions. Notwithstanding the above, nothing herein shall +supersede or modify the terms of any separate license agreement you may have +executed with Licensor regarding such Contributions. + +6. Trademarks. This License does not grant permission to use the trade names, +trademarks, service marks, or product names of the Licensor, except as required +for reasonable and customary use in describing the origin of the Work and +reproducing the content of the NOTICE file. + +7. Disclaimer of Warranty. Unless required by applicable law or agreed to in +writing, Licensor provides the Work (and each Contributor provides its +Contributions) on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +KIND, either express or implied, including, without limitation, any warranties +or conditions of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A +PARTICULAR PURPOSE. You are solely responsible for determining the +appropriateness of using or redistributing the Work and assume any risks +associated with Your exercise of permissions under this License. + +8. Limitation of Liability. In no event and under no legal theory, whether in +tort (including negligence), contract, or otherwise, unless required by +applicable law (such as deliberate and grossly negligent acts) or agreed to in +writing, shall any Contributor be liable to You for damages, including any +direct, indirect, special, incidental, or consequential damages of any character +arising as a result of this License or out of the use or inability to use the +Work (including but not limited to damages for loss of goodwill, work stoppage, +computer failure or malfunction, or any and all other commercial damages or +losses), even if such Contributor has been advised of the possibility of such +damages. + +9. Accepting Warranty or Additional Liability. While redistributing the Work or +Derivative Works thereof, You may choose to offer, and charge a fee for, +acceptance of support, warranty, indemnity, or other liability obligations +and/or rights consistent with this License. However, in accepting such +obligations, You may act only on Your own behalf and on Your sole +responsibility, not on behalf of any other Contributor, and only if You agree to +indemnify, defend, and hold each Contributor harmless for any liability incurred +by, or claims asserted against, such Contributor by reason of your accepting any +such warranty or additional liability. + +END OF TERMS AND CONDITIONS +APPENDIX: How to apply the Apache License to your work + +To apply the Apache License to your work, attach the following boilerplate +notice, with the fields enclosed by brackets "[]" replaced with your own +identifying information. (Don't include the brackets!) The text should be +enclosed in the appropriate comment syntax for the file format. We also +recommend that a file or class name and description of purpose be included on +the same "printed page" as the copyright notice for easier identification within +third-party archives. +Copyright [yyyy] [name of copyright owner] 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. +Apache Projects + +* HTTP Server +* ActiveMQ +* Ant +* APR +* Archiva +* Beehive +* Cayenne +* Cocoon +* Commons +* Continuum +* CXF +* DB +* Directory +* Excalibur +* Felix +* Forrest +* Geronimo +* Gump +* Hadoop +* Harmony +* HiveMind +* HttpComponents +* iBATIS +* Incubator +* Jackrabbit +* Jakarta +* James +* Labs +* Lenya +* Logging +* Lucene +* Maven +* Mina +* MyFaces +* ODE +* OFBiz +* OpenEJB +* OpenJPA +* Perl +* POI +* Portals +* Roller +* Santuario +* ServiceMix +* Shale +* SpamAssassin +* STDCXX +* Struts +* Synapse +* Tapestry +* TCL +* Tiles +* Tomcat +* Turbine +* Velocity +* Wicket +* Web Services +* Xalan +* Xerces +* XML +* XMLBeans +* XML Graphics + +Foundation + +* FAQ +* Licenses +* News +* Public Records +* Sponsorship +* Donations +* Thanks +* Contact + +Foundation Projects + +* Conferences +* Infrastructure +* JCP + +How it works + +* Introduction +* Meritocracy +* Structure +* Roles +* Collaboration +* Infrastructure +* Incubator +* Other entities +* Glossary +* Voting + +Get Involved + +* Mailing Lists +* Version Control +* Developer Info + +Download + +* from a mirror + +Related Sites + +* ApacheCon +* Bookstore +* Feathercast +* PlanetApache + +Copyright � 2008 The Apache Software Foundation, Licensed under the Apache +License, Version 2.0. +*************************************************************************** +%%The following software may be included in this product: +FreeHost3270 +Use of any of this software is governed by the terms of the license below: +GNU LESSER GENERAL PUBLIC LICENSE + +Version 2.1, February 1999 + +Copyright (C) 1991, 1999 Free Software Foundation, Inc. +51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA +Everyone is permitted to copy and distribute verbatim copies +of this license document, but changing it is not allowed. + +[This is the first released version of the Lesser GPL. It also counts +as the successor of the GNU Library Public License, version 2, hence +the version number 2.1.] +Preamble + +The licenses for most software are designed to take away your freedom to share and change it. By +contrast, the GNU General Public Licenses are intended to guarantee your freedom to share and change +free software--to make sure the software is free for all its users. + +This license, the Lesser General Public License, applies to some specially designated software +packages--typically libraries--of the Free Software Foundation and other authors who decide to use it. +You can use it too, but we suggest you first think carefully about whether this license or the ordinary +General Public License is the better strategy to use in any particular case, based on the explanations +below. + +When we speak of free software, we are referring to freedom of use, not price. Our General Public +Licenses are designed to make sure that you have the freedom to distribute copies of free software (and +charge for this service if you wish); that you receive source code or can get it if you want it; that you +can change the software and use pieces of it in new free programs; and that you are informed that you +can do these things. + +To protect your rights, we need to make restrictions that forbid distributors to deny you these rights or +to ask you to surrender these rights. These restrictions translate to certain responsibilities for you if +you distribute copies of the library or if you modify it. + +For example, if you distribute copies of the library, whether gratis or for a fee, you must give the +recipients all the rights that we gave you. You must make sure that they, too, receive or can get the +source code. If you link other code with the library, you must provide complete object files to the +recipients, so that they can relink them with the library after making changes to the library and +recompiling it. And you must show them these terms so they know their rights. + +We protect your rights with a two-step method: (1) we copyright the library, and (2) we offer you this +license, which gives you legal permission to copy, distribute and/or modify the library. + +To protect each distributor, we want to make it very clear that there is no warranty for the free library. +Also, if the library is modified by someone else and passed on, the recipients should know that what +they have is not the original version, so that the original author's reputation will not be affected by +problems that might be introduced by others. + +Finally, software patents pose a constant threat to the existence of any free program. We wish to make +sure that a company cannot effectively restrict the users of a free program by obtaining a restrictive +license from a patent holder. Therefore, we insist that any patent license obtained for a version of the +library must be consistent with the full freedom of use specified in this license. + +Most GNU software, including some libraries, is covered by the ordinary GNU General Public License. +This license, the GNU Lesser General Public License, applies to certain designated libraries, and is quite +different from the ordinary General Public License. We use this license for certain libraries in order to +permit linking those libraries into non-free programs. + +When a program is linked with a library, whether statically or using a shared library, the combination of +the two is legally speaking a combined work, a derivative of the original library. The ordinary General +Public License therefore permits such linking only if the entire combination fits its criteria of freedom. +The Lesser General Public License permits more lax criteria for linking other code with the library. + +We call this license the "Lesser" General Public License because it does Less to protect the user's +freedom than the ordinary General Public License. It also provides other free software developers Less +of an advantage over competing non-free programs. These disadvantages are the reason we use the +ordinary General Public License for many libraries. However, the Lesser license provides advantages in +certain special circumstances. + +For example, on rare occasions, there may be a special need to encourage the widest possible use of a +certain library, so that it becomes a de-facto standard. To achieve this, non-free programs must be +allowed to use the library. A more frequent case is that a free library does the same job as widely used +non-free libraries. In this case, there is little to gain by limiting the free library to free software only, so +we use the Lesser General Public License. + +In other cases, permission to use a particular library in non-free programs enables a greater number of +people to use a large body of free software. For example, permission to use the GNU C Library in non- +free programs enables many more people to use the whole GNU operating system, as well as its variant, +the GNU/Linux operating system. + +Although the Lesser General Public License is Less protective of the users' freedom, it does ensure that +the user of a program that is linked with the Library has the freedom and the wherewithal to run that +program using a modified version of the Library. + +The precise terms and conditions for copying, distribution and modification follow. Pay close attention +to the difference between a "work based on the library" and a "work that uses the library". The former +contains code derived from the library, whereas the latter must be combined with the library in order to +run. + +TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION + +0. This License Agreement applies to any software library or other program which contains a notice +placed by the copyright holder or other authorized party saying it may be distributed under the terms +of this Lesser General Public License (also called "this License"). Each licensee is addressed as "you". + +A "library" means a collection of software functions and/or data prepared so as to be conveniently +linked with application programs (which use some of those functions and data) to form executables. + +The "Library", below, refers to any such software library or work which has been distributed under these +terms. A "work based on the Library" means either the Library or any derivative work under copyright +law: that is to say, a work containing the Library or a portion of it, either verbatim or with modifications +and/or translated straightforwardly into another language. (Hereinafter, translation is included without +limitation in the term "modification".) + +"Source code" for a work means the preferred form of the work for making modifications to it. For a +library, complete source code means all the source code for all modules it contains, plus any associated +interface definition files, plus the scripts used to control compilation and installation of the library. + +Activities other than copying, distribution and modification are not covered by this License; they are +outside its scope. The act of running a program using the Library is not restricted, and output from +such a program is covered only if its contents constitute a work based on the Library (independent of +the use of the Library in a tool for writing it). Whether that is true depends on what the Library does +and what the program that uses the Library does. + +1. You may copy and distribute verbatim copies of the Library's complete source code as you receive it, +in any medium, provided that you conspicuously and appropriately publish on each copy an +appropriate copyright notice and disclaimer of warranty; keep intact all the notices that refer to this +License and to the absence of any warranty; and distribute a copy of this License along with the Library. + +You may charge a fee for the physical act of transferring a copy, and you may at your option offer +warranty protection in exchange for a fee. + +2. You may modify your copy or copies of the Library or any portion of it, thus forming a work based +on the Library, and copy and distribute such modifications or work under the terms of Section 1 above, +provided that you also meet all of these conditions: + +a) The modified work must itself be a software library. +b) You must cause the files modified to carry prominent notices stating that you changed the files and +the date of any change. +c) You must cause the whole of the work to be licensed at no charge to all third parties under the terms +of this License. +d) If a facility in the modified Library refers to a function or a table of data to be supplied by an +application program that uses the facility, other than as an argument passed when the facility is +invoked, then you must make a good faith effort to ensure that, in the event an application does not +supply such function or table, the facility still operates, and performs whatever part of its purpose +remains meaningful. +(For example, a function in a library to compute square roots has a purpose that is entirely well- +defined independent of the application. Therefore, Subsection 2d requires that any application- +supplied function or table used by this function must be optional: if the application does not supply it, +the square root function must still compute square roots.) + +These requirements apply to the modified work as a whole. If identifiable sections of that work are not +derived from the Library, and can be reasonably considered independent and separate works in +themselves, then this License, and its terms, do not apply to those sections when you distribute them as +separate works. But when you distribute the same sections as part of a whole which is a work based on +the Library, the distribution of the whole must be on the terms of this License, whose permissions for +other licensees extend to the entire whole, and thus to each and every part regardless of who wrote it. +Thus, it is not the intent of this section to claim rights or contest your rights to work written entirely by +you; rather, the intent is to exercise the right to control the distribution of derivative or collective works +based on the Library. + +In addition, mere aggregation of another work not based on the Library with the Library (or with a work +based on the Library) on a volume of a storage or distribution medium does not bring the other work +under the scope of this License. + +3. You may opt to apply the terms of the ordinary GNU General Public License instead of this License to +a given copy of the Library. To do this, you must alter all the notices that refer to this License, so that +they refer to the ordinary GNU General Public License, version 2, instead of to this License. (If a newer +version than version 2 of the ordinary GNU General Public License has appeared, then you can specify +that version instead if you wish.) Do not make any other change in these notices. + +Once this change is made in a given copy, it is irreversible for that copy, so the ordinary GNU General +Public License applies to all subsequent copies and derivative works made from that copy. + +This option is useful when you wish to copy part of the code of the Library into a program that is not a +library. + +4. You may copy and distribute the Library (or a portion or derivative of it, under Section 2) in object +code or executable form under the terms of Sections 1 and 2 above provided that you accompany it +with the complete corresponding machine-readable source code, which must be distributed under the +terms of Sections 1 and 2 above on a medium customarily used for software interchange. + +If distribution of object code is made by offering access to copy from a designated place, then offering +equivalent access to copy the source code from the same place satisfies the requirement to distribute +the source code, even though third parties are not compelled to copy the source along with the object +code. + +5. A program that contains no derivative of any portion of the Library, but is designed to work with the +Library by being compiled or linked with it, is called a "work that uses the Library". Such a work, in +isolation, is not a derivative work of the Library, and therefore falls outside the scope of this License. + +However, linking a "work that uses the Library" with the Library creates an executable that is a +derivative of the Library (because it contains portions of the Library), rather than a "work that uses the +library". The executable is therefore covered by this License. Section 6 states terms for distribution of +such executables. + +When a "work that uses the Library" uses material from a header file that is part of the Library, the +object code for the work may be a derivative work of the Library even though the source code is not. +Whether this is true is especially significant if the work can be linked without the Library, or if the work +is itself a library. The threshold for this to be true is not precisely defined by law. + +If such an object file uses only numerical parameters, data structure layouts and accessors, and small +macros and small inline functions (ten lines or less in length), then the use of the object file is +unrestricted, regardless of whether it is legally a derivative work. (Executables containing this object +code plus portions of the Library will still fall under Section 6.) + +Otherwise, if the work is a derivative of the Library, you may distribute the object code for the work +under the terms of Section 6. Any executables containing that work also fall under Section 6, whether +or not they are linked directly with the Library itself. + +6. As an exception to the Sections above, you may also combine or link a "work that uses the Library" +with the Library to produce a work containing portions of the Library, and distribute that work under +terms of your choice, provided that the terms permit modification of the work for the customer's own +use and reverse engineering for debugging such modifications. + +You must give prominent notice with each copy of the work that the Library is used in it and that the +Library and its use are covered by this License. You must supply a copy of this License. If the work +during execution displays copyright notices, you must include the copyright notice for the Library +among them, as well as a reference directing the user to the copy of this License. Also, you must do +one of these things: + +a) Accompany the work with the complete corresponding machine-readable source code for the Library +including whatever changes were used in the work (which must be distributed under Sections 1 and 2 +above); and, if the work is an executable linked with the Library, with the complete machine-readable +"work that uses the Library", as object code and/or source code, so that the user can modify the Library +and then relink to produce a modified executable containing the modified Library. (It is understood that +the user who changes the contents of definitions files in the Library will not necessarily be able to +recompile the application to use the modified definitions.) +b) Use a suitable shared library mechanism for linking with the Library. A suitable mechanism is one +that (1) uses at run time a copy of the library already present on the user's computer system, rather +than copying library functions into the executable, and (2) will operate properly with a modified version +of the library, if the user installs one, as long as the modified version is interface-compatible with the +version that the work was made with. +c) Accompany the work with a written offer, valid for at least three years, to give the same user the +materials specified in Subsection 6a, above, for a charge no more than the cost of performing this +distribution. +d) If distribution of the work is made by offering access to copy from a designated place, offer +equivalent access to copy the above specified materials from the same place. +e) Verify that the user has already received a copy of these materials or that you have already sent this +user a copy. +For an executable, the required form of the "work that uses the Library" must include any data and +utility programs needed for reproducing the executable from it. However, as a special exception, the +materials to be distributed need not include anything that is normally distributed (in either source or +binary form) with the major components (compiler, kernel, and so on) of the operating system on which +the executable runs, unless that component itself accompanies the executable. + +It may happen that this requirement contradicts the license restrictions of other proprietary libraries +that do not normally accompany the operating system. Such a contradiction means you cannot use both +them and the Library together in an executable that you distribute. + +7. You may place library facilities that are a work based on the Library side-by-side in a single library +together with other library facilities not covered by this License, and distribute such a combined library, +provided that the separate distribution of the work based on the Library and of the other library +facilities is otherwise permitted, and provided that you do these two things: + +a) Accompany the combined library with a copy of the same work based on the Library, uncombined +with any other library facilities. This must be distributed under the terms of the Sections above. +b) Give prominent notice with the combined library of the fact that part of it is a work based on the +Library, and explaining where to find the accompanying uncombined form of the same work. +8. You may not copy, modify, sublicense, link with, or distribute the Library except as expressly +provided under this License. Any attempt otherwise to copy, modify, sublicense, link with, or distribute +the Library is void, and will automatically terminate your rights under this License. However, parties +who have received copies, or rights, from you under this License will not have their licenses terminated +so long as such parties remain in full compliance. + +9. You are not required to accept this License, since you have not signed it. However, nothing else +grants you permission to modify or distribute the Library or its derivative works. These actions are +prohibited by law if you do not accept this License. Therefore, by modifying or distributing the Library +(or any work based on the Library), you indicate your acceptance of this License to do so, and all its +terms and conditions for copying, distributing or modifying the Library or works based on it. + +10. Each time you redistribute the Library (or any work based on the Library), the recipient +automatically receives a license from the original licensor to copy, distribute, link with or modify the +Library subject to these terms and conditions. You may not impose any further restrictions on the +recipients' exercise of the rights granted herein. You are not responsible for enforcing compliance by +third parties with this License. + +11. If, as a consequence of a court judgment or allegation of patent infringement or for any other +reason (not limited to patent issues), conditions are imposed on you (whether by court order, +agreement or otherwise) that contradict the conditions of this License, they do not excuse you from the +conditions of this License. If you cannot distribute so as to satisfy simultaneously your obligations +under this License and any other pertinent obligations, then as a consequence you may not distribute +the Library at all. For example, if a patent license would not permit royalty-free redistribution of the +Library by all those who receive copies directly or indirectly through you, then the only way you could +satisfy both it and this License would be to refrain entirely from distribution of the Library. + +If any portion of this section is held invalid or unenforceable under any particular circumstance, the +balance of the section is intended to apply, and the section as a whole is intended to apply in other +circumstances. + +It is not the purpose of this section to induce you to infringe any patents or other property right claims +or to contest validity of any such claims; this section has the sole purpose of protecting the integrity of +the free software distribution system which is implemented by public license practices. Many people +have made generous contributions to the wide range of software distributed through that system in +reliance on consistent application of that system; it is up to the author/donor to decide if he or she is +willing to distribute software through any other system and a licensee cannot impose that choice. + +This section is intended to make thoroughly clear what is believed to be a consequence of the rest of +this License. + +12. If the distribution and/or use of the Library is restricted in certain countries either by patents or by +copyrighted interfaces, the original copyright holder who places the Library under this License may add +an explicit geographical distribution limitation excluding those countries, so that distribution is +permitted only in or among countries not thus excluded. In such case, this License incorporates the +limitation as if written in the body of this License. + +13. The Free Software Foundation may publish revised and/or new versions of the Lesser General Public +License from time to time. Such new versions will be similar in spirit to the present version, but may +differ in detail to address new problems or concerns. + +Each version is given a distinguishing version number. If the Library specifies a version number of this +License which applies to it and "any later version", you have the option of following the terms and +conditions either of that version or of any later version published by the Free Software Foundation. If +the Library does not specify a license version number, you may choose any version ever published by +the Free Software Foundation. + +14. If you wish to incorporate parts of the Library into other free programs whose distribution +conditions are incompatible with these, write to the author to ask for permission. For software which is +copyrighted by the Free Software Foundation, write to the Free Software Foundation; we sometimes +make exceptions for this. Our decision will be guided by the two goals of preserving the free status of +all derivatives of our free software and of promoting the sharing and reuse of software generally. + +NO WARRANTY + +15. BECAUSE THE LIBRARY IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY FOR THE LIBRARY, +TO THE EXTENT PERMITTED BY APPLICABLE LAW. EXCEPT WHEN OTHERWISE STATED IN WRITING THE +COPYRIGHT HOLDERS AND/OR OTHER PARTIES PROVIDE THE LIBRARY "AS IS" WITHOUT WARRANTY OF +ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES +OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE ENTIRE RISK AS TO THE QUALITY +AND PERFORMANCE OF THE LIBRARY IS WITH YOU. SHOULD THE LIBRARY PROVE DEFECTIVE, YOU +ASSUME THE COST OF ALL NECESSARY SERVICING, REPAIR OR CORRECTION. + +16. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING WILL ANY +COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY AND/OR REDISTRIBUTE THE LIBRARY AS +PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL +OR CONSEQUENTIAL DAMAGES ARISING OUT OF THE USE OR INABILITY TO USE THE LIBRARY +(INCLUDING BUT NOT LIMITED TO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR LOSSES +SUSTAINED BY YOU OR THIRD PARTIES OR A FAILURE OF THE LIBRARY TO OPERATE WITH ANY OTHER +SOFTWARE), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH +DAMAGES. + +END OF TERMS AND CONDITIONS + +How to Apply These Terms to Your New Libraries + +If you develop a new library, and you want it to be of the greatest possible use to the public, we +recommend making it free software that everyone can redistribute and change. You can do so by +permitting redistribution under these terms (or, alternatively, under the terms of the ordinary General +Public License). + +To apply these terms, attach the following notices to the library. It is safest to attach them to the start +of each source file to most effectively convey the exclusion of warranty; and each file should have at +least the "copyright" line and a pointer to where the full notice is found. + +one line to give the library's name and an idea of what it does. +Copyright (C) year name of author + +This library is free software; you can redistribute it and/or +modify it under the terms of the GNU Lesser General Public +License as published by the Free Software Foundation; either +version 2.1 of the License, or (at your option) any later version. + +This library is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +Lesser General Public License for more details. + +You should have received a copy of the GNU Lesser General Public +License along with this library; if not, write to the Free Software +Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA +Also add information on how to contact you by electronic and paper mail. + +You should also get your employer (if you work as a programmer) or your school, if any, to sign a +"copyright disclaimer" for the library, if necessary. Here is a sample; alter the names: + +Yoyodyne, Inc., hereby disclaims all copyright interest in +the library `Frob' (a library for tweaking knobs) written +by James Random Hacker. + +signature of Ty Coon, 1 April 1990 +Ty Coon, President of Vice +That's all there is to it! +*************************************************************************** +%%The following software may be included in this product: +Boo +Use of any of this software is governed by the terms of the license below: +Copyright (c) 2003, 2004, Rodrigo B. de Oliveira (rbo@acm.org) +All rights reserved. + +Redistribution and use in source and binary forms, with or without modification, +are permitted provided that the following conditions are met: + +* Redistributions of source code must retain the above copyright notice, +this list of conditions and the following disclaimer. +* Redistributions in binary form must reproduce the above copyright notice, +this list of conditions and the following disclaimer in the documentation +and/or other materials provided with the distribution. +* Neither the name of Rodrigo B. de Oliveira nor the names of its +contributors may be used to endorse or promote products derived from this +software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND +ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED +WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE +FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL +DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR +SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER +CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, +OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF +THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +*************************************************************************** + From f869c0dac79d5f8cd364cb75f6683cc383361e78 Mon Sep 17 00:00:00 2001 From: Laszlo Hordos Date: Thu, 18 Aug 2011 08:37:27 +0000 Subject: [PATCH 280/342] Add ResultsHandlerConfiguration class to .Net Framework after Java --- Framework/Api.cs | 149 ++++++++++++++++++++++++++++++++ FrameworkInternal/Api.cs | 16 ++++ FrameworkInternal/Serializer.cs | 39 +++++++++ 3 files changed, 204 insertions(+) diff --git a/Framework/Api.cs b/Framework/Api.cs index af22550e..d5a7a716 100644 --- a/Framework/Api.cs +++ b/Framework/Api.cs @@ -47,6 +47,7 @@ public interface APIConfiguration ConfigurationProperties ConfigurationProperties { get; } bool IsConnectorPoolingSupported { get; } ObjectPoolConfiguration ConnectorPoolConfiguration { get; } + ResultsHandlerConfiguration ResultsHandlerConfiguration { get; } ICollection> SupportedOperations { get; } int GetTimeout(SafeType operation); @@ -589,4 +590,152 @@ public override String ToString() return "{host=" + _host + ", port=" + _port + "}"; } } + + + /// + /// Configuration for result handler chain + /// + public sealed class ResultsHandlerConfiguration + { + + /// + /// Enables the {@link NormalizingResultsHandler} in the handler chain. + /// + private bool _enableNormalizingResultsHandler = true; + + /// + /// Enables the {@link FilteredResultsHandler} in the handler chain. + /// + private bool _enableFilteredResultsHandler = true; + + /// + /// Enables the case insensitive filtering. + /// + /// + /// + /// + private bool _enableCaseInsensitiveFilter = false; + + /// + /// Enables the {@link AttributesToGetSearchResultsHandler} in the handler chain. + /// + /// + /// + /// + private bool _enableAttributesToGetSearchResultsHandler = true; + + + /// + /// Get the set number of maximum objects (idle+active) + /// + public bool EnableNormalizingResultsHandler + { + get + { + return _enableNormalizingResultsHandler; + } + set + { + _enableNormalizingResultsHandler = value; + } + } + + /// + /// Get the maximum number of idle objects. + /// + public bool EnableFilteredResultsHandler + { + get + { + return _enableFilteredResultsHandler; + } + set + { + _enableFilteredResultsHandler = value; + } + } + + /// + /// Max time to wait if the pool is waiting for a free object to become + /// available before failing. + /// + /// + /// Zero means don't wait + /// + public bool EnableCaseInsensitiveFilter + { + get + { + return _enableCaseInsensitiveFilter; + } + set + { + _enableCaseInsensitiveFilter = value; + } + } + + /// + /// Minimum time to wait before evicting an idle object. + /// + /// + /// Zero means don't wait + /// + public bool EnableAttributesToGetSearchResultsHandler + { + get + { + return _enableAttributesToGetSearchResultsHandler; + } + set + { + _enableAttributesToGetSearchResultsHandler = value; + } + } + + public override int GetHashCode() + { + unchecked + { + return 42;//return (int)(EnableNormalizingResultsHandler + EnableFilteredResultsHandler + EnableCaseInsensitiveFilter + EnableAttributesToGetSearchResultsHandler + MinIdle); + } + } + + public override bool Equals(Object obj) + { + if (obj is ResultsHandlerConfiguration) + { + ResultsHandlerConfiguration other = (ResultsHandlerConfiguration)obj; + + if (EnableNormalizingResultsHandler != other.EnableNormalizingResultsHandler) + { + return false; + } + if (EnableFilteredResultsHandler != other.EnableFilteredResultsHandler) + { + return false; + } + if (EnableCaseInsensitiveFilter != other.EnableCaseInsensitiveFilter) + { + return false; + } + if (EnableAttributesToGetSearchResultsHandler != other.EnableAttributesToGetSearchResultsHandler) + { + return false; + } + return true; + } + return false; + } + + public override String ToString() + { + // poor man's toString() + IDictionary bld = new Dictionary(); + bld["EnableNormalizingResultsHandler"] = EnableNormalizingResultsHandler; + bld["EnableFilteredResultsHandler"] = EnableFilteredResultsHandler; + bld["EnableCaseInsensitiveFilter"] = EnableCaseInsensitiveFilter; + bld["EnableAttributesToGetSearchResultsHandler"] = EnableAttributesToGetSearchResultsHandler; + return bld.ToString(); + } + } } diff --git a/FrameworkInternal/Api.cs b/FrameworkInternal/Api.cs index 4334a57b..3b515754 100644 --- a/FrameworkInternal/Api.cs +++ b/FrameworkInternal/Api.cs @@ -244,6 +244,7 @@ public class APIConfigurationImpl : APIConfiguration { private ObjectPoolConfiguration _connectorPooling; + private ResultsHandlerConfiguration _resultsHandlerConfiguration; private ConfigurationPropertiesImpl _configurationProperties; private ICollection> _supportedOperations = CollectionUtil.NewReadOnlySet>(new SafeType[0]); @@ -297,6 +298,21 @@ public ObjectPoolConfiguration ConnectorPoolConfiguration _connectorPooling = value; } } + public ResultsHandlerConfiguration ResultsHandlerConfiguration + { + get + { + if (_resultsHandlerConfiguration == null) + { + _resultsHandlerConfiguration = new ResultsHandlerConfiguration(); + } + return _resultsHandlerConfiguration; + } + set + { + _resultsHandlerConfiguration = value; + } + } public ICollection> SupportedOperations { get diff --git a/FrameworkInternal/Serializer.cs b/FrameworkInternal/Serializer.cs index 8030000a..098978ea 100644 --- a/FrameworkInternal/Serializer.cs +++ b/FrameworkInternal/Serializer.cs @@ -1242,6 +1242,7 @@ internal static class APIConfigurationHandlers static APIConfigurationHandlers() { HANDLERS.Add(new ConnectionPoolingConfigurationHandler()); + HANDLERS.Add(new ResultsHandlerConfigurationHandler()); HANDLERS.Add(new ConfigurationPropertyHandler()); HANDLERS.Add(new ConfigurationPropertiesHandler()); HANDLERS.Add(new APIConfigurationHandler()); @@ -1286,6 +1287,39 @@ public override void Serialize(Object obj, ObjectEncoder encoder) val.MinIdle); } } + private class ResultsHandlerConfigurationHandler : AbstractObjectSerializationHandler + { + public ResultsHandlerConfigurationHandler() + : base(typeof(ResultsHandlerConfiguration), "ResultsHandlerConfiguration") + { + + } + public override Object Deserialize(ObjectDecoder decoder) + { + ResultsHandlerConfiguration rv = + new ResultsHandlerConfiguration(); + rv.EnableNormalizingResultsHandler = (decoder.ReadBooleanField("enableNormalizingResultsHandler", rv.EnableNormalizingResultsHandler)); + rv.EnableFilteredResultsHandler = (decoder.ReadBooleanField("enableFilteredResultsHandler", rv.EnableFilteredResultsHandler)); + rv.EnableCaseInsensitiveFilter = (decoder.ReadBooleanField("enableCaseInsensitiveFilter", rv.EnableCaseInsensitiveFilter)); + rv.EnableAttributesToGetSearchResultsHandler = ( + decoder.ReadBooleanField("enableAttributesToGetSearchResultsHandler", rv.EnableAttributesToGetSearchResultsHandler)); + return rv; + } + + public override void Serialize(Object obj, ObjectEncoder encoder) + { + ResultsHandlerConfiguration val = + (ResultsHandlerConfiguration)obj; + encoder.WriteBooleanField("enableNormalizingResultsHandler", + val.EnableNormalizingResultsHandler); + encoder.WriteBooleanField("enableFilteredResultsHandler", + val.EnableFilteredResultsHandler); + encoder.WriteBooleanField("enableCaseInsensitiveFilter", + val.EnableCaseInsensitiveFilter); + encoder.WriteBooleanField("enableAttributesToGetSearchResultsHandler", + val.EnableAttributesToGetSearchResultsHandler); + } + } private class ConfigurationPropertyHandler : AbstractObjectSerializationHandler { public ConfigurationPropertyHandler() @@ -1404,6 +1438,9 @@ public override Object Deserialize(ObjectDecoder decoder) rv.ConnectorPoolConfiguration = ( (ObjectPoolConfiguration) decoder.ReadObjectField("connectorPoolConfiguration", null, null)); + rv.ResultsHandlerConfiguration = ( + (ResultsHandlerConfiguration) + decoder.ReadObjectField("resultsHandlerConfiguration", null, null)); rv.ConfigurationProperties = ((ConfigurationPropertiesImpl) decoder.ReadObjectField("ConfigurationProperties", typeof(ConfigurationPropertiesImpl), null)); IDictionary timeoutMapObj = @@ -1461,6 +1498,8 @@ public override void Serialize(Object obj, ObjectEncoder encoder) val.IsConnectorPoolingSupported); encoder.WriteObjectField("connectorPoolConfiguration", val.ConnectorPoolConfiguration, false); + encoder.WriteObjectField("resultsHandlerConfiguration", + val.ResultsHandlerConfiguration, false); encoder.WriteObjectField("ConfigurationProperties", val.ConfigurationProperties, true); encoder.WriteObjectField("timeoutMap", From 77f12ec8ba76c766dc5b534845daa35a8627c4ee Mon Sep 17 00:00:00 2001 From: Laszlo Hordos Date: Thu, 29 Sep 2011 08:17:47 +0000 Subject: [PATCH 281/342] Add SyncDeltaType#CREATE and UPDATE as an experimental types --- Framework/Api.cs | 7 ++++++- Framework/CommonObjects.cs | 22 ++++++++++++++++++++-- 2 files changed, 26 insertions(+), 3 deletions(-) diff --git a/Framework/Api.cs b/Framework/Api.cs index d5a7a716..62a1e7df 100644 --- a/Framework/Api.cs +++ b/Framework/Api.cs @@ -696,7 +696,12 @@ public override int GetHashCode() { unchecked { - return 42;//return (int)(EnableNormalizingResultsHandler + EnableFilteredResultsHandler + EnableCaseInsensitiveFilter + EnableAttributesToGetSearchResultsHandler + MinIdle); + int hash = 3; + hash = 79 * hash + (EnableNormalizingResultsHandler ? 1 : 0); + hash = 79 * hash + (EnableFilteredResultsHandler ? 1 : 0); + hash = 79 * hash + (EnableCaseInsensitiveFilter ? 1 : 0); + hash = 79 * hash + (EnableAttributesToGetSearchResultsHandler ? 1 : 0); + return hash; } } diff --git a/Framework/CommonObjects.cs b/Framework/CommonObjects.cs index 76075986..e440ed9b 100644 --- a/Framework/CommonObjects.cs +++ b/Framework/CommonObjects.cs @@ -4130,7 +4130,7 @@ internal SyncDelta(SyncToken token, SyncDeltaType deltaType, Assertions.NullCheck(uid, "uid"); //do not allow previous Uid for anything else than create or update - if (previousUid != null && deltaType != SyncDeltaType.CREATE_OR_UPDATE) + if (previousUid != null && deltaType == SyncDeltaType.DELETE) { throw new ArgumentException("The previous Uid can only be specified for create or update."); } @@ -4484,7 +4484,25 @@ public enum SyncDeltaType /// /// The change represents a DELETE in the resource /// - DELETE + DELETE, + + /// + /// The change represents a CREATE in the resource + /// + /// + /// Experimental type to support better event mechanism where it's possible. + /// @see #CREATE_OR_UPDATE + /// + CREATE, + + /// + /// The change represents a UPDATE in the resource + /// + /// + /// Experimental type to support better event mechanism where it's possible. + /// @see #CREATE_OR_UPDATE + /// + UPDATE } #endregion From dbb68268759ae7342fe1ec0f6b61b024dc28fafc Mon Sep 17 00:00:00 2001 From: Ludovic Poitou Date: Fri, 4 Nov 2011 15:38:41 +0000 Subject: [PATCH 282/342] add True to solve the HEAT5301 exception of heat.exe (3.5.2519.0) --- ServiceInstall/ServiceInstall.wixproj | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/ServiceInstall/ServiceInstall.wixproj b/ServiceInstall/ServiceInstall.wixproj index 562d11d2..2323c15a 100644 --- a/ServiceInstall/ServiceInstall.wixproj +++ b/ServiceInstall/ServiceInstall.wixproj @@ -68,26 +68,31 @@ Common {f140e8da-52b4-4159-992a-9da10ea8eefb} True + True FrameworkInternal {5b011775-b121-4eee-a410-ba2d2f5bfb8b} True + True Framework {8b24461b-456a-4032-89a1-cd418f7b5b62} True + True Service {a9d6374a-d51f-4fa3-8c02-5b1d23faa82e} True + True TestCommon {e6a207d2-e083-41bf-b522-d9d3ec09323e} True + True \ No newline at end of file From 284f5cc05cba89f5c7381a810b471cd7fe50b2e4 Mon Sep 17 00:00:00 2001 From: Laszlo Hordos Date: Sat, 5 Nov 2011 15:01:50 +0000 Subject: [PATCH 283/342] Preparation for EA release --- Common/version.template | 2 +- Framework/version.template | 2 +- FrameworkInternal/version.template | 2 +- FrameworkTests/version.template | 2 +- Service/version.template | 2 +- TestCommon/version.template | 2 +- 6 files changed, 6 insertions(+), 6 deletions(-) diff --git a/Common/version.template b/Common/version.template index 4e0321ef..25be1b0a 100644 --- a/Common/version.template +++ b/Common/version.template @@ -1 +1 @@ -1.2.0.0 \ No newline at end of file +1.1.0.0 \ No newline at end of file diff --git a/Framework/version.template b/Framework/version.template index 4e0321ef..25be1b0a 100644 --- a/Framework/version.template +++ b/Framework/version.template @@ -1 +1 @@ -1.2.0.0 \ No newline at end of file +1.1.0.0 \ No newline at end of file diff --git a/FrameworkInternal/version.template b/FrameworkInternal/version.template index 4e0321ef..25be1b0a 100644 --- a/FrameworkInternal/version.template +++ b/FrameworkInternal/version.template @@ -1 +1 @@ -1.2.0.0 \ No newline at end of file +1.1.0.0 \ No newline at end of file diff --git a/FrameworkTests/version.template b/FrameworkTests/version.template index 4e0321ef..25be1b0a 100644 --- a/FrameworkTests/version.template +++ b/FrameworkTests/version.template @@ -1 +1 @@ -1.2.0.0 \ No newline at end of file +1.1.0.0 \ No newline at end of file diff --git a/Service/version.template b/Service/version.template index 4e0321ef..25be1b0a 100644 --- a/Service/version.template +++ b/Service/version.template @@ -1 +1 @@ -1.2.0.0 \ No newline at end of file +1.1.0.0 \ No newline at end of file diff --git a/TestCommon/version.template b/TestCommon/version.template index 4e0321ef..25be1b0a 100644 --- a/TestCommon/version.template +++ b/TestCommon/version.template @@ -1 +1 @@ -1.2.0.0 \ No newline at end of file +1.1.0.0 \ No newline at end of file From 59f5ad6a3795f22d3bc07e0f631a38dc832256c8 Mon Sep 17 00:00:00 2001 From: Laszlo Hordos Date: Sun, 27 Nov 2011 21:57:55 +0000 Subject: [PATCH 284/342] Support the ResultsHandler configuration in .Net --- FrameworkInternal/ApiLocalOperations.cs | 51 ++++++++++++++++++------- 1 file changed, 37 insertions(+), 14 deletions(-) diff --git a/FrameworkInternal/ApiLocalOperations.cs b/FrameworkInternal/ApiLocalOperations.cs index f5c14742..277d3bbe 100644 --- a/FrameworkInternal/ApiLocalOperations.cs +++ b/FrameworkInternal/ApiLocalOperations.cs @@ -37,6 +37,7 @@ using System.Reflection; using System.Collections.Generic; using System.Linq; +using Org.IdentityConnectors.Framework.Api; namespace Org.IdentityConnectors.Framework.Impl.Api.Local.Operations { @@ -735,6 +736,12 @@ protected LocalConnectorInfoImpl GetConnectorInfo() { return connectorInfo; } + + + public ResultsHandlerConfiguration getResultsHandlerConfiguration() + { + return apiConfiguration.ResultsHandlerConfiguration; + } } #endregion @@ -1118,15 +1125,32 @@ public void Search(ObjectClass oclass, Filter originalFilter, ResultsHandler han { options = new OperationOptionsBuilder().Build(); } - ObjectNormalizerFacade normalizer = - GetNormalizer(oclass); - //chain a normalizing handler (must come before - //filter handler) - handler = - new NormalizingResultsHandler(handler, normalizer).Handle; - Filter normalizedFilter = - normalizer.NormalizeFilter(originalFilter); - + + ResultsHandlerConfiguration hdlCfg = null != GetOperationalContext() ? + GetOperationalContext().getResultsHandlerConfiguration() : new ResultsHandlerConfiguration(); + ResultsHandler handlerChain = handler; + + Filter finalFilter = originalFilter; + + if (hdlCfg.EnableNormalizingResultsHandler) { + ObjectNormalizerFacade normalizer = GetNormalizer(oclass); + //chain a normalizing handler (must come before + //filter handler) + ResultsHandler normalizingHandler = new NormalizingResultsHandler(handler, normalizer).Handle; + Filter normalizedFilter = normalizer.NormalizeFilter(originalFilter); + // chain a filter handler.. + if (hdlCfg.EnableFilteredResultsHandler) { + // chain a filter handler.. + handlerChain = new FilteredResultsHandler(handler, normalizedFilter).Handle; + finalFilter = normalizedFilter; + } else { + handlerChain = normalizingHandler; + } + } else if (hdlCfg.EnableFilteredResultsHandler) { + // chain a filter handler.. + ResultsHandler filteredHandler = new FilteredResultsHandler(handlerChain, originalFilter).Handle; + } + //get the IList interface that this type implements Type interfaceType = ReflectionUtil.FindInHierarchyOf (typeof(SearchOp<>), GetConnector().GetType()); @@ -1140,16 +1164,15 @@ public void Search(ObjectClass oclass, Filter originalFilter, ResultsHandler han Type searcherType = searcherRawType.MakeGenericType(queryType); RawSearcher searcher = (RawSearcher)Activator.CreateInstance(searcherType); - // add filtering handler - handler = new FilteredResultsHandler(handler, normalizedFilter).Handle; + // add attributes to get handler string[] attrsToGet = options.AttributesToGet; - if (attrsToGet != null && attrsToGet.Length > 0) + if (attrsToGet != null && attrsToGet.Length > 0 && hdlCfg.EnableAttributesToGetSearchResultsHandler) { - handler = new SearchAttributesToGetResultsHandler( + handlerChain = new SearchAttributesToGetResultsHandler( handler, attrsToGet).Handle; } - searcher.RawSearch(GetConnector(), oclass, normalizedFilter, handler, options); + searcher.RawSearch(GetConnector(), oclass, finalFilter, handlerChain, options); } } #endregion From ff6f8c199704d3862045f822fdc158dd1fa60df7 Mon Sep 17 00:00:00 2001 From: Laszlo Hordos Date: Mon, 13 Aug 2012 06:07:46 +0000 Subject: [PATCH 285/342] OPENICF-25 - Add simple heartbeat facility OPENICF-27 - Fix the broken communication between Java and .Net --- Framework/Api.cs | 8 +- Framework/Spi.cs | 36 ++++++ FrameworkInternal/Api.cs | 19 +++ FrameworkInternal/ApiLocal.cs | 8 ++ FrameworkInternal/ApiRemote.cs | 3 +- FrameworkInternal/ApiRemoteMessages.cs | 74 ++++++++++- FrameworkInternal/Resources.resx | 10 +- FrameworkInternal/Serializer.cs | 21 +++- FrameworkInternal/Server.cs | 58 ++++++--- FrameworkTests/ConnectorInfoManagerTests.cs | 2 + FrameworkTests/ObjectSerializationTests.cs | 25 +++- .../ShellScriptExecutorFactory.cs | 118 +++++++++++------- TestBundles/TestBundleV1/Messages.es-ES.resx | 3 + TestBundles/TestBundleV1/Messages.es.resx | 3 + TestBundles/TestBundleV1/Messages.resx | 3 + TestBundles/TestBundleV1/TestConnector.cs | 2 + TestBundles/TestBundleV2/TestConnector.cs | 1 + 17 files changed, 322 insertions(+), 72 deletions(-) diff --git a/Framework/Api.cs b/Framework/Api.cs index 62a1e7df..df3743fb 100644 --- a/Framework/Api.cs +++ b/Framework/Api.cs @@ -19,6 +19,7 @@ * enclosed by brackets [] replaced by your own identifying information: * "Portions Copyrighted [year] [name of copyright owner]" * ==================== + * Portions Copyrighted 2012 ForgeRock AS */ using System; using System.Reflection; @@ -103,10 +104,15 @@ public interface ConfigurationProperty string GetHelpMessage(string def); /// - /// Get the display name for the is configuration + /// Get the display name for this configuration property. /// string GetDisplayName(string def); + /// + /// Get name of the group for this configuration property. + /// + string GetGroup(string def); + /// /// Get the value from the property. /// diff --git a/Framework/Spi.cs b/Framework/Spi.cs index 55c8f958..288ba1a9 100644 --- a/Framework/Spi.cs +++ b/Framework/Spi.cs @@ -19,6 +19,7 @@ * enclosed by brackets [] replaced by your own identifying information: * "Portions Copyrighted [year] [name of copyright owner]" * ==================== + * Portions Copyrighted 2012 ForgeRock AS */ using System; using System.Globalization; @@ -162,15 +163,28 @@ public class ConnectorClassAttribute : System.Attribute { private readonly String _connectorDisplayNameKey; + private readonly String _connectorCategoryKey; private readonly SafeType _connectorConfigurationClass; public ConnectorClassAttribute(String connectorDisplayNameKey, Type connectorConfigurationClass) { _connectorDisplayNameKey = connectorDisplayNameKey; + _connectorCategoryKey = string.Empty; _connectorConfigurationClass = SafeType.ForRawType(connectorConfigurationClass); } + public ConnectorClassAttribute(String connectorDisplayNameKey, String connectorCategoryKey, + Type connectorConfigurationClass) + { + _connectorDisplayNameKey = connectorDisplayNameKey; + _connectorCategoryKey = connectorCategoryKey; + _connectorConfigurationClass = SafeType.ForRawType(connectorConfigurationClass); + } + + /// + /// The display name key. This must be a key in the message catalog. + /// public string ConnectorDisplayNameKey { get @@ -179,6 +193,17 @@ public string ConnectorDisplayNameKey } } + /// + /// Category the connector belongs to such as 'LDAP' or 'DB'. + /// + public string ConnectorCategoryKey + { + get + { + return _connectorCategoryKey; + } + } + public SafeType ConnectorConfigurationType { get @@ -187,6 +212,12 @@ public SafeType ConnectorConfigurationType } } + /// + /// The resource path(s) to the message catalog. + /// Message catalogs are searched in the order given such + /// that the first one wins. By default, if no paths are + /// specified, we use connector-package.Messages.resx + /// public string[] MessageCatalogPaths { get; set; } } @@ -231,6 +262,10 @@ public class ConfigurationPropertyAttribute : System.Attribute /// Change the default display message key. /// public string DisplayMessageKey { get; set; } + /// + /// Change the default group message key. + /// + public string GroupMessageKey { get; set; } /// /// List of operations for which this property must be specified. @@ -279,6 +314,7 @@ public ConfigurationPropertyAttribute() HelpMessageKey = null; DisplayMessageKey = null; OperationTypes = new Type[0]; + GroupMessageKey = null; } } #endregion diff --git a/FrameworkInternal/Api.cs b/FrameworkInternal/Api.cs index 3b515754..5b8f5cd0 100644 --- a/FrameworkInternal/Api.cs +++ b/FrameworkInternal/Api.cs @@ -19,6 +19,7 @@ * enclosed by brackets [] replaced by your own identifying information: * "Portions Copyrighted [year] [name of copyright owner]" * ==================== + * Portions Copyrighted 2012 ForgeRock AS */ using System; @@ -88,12 +89,19 @@ public string GetDisplayName(string def) public object Value { get; set; } + public string GroupMessageKey { get; set; } + public Type ValueType { get; set; } public bool IsConfidential { get; set; } public bool IsRequired { get; set; } + public string GetGroup(string def) + { + return FormatMessage(GroupMessageKey, def); + } + public override int GetHashCode() { return Name.GetHashCode(); @@ -124,6 +132,10 @@ public override bool Equals(Object o) { return false; } + if (!CollectionUtil.Equals(GroupMessageKey, other.GroupMessageKey)) + { + return false; + } if (IsConfidential != other.IsConfidential) { return false; @@ -364,6 +376,8 @@ public string GetConnectorDisplayName() public string ConnectorDisplayNameKey { get; set; } + public string ConnectorCategoryKey { get; set; } + public ConnectorMessages Messages { get; set; } public APIConfigurationImpl DefaultAPIConfiguration @@ -390,6 +404,11 @@ public APIConfiguration CreateDefaultAPIConfiguration() rv.ConnectorInfo = this; return rv; } + + public string GetConnectorCategory() + { + return Messages.Format(ConnectorCategoryKey, null); + } } #endregion diff --git a/FrameworkInternal/ApiLocal.cs b/FrameworkInternal/ApiLocal.cs index 0acc3c53..ea4d160f 100644 --- a/FrameworkInternal/ApiLocal.cs +++ b/FrameworkInternal/ApiLocal.cs @@ -19,6 +19,7 @@ * enclosed by brackets [] replaced by your own identifying information: * "Portions Copyrighted [year] [name of copyright owner]" * ==================== + * Portions Copyrighted 2012 ForgeRock AS */ using System; using System.Diagnostics; @@ -209,6 +210,7 @@ public static ConfigurationPropertiesImpl int order = 0; String helpKey = name + ".help"; String displKey = name + ".display"; + string grpKey = name + ".group"; bool confidential = false; bool required = false; if (options != null) @@ -222,6 +224,10 @@ public static ConfigurationPropertiesImpl { displKey = options.DisplayMessageKey; } + if (!StringUtil.IsBlank(options.GroupMessageKey)) + { + displKey = options.GroupMessageKey; + } // determine the order.. order = options.Order; required = options.Required; @@ -241,6 +247,7 @@ public static ConfigurationPropertiesImpl prop.IsRequired = required; prop.DisplayMessageKey = displKey; prop.HelpMessageKey = helpKey; + prop.GroupMessageKey = grpKey; prop.Name = name; prop.Order = order; prop.Value = value; @@ -461,6 +468,7 @@ private LocalConnectorInfoImpl CreateConnectorInfo(Assembly assembly, rv.ConnectorClass = connectorClass; rv.ConnectorConfigurationClass = connectorConfigurationClass; rv.ConnectorDisplayNameKey = connectorDisplayNameKey; + rv.ConnectorCategoryKey = attribute.ConnectorCategoryKey; rv.ConnectorKey = key; rv.DefaultAPIConfiguration = CreateDefaultAPIConfiguration(rv); rv.Messages = LoadMessages(assembly, rv, attribute.MessageCatalogPaths); diff --git a/FrameworkInternal/ApiRemote.cs b/FrameworkInternal/ApiRemote.cs index a4a0b7d7..4d1c16ad 100644 --- a/FrameworkInternal/ApiRemote.cs +++ b/FrameworkInternal/ApiRemote.cs @@ -19,6 +19,7 @@ * enclosed by brackets [] replaced by your own identifying information: * "Portions Copyrighted [year] [name of copyright owner]" * ==================== + * Portions Copyrighted 2012 ForgeRock AS */ using System; using System.Collections.Generic; @@ -185,7 +186,7 @@ public RemoteConnectorInfoManagerImpl(RemoteFrameworkConnectionInfo info) { connection.WriteObject(CultureInfo.CurrentUICulture); connection.WriteObject(info.Key); - connection.WriteObject(new HelloRequest()); + connection.WriteObject(new HelloRequest(HelloRequest.CONNECTOR_INFO)); HelloResponse response = (HelloResponse)connection.ReadObject(); if (response.Exception != null) { diff --git a/FrameworkInternal/ApiRemoteMessages.cs b/FrameworkInternal/ApiRemoteMessages.cs index 4aaf4cff..3d1fdab1 100644 --- a/FrameworkInternal/ApiRemoteMessages.cs +++ b/FrameworkInternal/ApiRemoteMessages.cs @@ -32,9 +32,41 @@ namespace Org.IdentityConnectors.Framework.Impl.Api.Remote.Messages /// public class HelloRequest : Message { + public const int SERVER_INFO = 4; + public const int CONNECTOR_KEY_LIST = 16; + private const int DEFAULT_CONFIG = 32; + public const int CONNECTOR_INFO = CONNECTOR_KEY_LIST | DEFAULT_CONFIG; - public HelloRequest() + private readonly int _level; + + public HelloRequest(int infoLevel) + { + _level = infoLevel; + } + + public int GetInfoLevel() + { + return _level; + } + + private bool checkInfoLevel(int info) + { + return ((_level & info) == info); + } + + public bool isServerInfo() { + return checkInfoLevel(SERVER_INFO); + } + + public bool isConnectorKeys() + { + return checkInfoLevel(CONNECTOR_KEY_LIST); + } + + public bool isConnectorInfo() + { + return checkInfoLevel(CONNECTOR_INFO); } } @@ -43,21 +75,35 @@ public HelloRequest() /// public class HelloResponse : Message { + public const string SERVER_START_TIME = "SERVER_START_TIME"; + /// /// The exception /// private Exception _exception; + private IDictionary _serverInfo; + /// /// List of connector infos, containing infos for all the connectors /// on the server. /// private IList _connectorInfos; + /// + /// List of connector keys, containing the keys of all the connectors + /// on the server. + /// + private IList _connectorKeys; + public HelloResponse(Exception exception, + IDictionary serverInfo, + IList connectorKeys, IList connectorInfos) { _exception = exception; + _serverInfo = CollectionUtil.AsReadOnlyDictionary(serverInfo); + _connectorKeys = CollectionUtil.NewReadOnlyList(connectorKeys); _connectorInfos = CollectionUtil.NewReadOnlyList(connectorInfos); } @@ -76,6 +122,32 @@ public IList ConnectorInfos return _connectorInfos; } } + + public IList ConnectorKeys + { + get + { + return _connectorKeys; + } + } + + public IDictionary ServerInfo + { + get + { + return _serverInfo; + } + } + + public DateTime? getStartTime() + { + object time = ServerInfo[SERVER_START_TIME]; + if (time is long) + { + return new DateTime((long)time); + } + return null; + } } /// diff --git a/FrameworkInternal/Resources.resx b/FrameworkInternal/Resources.resx index c9f28759..b263d768 100644 --- a/FrameworkInternal/Resources.resx +++ b/FrameworkInternal/Resources.resx @@ -267,6 +267,7 @@ OperationOptionInfo | SyncDeltaType | SyncToken | SyncDelta | QualifiedUid name CDATA #REQUIRED helpMessageKey CDATA #REQUIRED displayMessageKey CDATA #REQUIRED + groupMessageKey CDATA #REQUIRED type CDATA #REQUIRED > <!ELEMENT value (%xmlObject;)> @@ -292,6 +293,7 @@ OperationOptionInfo | SyncDeltaType | SyncToken | SyncDelta | QualifiedUid <!ELEMENT ConnectorInfo (ConnectorKey,ConnectorMessages,APIConfiguration)> <!ATTLIST ConnectorInfo connectorDisplayNameKey CDATA #REQUIRED + connectorCategoryKey CDATA #REQUIRED > <!--=======================================================--> @@ -471,10 +473,14 @@ OperationOptionInfo | SyncDeltaType | SyncToken | SyncDelta | QualifiedUid <!--= Messages =--> <!--= =--> <!--=======================================================--> -<!ELEMENT HelloRequest EMPTY> +<!ELEMENT HelloRequest EMPTY + infoLevel CDATA #REQUIRED +> +<!ELEMENT serverInfoMap (Map)> +<!ELEMENT ConnectorKeys ((ConnectorKey)*)> <!ELEMENT ConnectorInfos ((ConnectorInfo)*)> <!ELEMENT exception (%exceptionTypes;)> -<!ELEMENT HelloResponse (exception,ConnectorInfos)> +<!ELEMENT HelloResponse (exception,serverInfoMap,ConnectorInfos,ConnectorKeys)> <!ELEMENT OperationRequest (ConnectorKey,APIConfiguration,Arguments)> <!ATTLIST OperationRequest operation CDATA #REQUIRED diff --git a/FrameworkInternal/Serializer.cs b/FrameworkInternal/Serializer.cs index 098978ea..30597f63 100644 --- a/FrameworkInternal/Serializer.cs +++ b/FrameworkInternal/Serializer.cs @@ -19,6 +19,7 @@ * enclosed by brackets [] replaced by your own identifying information: * "Portions Copyrighted [year] [name of copyright owner]" * ==================== + * Portions Copyrighted 2012 ForgeRock AS */ using System; using System.IO; @@ -1338,6 +1339,8 @@ public override Object Deserialize(ObjectDecoder decoder) decoder.ReadStringField("helpMessageKey", null)); rv.DisplayMessageKey = ( decoder.ReadStringField("displayMessageKey", null)); + rv.GroupMessageKey = ( + decoder.ReadStringField("groupMessageKey", null)); rv.ValueType = ( decoder.ReadClassField("type", null)); rv.Value = ( @@ -1372,6 +1375,8 @@ public override void Serialize(Object obj, ObjectEncoder encoder) val.HelpMessageKey); encoder.WriteStringField("displayMessageKey", val.DisplayMessageKey); + encoder.WriteStringField("groupMessageKey", + val.GroupMessageKey); encoder.WriteClassField("type", val.ValueType); encoder.WriteObjectField("value", @@ -1586,6 +1591,8 @@ public override Object Deserialize(ObjectDecoder decoder) RemoteConnectorInfoImpl rv = new RemoteConnectorInfoImpl(); rv.ConnectorDisplayNameKey = ( decoder.ReadStringField("connectorDisplayNameKey", null)); + rv.ConnectorCategoryKey = ( + decoder.ReadStringField("connectorCategoryKey", null)); rv.ConnectorKey = ((ConnectorKey) decoder.ReadObjectField("ConnectorKey", typeof(ConnectorKey), null)); rv.Messages = ((ConnectorMessagesImpl) @@ -1601,6 +1608,8 @@ public override void Serialize(Object obj, ObjectEncoder encoder) (RemoteConnectorInfoImpl)obj; encoder.WriteStringField("connectorDisplayNameKey", val.ConnectorDisplayNameKey); + encoder.WriteStringField("connectorCategoryKey", + val.ConnectorCategoryKey); encoder.WriteObjectField("ConnectorKey", val.ConnectorKey, true); encoder.WriteObjectField("ConnectorMessages", @@ -2700,11 +2709,13 @@ public HelloRequestHandler() public override sealed Object Deserialize(ObjectDecoder decoder) { - return new HelloRequest(); + return new HelloRequest(decoder.ReadIntField("infoLevel", HelloRequest.CONNECTOR_INFO)); } public override sealed void Serialize(Object obj, ObjectEncoder encoder) { + HelloRequest val = (HelloRequest)obj; + encoder.WriteIntField("infoLevel", val.GetInfoLevel()); } } @@ -2722,18 +2733,24 @@ public override sealed Object Deserialize(ObjectDecoder decoder) { Exception exception = (Exception)decoder.ReadObjectField("exception", null, null); + IDictionary serverInfo = + (IDictionary)decoder.ReadObjectField("serverInfoMap", null, null); IList connectorInfosObj = (IList)decoder.ReadObjectField("ConnectorInfos", typeof(IList), null); IList connectorInfos = CollectionUtil.NewList(connectorInfosObj); - return new HelloResponse(exception, connectorInfos); + IList connectorKeys = + (IList)decoder.ReadObjectField("ConnectorKeys", typeof(IList), null); + return new HelloResponse(exception, serverInfo, connectorKeys, connectorInfos); } public override sealed void Serialize(Object obj, ObjectEncoder encoder) { HelloResponse val = (HelloResponse)obj; encoder.WriteObjectField("exception", val.Exception, false); + encoder.WriteObjectField("serverInfoMap", val.ServerInfo, false); encoder.WriteObjectField("ConnectorInfos", val.ConnectorInfos, true); + encoder.WriteObjectField("ConnectorKeys", val.ConnectorKeys, true); } } diff --git a/FrameworkInternal/Server.cs b/FrameworkInternal/Server.cs index 3316b670..1f55dc99 100644 --- a/FrameworkInternal/Server.cs +++ b/FrameworkInternal/Server.cs @@ -19,6 +19,7 @@ * enclosed by brackets [] replaced by your own identifying information: * "Portions Copyrighted [year] [name of copyright owner]" * ==================== + * Portions Copyrighted 2012 ForgeRock AS */ using System; using System.Collections.Generic; @@ -260,6 +261,11 @@ public String KeyHash /// abstract public void DumpRequests(); + /// + /// Gets the time when the servers was started last time. + /// + abstract public long StartTime(); + /// /// Starts the server. /// @@ -435,7 +441,7 @@ private bool ProcessRequest() if (authException != null) { HelloResponse response = - new HelloResponse(authException, null); + new HelloResponse(authException, null, null, null); _connection.WriteObject(response); } else @@ -503,22 +509,38 @@ private ConnectorInfoManager GetConnectorInfoManager() private HelloResponse ProcessHelloRequest(HelloRequest request) { - IList connectorInfo; + IList connectorInfo = null; + IList connectorKeys = null; + IDictionary serverInfo = null; Exception exception = null; try { - ConnectorInfoManager manager = - GetConnectorInfoManager(); - IList localInfos = - manager.ConnectorInfos; - connectorInfo = new List(); - foreach (ConnectorInfo localInfo in localInfos) + serverInfo = new Dictionary(1); + if (request.isServerInfo()) { - LocalConnectorInfoImpl localInfoImpl = - (LocalConnectorInfoImpl)localInfo; - RemoteConnectorInfoImpl remoteInfo = - localInfoImpl.ToRemote(); - connectorInfo.Add(remoteInfo); + serverInfo.Add(HelloResponse.SERVER_START_TIME, _server.StartTime()); + } + if (request.isConnectorKeys()) + { + ConnectorInfoManager manager = GetConnectorInfoManager(); + IList localInfos = manager.ConnectorInfos; + connectorKeys = new List(); + foreach (ConnectorInfo localInfo in localInfos) + { + connectorKeys.Add(localInfo.ConnectorKey); + } + if (request.isConnectorInfo()) + { + connectorInfo = new List(); + foreach (ConnectorInfo localInfo in localInfos) + { + LocalConnectorInfoImpl localInfoImpl = + (LocalConnectorInfoImpl)localInfo; + RemoteConnectorInfoImpl remoteInfo = + localInfoImpl.ToRemote(); + connectorInfo.Add(remoteInfo); + } + } } } catch (Exception e) @@ -527,7 +549,7 @@ private HelloResponse ProcessHelloRequest(HelloRequest request) exception = e; connectorInfo = null; } - return new HelloResponse(exception, connectorInfo); + return new HelloResponse(exception, serverInfo, connectorKeys, connectorInfo); } private MethodInfo GetOperationMethod(OperationRequest request) @@ -904,6 +926,7 @@ private readonly IDictionary _pendingRequests = CollectionUtil.NewIdentityDictionary(); private ConnectionListener _listener; private Object COUNT_LOCK = new Object(); + private long _startDate = 0; private long _requestCount = 0; public override bool IsStarted() @@ -911,6 +934,11 @@ public override bool IsStarted() return _listener != null; } + public override long StartTime() + { + return _startDate; + } + public void BeginRequest() { long requestID; @@ -1018,6 +1046,7 @@ public override void Start() //make sure we are configured properly ConnectorInfoManagerFactory.GetInstance().GetLocalManager(); _requestCount = 0; + _startDate = DateTimeUtil.GetCurrentUtcTimeMillis(); _pendingRequests.Clear(); TcpListener socket = CreateServerSocket(); @@ -1048,6 +1077,7 @@ public override void Stop() _listener.Shutdown(); _listener = null; } + _startDate = 0; ConnectorFacadeFactory.GetInstance().Dispose(); } } diff --git a/FrameworkTests/ConnectorInfoManagerTests.cs b/FrameworkTests/ConnectorInfoManagerTests.cs index 5d6095bd..58ec4abb 100644 --- a/FrameworkTests/ConnectorInfoManagerTests.cs +++ b/FrameworkTests/ConnectorInfoManagerTests.cs @@ -19,6 +19,7 @@ * enclosed by brackets [] replaced by your own identifying information: * "Portions Copyrighted [year] [name of copyright owner]" * ==================== + * Portions Copyrighted 2012 ForgeRock AS */ using System; using NUnit.Framework; @@ -121,6 +122,7 @@ public void TestAPIConfiguration() Thread.CurrentThread.CurrentUICulture = new CultureInfo("en"); Assert.AreEqual("Help for test field.", property.GetHelpMessage(null)); Assert.AreEqual("Display for test field.", property.GetDisplayName(null)); + Assert.AreEqual("Group for test field.", property.GetGroup(null)); Assert.AreEqual("Test Framework Value", info.Messages.Format("TEST_FRAMEWORK_KEY", "empty")); diff --git a/FrameworkTests/ObjectSerializationTests.cs b/FrameworkTests/ObjectSerializationTests.cs index 0985f6a3..5cf73f96 100644 --- a/FrameworkTests/ObjectSerializationTests.cs +++ b/FrameworkTests/ObjectSerializationTests.cs @@ -19,6 +19,7 @@ * enclosed by brackets [] replaced by your own identifying information: * "Portions Copyrighted [year] [name of copyright owner]" * ==================== + * Portions Copyrighted 2012 ForgeRock AS */ using System; using System.IO; @@ -418,6 +419,7 @@ public void TestConfigurationProperty() v1.Name = ("foo"); v1.HelpMessageKey = ("help key"); v1.DisplayMessageKey = ("display key"); + v1.GroupMessageKey = ("group key"); v1.Value = ("bar"); v1.ValueType = (typeof(string)); v1.Operations = FrameworkUtil.AllAPIOperations(); @@ -430,6 +432,7 @@ public void TestConfigurationProperty() Assert.AreEqual("foo", v2.Name); Assert.AreEqual("help key", v2.HelpMessageKey); Assert.AreEqual("display key", v2.DisplayMessageKey); + Assert.AreEqual("group key", v2.GroupMessageKey); Assert.AreEqual("bar", v2.Value); Assert.AreEqual(typeof(string), v2.ValueType); Assert.IsTrue(CollectionUtil.Equals( @@ -445,6 +448,7 @@ public void TestConfigurationProperties() prop1.Name = ("foo"); prop1.HelpMessageKey = ("help key"); prop1.DisplayMessageKey = ("display key"); + prop1.GroupMessageKey = ("group key"); prop1.Value = ("bar"); prop1.ValueType = (typeof(string)); prop1.Operations = null; @@ -467,6 +471,7 @@ public void TestAPIConfiguration() prop1.Name = ("foo"); prop1.HelpMessageKey = ("help key"); prop1.DisplayMessageKey = ("display key"); + prop1.GroupMessageKey = ("group key"); prop1.Value = ("bar"); prop1.ValueType = (typeof(string)); prop1.Operations = null; @@ -535,6 +540,7 @@ public void TestRemoteConnectorInfo() apiImpl.ConfigurationProperties = (configProperties); v1.DefaultAPIConfiguration = (apiImpl); v1.ConnectorDisplayNameKey = ("mykey"); + v1.ConnectorCategoryKey = ("LDAP"); RemoteConnectorInfoImpl v2 = (RemoteConnectorInfoImpl) CloneObject(v1); @@ -544,6 +550,7 @@ public void TestRemoteConnectorInfo() Assert.AreEqual("my version", v2.ConnectorKey.BundleVersion); Assert.AreEqual("my connector", v2.ConnectorKey.ConnectorName); Assert.AreEqual("mykey", v2.ConnectorDisplayNameKey); + Assert.AreEqual("LDAP", v2.ConnectorCategoryKey); Assert.IsNotNull(v2.DefaultAPIConfiguration); } @@ -906,30 +913,36 @@ public void TestExceptions() [Test] public void TestHelloRequest() { - HelloRequest v1 = new HelloRequest(); + HelloRequest v1 = new HelloRequest(HelloRequest.CONNECTOR_INFO); HelloRequest v2 = (HelloRequest)CloneObject(v1); Assert.IsNotNull(v2); + Assert.AreEqual(HelloRequest.CONNECTOR_INFO, v2.GetInfoLevel()); } [Test] public void TestHelloResponse() { + Exception ex = new Exception("foo"); + IDictionary serverInfo = new Dictionary(1); + serverInfo.Add(HelloResponse.SERVER_START_TIME, DateTimeUtil.GetCurrentUtcTimeMillis()); + ConnectorKey key = new ConnectorKey("my bundle", "my version", "my connector"); RemoteConnectorInfoImpl info = new RemoteConnectorInfoImpl(); info.Messages = (new ConnectorMessagesImpl()); - info.ConnectorKey = (new ConnectorKey("my bundle", - "my version", - "my connector")); + info.ConnectorKey = (key); ConfigurationPropertiesImpl configProperties = new ConfigurationPropertiesImpl(); configProperties.Properties = (new List()); APIConfigurationImpl apiImpl = new APIConfigurationImpl(); apiImpl.ConfigurationProperties = (configProperties); info.DefaultAPIConfiguration = (apiImpl); info.ConnectorDisplayNameKey = ("mykey"); + info.ConnectorCategoryKey = (""); - Exception ex = new Exception("foo"); - HelloResponse v1 = new HelloResponse(ex, CollectionUtil.NewReadOnlyList(info)); + + HelloResponse v1 = new HelloResponse(ex, serverInfo, CollectionUtil.NewReadOnlyList(key), CollectionUtil.NewReadOnlyList(info)); HelloResponse v2 = (HelloResponse)CloneObject(v1); Assert.IsNotNull(v2.Exception); + Assert.IsNotNull(v2.ServerInfo[HelloResponse.SERVER_START_TIME]); + Assert.IsNotNull(v2.ConnectorKeys.First()); Assert.IsNotNull(v2.ConnectorInfos.First()); } diff --git a/ShellScriptExecutorFactory/ShellScriptExecutorFactory.cs b/ShellScriptExecutorFactory/ShellScriptExecutorFactory.cs index 64367a3f..466a8423 100644 --- a/ShellScriptExecutorFactory/ShellScriptExecutorFactory.cs +++ b/ShellScriptExecutorFactory/ShellScriptExecutorFactory.cs @@ -19,6 +19,7 @@ * enclosed by brackets [] replaced by your own identifying information: * "Portions Copyrighted [year] [name of copyright owner]" * ==================== + * Portions Copyrighted 2012 ForgeRock AS */ using System; using System.IO; @@ -62,13 +63,15 @@ public ScriptExecutor NewScriptExecutor(Assembly[] refs, string script, bool com class ShellScriptExecutor : ScriptExecutor { private readonly string _script; - + public ShellScriptExecutor(string script) { _script = script; } public object Execute(IDictionary arguments) { + // + string fn = String.Empty; // create the process info.. Process process = new Process(); // set the defaults.. @@ -76,74 +79,99 @@ public object Execute(IDictionary arguments) process.StartInfo.UseShellExecute = true; // set the default timeout.. int timeout = 1000 * 30; // 30 secss - // if there are any environment varibles set to false.. - process.StartInfo.UseShellExecute = arguments.Count == 0; - // take out username and password if they're in the options. - foreach (KeyValuePair kv in arguments) + int exitCode = 1; + try { - if (kv.Key.ToUpper().Equals("USERNAME")) + // if there are any environment varibles set to false.. + process.StartInfo.UseShellExecute = arguments.Count == 0; + // take out username and password if they're in the options. + foreach (KeyValuePair kv in arguments) { - string domainUser = kv.Value.ToString(); - string[] split = domainUser.Split(new char[] { '\\' }); - if (split.Length == 1) + if (kv.Key.ToUpper().Equals("USERNAME")) { - process.StartInfo.UserName = split[0]; + string domainUser = kv.Value.ToString(); + string[] split = domainUser.Split(new char[] { '\\' }); + if (split.Length == 1) + { + process.StartInfo.UserName = split[0]; + } + else + { + process.StartInfo.Domain = split[0]; + process.StartInfo.UserName = split[1]; + } } - else + else if (kv.Key.ToUpper().Equals("PASSWORD")) { - process.StartInfo.Domain = split[0]; - process.StartInfo.UserName = split[1]; + if (kv.Value is SecureString) + { + process.StartInfo.Password = (SecureString)kv.Value; + } + else if (kv.Value is GuardedString) + { + process.StartInfo.Password = ((GuardedString)kv.Value).ToSecureString(); + } + else + { + throw new ArgumentException("Invalid type for password."); + } } - } - else if (kv.Key.ToUpper().Equals("PASSWORD")) - { - if (kv.Value is SecureString) + else if (kv.Key.ToUpper().Equals("WORKINGDIR")) { - process.StartInfo.Password = (SecureString)kv.Value; + process.StartInfo.WorkingDirectory = kv.Value.ToString(); } - else if (kv.Value is GuardedString) + else if (kv.Key.ToUpper().Equals("TIMEOUT")) { - process.StartInfo.Password = ((GuardedString)kv.Value).ToSecureString(); + timeout = Int32.Parse(kv.Value.ToString()); } else { - throw new ArgumentException("Invalid type for password."); + process.StartInfo.EnvironmentVariables[kv.Key] = kv.Value.ToString(); } } - else if (kv.Key.ToUpper().Equals("WORKINGDIR")) + // write out the script.. + fn = Path.GetTempFileName() + ".cmd"; + StreamWriter sw = null; + try { - process.StartInfo.WorkingDirectory = kv.Value.ToString(); + sw = new StreamWriter(fn); + sw.Write(_script); } - else if (kv.Key.ToUpper().Equals("TIMEOUT")) - { - timeout = Int32.Parse(kv.Value.ToString()); + finally + { + sw.Close(); + sw.Dispose(); } - else + // set temp file.. + process.StartInfo.FileName = fn; + // execute script.. + process.Start(); + // wait for the process to exit.. + if (!process.WaitForExit(timeout)) { - process.StartInfo.EnvironmentVariables[kv.Key] = kv.Value.ToString(); + throw new TimeoutException("Script failed to exit in time!"); } + exitCode = process.ExitCode; + } - // write out the script.. - string fn = Path.GetTempFileName() + ".cmd"; - using (StreamWriter sw = new StreamWriter(fn)) - { - sw.Write(_script); + catch (Exception e) { + } - // set temp file.. - process.StartInfo.FileName = fn; - // execute script.. - process.Start(); - // wait for the process to exit.. - if (!process.WaitForExit(timeout)) - { + finally { + // close up the process process.Close(); - throw new TimeoutException("Script failed to exit in time!"); + process.Dispose(); } - int exitCode = process.ExitCode; - // close up the process and return the exit code.. - process.Close(); // clean up temp file.. - File.Delete(fn); + try + { + File.Delete(fn); + } + catch (Exception e) + { + + } + return exitCode; } } diff --git a/TestBundles/TestBundleV1/Messages.es-ES.resx b/TestBundles/TestBundleV1/Messages.es-ES.resx index ec894ca7..c3a62438 100644 --- a/TestBundles/TestBundleV1/Messages.es-ES.resx +++ b/TestBundles/TestBundleV1/Messages.es-ES.resx @@ -123,4 +123,7 @@ tstField.help_es-ES + + tstField.group_es-ES + \ No newline at end of file diff --git a/TestBundles/TestBundleV1/Messages.es.resx b/TestBundles/TestBundleV1/Messages.es.resx index 523473e4..c6569e23 100644 --- a/TestBundles/TestBundleV1/Messages.es.resx +++ b/TestBundles/TestBundleV1/Messages.es.resx @@ -123,4 +123,7 @@ tstField.display_es + + tstField.group_es + \ No newline at end of file diff --git a/TestBundles/TestBundleV1/Messages.resx b/TestBundles/TestBundleV1/Messages.resx index a988e1e7..46001b1a 100644 --- a/TestBundles/TestBundleV1/Messages.resx +++ b/TestBundles/TestBundleV1/Messages.resx @@ -123,4 +123,7 @@ Display for test field. + + Group for test field. + \ No newline at end of file diff --git a/TestBundles/TestBundleV1/TestConnector.cs b/TestBundles/TestBundleV1/TestConnector.cs index 21fbbc8d..605e9dd5 100644 --- a/TestBundles/TestBundleV1/TestConnector.cs +++ b/TestBundles/TestBundleV1/TestConnector.cs @@ -19,6 +19,7 @@ * enclosed by brackets [] replaced by your own identifying information: * "Portions Copyrighted [year] [name of copyright owner]" * ==================== + * Portions Copyrighted 2012 ForgeRock AS */ using System; using System.Collections.Generic; @@ -101,6 +102,7 @@ public int GetConnectionNumber() } [ConnectorClass("TestConnector", + "TestConnector.category", typeof(TstConnectorConfig), MessageCatalogPaths = new String[] { "TestBundleV1.Messages" } )] diff --git a/TestBundles/TestBundleV2/TestConnector.cs b/TestBundles/TestBundleV2/TestConnector.cs index 5b74e5d7..9ce15e3b 100644 --- a/TestBundles/TestBundleV2/TestConnector.cs +++ b/TestBundles/TestBundleV2/TestConnector.cs @@ -19,6 +19,7 @@ * enclosed by brackets [] replaced by your own identifying information: * "Portions Copyrighted [year] [name of copyright owner]" * ==================== + * Portions Copyrighted 2012 ForgeRock AS */ using System; using System.Collections.Generic; From 88e49e542bc18aecd8c2729a454df796c69c4537 Mon Sep 17 00:00:00 2001 From: Gael Allioux Date: Tue, 21 Aug 2012 15:06:50 +0000 Subject: [PATCH 286/342] OPENICF-34 AD Connector should be able to run PowerShell script for the RunScriptOnResource operation --- .../PowerShellScriptExecutorFactory.cs | 126 ++++++++++++++++++ .../PowerShellScriptExecutorFactory.csproj | 67 ++++++++++ 2 files changed, 193 insertions(+) create mode 100644 PowerShellScriptExecutorFactory/PowerShellScriptExecutorFactory.cs create mode 100644 PowerShellScriptExecutorFactory/PowerShellScriptExecutorFactory.csproj diff --git a/PowerShellScriptExecutorFactory/PowerShellScriptExecutorFactory.cs b/PowerShellScriptExecutorFactory/PowerShellScriptExecutorFactory.cs new file mode 100644 index 00000000..650ae8de --- /dev/null +++ b/PowerShellScriptExecutorFactory/PowerShellScriptExecutorFactory.cs @@ -0,0 +1,126 @@ +/** +* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER. +* +* Copyright (c) 2012 ForgeRock AS. All Rights Reserved +* +* The contents of this file are subject to the terms +* of the Common Development and Distribution License +* (the License). You may not use this file except in +* compliance with the License. +* +* You can obtain a copy of the License at +* http://forgerock.org/license/CDDLv1.0.html +* See the License for the specific language governing +* permission and limitations under the License. +* +* When distributing Covered Code, include this CDDL +* Header Notice in each file and include the License file +* at http://forgerock.org/license/CDDLv1.0.html +* If applicable, add the following below the CDDL Header, +* with the fields enclosed by brackets [] replaced by +* your own identifying information: +* "Portions Copyrighted [year] [name of copyright owner]" +* +* +* * Author: Gael Allioux +*/ + +using System; +using System.Collections.Generic; +using System.Collections.ObjectModel; +using System.Text; +using System.Reflection; +using System.Management.Automation; +using System.Management.Automation.Runspaces; +using Org.IdentityConnectors.Common; +using Org.IdentityConnectors.Common.Script; + + +// namespace Org.IdentityConnectors.Common.Script.PowerShell +namespace Org.ForgeRock.OpenICF.Framework.Common.Script.PowerShell +{ + [ScriptExecutorFactoryClass("PowerShell")] + public class PowerShellScriptExecutorFactory : ScriptExecutorFactory + { + + /// + /// Creates a script executor given the PowerShell script. + /// + override + public ScriptExecutor NewScriptExecutor(Assembly[] referencedAssemblies, string script, bool compile) + { + return new PowerShellScriptExecutor(script); + } + + /// + /// Processes the script. + /// + class PowerShellScriptExecutor : ScriptExecutor + { + private readonly string _script; + + public PowerShellScriptExecutor() + { + } + + public PowerShellScriptExecutor(string script) + { + _script = script; + } + + public PowerShellScriptExecutor(Assembly[] referencedAssemblies, string script) + { + _script = script; + } + + public object Execute(IDictionary arguments) + { + Command myCommand = new Command(_script,true); + Runspace runspace = RunspaceFactory.CreateRunspace(); + Pipeline pipeline = null; + Collection results = null; + + //foreach (String argumentName in arguments.Keys) + //{ + // CommandParameter param = new CommandParameter(argumentName, arguments[argumentName]); + // myCommand.Parameters.Add(param); + //} + + try + { + runspace.Open(); + // create a pipeline and give it the command + pipeline = runspace.CreatePipeline(); + pipeline.Commands.Add(myCommand); + //pipeline.Commands.Add("Out-String"); + foreach (String argumentName in arguments.Keys) + { + runspace.SessionStateProxy.SetVariable(argumentName, arguments[argumentName]); + } + // execute the script + results = pipeline.Invoke(); + } + catch (Exception e) + { + TraceUtil.TraceException("Unable to run PowerShell script on resource.", e); + throw; + } + finally + { + pipeline.Dispose(); + // close & dispose the runspace + runspace.Close(); + runspace.Dispose(); + } + + // return the script result as a single string + StringBuilder stringBuilder = new StringBuilder(); + foreach (PSObject obj in results) + { + stringBuilder.AppendLine(obj.ToString()); + } + return stringBuilder.ToString(); + } + } + } +} diff --git a/PowerShellScriptExecutorFactory/PowerShellScriptExecutorFactory.csproj b/PowerShellScriptExecutorFactory/PowerShellScriptExecutorFactory.csproj new file mode 100644 index 00000000..8bdc6a8f --- /dev/null +++ b/PowerShellScriptExecutorFactory/PowerShellScriptExecutorFactory.csproj @@ -0,0 +1,67 @@ + + + + Debug + AnyCPU + 8.0.30703 + 2.0 + {57754FFA-BB1F-4722-A2FA-70C4F27C6784} + Library + Properties + PowerShellScriptExecutorFactory + PowerShell.ScriptExecutorFactory + v4.0 + 512 + + + true + full + false + bin\Debug\ + DEBUG;TRACE + prompt + 4 + + + pdbonly + true + bin\Release\ + TRACE + prompt + 4 + + + + + + False + ..\..\..\..\Program Files\Reference Assemblies\Microsoft\WindowsPowerShell\v1.0\System.Management.Automation.dll + + + + + + + + + + + + + + {F140E8DA-52B4-4159-992A-9DA10EA8EEFB} + Common + + + + + + + + \ No newline at end of file From 2ce801cbe86ebd98332bb63692bdf0fd6ff4e00a Mon Sep 17 00:00:00 2001 From: Gael Allioux Date: Tue, 21 Aug 2012 15:29:48 +0000 Subject: [PATCH 287/342] Fixed project definition --- .../PowerShellScriptExecutorFactory.csproj | 41 +++++++++++++------ 1 file changed, 29 insertions(+), 12 deletions(-) diff --git a/PowerShellScriptExecutorFactory/PowerShellScriptExecutorFactory.csproj b/PowerShellScriptExecutorFactory/PowerShellScriptExecutorFactory.csproj index 8bdc6a8f..6c8d9f6b 100644 --- a/PowerShellScriptExecutorFactory/PowerShellScriptExecutorFactory.csproj +++ b/PowerShellScriptExecutorFactory/PowerShellScriptExecutorFactory.csproj @@ -1,9 +1,34 @@ - + + Debug AnyCPU - 8.0.30703 2.0 {57754FFA-BB1F-4722-A2FA-70C4F27C6784} Library @@ -35,7 +60,6 @@ False - ..\..\..\..\Program Files\Reference Assemblies\Microsoft\WindowsPowerShell\v1.0\System.Management.Automation.dll @@ -45,7 +69,7 @@ - + @@ -56,12 +80,5 @@ - - + \ No newline at end of file From c5bf4664f038537fd247e7bd1a47afa49f9f6c77 Mon Sep 17 00:00:00 2001 From: Laszlo Hordos Date: Fri, 24 Aug 2012 15:29:38 +0000 Subject: [PATCH 288/342] OPENICF-37 - Package the OpenICF 1.1.0.1 .Net Connector server --- .../BooScriptExecutorFactory.csproj | 7 +- Common/Common.csproj | 7 +- ConnectorFramework.sln | 10 +- Framework.targets | 7 +- Framework/Framework.csproj | 7 +- FrameworkInternal/FrameworkInternal.csproj | 5 +- FrameworkTests/FrameworkTests.csproj | 3 +- .../PowerShellScriptExecutorFactory.csproj | 44 +++++---- Service/Service.csproj | 8 +- ServiceInstall/ExtBuild.proj | 94 ++++++++++++------- ServiceInstall/ServiceInstall.wixproj | 21 ++++- ServiceInstall/Setup.wxs | 2 +- .../ShellScriptExecutorFactory.csproj | 5 +- TestBundles/TestBundleV1/TestBundleV1.csproj | 5 +- TestBundles/TestBundleV2/TestBundleV2.csproj | 5 +- TestCommon/TestCommon.csproj | 5 +- 16 files changed, 147 insertions(+), 88 deletions(-) diff --git a/BooScriptExecutorFactory/BooScriptExecutorFactory.csproj b/BooScriptExecutorFactory/BooScriptExecutorFactory.csproj index c8e366bc..852db48e 100644 --- a/BooScriptExecutorFactory/BooScriptExecutorFactory.csproj +++ b/BooScriptExecutorFactory/BooScriptExecutorFactory.csproj @@ -1,3 +1,4 @@ + - + {0747C440-70E4-4E63-9F9D-03B3A010C991} Debug @@ -52,7 +53,7 @@ False True False - + lib\Boo.Lang.dll @@ -93,4 +94,4 @@ - + \ No newline at end of file diff --git a/Common/Common.csproj b/Common/Common.csproj index f04f7739..3178ce09 100644 --- a/Common/Common.csproj +++ b/Common/Common.csproj @@ -1,3 +1,4 @@ + - + {F140E8DA-52B4-4159-992A-9DA10EA8EEFB} Debug @@ -58,7 +59,7 @@ 4194304 AnyCPU 4096 - + @@ -93,4 +94,4 @@ - + \ No newline at end of file diff --git a/ConnectorFramework.sln b/ConnectorFramework.sln index e6ab7d0d..e6fd3060 100644 --- a/ConnectorFramework.sln +++ b/ConnectorFramework.sln @@ -1,6 +1,6 @@  -Microsoft Visual Studio Solution File, Format Version 10.00 -# Visual Studio 2008 +Microsoft Visual Studio Solution File, Format Version 11.00 +# Visual Studio 2010 Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Common", "Common\Common.csproj", "{F140E8DA-52B4-4159-992A-9DA10EA8EEFB}" EndProject Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Framework", "Framework\Framework.csproj", "{8B24461B-456A-4032-89A1-CD418F7B5B62}" @@ -23,6 +23,8 @@ Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "BooScriptExecutorFactory", EndProject Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "ShellScriptExecutorFactory", "ShellScriptExecutorFactory\ShellScriptExecutorFactory.csproj", "{4700690A-2D29-40A0-86AC-E5A9F71A479A}" EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "PowerShellScriptExecutorFactory", "PowerShellScriptExecutorFactory\PowerShellScriptExecutorFactory.csproj", "{57754FFA-BB1F-4722-A2FA-70C4F27C6784}" +EndProject Global GlobalSection(SolutionConfigurationPlatforms) = preSolution Debug|Any CPU = Debug|Any CPU @@ -72,6 +74,10 @@ Global {4700690A-2D29-40A0-86AC-E5A9F71A479A}.Debug|Any CPU.Build.0 = Debug|Any CPU {4700690A-2D29-40A0-86AC-E5A9F71A479A}.Release|Any CPU.ActiveCfg = Release|Any CPU {4700690A-2D29-40A0-86AC-E5A9F71A479A}.Release|Any CPU.Build.0 = Release|Any CPU + {57754FFA-BB1F-4722-A2FA-70C4F27C6784}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {57754FFA-BB1F-4722-A2FA-70C4F27C6784}.Debug|Any CPU.Build.0 = Debug|Any CPU + {57754FFA-BB1F-4722-A2FA-70C4F27C6784}.Release|Any CPU.ActiveCfg = Release|Any CPU + {57754FFA-BB1F-4722-A2FA-70C4F27C6784}.Release|Any CPU.Build.0 = Release|Any CPU EndGlobalSection GlobalSection(SolutionProperties) = preSolution HideSolutionNode = FALSE diff --git a/Framework.targets b/Framework.targets index 2b70ae98..0f1fdba4 100644 --- a/Framework.targets +++ b/Framework.targets @@ -81,9 +81,10 @@ SourceFiles="$(MSBuildProjectDirectory)\..\legal\THIRDPARTYREADME.txt" DestinationFolder="$(OutputPath)" /> - - - + + + + - + {8B24461B-456A-4032-89A1-CD418F7B5B62} Debug @@ -85,7 +86,7 @@ - + @@ -97,4 +98,4 @@ - + \ No newline at end of file diff --git a/FrameworkInternal/FrameworkInternal.csproj b/FrameworkInternal/FrameworkInternal.csproj index d47ebb75..af02d9bc 100644 --- a/FrameworkInternal/FrameworkInternal.csproj +++ b/FrameworkInternal/FrameworkInternal.csproj @@ -1,4 +1,5 @@ - - + {5B011775-B121-4EEE-A410-BA2D2F5BFB8B} Debug diff --git a/FrameworkTests/FrameworkTests.csproj b/FrameworkTests/FrameworkTests.csproj index 886fa52f..05efd6b3 100644 --- a/FrameworkTests/FrameworkTests.csproj +++ b/FrameworkTests/FrameworkTests.csproj @@ -1,3 +1,4 @@ + - + {32804F5A-812C-4FA6-835C-BDAE5B24D355} Debug diff --git a/PowerShellScriptExecutorFactory/PowerShellScriptExecutorFactory.csproj b/PowerShellScriptExecutorFactory/PowerShellScriptExecutorFactory.csproj index 6c8d9f6b..770061e0 100644 --- a/PowerShellScriptExecutorFactory/PowerShellScriptExecutorFactory.csproj +++ b/PowerShellScriptExecutorFactory/PowerShellScriptExecutorFactory.csproj @@ -27,34 +27,40 @@ --> + {57754FFA-BB1F-4722-A2FA-70C4F27C6784} Debug AnyCPU - 2.0 - {57754FFA-BB1F-4722-A2FA-70C4F27C6784} Library Properties - PowerShellScriptExecutorFactory + Org.ForgeRock.OpenICF.Framework.Common.Script.PowerShell PowerShell.ScriptExecutorFactory - v4.0 - 512 + v3.5 + true - - true - full - false - bin\Debug\ - DEBUG;TRACE + prompt 4 + AnyCPU + bin\Debug\ + True + Full + False + True + DEBUG;TRACE - + pdbonly - true bin\Release\ - TRACE prompt + False 4 + AnyCPU + None + True + False + TRACE + @@ -68,8 +74,8 @@ - + @@ -77,8 +83,8 @@ Common - - - - + + + + \ No newline at end of file diff --git a/Service/Service.csproj b/Service/Service.csproj index b6a67c92..420b89c8 100644 --- a/Service/Service.csproj +++ b/Service/Service.csproj @@ -1,3 +1,4 @@ + - + {A9D6374A-D51F-4FA3-8C02-5B1D23FAA82E} Debug @@ -32,7 +33,6 @@ v3.5 False false - true prompt @@ -96,7 +96,7 @@ - + {F140E8DA-52B4-4159-992A-9DA10EA8EEFB} @@ -111,4 +111,4 @@ Framework - + \ No newline at end of file diff --git a/ServiceInstall/ExtBuild.proj b/ServiceInstall/ExtBuild.proj index e47b6ebb..ae1644f2 100644 --- a/ServiceInstall/ExtBuild.proj +++ b/ServiceInstall/ExtBuild.proj @@ -19,47 +19,71 @@ enclosed by brackets [] replaced by your own identifying information: "Portions Copyrighted [year] [name of copyright owner]" ==================== + Portions Copyrighted 2012 ForgeRock AS --> - - Debug - - - - - - - - - + Debug + + + + + + + + + - - - + + - - - - - - + + + + + + + + + + + + + + + - - - - - - - - - - - - + + + + + + + + + + + + + + + + + + + + + + - - - + + + diff --git a/ServiceInstall/ServiceInstall.wixproj b/ServiceInstall/ServiceInstall.wixproj index 2323c15a..6076726d 100644 --- a/ServiceInstall/ServiceInstall.wixproj +++ b/ServiceInstall/ServiceInstall.wixproj @@ -1,4 +1,5 @@ - - + {1CBA8F74-050C-432B-8437-08BD13BDC684} Debug @@ -30,6 +31,7 @@ ServiceInstall $(MSBuildExtensionsPath)\Microsoft\WiX\v3.x\Wix.targets ICE45 + 1.1.0.1-SNAPSHOT prompt @@ -54,6 +56,7 @@ False + WixUIExtension @@ -68,7 +71,7 @@ Common {f140e8da-52b4-4159-992a-9da10ea8eefb} True - True + True FrameworkInternal @@ -82,12 +85,24 @@ True True + + PowerShellScriptExecutorFactory + {57754ffa-bb1f-4722-a2fa-70c4f27c6784} + True + True + Service {a9d6374a-d51f-4fa3-8c02-5b1d23faa82e} True True + + ShellScriptExecutorFactory + {4700690a-2d29-40a0-86ac-e5a9f71a479a} + True + True + TestCommon {e6a207d2-e083-41bf-b522-d9d3ec09323e} diff --git a/ServiceInstall/Setup.wxs b/ServiceInstall/Setup.wxs index 0050c748..0ff70304 100644 --- a/ServiceInstall/Setup.wxs +++ b/ServiceInstall/Setup.wxs @@ -98,7 +98,7 @@ - + diff --git a/ShellScriptExecutorFactory/ShellScriptExecutorFactory.csproj b/ShellScriptExecutorFactory/ShellScriptExecutorFactory.csproj index ef613f29..39ef2da8 100644 --- a/ShellScriptExecutorFactory/ShellScriptExecutorFactory.csproj +++ b/ShellScriptExecutorFactory/ShellScriptExecutorFactory.csproj @@ -1,3 +1,4 @@ + - + {4700690A-2D29-40A0-86AC-E5A9F71A479A} Debug @@ -80,4 +81,4 @@ - + \ No newline at end of file diff --git a/TestBundles/TestBundleV1/TestBundleV1.csproj b/TestBundles/TestBundleV1/TestBundleV1.csproj index 972d3da3..6a771272 100644 --- a/TestBundles/TestBundleV1/TestBundleV1.csproj +++ b/TestBundles/TestBundleV1/TestBundleV1.csproj @@ -1,3 +1,4 @@ + - + {0BC2A013-56FE-46DD-90FC-2D2D57748FB6} Debug @@ -88,4 +89,4 @@ - + \ No newline at end of file diff --git a/TestBundles/TestBundleV2/TestBundleV2.csproj b/TestBundles/TestBundleV2/TestBundleV2.csproj index ac224405..79f0dcf1 100644 --- a/TestBundles/TestBundleV2/TestBundleV2.csproj +++ b/TestBundles/TestBundleV2/TestBundleV2.csproj @@ -1,3 +1,4 @@ + - + {3E737796-3A83-4924-9FF1-DC542F21F59E} Debug @@ -79,4 +80,4 @@ - + \ No newline at end of file diff --git a/TestCommon/TestCommon.csproj b/TestCommon/TestCommon.csproj index 453a47c3..949082e2 100644 --- a/TestCommon/TestCommon.csproj +++ b/TestCommon/TestCommon.csproj @@ -1,9 +1,8 @@  - + Debug - AnyCPU - 9.0.21022 + AnyCPU 2.0 {E6A207D2-E083-41BF-B522-D9D3EC09323E} Library From e6ababb53a22d58f981e4a24e6a2f09fa5f8302f Mon Sep 17 00:00:00 2001 From: Laszlo Hordos Date: Fri, 24 Aug 2012 16:19:42 +0000 Subject: [PATCH 289/342] OPENICF-25 - Update the connector.dtd --- Framework.targets | 5 ++--- FrameworkInternal/Resources.resx | 31 +++++++++++++++++++------------ 2 files changed, 21 insertions(+), 15 deletions(-) diff --git a/Framework.targets b/Framework.targets index 0f1fdba4..f3ab6bb4 100644 --- a/Framework.targets +++ b/Framework.targets @@ -82,9 +82,8 @@ DestinationFolder="$(OutputPath)" /> - - - + + Date: Fri, 24 Aug 2012 16:50:02 +0000 Subject: [PATCH 290/342] OPENICF-37 - Update to .Net 4.0 --- .../BooScriptExecutorFactory.csproj | 6 +++--- Common/Common.csproj | 8 ++++---- Framework.targets | 2 +- Framework/Framework.csproj | 7 ++----- FrameworkInternal/FrameworkInternal.csproj | 4 ++-- FrameworkTests/FrameworkTests.csproj | 8 ++++---- .../PowerShellScriptExecutorFactory.csproj | 10 +++++++--- Service/Service.csproj | 6 +++--- ServiceInstall/ExtBuild.proj | 2 +- .../ShellScriptExecutorFactory.csproj | 6 +++--- TestBundles/TestBundleV1/TestBundleV1.csproj | 2 +- TestBundles/TestBundleV2/TestBundleV2.csproj | 2 +- TestCommon/TestCommon.csproj | 8 ++++---- 13 files changed, 36 insertions(+), 35 deletions(-) diff --git a/BooScriptExecutorFactory/BooScriptExecutorFactory.csproj b/BooScriptExecutorFactory/BooScriptExecutorFactory.csproj index 852db48e..7cf1864f 100644 --- a/BooScriptExecutorFactory/BooScriptExecutorFactory.csproj +++ b/BooScriptExecutorFactory/BooScriptExecutorFactory.csproj @@ -30,7 +30,7 @@ Org.IdentityConnectors.Common.Script.Boo Boo.ScriptExecutorFactory BooScriptExecutorFactory - v3.5 + v4.0 prompt @@ -72,11 +72,11 @@ - 3.5 + 4.0 - 3.5 + 4.0 diff --git a/Common/Common.csproj b/Common/Common.csproj index 3178ce09..704601d6 100644 --- a/Common/Common.csproj +++ b/Common/Common.csproj @@ -30,7 +30,7 @@ Org.IdentityConnectors.Common Common Open Connectors Common - v3.5 + v4.0 True False 4 @@ -64,15 +64,15 @@ - 3.5 + 4.0 - 3.5 + 4.0 - 3.5 + 4.0 diff --git a/Framework.targets b/Framework.targets index f3ab6bb4..5503ac9d 100644 --- a/Framework.targets +++ b/Framework.targets @@ -20,7 +20,7 @@ "Portions Copyrighted [year] [name of copyright owner]" ==================== --> - + $(MSBuildProjectDirectory)\version.template $(MSBuildProjectDirectory)\version.txt diff --git a/Framework/Framework.csproj b/Framework/Framework.csproj index fd590761..1f0cc269 100644 --- a/Framework/Framework.csproj +++ b/Framework/Framework.csproj @@ -30,7 +30,7 @@ Org.IdentityConnectors.Framework Framework Open Connectors Framework - v3.5 + v4.0 False False 4 @@ -69,10 +69,7 @@ - 3.5 - - - 3.5 + 4.0 diff --git a/FrameworkInternal/FrameworkInternal.csproj b/FrameworkInternal/FrameworkInternal.csproj index af02d9bc..aef2cfb5 100644 --- a/FrameworkInternal/FrameworkInternal.csproj +++ b/FrameworkInternal/FrameworkInternal.csproj @@ -30,7 +30,7 @@ Org.IdentityConnectors FrameworkInternal FrameworkInternal - v3.5 + v4.0 True False 4 @@ -69,7 +69,7 @@ - 3.5 + 4.0 diff --git a/FrameworkTests/FrameworkTests.csproj b/FrameworkTests/FrameworkTests.csproj index 05efd6b3..239cfd2e 100644 --- a/FrameworkTests/FrameworkTests.csproj +++ b/FrameworkTests/FrameworkTests.csproj @@ -30,7 +30,7 @@ FrameworkTests FrameworkTests FrameworkTests - v3.5 + v4.0 bin\Debug\ @@ -55,15 +55,15 @@ - 3.5 + 4.0 - 3.5 + 4.0 - 3.5 + 4.0 diff --git a/PowerShellScriptExecutorFactory/PowerShellScriptExecutorFactory.csproj b/PowerShellScriptExecutorFactory/PowerShellScriptExecutorFactory.csproj index 770061e0..ea078246 100644 --- a/PowerShellScriptExecutorFactory/PowerShellScriptExecutorFactory.csproj +++ b/PowerShellScriptExecutorFactory/PowerShellScriptExecutorFactory.csproj @@ -34,7 +34,7 @@ Properties Org.ForgeRock.OpenICF.Framework.Common.Script.PowerShell PowerShell.ScriptExecutorFactory - v3.5 + v4.0 true @@ -63,11 +63,15 @@ - + + 4.0 + False - + + 4.0 + diff --git a/Service/Service.csproj b/Service/Service.csproj index 420b89c8..63e1878a 100644 --- a/Service/Service.csproj +++ b/Service/Service.csproj @@ -30,7 +30,7 @@ Sun.OpenConnectors.Framework.Service ConnectorServer Service - v3.5 + v4.0 False false @@ -73,12 +73,12 @@ - 3.5 + 4.0 - 3.5 + 4.0 diff --git a/ServiceInstall/ExtBuild.proj b/ServiceInstall/ExtBuild.proj index ae1644f2..ac1b62e7 100644 --- a/ServiceInstall/ExtBuild.proj +++ b/ServiceInstall/ExtBuild.proj @@ -21,7 +21,7 @@ ==================== Portions Copyrighted 2012 ForgeRock AS --> - + Debug diff --git a/ShellScriptExecutorFactory/ShellScriptExecutorFactory.csproj b/ShellScriptExecutorFactory/ShellScriptExecutorFactory.csproj index 39ef2da8..3d2a3e60 100644 --- a/ShellScriptExecutorFactory/ShellScriptExecutorFactory.csproj +++ b/ShellScriptExecutorFactory/ShellScriptExecutorFactory.csproj @@ -30,7 +30,7 @@ Sun.OpenConnectors.Common.Script.Shell Shell.ScriptExecutorFactory ShellScriptExecutorFactory - v3.5 + v4.0 true @@ -60,11 +60,11 @@ - 3.5 + 4.0 - 3.5 + 4.0 diff --git a/TestBundles/TestBundleV1/TestBundleV1.csproj b/TestBundles/TestBundleV1/TestBundleV1.csproj index 6a771272..fd489351 100644 --- a/TestBundles/TestBundleV1/TestBundleV1.csproj +++ b/TestBundles/TestBundleV1/TestBundleV1.csproj @@ -29,7 +29,7 @@ Library TestBundleV1 TestBundleV1.Connector - v3.5 + v4.0 OnBuildSuccess diff --git a/TestBundles/TestBundleV2/TestBundleV2.csproj b/TestBundles/TestBundleV2/TestBundleV2.csproj index 79f0dcf1..fa43b2a1 100644 --- a/TestBundles/TestBundleV2/TestBundleV2.csproj +++ b/TestBundles/TestBundleV2/TestBundleV2.csproj @@ -29,7 +29,7 @@ Library TestBundleV2 TestBundleV2.Connector - v3.5 + v4.0 OnBuildSuccess diff --git a/TestCommon/TestCommon.csproj b/TestCommon/TestCommon.csproj index 949082e2..92b064a7 100644 --- a/TestCommon/TestCommon.csproj +++ b/TestCommon/TestCommon.csproj @@ -9,7 +9,7 @@ Properties Org.IdentityConnectors.Test.Common TestCommon - v3.5 + v4.0 512 FrameworkTests true @@ -35,13 +35,13 @@ - 3.5 + 4.0 - 3.5 + 4.0 - 3.5 + 4.0 From 77cc5b765dbd1c4100a5a0351a3c97465b84b146 Mon Sep 17 00:00:00 2001 From: Laszlo Hordos Date: Fri, 24 Aug 2012 17:29:12 +0000 Subject: [PATCH 291/342] OPENICF-37 - Update to .Net 4.0 --- Service/Service.csproj | 14 +++++++++----- 1 file changed, 9 insertions(+), 5 deletions(-) diff --git a/Service/Service.csproj b/Service/Service.csproj index 63e1878a..66e47604 100644 --- a/Service/Service.csproj +++ b/Service/Service.csproj @@ -69,18 +69,22 @@ - + - + + True + 4.0 - - + + + True + 4.0 - + From 73208f1592780ee78e289bb08b84195532b557fe Mon Sep 17 00:00:00 2001 From: Laszlo Hordos Date: Sun, 26 Aug 2012 15:11:34 +0000 Subject: [PATCH 292/342] OPENICF-38 - Use more structured ScriptExecutor return object --- .../PowerShellScriptExecutorFactory.cs | 10 ++-- .../PowerShellScriptExecutorFactory.csproj | 2 +- ServiceInstall/ExtBuild.proj | 8 +-- .../ShellScriptExecutorFactory.cs | 49 +++++++++++++++---- 4 files changed, 51 insertions(+), 18 deletions(-) diff --git a/PowerShellScriptExecutorFactory/PowerShellScriptExecutorFactory.cs b/PowerShellScriptExecutorFactory/PowerShellScriptExecutorFactory.cs index 650ae8de..5b98e401 100644 --- a/PowerShellScriptExecutorFactory/PowerShellScriptExecutorFactory.cs +++ b/PowerShellScriptExecutorFactory/PowerShellScriptExecutorFactory.cs @@ -114,12 +114,14 @@ public object Execute(IDictionary arguments) } // return the script result as a single string - StringBuilder stringBuilder = new StringBuilder(); - foreach (PSObject obj in results) + IDictionary result = new Dictionary(); + int index = 0; + foreach (PSObject obj in results) { - stringBuilder.AppendLine(obj.ToString()); + result.Add(index.ToString(),obj.ToString()); + index++; } - return stringBuilder.ToString(); + return result; } } } diff --git a/PowerShellScriptExecutorFactory/PowerShellScriptExecutorFactory.csproj b/PowerShellScriptExecutorFactory/PowerShellScriptExecutorFactory.csproj index ea078246..074cdba0 100644 --- a/PowerShellScriptExecutorFactory/PowerShellScriptExecutorFactory.csproj +++ b/PowerShellScriptExecutorFactory/PowerShellScriptExecutorFactory.csproj @@ -66,7 +66,7 @@ 4.0 - + False diff --git a/ServiceInstall/ExtBuild.proj b/ServiceInstall/ExtBuild.proj index ac1b62e7..c2174159 100644 --- a/ServiceInstall/ExtBuild.proj +++ b/ServiceInstall/ExtBuild.proj @@ -40,12 +40,12 @@ Lines="@(AllFiles->'<File Source="%(Fullpath)" Name="%(Filename)%(Extension)" />')"/> - + - + - + @@ -79,7 +79,7 @@ - + diff --git a/ShellScriptExecutorFactory/ShellScriptExecutorFactory.cs b/ShellScriptExecutorFactory/ShellScriptExecutorFactory.cs index 466a8423..4567649a 100644 --- a/ShellScriptExecutorFactory/ShellScriptExecutorFactory.cs +++ b/ShellScriptExecutorFactory/ShellScriptExecutorFactory.cs @@ -29,6 +29,7 @@ using System.Collections.Generic; using System.Text.RegularExpressions; using Org.IdentityConnectors.Common.Security; +using System.Threading; namespace Org.IdentityConnectors.Common.Script.Shell { @@ -63,7 +64,7 @@ public ScriptExecutor NewScriptExecutor(Assembly[] refs, string script, bool com class ShellScriptExecutor : ScriptExecutor { private readonly string _script; - + public ShellScriptExecutor(string script) { _script = script; @@ -77,8 +78,11 @@ public object Execute(IDictionary arguments) // set the defaults.. process.StartInfo.CreateNoWindow = true; process.StartInfo.UseShellExecute = true; + process.StartInfo.RedirectStandardOutput = true; + process.StartInfo.RedirectStandardError = true; // set the default timeout.. int timeout = 1000 * 30; // 30 secss + IDictionary result = new Dictionary(); int exitCode = 1; try { @@ -138,7 +142,7 @@ public object Execute(IDictionary arguments) sw.Write(_script); } finally - { + { sw.Close(); sw.Dispose(); } @@ -146,18 +150,29 @@ public object Execute(IDictionary arguments) process.StartInfo.FileName = fn; // execute script.. process.Start(); + string stdout = process.StandardOutput.ReadToEnd(); + // http://msdn.microsoft.com/en-us/library/system.diagnostics.process.standardoutput.aspx + // Use asynchronous read operations on at least one of the streams. + AsynchronousReader msr_stderr = new AsynchronousReader(process.StandardError); + // Create the thread objects to run the code asynchronously + Thread t_stderr = new Thread(msr_stderr.Go); + t_stderr.Start(); + t_stderr.Join(); // wait for the process to exit.. if (!process.WaitForExit(timeout)) { throw new TimeoutException("Script failed to exit in time!"); } exitCode = process.ExitCode; - + result.Add("stdout", stdout); + result.Add("stderr", msr_stderr.Text); } - catch (Exception e) { - + catch (Exception e) + { + Trace.TraceError("Failed to execute script with exception {0}", e.Message); } - finally { + finally + { // close up the process process.Close(); process.Dispose(); @@ -169,11 +184,27 @@ public object Execute(IDictionary arguments) } catch (Exception e) { - + } - - return exitCode; + result.Add("exitCode", exitCode); + return result; } } } + internal class AsynchronousReader + { + StreamReader _sr = null; + string _text = null; + public string Text { get { return _text; } } + + public AsynchronousReader(StreamReader sr) + { + _sr = sr; + } + + public void Go() + { + _text = _sr.ReadToEnd(); + } + } } From 542466b4ed4b7bf104243822f085715c258c10fb Mon Sep 17 00:00:00 2001 From: Laszlo Hordos Date: Fri, 31 Aug 2012 12:23:11 +0000 Subject: [PATCH 293/342] OPENICF-25 - Fix the incompatibility between Java and .Net OPENICF-39 - Add command prototype to install certificate file --- FrameworkInternal/ApiRemoteMessages.cs | 4 +- FrameworkInternal/Server.cs | 9 ++++- Service/Program.cs | 42 +++++++++++++++++++ Service/app.config | 56 +++++++++++++------------- 4 files changed, 80 insertions(+), 31 deletions(-) diff --git a/FrameworkInternal/ApiRemoteMessages.cs b/FrameworkInternal/ApiRemoteMessages.cs index 3d1fdab1..d9bd67fb 100644 --- a/FrameworkInternal/ApiRemoteMessages.cs +++ b/FrameworkInternal/ApiRemoteMessages.cs @@ -34,8 +34,8 @@ public class HelloRequest : Message { public const int SERVER_INFO = 4; public const int CONNECTOR_KEY_LIST = 16; - private const int DEFAULT_CONFIG = 32; - public const int CONNECTOR_INFO = CONNECTOR_KEY_LIST | DEFAULT_CONFIG; + // private const int DEFAULT_CONFIG = 32; + public const int CONNECTOR_INFO = CONNECTOR_KEY_LIST | SERVER_INFO; private readonly int _level; diff --git a/FrameworkInternal/Server.cs b/FrameworkInternal/Server.cs index 1f55dc99..3eb8a07d 100644 --- a/FrameworkInternal/Server.cs +++ b/FrameworkInternal/Server.cs @@ -1046,7 +1046,14 @@ public override void Start() //make sure we are configured properly ConnectorInfoManagerFactory.GetInstance().GetLocalManager(); _requestCount = 0; - _startDate = DateTimeUtil.GetCurrentUtcTimeMillis(); + /* + * the Java and .Net dates have a different starting point: zero milliseconds in Java corresponds to January 1, 1970, 00:00:00 GMT (aka the epoch). + * In .Net zero milliseconds* corresponds to 12:00 A.M., January 1, 0001 GMT. + * So the basic is to bridge over the reference points gap with adding (or substracting) the corresponding number of milliseconds + * such that zero milliseconds in .Net is mapped to -62135769600000L milliseconds in Java. + * This number of milliseconds corresponds to GMT zone, so do not forget to include your time zone offset into the calculations. + */ + _startDate = (DateTime.UtcNow.Ticks - 621355968000000000) / 10000; _pendingRequests.Clear(); TcpListener socket = CreateServerSocket(); diff --git a/Service/Program.cs b/Service/Program.cs index 1cb3337c..135bc01c 100644 --- a/Service/Program.cs +++ b/Service/Program.cs @@ -30,6 +30,7 @@ using System.ServiceProcess; using System.Text; using Org.IdentityConnectors.Common.Security; +using System.Security.Cryptography.X509Certificates; namespace Org.IdentityConnectors.Framework.Service { @@ -37,6 +38,8 @@ namespace Org.IdentityConnectors.Framework.Service static class Program { private const string OPT_SERVICE_NAME = "/serviceName"; + private const string OPT_CERTSTOR_NAME = "/storeName"; + private const string OPT_CERTFILE_NAME = "/certificateFile"; private static void Usage() { @@ -45,6 +48,7 @@ private static void Usage() Console.WriteLine(" /uninstall [/serviceName ] - Uninstalls the service."); Console.WriteLine(" /run - Runs the service from the console."); Console.WriteLine(" /setKey [] - Sets the connector server key."); + Console.WriteLine(" /storeCertificate [/storeName ] [/certificateFile ]- Stores the Certificate in the storage."); } private static IDictionary ParseOptions(string[] args) @@ -119,6 +123,10 @@ static void Main(string[] args) { DoRun(options); } + else if ("/storeCertificate".Equals(cmd)) + { + DoStoreCertificate(options); + } else { Usage(); @@ -247,5 +255,39 @@ private static void DoSetKey(string key) config.Save(ConfigurationSaveMode.Modified); Console.WriteLine("Key Updated."); } + + private static void DoStoreCertificate(IDictionary options) + { + string storeName = null != options[OPT_CERTSTOR_NAME] ? options[OPT_CERTSTOR_NAME] : "ConnectorServerSSLCertificate"; + string certificateFile = options[OPT_CERTFILE_NAME]; + + if (certificateFile == null) + { + Usage(); + throw new Org.IdentityConnectors.Framework.Common.Exceptions.ConfigurationException("Missing required argument: " + OPT_CERTFILE_NAME); + } + + X509Certificate2 certificate = new X509Certificate2(certificateFile); + X509Store store = new X509Store(storeName, StoreLocation.LocalMachine); + + store.Open(OpenFlags.ReadWrite); + X509CertificateCollection certificates = store.Certificates; + if (certificates.Count != 0) + { + if (certificates.Count == 1) + { + store.Remove(store.Certificates[0]); + Console.WriteLine("Previous certificate has been removed."); + } + else + { + Console.WriteLine("There are multiple certificates were found. You may point to the wrong store."); + throw new Org.IdentityConnectors.Framework.Common.Exceptions.ConfigurationException("There is supported to be exactly one certificate in the store: " + storeName); + } + } + store.Add(certificate); + store.Close(); + Console.WriteLine("Certificate is stored in " + storeName); + } } } \ No newline at end of file diff --git a/Service/app.config b/Service/app.config index 342a7974..e2e4e967 100644 --- a/Service/app.config +++ b/Service/app.config @@ -1,37 +1,37 @@ - - - + - - - - - - - - - + + + + + + - - - - - - - - - - - - + + + + + + + + + + + + From 067678b9667b7dddbde743cb2aefea7a6d6362c1 Mon Sep 17 00:00:00 2001 From: Laszlo Hordos Date: Sat, 15 Dec 2012 19:23:22 +0000 Subject: [PATCH 294/342] Create branch for 1.1.1.x release --- ServiceInstall/ServiceInstall.wixproj | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/ServiceInstall/ServiceInstall.wixproj b/ServiceInstall/ServiceInstall.wixproj index 6076726d..540db240 100644 --- a/ServiceInstall/ServiceInstall.wixproj +++ b/ServiceInstall/ServiceInstall.wixproj @@ -31,7 +31,7 @@ ServiceInstall $(MSBuildExtensionsPath)\Microsoft\WiX\v3.x\Wix.targets ICE45 - 1.1.0.1-SNAPSHOT + 1.1.2.0-SNAPSHOT prompt From 7abdb49f9ffc4c3dbdc4f72cef4dea7febcada29 Mon Sep 17 00:00:00 2001 From: Laszlo Hordos Date: Tue, 8 Jan 2013 06:40:29 +0000 Subject: [PATCH 295/342] OPENICF-63 / CR-1163 - Add new VersionRange class --- Framework/Common.cs | 335 +++++++++++++++++++++++++++ FrameworkTests/FrameworkTests.csproj | 1 + FrameworkTests/VersionRangeTests.cs | 129 +++++++++++ 3 files changed, 465 insertions(+) create mode 100755 FrameworkTests/VersionRangeTests.cs diff --git a/Framework/Common.cs b/Framework/Common.cs index 47d1b6cc..fd7e0100 100644 --- a/Framework/Common.cs +++ b/Framework/Common.cs @@ -22,6 +22,7 @@ */ using System; using System.Reflection; +using System.Text; using System.Collections.Generic; using Org.IdentityConnectors.Common; using Org.IdentityConnectors.Common.Security; @@ -379,4 +380,338 @@ public static Version GetFrameworkVersion() return Assembly.GetExecutingAssembly().GetName().Version; } } + + /// + /// A version range is an interval describing a set of . + ///

+ /// A range has a left (lower) endpoint and a right (upper) endpoint. Each + /// endpoint can be open (excluded from the set) or closed (included in the set). + /// + ///

+ /// {@code VersionRange} objects are immutable. + /// + /// @author Laszlo Hordos + /// @Immutable + ///

+ public class VersionRange + { + + /// + /// The left endpoint is open and is excluded from the range. + ///

+ /// The value of {@code LEFT_OPEN} is {@code '('}. + ///

+ public const char LEFT_OPEN = '('; + /// + /// The left endpoint is closed and is included in the range. + ///

+ /// The value of {@code LEFT_CLOSED} is {@code '['}. + ///

+ public const char LEFT_CLOSED = '['; + /// + /// The right endpoint is open and is excluded from the range. + ///

+ /// The value of {@code RIGHT_OPEN} is {@code ')'}. + ///

+ public const char RIGHT_OPEN = ')'; + /// + /// The right endpoint is closed and is included in the range. + ///

+ /// The value of {@code RIGHT_CLOSED} is {@code ']'}. + ///

+ public const char RIGHT_CLOSED = ']'; + + private const string ENDPOINT_DELIMITER = ","; + + private readonly Version floorVersion; + private readonly bool isFloorInclusive; + private readonly Version ceilingVersion; + private readonly bool isCeilingInclusive; + private readonly bool empty; + + /// + /// Parse version component into a Version. + /// + /// + /// version component string + /// + /// Complete range string for exception message, if any + /// Version + private static Version parseVersion(string version, string range) + { + try + { + return Version.Parse(version); + } + catch (System.ArgumentException e) + { + throw new System.ArgumentException("invalid range \"" + range + "\": " + e.Message, e); + } + } + + /// + /// Creates a version range from the specified string. + /// + ///

+ /// Version range string grammar: + /// + ///

+        /// range ::= interval | at least
+        /// interval ::= ( '[' | '(' ) left ',' right ( ']' | ')' )
+        /// left ::= version
+        /// right ::= version
+        /// at least ::= version
+        /// 
+ ///
+ /// + /// String representation of the version range. The versions in + /// the range must contain no whitespace. Other whitespace in the + /// range string is ignored. + /// + /// If {@code range} is improperly formatted. + public static VersionRange Parse(string range) + { + Assertions.BlankCheck(range, "range"); + int idx = range.IndexOf(ENDPOINT_DELIMITER); + // Check if the version is an interval. + if (idx > 1 && idx == range.LastIndexOf(ENDPOINT_DELIMITER)) + { + string vlo = range.Substring(0, idx).Trim(); + string vhi = range.Substring(idx + 1).Trim(); + + bool isLowInclusive = true; + bool isHighInclusive = true; + if (vlo[0] == LEFT_OPEN) + { + isLowInclusive = false; + } + else if (vlo[0] != LEFT_CLOSED) + { + throw new System.ArgumentException("invalid range \"" + range + "\": invalid format"); + } + vlo = vlo.Substring(1).Trim(); + + if (vhi[vhi.Length - 1] == RIGHT_OPEN) + { + isHighInclusive = false; + } + else if (vhi[vhi.Length - 1] != RIGHT_CLOSED) + { + throw new System.ArgumentException("invalid range \"" + range + "\": invalid format"); + } + vhi = vhi.Substring(0, vhi.Length - 1).Trim(); + + return new VersionRange(parseVersion(vlo, range), isLowInclusive, parseVersion(vhi, range), isHighInclusive); + } + else if (idx == -1) + { + return new VersionRange(VersionRange.parseVersion(range.Trim(), range), true, null, false); + } + else + { + throw new System.ArgumentException("invalid range \"" + range + "\": invalid format"); + } + } + + public VersionRange(Version low, bool isLowInclusive, Version high, bool isHighInclusive) + { + Assertions.NullCheck(low, "floorVersion"); + floorVersion = low; + isFloorInclusive = isLowInclusive; + ceilingVersion = high; + isCeilingInclusive = isHighInclusive; + empty = Empty0; + } + + public virtual Version Floor + { + get + { + return floorVersion; + } + } + + public virtual bool FloorInclusive + { + get + { + return isFloorInclusive; + } + } + + public virtual Version Ceiling + { + get + { + return ceilingVersion; + } + } + + public virtual bool CeilingInclusive + { + get + { + return isCeilingInclusive; + } + } + + public virtual bool IsInRange(Version version) + { + if (empty) + { + return false; + } + if (floorVersion.CompareTo(version) >= (isFloorInclusive ? 1 : 0)) + { + return false; + } + if (ceilingVersion == null) + { + return true; + } + return ceilingVersion.CompareTo(version) >= (isCeilingInclusive ? 0 : 1); + + } + + /// + /// Returns whether this version range contains only a single version. + /// + /// {@code true} if this version range contains only a single + /// version; {@code false} otherwise. + public virtual bool Exact + { + get + { + if (empty) + { + return false; + } + else if (ceilingVersion == null) + { + return true; + } + if (isFloorInclusive) + { + if (isCeilingInclusive) + { + // [f,c]: exact if f == c + return floorVersion.Equals(ceilingVersion); + } + else + { + // [f,c): exact if f++ >= c + Version adjacent1 = new Version(floorVersion.Major, floorVersion.Minor, floorVersion.Build, floorVersion.Revision + 1); + return adjacent1.CompareTo(ceilingVersion) >= 0; + } + } + else + { + if (isCeilingInclusive) + { + // (f,c] is equivalent to [f++,c]: exact if f++ == c + Version adjacent1 = new Version(floorVersion.Major, floorVersion.Minor, floorVersion.Build, floorVersion.Revision + 1); + return adjacent1.Equals(ceilingVersion); + } + else + { + // (f,c) is equivalent to [f++,c): exact if (f++)++ >=c + Version adjacent2 = new Version(floorVersion.Major, floorVersion.Minor, floorVersion.Build, floorVersion.Revision + 2); + return adjacent2.CompareTo(ceilingVersion) >= 0; + } + } + } + } + + /// + /// Returns whether this version range is empty. A version range is empty if + /// the set of versions defined by the interval is empty. + /// + /// {@code true} if this version range is empty; {@code false} + /// otherwise. + public virtual bool Empty + { + get + { + return empty; + } + } + + /// + /// Internal isEmpty behavior. + /// + /// {@code true} if this version range is empty; {@code false} + /// otherwise. + private bool Empty0 + { + get + { + if (ceilingVersion == null) // infinity + { + return false; + } + int comparison = floorVersion.CompareTo(ceilingVersion); + if (comparison == 0) // endpoints equal + { + return !isFloorInclusive || !isCeilingInclusive; + } + return comparison > 0; // true if left > right + } + } + + public virtual bool Equals(object obj) + { + if (obj == null) + { + return false; + } + if (this.GetType() != obj.GetType()) + { + return false; + } + VersionRange other = (VersionRange)obj; + if (floorVersion != other.floorVersion && (floorVersion == null || !floorVersion.Equals(other.floorVersion))) + { + return false; + } + if (isFloorInclusive != other.isFloorInclusive) + { + return false; + } + if (ceilingVersion != other.ceilingVersion && (ceilingVersion == null || !ceilingVersion.Equals(other.ceilingVersion))) + { + return false; + } + if (isCeilingInclusive != other.isCeilingInclusive) + { + return false; + } + return true; + } + + public override int GetHashCode() + { + int result = floorVersion.GetHashCode(); + result = 31 * result + (isFloorInclusive ? 1 : 0); + result = 31 * result + (ceilingVersion != null ? ceilingVersion.GetHashCode() : 0); + result = 31 * result + (isCeilingInclusive ? 1 : 0); + return result; + } + + public virtual string ToString() + { + if (ceilingVersion != null) + { + StringBuilder sb = new StringBuilder(); + sb.Append(isFloorInclusive ? LEFT_CLOSED : LEFT_OPEN); + sb.Append(floorVersion.ToString()).Append(ENDPOINT_DELIMITER).Append(ceilingVersion.ToString()); + sb.Append(isCeilingInclusive ? RIGHT_CLOSED : RIGHT_OPEN); + return sb.ToString(); + } + else + { + return floorVersion.ToString(); + } + } + } + } \ No newline at end of file diff --git a/FrameworkTests/FrameworkTests.csproj b/FrameworkTests/FrameworkTests.csproj index 239cfd2e..d1ab9b2f 100644 --- a/FrameworkTests/FrameworkTests.csproj +++ b/FrameworkTests/FrameworkTests.csproj @@ -92,6 +92,7 @@ + Always diff --git a/FrameworkTests/VersionRangeTests.cs b/FrameworkTests/VersionRangeTests.cs new file mode 100755 index 00000000..567c732c --- /dev/null +++ b/FrameworkTests/VersionRangeTests.cs @@ -0,0 +1,129 @@ +/* + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER. + * + * Copyright (c) 2013 ForgeRock AS. All Rights Reserved + * + * The contents of this file are subject to the terms + * of the Common Development and Distribution License + * (the License). You may not use this file except in + * compliance with the License. + * + * You can obtain a copy of the License at + * http://forgerock.org/license/CDDLv1.0.html + * See the License for the specific language governing + * permission and limitations under the License. + * + * When distributing Covered Code, include this CDDL + * Header Notice in each file and include the License file + * at http://forgerock.org/license/CDDLv1.0.html + * If applicable, add the following below the CDDL Header, + * with the fields enclosed by brackets [] replaced by + * your own identifying information: + * "Portions Copyrighted [year] [name of copyright owner]" + */ +using System; +using NUnit.Framework; +using Org.IdentityConnectors.Framework.Common; + +namespace FrameworkTests +{ + + [TestFixture] + public class VersionRangeTests + { + + [Test] + public virtual void TestIsInRange() + { + Version reference0 = new Version(1, 1, 0, 0); + Version reference1 = new Version(1, 1, 0, 1); + Version reference2 = new Version(1, 1, 0, 2); + Version reference3 = new Version(1, 1, 0, 3); + Version reference4 = new Version(1, 1, 0, 4); + VersionRange range = VersionRange.Parse("[1.1.0.1,1.1.0.3)"); + + Assert.IsFalse(range.IsInRange(reference0)); + Assert.IsTrue(range.IsInRange(reference1)); + Assert.IsTrue(range.IsInRange(reference2)); + Assert.IsFalse(range.IsInRange(reference3)); + Assert.IsFalse(range.IsInRange(reference4)); + } + + [Test] + public virtual void TestIsExact() + { + Assert.IsTrue(VersionRange.Parse("1.1.0.0").Exact); + Assert.IsTrue(VersionRange.Parse(" [ 1 , 1 ] ").Exact); + Assert.IsTrue(VersionRange.Parse("[ 1.1 , 1.1 ]").Exact); + Assert.IsTrue(VersionRange.Parse(" [1.1.1 , 1.1.1] ").Exact); + Assert.IsTrue(VersionRange.Parse("[1.1.0.0,1.1.0.0]").Exact); + Assert.IsTrue(VersionRange.Parse("(1.1.0.0,1.1.0.2)").Exact); + } + + [Test] + public virtual void TestIsEmpty() + { + Assert.IsTrue(VersionRange.Parse("(1.1.0.0,1.1.0.0)").Empty); + Assert.IsTrue(VersionRange.Parse("(1.2.0.0,1.1.0.0]").Empty); + } + + [Test] + public virtual void TestValidSyntax() + { + try + { + VersionRange.Parse("(1.1.0.0)"); + Assert.Fail("Invalid syntax not failed"); + } + catch (System.ArgumentException e) + { + // ok + } + try + { + VersionRange.Parse("1.1.0.0,1.1)]"); + Assert.Fail("Invalid syntax not failed"); + } + catch (System.ArgumentException e) + { + // ok + } + try + { + VersionRange.Parse("(1.1.0.0-1.1)"); + Assert.Fail("Invalid syntax not failed"); + } + catch (System.ArgumentException e) + { + // ok + } + try + { + VersionRange.Parse("1.1.0.0,1.1"); + Assert.Fail("Invalid syntax not failed"); + } + catch (System.ArgumentException e) + { + // ok + } + try + { + VersionRange.Parse("( , 1.1)"); + Assert.Fail("Invalid syntax not failed"); + } + catch (System.ArgumentException e) + { + // ok + } + } + + [Test] + public virtual void TestIsEqual() + { + VersionRange range1 = VersionRange.Parse("[1.1.0.1,1.1.0.3)"); + VersionRange range2 = VersionRange.Parse(range1.ToString()); + Assert.IsTrue(range1.Equals(range2)); + } + } + +} \ No newline at end of file From a73eb6f2ec72faff396ee4733e1bf14d43248b25 Mon Sep 17 00:00:00 2001 From: Gael Allioux Date: Tue, 28 May 2013 17:04:49 +0000 Subject: [PATCH 296/342] CR-1753. Fix the OPENICF-54 stdole.dll problem --- FrameworkInternal/ApiLocalOperations.cs | 19 ++----------------- 1 file changed, 2 insertions(+), 17 deletions(-) diff --git a/FrameworkInternal/ApiLocalOperations.cs b/FrameworkInternal/ApiLocalOperations.cs index 277d3bbe..f6a295d4 100644 --- a/FrameworkInternal/ApiLocalOperations.cs +++ b/FrameworkInternal/ApiLocalOperations.cs @@ -1050,25 +1050,10 @@ public Object RunScriptOnConnector(ScriptContext request, private Assembly[] BuildReferenceList(Assembly assembly) { List list = new List(); - BuildReferenceList2(assembly, list, new HashSet()); + // Just add the connector itself. + list.Add(assembly); return list.ToArray(); } - - private void BuildReferenceList2(Assembly assembly, - List list, - HashSet visited) - { - bool notThere = visited.Add(assembly.GetName().FullName); - if (notThere) - { - list.Add(assembly); - foreach (AssemblyName referenced in assembly.GetReferencedAssemblies()) - { - Assembly assembly2 = Assembly.Load(referenced); - BuildReferenceList2(assembly2, list, visited); - } - } - } } #endregion From 1e9a0f40a031b506864524998ce9f30d7d214eee Mon Sep 17 00:00:00 2001 From: Laszlo Hordos Date: Tue, 6 Aug 2013 08:24:50 +0000 Subject: [PATCH 297/342] OPENICF-37 - Update the package, fix the build order OPENICF-125 - Update the version.template files. OPENICF-136 - Fix the SearchImpl --- .../BooScriptExecutorFactory.cs | 4 +- BooScriptExecutorFactory/version.template | 2 +- Common/Assertions.cs | 4 +- Common/CollectionUtil.cs | 4 +- Common/Common.csproj | 3 + Common/DateTimeUtil.cs | 4 +- Common/FrameworkInternalBridge.cs | 4 +- Common/IOUtil.cs | 4 +- Common/Locale.cs | 4 +- Common/Pair.cs | 4 +- Common/Pooling.cs | 4 +- Common/Proxy.cs | 4 +- Common/ReflectionUtil.cs | 4 +- Common/SafeType.cs | 4 +- Common/Script.cs | 4 +- Common/Security.cs | 4 +- Common/StringUtil.cs | 4 +- Common/TraceUtil.cs | 4 +- Common/XmlUtil.cs | 4 +- Common/version.template | 2 +- ConnectorFramework.sln | 67 ++++++-- Framework.targets | 59 ++----- Framework/Api.cs | 4 +- Framework/ApiOperations.cs | 4 +- Framework/Common.cs | 8 +- Framework/CommonExceptions.cs | 4 +- Framework/CommonObjects.cs | 4 +- Framework/CommonObjectsFilter.cs | 4 +- Framework/CommonSerializer.cs | 4 +- Framework/Spi.cs | 4 +- Framework/SpiOperations.cs | 4 +- Framework/version.template | 2 +- FrameworkInternal/Api.cs | 4 +- FrameworkInternal/ApiLocal.cs | 4 +- FrameworkInternal/ApiLocalOperations.cs | 58 +++---- FrameworkInternal/ApiRemote.cs | 4 +- FrameworkInternal/ApiRemoteMessages.cs | 4 +- FrameworkInternal/ExceptionUtil.cs | 4 +- FrameworkInternal/FrameworkInternal.csproj | 4 + FrameworkInternal/Security.cs | 4 +- FrameworkInternal/Serializer.cs | 23 ++- FrameworkInternal/SerializerBinary.cs | 4 +- FrameworkInternal/SerializerXml.cs | 7 +- FrameworkInternal/Server.cs | 4 +- FrameworkInternal/Test.cs | 4 +- FrameworkInternal/version.template | 2 +- FrameworkTests/CollectionUtilTests.cs | 4 +- FrameworkTests/ConnectorAttributeUtilTests.cs | 4 +- .../ConnectorFacadeExceptionTests.cs | 4 +- FrameworkTests/ConnectorFacadeTests.cs | 4 +- FrameworkTests/ConnectorInfoManagerTests.cs | 4 +- FrameworkTests/ExceptionUtilTests.cs | 4 +- FrameworkTests/FilterTranslatorTests.cs | 4 +- FrameworkTests/FrameworkTests.csproj | 4 +- FrameworkTests/GuardedByteArrayTests.cs | 4 +- FrameworkTests/GuardedStringTests.cs | 4 +- FrameworkTests/LocaleTests.cs | 8 +- FrameworkTests/MockConnector.cs | 4 +- FrameworkTests/ObjectClassUtilTests.cs | 4 +- FrameworkTests/ObjectNormalizerFacadeTests.cs | 4 +- FrameworkTests/ObjectPoolTests.cs | 4 +- FrameworkTests/ObjectSerializationTests.cs | 6 +- .../config/config.xml | 4 +- .../config/myconfig/config.xml | 4 +- FrameworkTests/PropertyBagTests.cs | 4 +- FrameworkTests/ProxyTests.cs | 4 +- FrameworkTests/SafeTypeTest.cs | 4 +- FrameworkTests/ScriptTests.cs | 4 +- FrameworkTests/TestHelperTests.cs | 4 +- FrameworkTests/TestUtil.cs | 4 +- FrameworkTests/UpdateImplTests.cs | 4 +- FrameworkTests/VersionRangeTests.cs | 5 +- FrameworkTests/version.template | 2 +- .../version.template | 1 + Service/Program.cs | 4 +- Service/ProjectInstaller.cs | 4 +- Service/Service.cs | 4 +- Service/version.template | 2 +- ServiceInstall/ExtBuild.proj | 34 +++-- ServiceInstall/File.top | 6 +- ServiceInstall/ServiceInstall.wixproj | 2 +- ServiceInstall/Setup.wxs | 4 +- .../ShellScriptExecutorFactory.cs | 4 +- ShellScriptExecutorFactory/version.template | 2 +- TestBundles/TestBundleV1/AssemblyInfo.cs | 4 +- TestBundles/TestBundleV1/TestConnector.cs | 4 +- TestBundles/TestBundleV2/AssemblyInfo.cs | 4 +- TestBundles/TestBundleV2/TestConnector.cs | 4 +- TestCommon/FrameworkInternalBridge.cs | 4 +- TestCommon/PropertyBag.cs | 4 +- TestCommon/Test.cs | 4 +- TestCommon/TestCommon.csproj | 3 + TestCommon/TestHelpersSpi.cs | 4 +- TestCommon/config.xsd | 4 +- TestCommon/version.template | 2 +- legal/ForgeRock_License.txt | 144 ++++++++++++++++++ 96 files changed, 467 insertions(+), 271 deletions(-) create mode 100644 PowerShellScriptExecutorFactory/version.template create mode 100644 legal/ForgeRock_License.txt diff --git a/BooScriptExecutorFactory/BooScriptExecutorFactory.cs b/BooScriptExecutorFactory/BooScriptExecutorFactory.cs index 3b1a1b8a..ccb4094c 100644 --- a/BooScriptExecutorFactory/BooScriptExecutorFactory.cs +++ b/BooScriptExecutorFactory/BooScriptExecutorFactory.cs @@ -9,12 +9,12 @@ * except in compliance with the License. * * You can obtain a copy of the License at - * http://IdentityConnectors.dev.java.net/legal/license.txt + * http://opensource.org/licenses/cddl1.php * See the License for the specific language governing permissions and limitations * under the License. * * When distributing the Covered Code, include this CDDL Header Notice in each file - * and include the License file at identityconnectors/legal/license.txt. + * and include the License file at http://opensource.org/licenses/cddl1.php. * If applicable, add the following below this CDDL Header, with the fields * enclosed by brackets [] replaced by your own identifying information: * "Portions Copyrighted [year] [name of copyright owner]" diff --git a/BooScriptExecutorFactory/version.template b/BooScriptExecutorFactory/version.template index bd2666ab..77d55b2a 100644 --- a/BooScriptExecutorFactory/version.template +++ b/BooScriptExecutorFactory/version.template @@ -1 +1 @@ -1.0.0.0 \ No newline at end of file +1.4.0.0 \ No newline at end of file diff --git a/Common/Assertions.cs b/Common/Assertions.cs index 181c362d..3b1c1c0d 100644 --- a/Common/Assertions.cs +++ b/Common/Assertions.cs @@ -9,12 +9,12 @@ * except in compliance with the License. * * You can obtain a copy of the License at - * http://IdentityConnectors.dev.java.net/legal/license.txt + * http://opensource.org/licenses/cddl1.php * See the License for the specific language governing permissions and limitations * under the License. * * When distributing the Covered Code, include this CDDL Header Notice in each file - * and include the License file at identityconnectors/legal/license.txt. + * and include the License file at http://opensource.org/licenses/cddl1.php. * If applicable, add the following below this CDDL Header, with the fields * enclosed by brackets [] replaced by your own identifying information: * "Portions Copyrighted [year] [name of copyright owner]" diff --git a/Common/CollectionUtil.cs b/Common/CollectionUtil.cs index 731ac6b8..31454282 100644 --- a/Common/CollectionUtil.cs +++ b/Common/CollectionUtil.cs @@ -9,12 +9,12 @@ * except in compliance with the License. * * You can obtain a copy of the License at - * http://IdentityConnectors.dev.java.net/legal/license.txt + * http://opensource.org/licenses/cddl1.php * See the License for the specific language governing permissions and limitations * under the License. * * When distributing the Covered Code, include this CDDL Header Notice in each file - * and include the License file at identityconnectors/legal/license.txt. + * and include the License file at http://opensource.org/licenses/cddl1.php. * If applicable, add the following below this CDDL Header, with the fields * enclosed by brackets [] replaced by your own identifying information: * "Portions Copyrighted [year] [name of copyright owner]" diff --git a/Common/Common.csproj b/Common/Common.csproj index 704601d6..4228650f 100644 --- a/Common/Common.csproj +++ b/Common/Common.csproj @@ -94,4 +94,7 @@
+ + +
\ No newline at end of file diff --git a/Common/DateTimeUtil.cs b/Common/DateTimeUtil.cs index 46a7c471..e5922ebe 100644 --- a/Common/DateTimeUtil.cs +++ b/Common/DateTimeUtil.cs @@ -9,12 +9,12 @@ * except in compliance with the License. * * You can obtain a copy of the License at - * http://IdentityConnectors.dev.java.net/legal/license.txt + * http://opensource.org/licenses/cddl1.php * See the License for the specific language governing permissions and limitations * under the License. * * When distributing the Covered Code, include this CDDL Header Notice in each file - * and include the License file at identityconnectors/legal/license.txt. + * and include the License file at http://opensource.org/licenses/cddl1.php. * If applicable, add the following below this CDDL Header, with the fields * enclosed by brackets [] replaced by your own identifying information: * "Portions Copyrighted [year] [name of copyright owner]" diff --git a/Common/FrameworkInternalBridge.cs b/Common/FrameworkInternalBridge.cs index 9f3e0dab..7f64841e 100644 --- a/Common/FrameworkInternalBridge.cs +++ b/Common/FrameworkInternalBridge.cs @@ -9,12 +9,12 @@ * except in compliance with the License. * * You can obtain a copy of the License at - * http://IdentityConnectors.dev.java.net/legal/license.txt + * http://opensource.org/licenses/cddl1.php * See the License for the specific language governing permissions and limitations * under the License. * * When distributing the Covered Code, include this CDDL Header Notice in each file - * and include the License file at identityconnectors/legal/license.txt. + * and include the License file at http://opensource.org/licenses/cddl1.php. * If applicable, add the following below this CDDL Header, with the fields * enclosed by brackets [] replaced by your own identifying information: * "Portions Copyrighted [year] [name of copyright owner]" diff --git a/Common/IOUtil.cs b/Common/IOUtil.cs index 38f747dc..507f1553 100644 --- a/Common/IOUtil.cs +++ b/Common/IOUtil.cs @@ -9,12 +9,12 @@ * except in compliance with the License. * * You can obtain a copy of the License at - * http://IdentityConnectors.dev.java.net/legal/license.txt + * http://opensource.org/licenses/cddl1.php * See the License for the specific language governing permissions and limitations * under the License. * * When distributing the Covered Code, include this CDDL Header Notice in each file - * and include the License file at identityconnectors/legal/license.txt. + * and include the License file at http://opensource.org/licenses/cddl1.php. * If applicable, add the following below this CDDL Header, with the fields * enclosed by brackets [] replaced by your own identifying information: * "Portions Copyrighted [year] [name of copyright owner]" diff --git a/Common/Locale.cs b/Common/Locale.cs index c0cc45a7..342b515e 100644 --- a/Common/Locale.cs +++ b/Common/Locale.cs @@ -9,12 +9,12 @@ * except in compliance with the License. * * You can obtain a copy of the License at - * http://IdentityConnectors.dev.java.net/legal/license.txt + * http://opensource.org/licenses/cddl1.php * See the License for the specific language governing permissions and limitations * under the License. * * When distributing the Covered Code, include this CDDL Header Notice in each file - * and include the License file at identityconnectors/legal/license.txt. + * and include the License file at http://opensource.org/licenses/cddl1.php. * If applicable, add the following below this CDDL Header, with the fields * enclosed by brackets [] replaced by your own identifying information: * "Portions Copyrighted [year] [name of copyright owner]" diff --git a/Common/Pair.cs b/Common/Pair.cs index 15134419..75fcb0d9 100644 --- a/Common/Pair.cs +++ b/Common/Pair.cs @@ -9,12 +9,12 @@ * except in compliance with the License. * * You can obtain a copy of the License at - * http://IdentityConnectors.dev.java.net/legal/license.txt + * http://opensource.org/licenses/cddl1.php * See the License for the specific language governing permissions and limitations * under the License. * * When distributing the Covered Code, include this CDDL Header Notice in each file - * and include the License file at identityconnectors/legal/license.txt. + * and include the License file at http://opensource.org/licenses/cddl1.php. * If applicable, add the following below this CDDL Header, with the fields * enclosed by brackets [] replaced by your own identifying information: * "Portions Copyrighted [year] [name of copyright owner]" diff --git a/Common/Pooling.cs b/Common/Pooling.cs index df00961c..5148d54d 100644 --- a/Common/Pooling.cs +++ b/Common/Pooling.cs @@ -9,12 +9,12 @@ * except in compliance with the License. * * You can obtain a copy of the License at - * http://IdentityConnectors.dev.java.net/legal/license.txt + * http://opensource.org/licenses/cddl1.php * See the License for the specific language governing permissions and limitations * under the License. * * When distributing the Covered Code, include this CDDL Header Notice in each file - * and include the License file at identityconnectors/legal/license.txt. + * and include the License file at http://opensource.org/licenses/cddl1.php. * If applicable, add the following below this CDDL Header, with the fields * enclosed by brackets [] replaced by your own identifying information: * "Portions Copyrighted [year] [name of copyright owner]" diff --git a/Common/Proxy.cs b/Common/Proxy.cs index 546fd140..2b0dec83 100644 --- a/Common/Proxy.cs +++ b/Common/Proxy.cs @@ -9,12 +9,12 @@ * except in compliance with the License. * * You can obtain a copy of the License at - * http://IdentityConnectors.dev.java.net/legal/license.txt + * http://opensource.org/licenses/cddl1.php * See the License for the specific language governing permissions and limitations * under the License. * * When distributing the Covered Code, include this CDDL Header Notice in each file - * and include the License file at identityconnectors/legal/license.txt. + * and include the License file at http://opensource.org/licenses/cddl1.php. * If applicable, add the following below this CDDL Header, with the fields * enclosed by brackets [] replaced by your own identifying information: * "Portions Copyrighted [year] [name of copyright owner]" diff --git a/Common/ReflectionUtil.cs b/Common/ReflectionUtil.cs index 7babc343..6b7fbe7c 100644 --- a/Common/ReflectionUtil.cs +++ b/Common/ReflectionUtil.cs @@ -9,12 +9,12 @@ * except in compliance with the License. * * You can obtain a copy of the License at - * http://IdentityConnectors.dev.java.net/legal/license.txt + * http://opensource.org/licenses/cddl1.php * See the License for the specific language governing permissions and limitations * under the License. * * When distributing the Covered Code, include this CDDL Header Notice in each file - * and include the License file at identityconnectors/legal/license.txt. + * and include the License file at http://opensource.org/licenses/cddl1.php. * If applicable, add the following below this CDDL Header, with the fields * enclosed by brackets [] replaced by your own identifying information: * "Portions Copyrighted [year] [name of copyright owner]" diff --git a/Common/SafeType.cs b/Common/SafeType.cs index 07bd0267..8dd72699 100644 --- a/Common/SafeType.cs +++ b/Common/SafeType.cs @@ -9,12 +9,12 @@ * except in compliance with the License. * * You can obtain a copy of the License at - * http://IdentityConnectors.dev.java.net/legal/license.txt + * http://opensource.org/licenses/cddl1.php * See the License for the specific language governing permissions and limitations * under the License. * * When distributing the Covered Code, include this CDDL Header Notice in each file - * and include the License file at identityconnectors/legal/license.txt. + * and include the License file at http://opensource.org/licenses/cddl1.php. * If applicable, add the following below this CDDL Header, with the fields * enclosed by brackets [] replaced by your own identifying information: * "Portions Copyrighted [year] [name of copyright owner]" diff --git a/Common/Script.cs b/Common/Script.cs index ea41773e..0ba6adac 100644 --- a/Common/Script.cs +++ b/Common/Script.cs @@ -9,12 +9,12 @@ * except in compliance with the License. * * You can obtain a copy of the License at - * http://IdentityConnectors.dev.java.net/legal/license.txt + * http://opensource.org/licenses/cddl1.php * See the License for the specific language governing permissions and limitations * under the License. * * When distributing the Covered Code, include this CDDL Header Notice in each file - * and include the License file at identityconnectors/legal/license.txt. + * and include the License file at http://opensource.org/licenses/cddl1.php. * If applicable, add the following below this CDDL Header, with the fields * enclosed by brackets [] replaced by your own identifying information: * "Portions Copyrighted [year] [name of copyright owner]" diff --git a/Common/Security.cs b/Common/Security.cs index c497e87c..b0873455 100644 --- a/Common/Security.cs +++ b/Common/Security.cs @@ -9,12 +9,12 @@ * except in compliance with the License. * * You can obtain a copy of the License at - * http://IdentityConnectors.dev.java.net/legal/license.txt + * http://opensource.org/licenses/cddl1.php * See the License for the specific language governing permissions and limitations * under the License. * * When distributing the Covered Code, include this CDDL Header Notice in each file - * and include the License file at identityconnectors/legal/license.txt. + * and include the License file at http://opensource.org/licenses/cddl1.php. * If applicable, add the following below this CDDL Header, with the fields * enclosed by brackets [] replaced by your own identifying information: * "Portions Copyrighted [year] [name of copyright owner]" diff --git a/Common/StringUtil.cs b/Common/StringUtil.cs index daebf339..a95aa89b 100644 --- a/Common/StringUtil.cs +++ b/Common/StringUtil.cs @@ -9,12 +9,12 @@ * except in compliance with the License. * * You can obtain a copy of the License at - * http://IdentityConnectors.dev.java.net/legal/license.txt + * http://opensource.org/licenses/cddl1.php * See the License for the specific language governing permissions and limitations * under the License. * * When distributing the Covered Code, include this CDDL Header Notice in each file - * and include the License file at identityconnectors/legal/license.txt. + * and include the License file at http://opensource.org/licenses/cddl1.php. * If applicable, add the following below this CDDL Header, with the fields * enclosed by brackets [] replaced by your own identifying information: * "Portions Copyrighted [year] [name of copyright owner]" diff --git a/Common/TraceUtil.cs b/Common/TraceUtil.cs index bf5fbc4e..60443401 100644 --- a/Common/TraceUtil.cs +++ b/Common/TraceUtil.cs @@ -9,12 +9,12 @@ * except in compliance with the License. * * You can obtain a copy of the License at - * http://IdentityConnectors.dev.java.net/legal/license.txt + * http://opensource.org/licenses/cddl1.php * See the License for the specific language governing permissions and limitations * under the License. * * When distributing the Covered Code, include this CDDL Header Notice in each file - * and include the License file at identityconnectors/legal/license.txt. + * and include the License file at http://opensource.org/licenses/cddl1.php. * If applicable, add the following below this CDDL Header, with the fields * enclosed by brackets [] replaced by your own identifying information: * "Portions Copyrighted [year] [name of copyright owner]" diff --git a/Common/XmlUtil.cs b/Common/XmlUtil.cs index 6f4894db..9478f83e 100644 --- a/Common/XmlUtil.cs +++ b/Common/XmlUtil.cs @@ -9,12 +9,12 @@ * except in compliance with the License. * * You can obtain a copy of the License at - * http://IdentityConnectors.dev.java.net/legal/license.txt + * http://opensource.org/licenses/cddl1.php * See the License for the specific language governing permissions and limitations * under the License. * * When distributing the Covered Code, include this CDDL Header Notice in each file - * and include the License file at identityconnectors/legal/license.txt. + * and include the License file at http://opensource.org/licenses/cddl1.php. * If applicable, add the following below this CDDL Header, with the fields * enclosed by brackets [] replaced by your own identifying information: * "Portions Copyrighted [year] [name of copyright owner]" diff --git a/Common/version.template b/Common/version.template index 25be1b0a..77d55b2a 100644 --- a/Common/version.template +++ b/Common/version.template @@ -1 +1 @@ -1.1.0.0 \ No newline at end of file +1.4.0.0 \ No newline at end of file diff --git a/ConnectorFramework.sln b/ConnectorFramework.sln index e6fd3060..02259bb0 100644 --- a/ConnectorFramework.sln +++ b/ConnectorFramework.sln @@ -4,26 +4,75 @@ Microsoft Visual Studio Solution File, Format Version 11.00 Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Common", "Common\Common.csproj", "{F140E8DA-52B4-4159-992A-9DA10EA8EEFB}" EndProject Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Framework", "Framework\Framework.csproj", "{8B24461B-456A-4032-89A1-CD418F7B5B62}" + ProjectSection(ProjectDependencies) = postProject + {F140E8DA-52B4-4159-992A-9DA10EA8EEFB} = {F140E8DA-52B4-4159-992A-9DA10EA8EEFB} + EndProjectSection EndProject -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "FrameworkInternal", "FrameworkInternal\FrameworkInternal.csproj", "{5B011775-B121-4EEE-A410-BA2D2F5BFB8B}" +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "TestCommon", "TestCommon\TestCommon.csproj", "{E6A207D2-E083-41BF-B522-D9D3EC09323E}" + ProjectSection(ProjectDependencies) = postProject + {F140E8DA-52B4-4159-992A-9DA10EA8EEFB} = {F140E8DA-52B4-4159-992A-9DA10EA8EEFB} + {8B24461B-456A-4032-89A1-CD418F7B5B62} = {8B24461B-456A-4032-89A1-CD418F7B5B62} + EndProjectSection EndProject -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "FrameworkTests", "FrameworkTests\FrameworkTests.csproj", "{32804F5A-812C-4FA6-835C-BDAE5B24D355}" +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "FrameworkInternal", "FrameworkInternal\FrameworkInternal.csproj", "{5B011775-B121-4EEE-A410-BA2D2F5BFB8B}" + ProjectSection(ProjectDependencies) = postProject + {F140E8DA-52B4-4159-992A-9DA10EA8EEFB} = {F140E8DA-52B4-4159-992A-9DA10EA8EEFB} + {8B24461B-456A-4032-89A1-CD418F7B5B62} = {8B24461B-456A-4032-89A1-CD418F7B5B62} + {E6A207D2-E083-41BF-B522-D9D3EC09323E} = {E6A207D2-E083-41BF-B522-D9D3EC09323E} + EndProjectSection EndProject Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Service", "Service\Service.csproj", "{A9D6374A-D51F-4FA3-8C02-5B1D23FAA82E}" + ProjectSection(ProjectDependencies) = postProject + {F140E8DA-52B4-4159-992A-9DA10EA8EEFB} = {F140E8DA-52B4-4159-992A-9DA10EA8EEFB} + {8B24461B-456A-4032-89A1-CD418F7B5B62} = {8B24461B-456A-4032-89A1-CD418F7B5B62} + {5B011775-B121-4EEE-A410-BA2D2F5BFB8B} = {5B011775-B121-4EEE-A410-BA2D2F5BFB8B} + EndProjectSection EndProject -Project("{930C7802-8A8C-48F9-8165-68863BCCD9DD}") = "ServiceInstall", "ServiceInstall\ServiceInstall.wixproj", "{1CBA8F74-050C-432B-8437-08BD13BDC684}" +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "ShellScriptExecutorFactory", "ShellScriptExecutorFactory\ShellScriptExecutorFactory.csproj", "{4700690A-2D29-40A0-86AC-E5A9F71A479A}" + ProjectSection(ProjectDependencies) = postProject + {F140E8DA-52B4-4159-992A-9DA10EA8EEFB} = {F140E8DA-52B4-4159-992A-9DA10EA8EEFB} + EndProjectSection EndProject -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "TestCommon", "TestCommon\TestCommon.csproj", "{E6A207D2-E083-41BF-B522-D9D3EC09323E}" +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "PowerShellScriptExecutorFactory", "PowerShellScriptExecutorFactory\PowerShellScriptExecutorFactory.csproj", "{57754FFA-BB1F-4722-A2FA-70C4F27C6784}" + ProjectSection(ProjectDependencies) = postProject + {F140E8DA-52B4-4159-992A-9DA10EA8EEFB} = {F140E8DA-52B4-4159-992A-9DA10EA8EEFB} + EndProjectSection EndProject -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "TestBundleV1", "TestBundles\TestBundleV1\TestBundleV1.csproj", "{0BC2A013-56FE-46DD-90FC-2D2D57748FB6}" +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "BooScriptExecutorFactory", "BooScriptExecutorFactory\BooScriptExecutorFactory.csproj", "{0747C440-70E4-4E63-9F9D-03B3A010C991}" + ProjectSection(ProjectDependencies) = postProject + {F140E8DA-52B4-4159-992A-9DA10EA8EEFB} = {F140E8DA-52B4-4159-992A-9DA10EA8EEFB} + EndProjectSection EndProject -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "TestBundleV2", "TestBundles\TestBundleV2\TestBundleV2.csproj", "{3E737796-3A83-4924-9FF1-DC542F21F59E}" +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "FrameworkTests", "FrameworkTests\FrameworkTests.csproj", "{32804F5A-812C-4FA6-835C-BDAE5B24D355}" + ProjectSection(ProjectDependencies) = postProject + {F140E8DA-52B4-4159-992A-9DA10EA8EEFB} = {F140E8DA-52B4-4159-992A-9DA10EA8EEFB} + {8B24461B-456A-4032-89A1-CD418F7B5B62} = {8B24461B-456A-4032-89A1-CD418F7B5B62} + {5b011775-b121-4eee-a410-ba2d2f5bfb8b} = {5b011775-b121-4eee-a410-ba2d2f5bfb8b} + {e6a207d2-e083-41bf-b522-d9d3ec09323e} = {e6a207d2-e083-41bf-b522-d9d3ec09323e} + EndProjectSection EndProject -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "BooScriptExecutorFactory", "BooScriptExecutorFactory\BooScriptExecutorFactory.csproj", "{0747C440-70E4-4E63-9F9D-03B3A010C991}" +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "TestBundleV1", "TestBundles\TestBundleV1\TestBundleV1.csproj", "{0BC2A013-56FE-46DD-90FC-2D2D57748FB6}" + ProjectSection(ProjectDependencies) = postProject + {F140E8DA-52B4-4159-992A-9DA10EA8EEFB} = {F140E8DA-52B4-4159-992A-9DA10EA8EEFB} + {8B24461B-456A-4032-89A1-CD418F7B5B62} = {8B24461B-456A-4032-89A1-CD418F7B5B62} + EndProjectSection EndProject -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "ShellScriptExecutorFactory", "ShellScriptExecutorFactory\ShellScriptExecutorFactory.csproj", "{4700690A-2D29-40A0-86AC-E5A9F71A479A}" +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "TestBundleV2", "TestBundles\TestBundleV2\TestBundleV2.csproj", "{3E737796-3A83-4924-9FF1-DC542F21F59E}" + ProjectSection(ProjectDependencies) = postProject + {F140E8DA-52B4-4159-992A-9DA10EA8EEFB} = {F140E8DA-52B4-4159-992A-9DA10EA8EEFB} + {8B24461B-456A-4032-89A1-CD418F7B5B62} = {8B24461B-456A-4032-89A1-CD418F7B5B62} + EndProjectSection EndProject -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "PowerShellScriptExecutorFactory", "PowerShellScriptExecutorFactory\PowerShellScriptExecutorFactory.csproj", "{57754FFA-BB1F-4722-A2FA-70C4F27C6784}" +Project("{930C7802-8A8C-48F9-8165-68863BCCD9DD}") = "ServiceInstall", "ServiceInstall\ServiceInstall.wixproj", "{1CBA8F74-050C-432B-8437-08BD13BDC684}" + ProjectSection(ProjectDependencies) = postProject + {F140E8DA-52B4-4159-992A-9DA10EA8EEFB} = {F140E8DA-52B4-4159-992A-9DA10EA8EEFB} + {8B24461B-456A-4032-89A1-CD418F7B5B62} = {8B24461B-456A-4032-89A1-CD418F7B5B62} + {5b011775-b121-4eee-a410-ba2d2f5bfb8b} = {5b011775-b121-4eee-a410-ba2d2f5bfb8b} + {57754ffa-bb1f-4722-a2fa-70c4f27c6784} = {57754ffa-bb1f-4722-a2fa-70c4f27c6784} + {4700690a-2d29-40a0-86ac-e5a9f71a479a} = {4700690a-2d29-40a0-86ac-e5a9f71a479a} + {a9d6374a-d51f-4fa3-8c02-5b1d23faa82e} = {a9d6374a-d51f-4fa3-8c02-5b1d23faa82e} + {e6a207d2-e083-41bf-b522-d9d3ec09323e} = {e6a207d2-e083-41bf-b522-d9d3ec09323e} + EndProjectSection EndProject Global GlobalSection(SolutionConfigurationPlatforms) = preSolution diff --git a/Framework.targets b/Framework.targets index 5503ac9d..3f156d72 100644 --- a/Framework.targets +++ b/Framework.targets @@ -9,12 +9,12 @@ except in compliance with the License. You can obtain a copy of the License at - http://IdentityConnectors.dev.java.net/legal/license.txt + http://opensource.org/licenses/cddl1.php See the License for the specific language governing permissions and limitations under the License. When distributing the Covered Code, include this CDDL Header Notice in each file - and include the License file at identityconnectors/legal/license.txt. + and include the License file at http://opensource.org/licenses/cddl1.php. If applicable, add the following below this CDDL Header, with the fields enclosed by brackets [] replaced by your own identifying information: "Portions Copyrighted [year] [name of copyright owner]" @@ -28,69 +28,41 @@ Copyright 2008-2009 Sun Microsystems, Inc. All rights reserved. $(MSBuildProjectDirectory)\..\Dist - $(MSBuildProjectDirectory) + $(MSBuildProjectDirectory) - - - - - + + + + - + + - + + AssemblyVersion="$(Major).$(Minor).$(Build).$(Revision)" + AssemblyFileVersion="$(Major).$(Minor).$(Build).$(Revision)" + InternalsVisibleTo="$(InternalsVisibleTo)" /> - - - - - - - - - - - + DestinationFolder="$(FrameworkDistDir)" /> @@ -119,8 +91,7 @@ - - + diff --git a/Framework/Api.cs b/Framework/Api.cs index df3743fb..60d6c71f 100644 --- a/Framework/Api.cs +++ b/Framework/Api.cs @@ -9,12 +9,12 @@ * except in compliance with the License. * * You can obtain a copy of the License at - * http://IdentityConnectors.dev.java.net/legal/license.txt + * http://opensource.org/licenses/cddl1.php * See the License for the specific language governing permissions and limitations * under the License. * * When distributing the Covered Code, include this CDDL Header Notice in each file - * and include the License file at identityconnectors/legal/license.txt. + * and include the License file at http://opensource.org/licenses/cddl1.php. * If applicable, add the following below this CDDL Header, with the fields * enclosed by brackets [] replaced by your own identifying information: * "Portions Copyrighted [year] [name of copyright owner]" diff --git a/Framework/ApiOperations.cs b/Framework/ApiOperations.cs index 0af94b63..8ae077ce 100644 --- a/Framework/ApiOperations.cs +++ b/Framework/ApiOperations.cs @@ -9,12 +9,12 @@ * except in compliance with the License. * * You can obtain a copy of the License at - * http://IdentityConnectors.dev.java.net/legal/license.txt + * http://opensource.org/licenses/cddl1.php * See the License for the specific language governing permissions and limitations * under the License. * * When distributing the Covered Code, include this CDDL Header Notice in each file - * and include the License file at identityconnectors/legal/license.txt. + * and include the License file at http://opensource.org/licenses/cddl1.php. * If applicable, add the following below this CDDL Header, with the fields * enclosed by brackets [] replaced by your own identifying information: * "Portions Copyrighted [year] [name of copyright owner]" diff --git a/Framework/Common.cs b/Framework/Common.cs index fd7e0100..99e7394b 100644 --- a/Framework/Common.cs +++ b/Framework/Common.cs @@ -9,12 +9,12 @@ * except in compliance with the License. * * You can obtain a copy of the License at - * http://IdentityConnectors.dev.java.net/legal/license.txt + * http://opensource.org/licenses/cddl1.php * See the License for the specific language governing permissions and limitations * under the License. * * When distributing the Covered Code, include this CDDL Header Notice in each file - * and include the License file at identityconnectors/legal/license.txt. + * and include the License file at http://opensource.org/licenses/cddl1.php. * If applicable, add the following below this CDDL Header, with the fields * enclosed by brackets [] replaced by your own identifying information: * "Portions Copyrighted [year] [name of copyright owner]" @@ -658,7 +658,7 @@ private bool Empty0 } } - public virtual bool Equals(object obj) + public override bool Equals(object obj) { if (obj == null) { @@ -697,7 +697,7 @@ public override int GetHashCode() return result; } - public virtual string ToString() + public override string ToString() { if (ceilingVersion != null) { diff --git a/Framework/CommonExceptions.cs b/Framework/CommonExceptions.cs index 911bdf12..306a1a9e 100644 --- a/Framework/CommonExceptions.cs +++ b/Framework/CommonExceptions.cs @@ -9,12 +9,12 @@ * except in compliance with the License. * * You can obtain a copy of the License at - * http://IdentityConnectors.dev.java.net/legal/license.txt + * http://opensource.org/licenses/cddl1.php * See the License for the specific language governing permissions and limitations * under the License. * * When distributing the Covered Code, include this CDDL Header Notice in each file - * and include the License file at identityconnectors/legal/license.txt. + * and include the License file at http://opensource.org/licenses/cddl1.php. * If applicable, add the following below this CDDL Header, with the fields * enclosed by brackets [] replaced by your own identifying information: * "Portions Copyrighted [year] [name of copyright owner]" diff --git a/Framework/CommonObjects.cs b/Framework/CommonObjects.cs index e440ed9b..1df4cf45 100644 --- a/Framework/CommonObjects.cs +++ b/Framework/CommonObjects.cs @@ -9,12 +9,12 @@ * except in compliance with the License. * * You can obtain a copy of the License at - * http://IdentityConnectors.dev.java.net/legal/license.txt + * http://opensource.org/licenses/cddl1.php * See the License for the specific language governing permissions and limitations * under the License. * * When distributing the Covered Code, include this CDDL Header Notice in each file - * and include the License file at identityconnectors/legal/license.txt. + * and include the License file at http://opensource.org/licenses/cddl1.php. * If applicable, add the following below this CDDL Header, with the fields * enclosed by brackets [] replaced by your own identifying information: * "Portions Copyrighted [year] [name of copyright owner]" diff --git a/Framework/CommonObjectsFilter.cs b/Framework/CommonObjectsFilter.cs index 380102d9..d2cf673b 100644 --- a/Framework/CommonObjectsFilter.cs +++ b/Framework/CommonObjectsFilter.cs @@ -9,12 +9,12 @@ * except in compliance with the License. * * You can obtain a copy of the License at - * http://IdentityConnectors.dev.java.net/legal/license.txt + * http://opensource.org/licenses/cddl1.php * See the License for the specific language governing permissions and limitations * under the License. * * When distributing the Covered Code, include this CDDL Header Notice in each file - * and include the License file at identityconnectors/legal/license.txt. + * and include the License file at http://opensource.org/licenses/cddl1.php. * If applicable, add the following below this CDDL Header, with the fields * enclosed by brackets [] replaced by your own identifying information: * "Portions Copyrighted [year] [name of copyright owner]" diff --git a/Framework/CommonSerializer.cs b/Framework/CommonSerializer.cs index 9524ba1c..2c6a35a1 100644 --- a/Framework/CommonSerializer.cs +++ b/Framework/CommonSerializer.cs @@ -9,12 +9,12 @@ * except in compliance with the License. * * You can obtain a copy of the License at - * http://IdentityConnectors.dev.java.net/legal/license.txt + * http://opensource.org/licenses/cddl1.php * See the License for the specific language governing permissions and limitations * under the License. * * When distributing the Covered Code, include this CDDL Header Notice in each file - * and include the License file at identityconnectors/legal/license.txt. + * and include the License file at http://opensource.org/licenses/cddl1.php. * If applicable, add the following below this CDDL Header, with the fields * enclosed by brackets [] replaced by your own identifying information: * "Portions Copyrighted [year] [name of copyright owner]" diff --git a/Framework/Spi.cs b/Framework/Spi.cs index 288ba1a9..71d434f8 100644 --- a/Framework/Spi.cs +++ b/Framework/Spi.cs @@ -9,12 +9,12 @@ * except in compliance with the License. * * You can obtain a copy of the License at - * http://IdentityConnectors.dev.java.net/legal/license.txt + * http://opensource.org/licenses/cddl1.php * See the License for the specific language governing permissions and limitations * under the License. * * When distributing the Covered Code, include this CDDL Header Notice in each file - * and include the License file at identityconnectors/legal/license.txt. + * and include the License file at http://opensource.org/licenses/cddl1.php. * If applicable, add the following below this CDDL Header, with the fields * enclosed by brackets [] replaced by your own identifying information: * "Portions Copyrighted [year] [name of copyright owner]" diff --git a/Framework/SpiOperations.cs b/Framework/SpiOperations.cs index 9698a3f4..6e2c0160 100644 --- a/Framework/SpiOperations.cs +++ b/Framework/SpiOperations.cs @@ -9,12 +9,12 @@ * except in compliance with the License. * * You can obtain a copy of the License at - * http://IdentityConnectors.dev.java.net/legal/license.txt + * http://opensource.org/licenses/cddl1.php * See the License for the specific language governing permissions and limitations * under the License. * * When distributing the Covered Code, include this CDDL Header Notice in each file - * and include the License file at identityconnectors/legal/license.txt. + * and include the License file at http://opensource.org/licenses/cddl1.php. * If applicable, add the following below this CDDL Header, with the fields * enclosed by brackets [] replaced by your own identifying information: * "Portions Copyrighted [year] [name of copyright owner]" diff --git a/Framework/version.template b/Framework/version.template index 25be1b0a..77d55b2a 100644 --- a/Framework/version.template +++ b/Framework/version.template @@ -1 +1 @@ -1.1.0.0 \ No newline at end of file +1.4.0.0 \ No newline at end of file diff --git a/FrameworkInternal/Api.cs b/FrameworkInternal/Api.cs index 5b8f5cd0..84e8be2a 100644 --- a/FrameworkInternal/Api.cs +++ b/FrameworkInternal/Api.cs @@ -9,12 +9,12 @@ * except in compliance with the License. * * You can obtain a copy of the License at - * http://IdentityConnectors.dev.java.net/legal/license.txt + * http://opensource.org/licenses/cddl1.php * See the License for the specific language governing permissions and limitations * under the License. * * When distributing the Covered Code, include this CDDL Header Notice in each file - * and include the License file at identityconnectors/legal/license.txt. + * and include the License file at http://opensource.org/licenses/cddl1.php. * If applicable, add the following below this CDDL Header, with the fields * enclosed by brackets [] replaced by your own identifying information: * "Portions Copyrighted [year] [name of copyright owner]" diff --git a/FrameworkInternal/ApiLocal.cs b/FrameworkInternal/ApiLocal.cs index ea4d160f..7ed11158 100644 --- a/FrameworkInternal/ApiLocal.cs +++ b/FrameworkInternal/ApiLocal.cs @@ -9,12 +9,12 @@ * except in compliance with the License. * * You can obtain a copy of the License at - * http://IdentityConnectors.dev.java.net/legal/license.txt + * http://opensource.org/licenses/cddl1.php * See the License for the specific language governing permissions and limitations * under the License. * * When distributing the Covered Code, include this CDDL Header Notice in each file - * and include the License file at identityconnectors/legal/license.txt. + * and include the License file at http://opensource.org/licenses/cddl1.php. * If applicable, add the following below this CDDL Header, with the fields * enclosed by brackets [] replaced by your own identifying information: * "Portions Copyrighted [year] [name of copyright owner]" diff --git a/FrameworkInternal/ApiLocalOperations.cs b/FrameworkInternal/ApiLocalOperations.cs index f6a295d4..651e19d2 100644 --- a/FrameworkInternal/ApiLocalOperations.cs +++ b/FrameworkInternal/ApiLocalOperations.cs @@ -9,12 +9,12 @@ * except in compliance with the License. * * You can obtain a copy of the License at - * http://IdentityConnectors.dev.java.net/legal/license.txt + * http://opensource.org/licenses/cddl1.php * See the License for the specific language governing permissions and limitations * under the License. * * When distributing the Covered Code, include this CDDL Header Notice in each file - * and include the License file at identityconnectors/legal/license.txt. + * and include the License file at http://opensource.org/licenses/cddl1.php. * If applicable, add the following below this CDDL Header, with the fields * enclosed by brackets [] replaced by your own identifying information: * "Portions Copyrighted [year] [name of copyright owner]" @@ -1110,32 +1110,38 @@ public void Search(ObjectClass oclass, Filter originalFilter, ResultsHandler han { options = new OperationOptionsBuilder().Build(); } - - ResultsHandlerConfiguration hdlCfg = null != GetOperationalContext() ? - GetOperationalContext().getResultsHandlerConfiguration() : new ResultsHandlerConfiguration(); - ResultsHandler handlerChain = handler; + ResultsHandlerConfiguration hdlCfg = null != GetOperationalContext() ? + GetOperationalContext().getResultsHandlerConfiguration() : new ResultsHandlerConfiguration(); + ResultsHandler handlerChain = handler; Filter finalFilter = originalFilter; - - if (hdlCfg.EnableNormalizingResultsHandler) { - ObjectNormalizerFacade normalizer = GetNormalizer(oclass); - //chain a normalizing handler (must come before - //filter handler) + + if (hdlCfg.EnableNormalizingResultsHandler) + { + ObjectNormalizerFacade normalizer = GetNormalizer(oclass); + //chain a normalizing handler (must come before + //filter handler) ResultsHandler normalizingHandler = new NormalizingResultsHandler(handler, normalizer).Handle; - Filter normalizedFilter = normalizer.NormalizeFilter(originalFilter); - // chain a filter handler.. - if (hdlCfg.EnableFilteredResultsHandler) { - // chain a filter handler.. - handlerChain = new FilteredResultsHandler(handler, normalizedFilter).Handle; - finalFilter = normalizedFilter; - } else { - handlerChain = normalizingHandler; - } - } else if (hdlCfg.EnableFilteredResultsHandler) { - // chain a filter handler.. - ResultsHandler filteredHandler = new FilteredResultsHandler(handlerChain, originalFilter).Handle; - } - + Filter normalizedFilter = normalizer.NormalizeFilter(originalFilter); + // chain a filter handler.. + if (hdlCfg.EnableFilteredResultsHandler) + { + // chain a filter handler.. + handlerChain = new FilteredResultsHandler(normalizingHandler, normalizedFilter).Handle; + finalFilter = normalizedFilter; + } + else + { + handlerChain = normalizingHandler; + } + } + else if (hdlCfg.EnableFilteredResultsHandler) + { + // chain a filter handler.. + handlerChain = new FilteredResultsHandler(handlerChain, originalFilter).Handle; + finalFilter = originalFilter; + } + //get the IList interface that this type implements Type interfaceType = ReflectionUtil.FindInHierarchyOf (typeof(SearchOp<>), GetConnector().GetType()); @@ -1155,7 +1161,7 @@ public void Search(ObjectClass oclass, Filter originalFilter, ResultsHandler han if (attrsToGet != null && attrsToGet.Length > 0 && hdlCfg.EnableAttributesToGetSearchResultsHandler) { handlerChain = new SearchAttributesToGetResultsHandler( - handler, attrsToGet).Handle; + handlerChain, attrsToGet).Handle; } searcher.RawSearch(GetConnector(), oclass, finalFilter, handlerChain, options); } diff --git a/FrameworkInternal/ApiRemote.cs b/FrameworkInternal/ApiRemote.cs index 4d1c16ad..d06762ed 100644 --- a/FrameworkInternal/ApiRemote.cs +++ b/FrameworkInternal/ApiRemote.cs @@ -9,12 +9,12 @@ * except in compliance with the License. * * You can obtain a copy of the License at - * http://IdentityConnectors.dev.java.net/legal/license.txt + * http://opensource.org/licenses/cddl1.php * See the License for the specific language governing permissions and limitations * under the License. * * When distributing the Covered Code, include this CDDL Header Notice in each file - * and include the License file at identityconnectors/legal/license.txt. + * and include the License file at http://opensource.org/licenses/cddl1.php. * If applicable, add the following below this CDDL Header, with the fields * enclosed by brackets [] replaced by your own identifying information: * "Portions Copyrighted [year] [name of copyright owner]" diff --git a/FrameworkInternal/ApiRemoteMessages.cs b/FrameworkInternal/ApiRemoteMessages.cs index d9bd67fb..37a74eab 100644 --- a/FrameworkInternal/ApiRemoteMessages.cs +++ b/FrameworkInternal/ApiRemoteMessages.cs @@ -9,12 +9,12 @@ * except in compliance with the License. * * You can obtain a copy of the License at - * http://IdentityConnectors.dev.java.net/legal/license.txt + * http://opensource.org/licenses/cddl1.php * See the License for the specific language governing permissions and limitations * under the License. * * When distributing the Covered Code, include this CDDL Header Notice in each file - * and include the License file at identityconnectors/legal/license.txt. + * and include the License file at http://opensource.org/licenses/cddl1.php. * If applicable, add the following below this CDDL Header, with the fields * enclosed by brackets [] replaced by your own identifying information: * "Portions Copyrighted [year] [name of copyright owner]" diff --git a/FrameworkInternal/ExceptionUtil.cs b/FrameworkInternal/ExceptionUtil.cs index 4b06af70..725cfa8d 100644 --- a/FrameworkInternal/ExceptionUtil.cs +++ b/FrameworkInternal/ExceptionUtil.cs @@ -9,12 +9,12 @@ * except in compliance with the License. * * You can obtain a copy of the License at - * http://IdentityConnectors.dev.java.net/legal/license.txt + * http://opensource.org/licenses/cddl1.php * See the License for the specific language governing permissions and limitations * under the License. * * When distributing the Covered Code, include this CDDL Header Notice in each file - * and include the License file at identityconnectors/legal/license.txt. + * and include the License file at http://opensource.org/licenses/cddl1.php. * If applicable, add the following below this CDDL Header, with the fields * enclosed by brackets [] replaced by your own identifying information: * "Portions Copyrighted [year] [name of copyright owner]" diff --git a/FrameworkInternal/FrameworkInternal.csproj b/FrameworkInternal/FrameworkInternal.csproj index aef2cfb5..2edafd82 100644 --- a/FrameworkInternal/FrameworkInternal.csproj +++ b/FrameworkInternal/FrameworkInternal.csproj @@ -108,4 +108,8 @@ TestCommon
+ + + + \ No newline at end of file diff --git a/FrameworkInternal/Security.cs b/FrameworkInternal/Security.cs index 3adf6088..c4712bf5 100644 --- a/FrameworkInternal/Security.cs +++ b/FrameworkInternal/Security.cs @@ -9,12 +9,12 @@ * except in compliance with the License. * * You can obtain a copy of the License at - * http://IdentityConnectors.dev.java.net/legal/license.txt + * http://opensource.org/licenses/cddl1.php * See the License for the specific language governing permissions and limitations * under the License. * * When distributing the Covered Code, include this CDDL Header Notice in each file - * and include the License file at identityconnectors/legal/license.txt. + * and include the License file at http://opensource.org/licenses/cddl1.php. * If applicable, add the following below this CDDL Header, with the fields * enclosed by brackets [] replaced by your own identifying information: * "Portions Copyrighted [year] [name of copyright owner]" diff --git a/FrameworkInternal/Serializer.cs b/FrameworkInternal/Serializer.cs index 30597f63..23a770d3 100644 --- a/FrameworkInternal/Serializer.cs +++ b/FrameworkInternal/Serializer.cs @@ -9,12 +9,12 @@ * except in compliance with the License. * * You can obtain a copy of the License at - * http://IdentityConnectors.dev.java.net/legal/license.txt + * http://opensource.org/licenses/cddl1.php * See the License for the specific language governing permissions and limitations * under the License. * * When distributing the Covered Code, include this CDDL Header Notice in each file - * and include the License file at identityconnectors/legal/license.txt. + * and include the License file at http://opensource.org/licenses/cddl1.php. * If applicable, add the following below this CDDL Header, with the fields * enclosed by brackets [] replaced by your own identifying information: * "Portions Copyrighted [year] [name of copyright owner]" @@ -2733,14 +2733,25 @@ public override sealed Object Deserialize(ObjectDecoder decoder) { Exception exception = (Exception)decoder.ReadObjectField("exception", null, null); - IDictionary serverInfo = - (IDictionary)decoder.ReadObjectField("serverInfoMap", null, null); + IDictionary serverInfoRaw = + (IDictionary)decoder.ReadObjectField("serverInfoMap", null, null); + IDictionary serverInfo = null; + if (null != serverInfoRaw) + { + serverInfo = new Dictionary(serverInfoRaw.Count); + foreach (KeyValuePair entry in serverInfoRaw) + { + serverInfo.Add(entry.Key.ToString(), entry.Value); + } + } + IList connectorInfosObj = (IList)decoder.ReadObjectField("ConnectorInfos", typeof(IList), null); IList connectorInfos = CollectionUtil.NewList(connectorInfosObj); - IList connectorKeys = - (IList)decoder.ReadObjectField("ConnectorKeys", typeof(IList), null); + IList connectorKeysObj = + (IList)decoder.ReadObjectField("ConnectorKeys", typeof(IList), null); + IList connectorKeys = CollectionUtil.NewList(connectorKeysObj); return new HelloResponse(exception, serverInfo, connectorKeys, connectorInfos); } diff --git a/FrameworkInternal/SerializerBinary.cs b/FrameworkInternal/SerializerBinary.cs index 20e19b2c..7f1714a0 100644 --- a/FrameworkInternal/SerializerBinary.cs +++ b/FrameworkInternal/SerializerBinary.cs @@ -9,12 +9,12 @@ * except in compliance with the License. * * You can obtain a copy of the License at - * http://IdentityConnectors.dev.java.net/legal/license.txt + * http://opensource.org/licenses/cddl1.php * See the License for the specific language governing permissions and limitations * under the License. * * When distributing the Covered Code, include this CDDL Header Notice in each file - * and include the License file at identityconnectors/legal/license.txt. + * and include the License file at http://opensource.org/licenses/cddl1.php. * If applicable, add the following below this CDDL Header, with the fields * enclosed by brackets [] replaced by your own identifying information: * "Portions Copyrighted [year] [name of copyright owner]" diff --git a/FrameworkInternal/SerializerXml.cs b/FrameworkInternal/SerializerXml.cs index 59d52a98..5cb1ce44 100644 --- a/FrameworkInternal/SerializerXml.cs +++ b/FrameworkInternal/SerializerXml.cs @@ -9,12 +9,12 @@ * except in compliance with the License. * * You can obtain a copy of the License at - * http://IdentityConnectors.dev.java.net/legal/license.txt + * http://opensource.org/licenses/cddl1.php * See the License for the specific language governing permissions and limitations * under the License. * * When distributing the Covered Code, include this CDDL Header Notice in each file - * and include the License file at identityconnectors/legal/license.txt. + * and include the License file at http://opensource.org/licenses/cddl1.php. * If applicable, add the following below this CDDL Header, with the fields * enclosed by brackets [] replaced by your own identifying information: * "Portions Copyrighted [year] [name of copyright owner]" @@ -616,7 +616,8 @@ public static void parse(TextReader inputSource, { mySettings.ValidationType = ValidationType.DTD; } - mySettings.ProhibitDtd = false; + mySettings.DtdProcessing = DtdProcessing.Parse; + //mySettings.ProhibitDtd = false; mySettings.XmlResolver = new MyEntityResolver(validate); XmlReader reader = XmlReader.Create(inputSource, mySettings); MyParser parser = new MyParser(handler); diff --git a/FrameworkInternal/Server.cs b/FrameworkInternal/Server.cs index 3eb8a07d..7f7a1aa3 100644 --- a/FrameworkInternal/Server.cs +++ b/FrameworkInternal/Server.cs @@ -9,12 +9,12 @@ * except in compliance with the License. * * You can obtain a copy of the License at - * http://IdentityConnectors.dev.java.net/legal/license.txt + * http://opensource.org/licenses/cddl1.php * See the License for the specific language governing permissions and limitations * under the License. * * When distributing the Covered Code, include this CDDL Header Notice in each file - * and include the License file at identityconnectors/legal/license.txt. + * and include the License file at http://opensource.org/licenses/cddl1.php. * If applicable, add the following below this CDDL Header, with the fields * enclosed by brackets [] replaced by your own identifying information: * "Portions Copyrighted [year] [name of copyright owner]" diff --git a/FrameworkInternal/Test.cs b/FrameworkInternal/Test.cs index 11d65483..fbc41fb9 100644 --- a/FrameworkInternal/Test.cs +++ b/FrameworkInternal/Test.cs @@ -9,12 +9,12 @@ * except in compliance with the License. * * You can obtain a copy of the License at - * http://IdentityConnectors.dev.java.net/legal/license.txt + * http://opensource.org/licenses/cddl1.php * See the License for the specific language governing permissions and limitations * under the License. * * When distributing the Covered Code, include this CDDL Header Notice in each file - * and include the License file at identityconnectors/legal/license.txt. + * and include the License file at http://opensource.org/licenses/cddl1.php. * If applicable, add the following below this CDDL Header, with the fields * enclosed by brackets [] replaced by your own identifying information: * "Portions Copyrighted [year] [name of copyright owner]" diff --git a/FrameworkInternal/version.template b/FrameworkInternal/version.template index 25be1b0a..77d55b2a 100644 --- a/FrameworkInternal/version.template +++ b/FrameworkInternal/version.template @@ -1 +1 @@ -1.1.0.0 \ No newline at end of file +1.4.0.0 \ No newline at end of file diff --git a/FrameworkTests/CollectionUtilTests.cs b/FrameworkTests/CollectionUtilTests.cs index 4fd40478..beb0fc5f 100644 --- a/FrameworkTests/CollectionUtilTests.cs +++ b/FrameworkTests/CollectionUtilTests.cs @@ -9,12 +9,12 @@ * except in compliance with the License. * * You can obtain a copy of the License at - * http://IdentityConnectors.dev.java.net/legal/license.txt + * http://opensource.org/licenses/cddl1.php * See the License for the specific language governing permissions and limitations * under the License. * * When distributing the Covered Code, include this CDDL Header Notice in each file - * and include the License file at identityconnectors/legal/license.txt. + * and include the License file at http://opensource.org/licenses/cddl1.php. * If applicable, add the following below this CDDL Header, with the fields * enclosed by brackets [] replaced by your own identifying information: * "Portions Copyrighted [year] [name of copyright owner]" diff --git a/FrameworkTests/ConnectorAttributeUtilTests.cs b/FrameworkTests/ConnectorAttributeUtilTests.cs index bc971b33..c45d676d 100644 --- a/FrameworkTests/ConnectorAttributeUtilTests.cs +++ b/FrameworkTests/ConnectorAttributeUtilTests.cs @@ -9,12 +9,12 @@ * except in compliance with the License. * * You can obtain a copy of the License at - * http://IdentityConnectors.dev.java.net/legal/license.txt + * http://opensource.org/licenses/cddl1.php * See the License for the specific language governing permissions and limitations * under the License. * * When distributing the Covered Code, include this CDDL Header Notice in each file - * and include the License file at identityconnectors/legal/license.txt. + * and include the License file at http://opensource.org/licenses/cddl1.php. * If applicable, add the following below this CDDL Header, with the fields * enclosed by brackets [] replaced by your own identifying information: * "Portions Copyrighted [year] [name of copyright owner]" diff --git a/FrameworkTests/ConnectorFacadeExceptionTests.cs b/FrameworkTests/ConnectorFacadeExceptionTests.cs index a2eef988..d700e512 100644 --- a/FrameworkTests/ConnectorFacadeExceptionTests.cs +++ b/FrameworkTests/ConnectorFacadeExceptionTests.cs @@ -9,12 +9,12 @@ * except in compliance with the License. * * You can obtain a copy of the License at - * http://IdentityConnectors.dev.java.net/legal/license.txt + * http://opensource.org/licenses/cddl1.php * See the License for the specific language governing permissions and limitations * under the License. * * When distributing the Covered Code, include this CDDL Header Notice in each file - * and include the License file at identityconnectors/legal/license.txt. + * and include the License file at http://opensource.org/licenses/cddl1.php. * If applicable, add the following below this CDDL Header, with the fields * enclosed by brackets [] replaced by your own identifying information: * "Portions Copyrighted [year] [name of copyright owner]" diff --git a/FrameworkTests/ConnectorFacadeTests.cs b/FrameworkTests/ConnectorFacadeTests.cs index 68a50ea2..dd85f884 100644 --- a/FrameworkTests/ConnectorFacadeTests.cs +++ b/FrameworkTests/ConnectorFacadeTests.cs @@ -9,12 +9,12 @@ * except in compliance with the License. * * You can obtain a copy of the License at - * http://IdentityConnectors.dev.java.net/legal/license.txt + * http://opensource.org/licenses/cddl1.php * See the License for the specific language governing permissions and limitations * under the License. * * When distributing the Covered Code, include this CDDL Header Notice in each file - * and include the License file at identityconnectors/legal/license.txt. + * and include the License file at http://opensource.org/licenses/cddl1.php. * If applicable, add the following below this CDDL Header, with the fields * enclosed by brackets [] replaced by your own identifying information: * "Portions Copyrighted [year] [name of copyright owner]" diff --git a/FrameworkTests/ConnectorInfoManagerTests.cs b/FrameworkTests/ConnectorInfoManagerTests.cs index 58ec4abb..44f5b9fc 100644 --- a/FrameworkTests/ConnectorInfoManagerTests.cs +++ b/FrameworkTests/ConnectorInfoManagerTests.cs @@ -9,12 +9,12 @@ * except in compliance with the License. * * You can obtain a copy of the License at - * http://IdentityConnectors.dev.java.net/legal/license.txt + * http://opensource.org/licenses/cddl1.php * See the License for the specific language governing permissions and limitations * under the License. * * When distributing the Covered Code, include this CDDL Header Notice in each file - * and include the License file at identityconnectors/legal/license.txt. + * and include the License file at http://opensource.org/licenses/cddl1.php. * If applicable, add the following below this CDDL Header, with the fields * enclosed by brackets [] replaced by your own identifying information: * "Portions Copyrighted [year] [name of copyright owner]" diff --git a/FrameworkTests/ExceptionUtilTests.cs b/FrameworkTests/ExceptionUtilTests.cs index feb4ac8d..dc9a4ad3 100644 --- a/FrameworkTests/ExceptionUtilTests.cs +++ b/FrameworkTests/ExceptionUtilTests.cs @@ -9,12 +9,12 @@ * except in compliance with the License. * * You can obtain a copy of the License at - * http://IdentityConnectors.dev.java.net/legal/license.txt + * http://opensource.org/licenses/cddl1.php * See the License for the specific language governing permissions and limitations * under the License. * * When distributing the Covered Code, include this CDDL Header Notice in each file - * and include the License file at identityconnectors/legal/license.txt. + * and include the License file at http://opensource.org/licenses/cddl1.php. * If applicable, add the following below this CDDL Header, with the fields * enclosed by brackets [] replaced by your own identifying information: * "Portions Copyrighted [year] [name of copyright owner]" diff --git a/FrameworkTests/FilterTranslatorTests.cs b/FrameworkTests/FilterTranslatorTests.cs index 9cd7e45d..7d024f7a 100644 --- a/FrameworkTests/FilterTranslatorTests.cs +++ b/FrameworkTests/FilterTranslatorTests.cs @@ -9,12 +9,12 @@ * except in compliance with the License. * * You can obtain a copy of the License at - * http://IdentityConnectors.dev.java.net/legal/license.txt + * http://opensource.org/licenses/cddl1.php * See the License for the specific language governing permissions and limitations * under the License. * * When distributing the Covered Code, include this CDDL Header Notice in each file - * and include the License file at identityconnectors/legal/license.txt. + * and include the License file at http://opensource.org/licenses/cddl1.php. * If applicable, add the following below this CDDL Header, with the fields * enclosed by brackets [] replaced by your own identifying information: * "Portions Copyrighted [year] [name of copyright owner]" diff --git a/FrameworkTests/FrameworkTests.csproj b/FrameworkTests/FrameworkTests.csproj index d1ab9b2f..ec3bde17 100644 --- a/FrameworkTests/FrameworkTests.csproj +++ b/FrameworkTests/FrameworkTests.csproj @@ -10,12 +10,12 @@ except in compliance with the License. You can obtain a copy of the License at - http://IdentityConnectors.dev.java.net/legal/license.txt + http://opensource.org/licenses/cddl1.php See the License for the specific language governing permissions and limitations under the License. When distributing the Covered Code, include this CDDL Header Notice in each file - and include the License file at identityconnectors/legal/license.txt. + and include the License file at http://opensource.org/licenses/cddl1.php. If applicable, add the following below this CDDL Header, with the fields enclosed by brackets [] replaced by your own identifying information: "Portions Copyrighted [year] [name of copyright owner]" diff --git a/FrameworkTests/GuardedByteArrayTests.cs b/FrameworkTests/GuardedByteArrayTests.cs index 18498d25..966d90bf 100644 --- a/FrameworkTests/GuardedByteArrayTests.cs +++ b/FrameworkTests/GuardedByteArrayTests.cs @@ -9,12 +9,12 @@ * except in compliance with the License. * * You can obtain a copy of the License at - * http://IdentityConnectors.dev.java.net/legal/license.txt + * http://opensource.org/licenses/cddl1.php * See the License for the specific language governing permissions and limitations * under the License. * * When distributing the Covered Code, include this CDDL Header Notice in each file - * and include the License file at identityconnectors/legal/license.txt. + * and include the License file at http://opensource.org/licenses/cddl1.php. * If applicable, add the following below this CDDL Header, with the fields * enclosed by brackets [] replaced by your own identifying information: * "Portions Copyrighted [year] [name of copyright owner]" diff --git a/FrameworkTests/GuardedStringTests.cs b/FrameworkTests/GuardedStringTests.cs index 34c66231..9b671e4f 100644 --- a/FrameworkTests/GuardedStringTests.cs +++ b/FrameworkTests/GuardedStringTests.cs @@ -9,12 +9,12 @@ * except in compliance with the License. * * You can obtain a copy of the License at - * http://IdentityConnectors.dev.java.net/legal/license.txt + * http://opensource.org/licenses/cddl1.php * See the License for the specific language governing permissions and limitations * under the License. * * When distributing the Covered Code, include this CDDL Header Notice in each file - * and include the License file at identityconnectors/legal/license.txt. + * and include the License file at http://opensource.org/licenses/cddl1.php. * If applicable, add the following below this CDDL Header, with the fields * enclosed by brackets [] replaced by your own identifying information: * "Portions Copyrighted [year] [name of copyright owner]" diff --git a/FrameworkTests/LocaleTests.cs b/FrameworkTests/LocaleTests.cs index f352bb2e..6f139239 100644 --- a/FrameworkTests/LocaleTests.cs +++ b/FrameworkTests/LocaleTests.cs @@ -9,12 +9,12 @@ * except in compliance with the License. * * You can obtain a copy of the License at - * http://IdentityConnectors.dev.java.net/legal/license.txt + * http://opensource.org/licenses/cddl1.php * See the License for the specific language governing permissions and limitations * under the License. * * When distributing the Covered Code, include this CDDL Header Notice in each file - * and include the License file at identityconnectors/legal/license.txt. + * and include the License file at http://opensource.org/licenses/cddl1.php. * If applicable, add the following below this CDDL Header, with the fields * enclosed by brackets [] replaced by your own identifying information: * "Portions Copyrighted [year] [name of copyright owner]" @@ -111,9 +111,7 @@ public void TestJava2CSharp() TestJavaLocale(cultures, new Locale("en", "CA", ""), "English (Canada)"); TestJavaLocale(cultures, new Locale("en", "GB", ""), "English (United Kingdom)"); TestJavaLocale(cultures, new Locale("en", "IE", ""), "English (Ireland)"); - TestJavaLocale(cultures, new Locale("en", "IN", ""), - "English (India)", - new Locale("en")); + TestJavaLocale(cultures, new Locale("en", "IN", ""), "English (India)"); TestJavaLocale(cultures, new Locale("en", "NZ", ""), "English (New Zealand)"); TestJavaLocale(cultures, new Locale("en", "US", ""), "English (United States)"); TestJavaLocale(cultures, new Locale("en", "ZA", ""), "English (South Africa)"); diff --git a/FrameworkTests/MockConnector.cs b/FrameworkTests/MockConnector.cs index e3aefa48..3809ab80 100644 --- a/FrameworkTests/MockConnector.cs +++ b/FrameworkTests/MockConnector.cs @@ -9,12 +9,12 @@ * except in compliance with the License. * * You can obtain a copy of the License at - * http://IdentityConnectors.dev.java.net/legal/license.txt + * http://opensource.org/licenses/cddl1.php * See the License for the specific language governing permissions and limitations * under the License. * * When distributing the Covered Code, include this CDDL Header Notice in each file - * and include the License file at identityconnectors/legal/license.txt. + * and include the License file at http://opensource.org/licenses/cddl1.php. * If applicable, add the following below this CDDL Header, with the fields * enclosed by brackets [] replaced by your own identifying information: * "Portions Copyrighted [year] [name of copyright owner]" diff --git a/FrameworkTests/ObjectClassUtilTests.cs b/FrameworkTests/ObjectClassUtilTests.cs index b74f2eda..2c75c3c9 100644 --- a/FrameworkTests/ObjectClassUtilTests.cs +++ b/FrameworkTests/ObjectClassUtilTests.cs @@ -9,12 +9,12 @@ * except in compliance with the License. * * You can obtain a copy of the License at - * http://IdentityConnectors.dev.java.net/legal/license.txt + * http://opensource.org/licenses/cddl1.php * See the License for the specific language governing permissions and limitations * under the License. * * When distributing the Covered Code, include this CDDL Header Notice in each file - * and include the License file at identityconnectors/legal/license.txt. + * and include the License file at http://opensource.org/licenses/cddl1.php. * If applicable, add the following below this CDDL Header, with the fields * enclosed by brackets [] replaced by your own identifying information: * "Portions Copyrighted [year] [name of copyright owner]" diff --git a/FrameworkTests/ObjectNormalizerFacadeTests.cs b/FrameworkTests/ObjectNormalizerFacadeTests.cs index d92f5282..21377a6d 100644 --- a/FrameworkTests/ObjectNormalizerFacadeTests.cs +++ b/FrameworkTests/ObjectNormalizerFacadeTests.cs @@ -9,12 +9,12 @@ * except in compliance with the License. * * You can obtain a copy of the License at - * http://IdentityConnectors.dev.java.net/legal/license.txt + * http://opensource.org/licenses/cddl1.php * See the License for the specific language governing permissions and limitations * under the License. * * When distributing the Covered Code, include this CDDL Header Notice in each file - * and include the License file at identityconnectors/legal/license.txt. + * and include the License file at http://opensource.org/licenses/cddl1.php. * If applicable, add the following below this CDDL Header, with the fields * enclosed by brackets [] replaced by your own identifying information: * "Portions Copyrighted [year] [name of copyright owner]" diff --git a/FrameworkTests/ObjectPoolTests.cs b/FrameworkTests/ObjectPoolTests.cs index b4832be5..bfdccdd4 100644 --- a/FrameworkTests/ObjectPoolTests.cs +++ b/FrameworkTests/ObjectPoolTests.cs @@ -9,12 +9,12 @@ * except in compliance with the License. * * You can obtain a copy of the License at - * http://IdentityConnectors.dev.java.net/legal/license.txt + * http://opensource.org/licenses/cddl1.php * See the License for the specific language governing permissions and limitations * under the License. * * When distributing the Covered Code, include this CDDL Header Notice in each file - * and include the License file at identityconnectors/legal/license.txt. + * and include the License file at http://opensource.org/licenses/cddl1.php. * If applicable, add the following below this CDDL Header, with the fields * enclosed by brackets [] replaced by your own identifying information: * "Portions Copyrighted [year] [name of copyright owner]" diff --git a/FrameworkTests/ObjectSerializationTests.cs b/FrameworkTests/ObjectSerializationTests.cs index 5cf73f96..e5872370 100644 --- a/FrameworkTests/ObjectSerializationTests.cs +++ b/FrameworkTests/ObjectSerializationTests.cs @@ -9,17 +9,17 @@ * except in compliance with the License. * * You can obtain a copy of the License at - * http://IdentityConnectors.dev.java.net/legal/license.txt + * http://opensource.org/licenses/cddl1.php * See the License for the specific language governing permissions and limitations * under the License. * * When distributing the Covered Code, include this CDDL Header Notice in each file - * and include the License file at identityconnectors/legal/license.txt. + * and include the License file at http://opensource.org/licenses/cddl1.php. * If applicable, add the following below this CDDL Header, with the fields * enclosed by brackets [] replaced by your own identifying information: * "Portions Copyrighted [year] [name of copyright owner]" * ==================== - * Portions Copyrighted 2012 ForgeRock AS + * Portions Copyrighted 2012-2013 ForgeRock AS */ using System; using System.IO; diff --git a/FrameworkTests/Org.IdentityConnectors.TestConnector.FakeConnector/config/config.xml b/FrameworkTests/Org.IdentityConnectors.TestConnector.FakeConnector/config/config.xml index f2546a2e..f080732c 100644 --- a/FrameworkTests/Org.IdentityConnectors.TestConnector.FakeConnector/config/config.xml +++ b/FrameworkTests/Org.IdentityConnectors.TestConnector.FakeConnector/config/config.xml @@ -10,12 +10,12 @@ except in compliance with the License. You can obtain a copy of the License at - http://IdentityConnectors.dev.java.net/legal/license.txt + http://opensource.org/licenses/cddl1.php See the License for the specific language governing permissions and limitations under the License. When distributing the Covered Code, include this CDDL Header Notice in each file - and include the License file at identityconnectors/legal/license.txt. + and include the License file at http://opensource.org/licenses/cddl1.php. If applicable, add the following below this CDDL Header, with the fields enclosed by brackets [] replaced by your own identifying information: "Portions Copyrighted [year] [name of copyright owner]" diff --git a/FrameworkTests/Org.IdentityConnectors.TestConnector.FakeConnector/config/myconfig/config.xml b/FrameworkTests/Org.IdentityConnectors.TestConnector.FakeConnector/config/myconfig/config.xml index 5df51e6c..b9c4303d 100644 --- a/FrameworkTests/Org.IdentityConnectors.TestConnector.FakeConnector/config/myconfig/config.xml +++ b/FrameworkTests/Org.IdentityConnectors.TestConnector.FakeConnector/config/myconfig/config.xml @@ -10,12 +10,12 @@ except in compliance with the License. You can obtain a copy of the License at - http://IdentityConnectors.dev.java.net/legal/license.txt + http://opensource.org/licenses/cddl1.php See the License for the specific language governing permissions and limitations under the License. When distributing the Covered Code, include this CDDL Header Notice in each file - and include the License file at identityconnectors/legal/license.txt. + and include the License file at http://opensource.org/licenses/cddl1.php. If applicable, add the following below this CDDL Header, with the fields enclosed by brackets [] replaced by your own identifying information: "Portions Copyrighted [year] [name of copyright owner]" diff --git a/FrameworkTests/PropertyBagTests.cs b/FrameworkTests/PropertyBagTests.cs index 1358b25c..c14b13de 100644 --- a/FrameworkTests/PropertyBagTests.cs +++ b/FrameworkTests/PropertyBagTests.cs @@ -9,12 +9,12 @@ * except in compliance with the License. * * You can obtain a copy of the License at - * http://IdentityConnectors.dev.java.net/legal/license.txt + * http://opensource.org/licenses/cddl1.php * See the License for the specific language governing permissions and limitations * under the License. * * When distributing the Covered Code, include this CDDL Header Notice in each file - * and include the License file at identityconnectors/legal/license.txt. + * and include the License file at http://opensource.org/licenses/cddl1.php. * If applicable, add the following below this CDDL Header, with the fields * enclosed by brackets [] replaced by your own identifying information: * "Portions Copyrighted [year] [name of copyright owner]" diff --git a/FrameworkTests/ProxyTests.cs b/FrameworkTests/ProxyTests.cs index 568a7af5..734f0b92 100644 --- a/FrameworkTests/ProxyTests.cs +++ b/FrameworkTests/ProxyTests.cs @@ -9,12 +9,12 @@ * except in compliance with the License. * * You can obtain a copy of the License at - * http://IdentityConnectors.dev.java.net/legal/license.txt + * http://opensource.org/licenses/cddl1.php * See the License for the specific language governing permissions and limitations * under the License. * * When distributing the Covered Code, include this CDDL Header Notice in each file - * and include the License file at identityconnectors/legal/license.txt. + * and include the License file at http://opensource.org/licenses/cddl1.php. * If applicable, add the following below this CDDL Header, with the fields * enclosed by brackets [] replaced by your own identifying information: * "Portions Copyrighted [year] [name of copyright owner]" diff --git a/FrameworkTests/SafeTypeTest.cs b/FrameworkTests/SafeTypeTest.cs index 91a4f0ae..5b126a2f 100644 --- a/FrameworkTests/SafeTypeTest.cs +++ b/FrameworkTests/SafeTypeTest.cs @@ -9,12 +9,12 @@ * except in compliance with the License. * * You can obtain a copy of the License at - * http://IdentityConnectors.dev.java.net/legal/license.txt + * http://opensource.org/licenses/cddl1.php * See the License for the specific language governing permissions and limitations * under the License. * * When distributing the Covered Code, include this CDDL Header Notice in each file - * and include the License file at identityconnectors/legal/license.txt. + * and include the License file at http://opensource.org/licenses/cddl1.php. * If applicable, add the following below this CDDL Header, with the fields * enclosed by brackets [] replaced by your own identifying information: * "Portions Copyrighted [year] [name of copyright owner]" diff --git a/FrameworkTests/ScriptTests.cs b/FrameworkTests/ScriptTests.cs index 0fe07410..8444de67 100644 --- a/FrameworkTests/ScriptTests.cs +++ b/FrameworkTests/ScriptTests.cs @@ -9,12 +9,12 @@ * except in compliance with the License. * * You can obtain a copy of the License at - * http://IdentityConnectors.dev.java.net/legal/license.txt + * http://opensource.org/licenses/cddl1.php * See the License for the specific language governing permissions and limitations * under the License. * * When distributing the Covered Code, include this CDDL Header Notice in each file - * and include the License file at identityconnectors/legal/license.txt. + * and include the License file at http://opensource.org/licenses/cddl1.php. * If applicable, add the following below this CDDL Header, with the fields * enclosed by brackets [] replaced by your own identifying information: * "Portions Copyrighted [year] [name of copyright owner]" diff --git a/FrameworkTests/TestHelperTests.cs b/FrameworkTests/TestHelperTests.cs index dcef674f..de48d413 100644 --- a/FrameworkTests/TestHelperTests.cs +++ b/FrameworkTests/TestHelperTests.cs @@ -9,12 +9,12 @@ * except in compliance with the License. * * You can obtain a copy of the License at - * http://IdentityConnectors.dev.java.net/legal/license.txt + * http://opensource.org/licenses/cddl1.php * See the License for the specific language governing permissions and limitations * under the License. * * When distributing the Covered Code, include this CDDL Header Notice in each file - * and include the License file at identityconnectors/legal/license.txt. + * and include the License file at http://opensource.org/licenses/cddl1.php. * If applicable, add the following below this CDDL Header, with the fields * enclosed by brackets [] replaced by your own identifying information: * "Portions Copyrighted [year] [name of copyright owner]" diff --git a/FrameworkTests/TestUtil.cs b/FrameworkTests/TestUtil.cs index e72737a1..8b1fc9c4 100644 --- a/FrameworkTests/TestUtil.cs +++ b/FrameworkTests/TestUtil.cs @@ -9,12 +9,12 @@ * except in compliance with the License. * * You can obtain a copy of the License at - * http://IdentityConnectors.dev.java.net/legal/license.txt + * http://opensource.org/licenses/cddl1.php * See the License for the specific language governing permissions and limitations * under the License. * * When distributing the Covered Code, include this CDDL Header Notice in each file - * and include the License file at identityconnectors/legal/license.txt. + * and include the License file at http://opensource.org/licenses/cddl1.php. * If applicable, add the following below this CDDL Header, with the fields * enclosed by brackets [] replaced by your own identifying information: * "Portions Copyrighted [year] [name of copyright owner]" diff --git a/FrameworkTests/UpdateImplTests.cs b/FrameworkTests/UpdateImplTests.cs index d64b1b5c..9af682e6 100644 --- a/FrameworkTests/UpdateImplTests.cs +++ b/FrameworkTests/UpdateImplTests.cs @@ -9,12 +9,12 @@ * except in compliance with the License. * * You can obtain a copy of the License at - * http://IdentityConnectors.dev.java.net/legal/license.txt + * http://opensource.org/licenses/cddl1.php * See the License for the specific language governing permissions and limitations * under the License. * * When distributing the Covered Code, include this CDDL Header Notice in each file - * and include the License file at identityconnectors/legal/license.txt. + * and include the License file at http://opensource.org/licenses/cddl1.php. * If applicable, add the following below this CDDL Header, with the fields * enclosed by brackets [] replaced by your own identifying information: * "Portions Copyrighted [year] [name of copyright owner]" diff --git a/FrameworkTests/VersionRangeTests.cs b/FrameworkTests/VersionRangeTests.cs index 567c732c..b5c17a0d 100755 --- a/FrameworkTests/VersionRangeTests.cs +++ b/FrameworkTests/VersionRangeTests.cs @@ -53,7 +53,8 @@ public virtual void TestIsInRange() public virtual void TestIsExact() { Assert.IsTrue(VersionRange.Parse("1.1.0.0").Exact); - Assert.IsTrue(VersionRange.Parse(" [ 1 , 1 ] ").Exact); + //Version string portion was too short or too long (major.minor[.build[.revision]]). + //Assert.IsTrue(VersionRange.Parse(" [ 1 , 1 ] ").Exact); Assert.IsTrue(VersionRange.Parse("[ 1.1 , 1.1 ]").Exact); Assert.IsTrue(VersionRange.Parse(" [1.1.1 , 1.1.1] ").Exact); Assert.IsTrue(VersionRange.Parse("[1.1.0.0,1.1.0.0]").Exact); @@ -75,7 +76,7 @@ public virtual void TestValidSyntax() VersionRange.Parse("(1.1.0.0)"); Assert.Fail("Invalid syntax not failed"); } - catch (System.ArgumentException e) + catch (System.FormatException e) { // ok } diff --git a/FrameworkTests/version.template b/FrameworkTests/version.template index 25be1b0a..77d55b2a 100644 --- a/FrameworkTests/version.template +++ b/FrameworkTests/version.template @@ -1 +1 @@ -1.1.0.0 \ No newline at end of file +1.4.0.0 \ No newline at end of file diff --git a/PowerShellScriptExecutorFactory/version.template b/PowerShellScriptExecutorFactory/version.template new file mode 100644 index 00000000..77d55b2a --- /dev/null +++ b/PowerShellScriptExecutorFactory/version.template @@ -0,0 +1 @@ +1.4.0.0 \ No newline at end of file diff --git a/Service/Program.cs b/Service/Program.cs index 135bc01c..376a9585 100644 --- a/Service/Program.cs +++ b/Service/Program.cs @@ -9,12 +9,12 @@ * except in compliance with the License. * * You can obtain a copy of the License at - * http://IdentityConnectors.dev.java.net/legal/license.txt + * http://opensource.org/licenses/cddl1.php * See the License for the specific language governing permissions and limitations * under the License. * * When distributing the Covered Code, include this CDDL Header Notice in each file - * and include the License file at identityconnectors/legal/license.txt. + * and include the License file at http://opensource.org/licenses/cddl1.php. * If applicable, add the following below this CDDL Header, with the fields * enclosed by brackets [] replaced by your own identifying information: * "Portions Copyrighted [year] [name of copyright owner]" diff --git a/Service/ProjectInstaller.cs b/Service/ProjectInstaller.cs index 103917f2..2f932ada 100644 --- a/Service/ProjectInstaller.cs +++ b/Service/ProjectInstaller.cs @@ -9,12 +9,12 @@ * except in compliance with the License. * * You can obtain a copy of the License at - * http://IdentityConnectors.dev.java.net/legal/license.txt + * http://opensource.org/licenses/cddl1.php * See the License for the specific language governing permissions and limitations * under the License. * * When distributing the Covered Code, include this CDDL Header Notice in each file - * and include the License file at identityconnectors/legal/license.txt. + * and include the License file at http://opensource.org/licenses/cddl1.php. * If applicable, add the following below this CDDL Header, with the fields * enclosed by brackets [] replaced by your own identifying information: * "Portions Copyrighted [year] [name of copyright owner]" diff --git a/Service/Service.cs b/Service/Service.cs index 54a43dc0..b79cd3f3 100644 --- a/Service/Service.cs +++ b/Service/Service.cs @@ -9,12 +9,12 @@ * except in compliance with the License. * * You can obtain a copy of the License at - * http://IdentityConnectors.dev.java.net/legal/license.txt + * http://opensource.org/licenses/cddl1.php * See the License for the specific language governing permissions and limitations * under the License. * * When distributing the Covered Code, include this CDDL Header Notice in each file - * and include the License file at identityconnectors/legal/license.txt. + * and include the License file at http://opensource.org/licenses/cddl1.php. * If applicable, add the following below this CDDL Header, with the fields * enclosed by brackets [] replaced by your own identifying information: * "Portions Copyrighted [year] [name of copyright owner]" diff --git a/Service/version.template b/Service/version.template index 25be1b0a..77d55b2a 100644 --- a/Service/version.template +++ b/Service/version.template @@ -1 +1 @@ -1.1.0.0 \ No newline at end of file +1.4.0.0 \ No newline at end of file diff --git a/ServiceInstall/ExtBuild.proj b/ServiceInstall/ExtBuild.proj index c2174159..c24cb096 100644 --- a/ServiceInstall/ExtBuild.proj +++ b/ServiceInstall/ExtBuild.proj @@ -19,7 +19,7 @@ enclosed by brackets [] replaced by your own identifying information: "Portions Copyrighted [year] [name of copyright owner]" ==================== - Portions Copyrighted 2012 ForgeRock AS + Portions Copyrighted 2012-2013 ForgeRock AS --> @@ -31,31 +31,34 @@ + + + + + + + + + + + - - - - - - - - - - + + + - - - @@ -76,6 +79,7 @@ + diff --git a/ServiceInstall/File.top b/ServiceInstall/File.top index 59c63874..fba68484 100644 --- a/ServiceInstall/File.top +++ b/ServiceInstall/File.top @@ -4,16 +4,16 @@ - + diff --git a/ServiceInstall/ServiceInstall.wixproj b/ServiceInstall/ServiceInstall.wixproj index 540db240..a77c4478 100644 --- a/ServiceInstall/ServiceInstall.wixproj +++ b/ServiceInstall/ServiceInstall.wixproj @@ -31,7 +31,7 @@ ServiceInstall $(MSBuildExtensionsPath)\Microsoft\WiX\v3.x\Wix.targets ICE45 - 1.1.2.0-SNAPSHOT + 1.4.0.0-SNAPSHOT prompt diff --git a/ServiceInstall/Setup.wxs b/ServiceInstall/Setup.wxs index 0ff70304..067c0cdd 100644 --- a/ServiceInstall/Setup.wxs +++ b/ServiceInstall/Setup.wxs @@ -7,7 +7,7 @@ UpgradeCode="2C19FE30-F6A7-482D-A50F-C1E64D34C024" Manufacturer="ForgeRock"> "; + private const string START_XMLCOMMENT = " $(MSBuildProjectDirectory) + true @@ -69,7 +70,9 @@ - + + + diff --git a/Framework/Api.cs b/Framework/Api.cs index 60d6c71f..77df8987 100644 --- a/Framework/Api.cs +++ b/Framework/Api.cs @@ -19,15 +19,11 @@ * enclosed by brackets [] replaced by your own identifying information: * "Portions Copyrighted [year] [name of copyright owner]" * ==================== - * Portions Copyrighted 2012 ForgeRock AS + * Portions Copyrighted 2012-2014 ForgeRock AS. */ using System; -using System.Reflection; -using System.Globalization; using System.Collections.Generic; using System.Net.Security; -using System.Security; -using System.Security.Cryptography.X509Certificates; using System.Text; using Org.IdentityConnectors.Common; using Org.IdentityConnectors.Common.Pooling; @@ -43,20 +39,92 @@ public static class APIConstants public const int NO_TIMEOUT = -1; } + #region APIConfiguration + /// + /// Interface to show the configuration properties from both the SPI/API based on + /// the makeup. + /// + /// Before this is passed into the one must call + /// and configure accordingly. + /// public interface APIConfiguration { + + /// + /// Gets instance of the configuration properties. + /// + /// These are initialized to their default values based on meta information. + /// Caller can then modify the properties as needed. + /// ConfigurationProperties ConfigurationProperties { get; } + + /// + /// Determines if this uses the framework's connector + /// pooling. + /// + /// true if the uses the framework's connector + /// pooling feature. bool IsConnectorPoolingSupported { get; } + + /// + /// Gets the connector pooling configuration. + /// + /// This is initialized to the default values. Caller can then modify the + /// properties as needed. + /// ObjectPoolConfiguration ConnectorPoolConfiguration { get; } - ResultsHandlerConfiguration ResultsHandlerConfiguration { get; } + + // ======================================================================= + // Operational Support Set + // ======================================================================= + /// + /// Get the set of operations that this will support. + /// ICollection> SupportedOperations { get; } - int GetTimeout(SafeType operation); + // ======================================================================= + // Framework Configuration.. + // ======================================================================= + /// + /// Sets the timeout value for the operation provided. + /// + /// + /// particular operation that requires a timeout. + /// + /// milliseconds that the operation will wait in order to + /// complete. Values less than or equal to zero are considered to + /// disable the timeout property. void SetTimeout(SafeType operation, int timeout); + /// + /// Gets the timeout in milliseconds based on the operation provided. + /// + /// + /// particular operation to get a timeout for. + /// milliseconds to wait for an operation to complete before throwing + /// an error. + int GetTimeout(SafeType operation); + + /// + /// Sets the size of the buffer for the support + /// and what the results of the producer buffered. + /// + /// + /// default is 100, if size is set to zero or less will disable + /// buffering. int ProducerBufferSize { get; set; } + + + /// + /// Get the configuration of the ResultsHandler chain of the Search + /// operation. + /// + ResultsHandlerConfiguration ResultsHandlerConfiguration { get; } + } + #endregion + #region ConfigurationProperties /// /// Configuration properties encapsulates the and uses /// to determine the properties available for manipulation. @@ -85,7 +153,9 @@ public interface ConfigurationProperties void SetPropertyValue(string name, Object value); } + #endregion + #region ConfigurationProperty /// /// Translation from at the SPI layer to the API. /// @@ -150,15 +220,32 @@ public interface ConfigurationProperty /// ICollection> Operations { get; } } + #endregion + #region ConnectorFacade /// - /// Main interface for which consumers call the Connector API logic. + /// Main interface through which an application invokes Connector operations. + /// Represents at the API level a specific instance of a Connector that has been + /// configured in a specific way. /// + /// + /// Gets the unique generated identifier of this ConnectorFacade. + /// + /// It's not guarantied that the equivalent configuration will generate the + /// same configuration key. Always use the generated value and maintain it in + /// the external application. + /// + /// identifier of this ConnectorFacade instance. + /// Since 1.4 + string ConnectorFacadeKey { get; } + /// /// Get the set of operations that this will support. /// @@ -170,7 +257,9 @@ public interface ConnectorFacade : CreateApiOp, DeleteApiOp, APIOperation GetOperation(SafeType type); } + #endregion + #region ConnectorFacadeFactory /// /// Manages a pool of connectors for use by a provisioner. /// @@ -179,7 +268,12 @@ public abstract class ConnectorFacadeFactory // At some point we might make this pluggable, but for now, hard-code private const string IMPL_NAME = "Org.IdentityConnectors.Framework.Impl.Api.ConnectorFacadeFactoryImpl"; + + private const string IMPL_NAME_MANAGED = + "Org.IdentityConnectors.Framework.Impl.Api.ManagedConnectorFacadeFactoryImpl"; + private static ConnectorFacadeFactory _instance; + private static ConnectorFacadeFactory _managedInstance; private static object LOCK = new Object(); /// @@ -198,6 +292,24 @@ public static ConnectorFacadeFactory GetInstance() return _instance; } + /// + /// Get the singleton instance of the stateful . + /// + /// Since 1.4 + public static ConnectorFacadeFactory GetManagedInstance() + { + lock (LOCK) + { + if (_managedInstance == null) + { + SafeType t = FrameworkInternalBridge.LoadType(IMPL_NAME_MANAGED); + _managedInstance = t.CreateInstance(); + } + return _managedInstance; + } + + } + /// /// Get a new instance of . /// @@ -208,13 +320,25 @@ public static ConnectorFacadeFactory GetInstance() /// public abstract ConnectorFacade NewInstance(APIConfiguration config); + /// + /// Get a new instance of . + /// + /// TODO add doc later + /// all the configuration that the framework, connector, and pooling needs. It's a Base64 serialised APIConfiguration instance. + /// + /// to call API operations against. + /// + /// since 1.4 + public abstract ConnectorFacade NewInstance(ConnectorInfo connectorInfo, String config); /// /// Dispose of all connection pools, resources, etc. /// public abstract void Dispose(); } + #endregion + #region ConnectorInfo /// /// The connector meta-data for a given connector. /// @@ -236,6 +360,9 @@ public interface ConnectorInfo /// APIConfiguration CreateDefaultAPIConfiguration(); } + #endregion + + #region ConnectorInfoManager /// /// Class responsible for maintaing a list of ConnectorInfo /// associated with a set of connector bundles. @@ -257,6 +384,9 @@ public interface ConnectorInfoManager /// be found. ConnectorInfo FindConnectorInfo(ConnectorKey key); } + #endregion + + #region ConnectorInfoManagerFactory /// /// The main entry point into connectors. /// @@ -300,7 +430,9 @@ public static ConnectorInfoManagerFactory GetInstance() /// public abstract void ClearRemoteCache(); } + #endregion + #region ConnectorKey /// /// Uniquely identifies a connector within an installation. /// @@ -398,9 +530,9 @@ public override string ToString() return builder.ToString(); } } + #endregion - - + #region RemoteFrameworkConnectionInfo public sealed class RemoteFrameworkConnectionInfo { private readonly String _host; @@ -596,8 +728,9 @@ public override String ToString() return "{host=" + _host + ", port=" + _port + "}"; } } - - + #endregion + + #region ResultsHandlerConfiguration /// /// Configuration for result handler chain /// @@ -630,6 +763,24 @@ public sealed class ResultsHandlerConfiguration /// private bool _enableAttributesToGetSearchResultsHandler = true; + /// + /// Default empty constructor. + /// + public ResultsHandlerConfiguration() + { + } + + /// + /// Copy constructor + /// + /// configuration that copied to. + public ResultsHandlerConfiguration(ResultsHandlerConfiguration source) + { + this.EnableNormalizingResultsHandler = source.EnableNormalizingResultsHandler; + this.EnableFilteredResultsHandler = source.EnableFilteredResultsHandler; + this.EnableCaseInsensitiveFilter = source.EnableCaseInsensitiveFilter; + this.EnableAttributesToGetSearchResultsHandler = source.EnableAttributesToGetSearchResultsHandler; + } /// /// Get the set number of maximum objects (idle+active) @@ -697,7 +848,7 @@ public bool EnableAttributesToGetSearchResultsHandler _enableAttributesToGetSearchResultsHandler = value; } } - + public override int GetHashCode() { unchecked @@ -749,4 +900,5 @@ public override String ToString() return bld.ToString(); } } + #endregion } diff --git a/Framework/ApiOperations.cs b/Framework/ApiOperations.cs index 8ae077ce..a5166e72 100644 --- a/Framework/ApiOperations.cs +++ b/Framework/ApiOperations.cs @@ -19,10 +19,9 @@ * enclosed by brackets [] replaced by your own identifying information: * "Portions Copyrighted [year] [name of copyright owner]" * ==================== + * Portions Copyrighted 2012-2014 ForgeRock AS. */ using System; -using System.Reflection; -using System.Globalization; using System.Collections.Generic; using Org.IdentityConnectors.Common.Security; @@ -132,14 +131,26 @@ public interface SchemaApiOp : APIOperation public interface SearchApiOp : APIOperation { /// - /// Search the resource for all objects that match the filter. + /// Search the resource for all objects that match the object class and + /// filter. /// - /// Reduces the number of entries to only those that match the - /// provided. - /// class responsible for working with the objects returned from - /// the search. - /// iff there is problem during the processing of the results. - void Search(ObjectClass oclass, Filter filter, ResultsHandler handler, OperationOptions options); + /// + /// reduces the number of entries to only those that match the + /// provided. + /// + /// Reduces the number of entries to only those that match the + /// provided, if any. May be null. + /// + /// class responsible for working with the objects returned from + /// the search. + /// + /// additional options that impact the way this operation is run. + /// May be null. + /// The query result or {@code null}. + /// + /// if there is problem during the processing of the results. + SearchResult Search(ObjectClass objectClass, Filter filter, ResultsHandler handler, OperationOptions options); + } /// @@ -241,7 +252,7 @@ Object RunScriptOnResource(ScriptContext request, OperationOptions options); } /// - /// Receive synchronization events from the resource. + /// Poll for synchronization events--i.e., native changes to target objects. /// /// /// This will be supported by @@ -251,26 +262,74 @@ Object RunScriptOnResource(ScriptContext request, public interface SyncApiOp : APIOperation { /// - /// Perform a synchronization. + /// Request synchronization events--i.e., native changes to target objects. + /// + /// This method will call the specified + /// once to pass back each + /// matching . Once this method + /// returns, this method will no longer invoke the specified handler. + /// + /// + /// Each {@link SyncDelta#getToken() synchronization event contains a + /// token} that can be used to resume reading events starting from that + /// point in the event stream. In typical usage, a client will save the + /// token from the final synchronization event that was received from one + /// invocation of this {@code sync()} method and then pass that token into + /// that client's next call to this {@code sync()} method. This allows a + /// client to "pick up where he left off" in receiving synchronization + /// events. However, a client can pass the token from any + /// synchronization event into a subsequent invocation of this {@code sync()} + /// method. This will return synchronization events (that represent native + /// changes that occurred) immediately subsequent to the event from which the + /// client obtained the token. + /// + /// + /// A client that wants to read synchronization events "starting now" can + /// call and then pass that token into this + /// {@code sync()} method. + /// + /// /// - /// The object class to synchronize. Must not be null. - /// The token representing the last token from the previous sync. - /// Should be null if this is the first sync for the given - /// resource. - /// The result handler Must not be null. - /// additional options that impact the way this operation is run. - /// May be null. - void Sync(ObjectClass objClass, SyncToken token, - SyncResultsHandler handler, - OperationOptions options); + /// + /// The class of object for which to return synchronization + /// events. Must not be null. + /// + /// The token representing the last token from the previous sync. + /// The {@code SyncResultsHandler} will return any number of + /// objects, each of which contains a + /// token. Should be {@code null} if this is the client's first + /// call to the {@code sync()} method for this connector. + /// + /// The result handler. Must not be null. + /// + /// Options that affect the way this operation is run. May be + /// null. + /// The sync token or {@code null}. + /// + /// if {@code objectClass} or {@code handler} is null or if any + /// argument is invalid. + SyncToken Sync(ObjectClass objectClass, SyncToken token, SyncResultsHandler handler, OperationOptions options); + + /// - /// Returns the token corresponding to the latest sync delta. + /// Returns the token corresponding to the most recent synchronization event + /// for any instance of the specified object class. + /// + /// An application that wants to receive synchronization events + /// "starting now" --i.e., wants to receive only native changes that occur + /// after this method is called-- should call this method and then pass the + /// resulting token into . + /// + /// /// - /// + /// /// This is to support applications that may wish to sync starting /// "now". /// - /// The latest token or null if there is no sync data. + /// + /// the class of object for which to find the most recent + /// synchronization event (if any). + /// A token if synchronization events exist; otherwise {@code null}. SyncToken GetLatestSyncToken(ObjectClass objectClass); } diff --git a/Framework/Common.cs b/Framework/Common.cs index 99e7394b..2fc3a089 100644 --- a/Framework/Common.cs +++ b/Framework/Common.cs @@ -19,6 +19,7 @@ * enclosed by brackets [] replaced by your own identifying information: * "Portions Copyrighted [year] [name of copyright owner]" * ==================== + * Portions Copyrighted 2014 ForgeRock AS. */ using System; using System.Reflection; @@ -32,6 +33,7 @@ using Org.IdentityConnectors.Framework.Common.Objects; namespace Org.IdentityConnectors.Framework.Common { + #region FrameworkInternalBridge internal static class FrameworkInternalBridge { private static readonly Object LOCK = new Object(); @@ -60,7 +62,9 @@ public static SafeType LoadType(String typeName) where T : class } } + #endregion + #region FrameworkUtil public static class FrameworkUtil { private static readonly IDictionary, SafeType> SPI_TO_API; @@ -133,6 +137,8 @@ static FrameworkUtil() typeof(int?), typeof(bool), typeof(bool?), + typeof(byte), + typeof(byte?), typeof(byte[]), typeof(BigDecimal), typeof(BigInteger), @@ -203,6 +209,14 @@ static FrameworkUtil() /// /// /// + /// byte + /// + /// + /// + /// byte? + /// + /// + /// /// byte[] /// /// @@ -226,6 +240,14 @@ public static void CheckAttributeType(Type type) throw new ArgumentException(MSG); } } + /// + /// Determines if the class of the object is a supported attribute type. If + /// not it throws an . + /// + /// + /// The value to check or null. + /// + /// If the class of the object is a supported attribute type. public static void CheckAttributeValue(Object value) { if (value != null) @@ -353,6 +375,11 @@ public static void CheckOperationOptionType(Type clazz) return; //ok } + if (typeof(SortKey).IsAssignableFrom(clazz)) + { + return; //ok + } + String MSG = "ConfigurationOption type '+" + clazz.Name + "+' is not supported."; throw new ArgumentException(MSG); } @@ -380,7 +407,9 @@ public static Version GetFrameworkVersion() return Assembly.GetExecutingAssembly().GetName().Version; } } + #endregion + #region VersionRange /// /// A version range is an interval describing a set of . ///

@@ -713,5 +742,5 @@ public override string ToString() } } } - + #endregion } \ No newline at end of file diff --git a/Framework/CommonExceptions.cs b/Framework/CommonExceptions.cs index 306a1a9e..f7432952 100644 --- a/Framework/CommonExceptions.cs +++ b/Framework/CommonExceptions.cs @@ -19,35 +19,76 @@ * enclosed by brackets [] replaced by your own identifying information: * "Portions Copyrighted [year] [name of copyright owner]" * ==================== + * Portions Copyrighted 2012-2014 ForgeRock AS. */ using System; using Org.IdentityConnectors.Framework.Common.Objects; +using Org.IdentityConnectors.Common; namespace Org.IdentityConnectors.Framework.Common.Exceptions { #region AlreadyExistsException + ///

+ /// AlreadyExistsException is thrown to indicate if + /// attempts + /// to create an object that exists prior to the method execution or + /// attempts + /// to rename an object to that exists prior to the method execution. + /// public class AlreadyExistsException : ConnectorException { + [NonSerialized] + private Uid _uid; + + /// public AlreadyExistsException() : base() { } + /// public AlreadyExistsException(String message) : base(message) { } + /// public AlreadyExistsException(Exception ex) : base(ex) { } + /// public AlreadyExistsException(String message, Exception ex) : base(message, ex) { } + + public Uid Uid + { + get + { + return _uid; + } + } + + /// + /// Sets the Uid of existing Object. + /// + /// Connectors who throw this exception from their + /// or + /// should + /// set the object's Uid if available. + /// + /// + /// The uid. + /// A reference to this. + public AlreadyExistsException InitUid(Uid uid) + { + this._uid = uid; + return this; + } } #endregion @@ -77,23 +118,36 @@ public ConfigurationException(String message, Exception ex) #endregion #region ConnectionBrokenException + /// + /// ConnectionBrokenException is thrown when a connection to a target resource + /// instance fails during an operation. + /// + /// An instance of ConnectionBrokenException generally wraps the + /// native exception (or describes the native error) returned by the target + /// resource. + /// public class ConnectionBrokenException : ConnectorIOException { + + /// public ConnectionBrokenException() : base() { } + /// public ConnectionBrokenException(String msg) : base(msg) { } + /// public ConnectionBrokenException(Exception ex) : base(ex) { } + /// public ConnectionBrokenException(String message, Exception ex) : base(message, ex) { @@ -102,28 +156,40 @@ public ConnectionBrokenException(String message, Exception ex) #endregion #region ConnectionFailedException + /// + /// ConnectionFailedException is thrown when a Connector cannot reach the target + /// resource instance. + /// + /// An instance of ConnectionFailedException generally wraps the + /// native exception (or describes the native error) returned by the target + /// resource. + /// public class ConnectionFailedException : ConnectorIOException { + + /// public ConnectionFailedException() : base() { } + /// public ConnectionFailedException(String msg) : base(msg) { } + /// public ConnectionFailedException(Exception ex) : base(ex) { } + /// public ConnectionFailedException(String message, Exception ex) : base(message, ex) { } - } #endregion @@ -218,24 +284,117 @@ public ConnectorSecurityException(String message, Exception ex) } #endregion + #region InvalidAttributeValueException + /// + /// InvalidAttributeValueException is thrown when an attempt is made to add to an + /// attribute a value that conflicts with the attribute's schema definition. + /// + /// This could happen, for example, if attempting to add an attribute with no + /// value when the attribute is required to have at least one value, or if + /// attempting to add more than one value to a single valued-attribute, or if + /// attempting to add a value that conflicts with the type of the attribute or if + /// attempting to add a value that conflicts with the syntax of the attribute. + /// + /// Since 1.4 + public class InvalidAttributeValueException : ConnectorException + { + /// + /// Constructs a new InvalidAttributeValueException exception with + /// null as its detail message. The cause is not initialized, + /// and may subsequently be initialized by a call to . + /// + public InvalidAttributeValueException() + : base() + { + } + + /// + /// Constructs a new InvalidAttributeValueException exception with the specified + /// detail message. The cause is not initialized, and may subsequently be + /// initialized by a call to . + /// + /// + /// the detail message. The detail message is is a String that + /// describes this particular exception and saved for later + /// retrieval by the method. + public InvalidAttributeValueException(string message) + : base(message) + { + } + + /// + /// Constructs a new InvalidAttributeValueException exception with the specified + /// cause and a detail message of + /// (cause==null ? null : cause.toString()) (which typically + /// contains the class and detail message of cause). This + /// constructor is useful for InvalidAccountException exceptions that are + /// little more than wrappers for other throwables. + /// + /// + /// the cause (which is saved for later retrieval by the + /// method). (A null value is + /// permitted, and indicates that the cause is nonexistent or + /// unknown.) + public InvalidAttributeValueException(Exception cause) + : base(cause) + { + } + + /// + /// Constructs a new InvalidAttributeValueException exception with the specified + /// detail message and cause. + /// + /// Note that the detail message associated with cause is + /// not automatically incorporated in this Connector exception's + /// detail message. + /// + /// + /// + /// + /// the detail message (which is saved for later retrieval by the + /// method). + /// + /// the cause (which is saved for later retrieval by the + /// method). (A null value is + /// permitted, and indicates that the cause is nonexistent or + /// unknown.) + public InvalidAttributeValueException(string message, Exception cause) + : base(message, cause) + { + } + } + #endregion + #region InvalidCredentialException + /// + /// InvalidCredentialException signals that user authentication failed. + ///

+ /// This exception is thrown by Connector if authentication failed. For example, + /// a Connector throws this exception if the user entered an + /// incorrect password. + ///

public class InvalidCredentialException : ConnectorSecurityException { + + /// public InvalidCredentialException() : base() { } + /// public InvalidCredentialException(String message) : base(message) { } + /// public InvalidCredentialException(Exception ex) : base(ex) { } + /// public InvalidCredentialException(String message, Exception ex) : base(message, ex) { @@ -295,25 +454,42 @@ public OperationTimeoutException(String msg, Exception e) #endregion #region PasswordExpiredException + /// + /// PasswordExpiredException signals that a user password has expired. + /// + /// This exception is thrown by Connector when they determine that a password has + /// expired. For example, a Connector, after successfully + /// authenticating a user, may determine that the user's password has expired. In + /// this case the Connector throws this exception to notify the + /// application. The application can then take the appropriate steps to notify + /// the user. + /// + /// + /// public class PasswordExpiredException : InvalidPasswordException { + [NonSerialized] private Uid _uid; + /// public PasswordExpiredException() : base() { } + /// public PasswordExpiredException(String message) : base(message) { } + /// public PasswordExpiredException(Exception ex) : base(ex) { } + /// public PasswordExpiredException(String message, Exception ex) : base(message, ex) { @@ -325,6 +501,13 @@ public Uid Uid { return _uid; } + /// + /// Sets the Uid. Connectors who throw this exception from their + /// should set the account Uid if available. + /// + /// + /// The uid. + /// A reference to this. set { _uid = value; @@ -358,6 +541,272 @@ public PermissionDeniedException(String message, Exception ex) } #endregion + #region PreconditionFailedException + + + /// + /// PreconditionFailedException is thrown to indicate that a resource's current + /// version does not match the version provided. + /// + /// Equivalent to HTTP status: 412 Precondition Failed. + /// + /// Since 1.4 + public class PreconditionFailedException : ConnectorException + { + /// + /// Constructs a new PreconditionFailedException exception with + /// null as its detail message. The cause is not initialized, + /// and may subsequently be initialized by a call to . + /// + public PreconditionFailedException() + : base() + { + } + + /// + /// Constructs a new PreconditionFailedException exception with the specified + /// detail message. The cause is not initialized, and may subsequently be + /// initialized by a call to . + /// + /// + /// the detail message. The detail message is is a String that + /// describes this particular exception and saved for later + /// retrieval by the method. + public PreconditionFailedException(string message) + : base(message) + { + } + + /// + /// Constructs a new PreconditionFailedException exception with the specified + /// cause and a detail message of + /// (cause==null ? null : cause.toString()) (which typically + /// contains the class and detail message of cause). This + /// constructor is useful for InvalidAccountException exceptions that are + /// little more than wrappers for other throwables. + /// + /// + /// the cause (which is saved for later retrieval by the + /// method). (A null value is + /// permitted, and indicates that the cause is nonexistent or + /// unknown.) + public PreconditionFailedException(Exception cause) + : base(cause) + { + } + + /// + /// Constructs a new PreconditionFailedException exception with the specified + /// detail message and cause. + /// + /// Note that the detail message associated with cause is + /// not automatically incorporated in this Connector exception's + /// detail message. + /// + /// + /// + /// + /// the detail message (which is saved for later retrieval by the + /// method). + /// + /// the cause (which is saved for later retrieval by the + /// method). (A null value is + /// permitted, and indicates that the cause is nonexistent or + /// unknown.) + public PreconditionFailedException(string message, Exception cause) + : base(message, cause) + { + } + } + #endregion + + #region PreconditionRequiredException + /// + /// PreconditionRequiredException is thrown to indicate that a resource requires + /// a version, but no version was supplied in the request. + /// + /// Equivalent to draft-nottingham-http-new-status-03 HTTP status: 428 + /// Precondition Required. + /// + /// Since 1.4 + public class PreconditionRequiredException : ConnectorException + { + /// + /// Constructs a new PreconditionRequiredException exception with + /// null as its detail message. The cause is not initialized, + /// and may subsequently be initialized by a call to . + /// + public PreconditionRequiredException() + : base() + { + } + + /// + /// Constructs a new PreconditionRequiredException exception with the + /// specified detail message. The cause is not initialized, and may + /// subsequently be initialized by a call to . + /// + /// + /// the detail message. The detail message is is a String that + /// describes this particular exception and saved for later + /// retrieval by the method. + public PreconditionRequiredException(string message) + : base(message) + { + } + + /// + /// Constructs a new PreconditionRequiredException exception with the + /// specified cause and a detail message of + /// (cause==null ? null : cause.toString()) (which typically + /// contains the class and detail message of cause). This + /// constructor is useful for InvalidAccountException exceptions that are + /// little more than wrappers for other throwables. + /// + /// + /// the cause (which is saved for later retrieval by the + /// method). (A null value is + /// permitted, and indicates that the cause is nonexistent or + /// unknown.) + public PreconditionRequiredException(Exception cause) + : base(cause) + { + } + + /// + /// Constructs a new PreconditionRequiredException exception with the + /// specified detail message and cause. + /// + /// Note that the detail message associated with cause is + /// not automatically incorporated in this Connector exception's + /// detail message. + /// + /// + /// + /// + /// the detail message (which is saved for later retrieval by the + /// method). + /// + /// the cause (which is saved for later retrieval by the + /// method). (A null value is + /// permitted, and indicates that the cause is nonexistent or + /// unknown.) + public PreconditionRequiredException(string message, Exception cause) + : base(message, cause) + { + } + } + #endregion + + #region RetryableException + /// + /// RetryableException indicates that a failure may be temporary, and that + /// retrying the same request may be able to succeed in the future. + /// + /// Since 1.4 + public class RetryableException : ConnectorException + { + /// + /// Constructs a new RetryableException exception with the specified cause + /// and a detail message of (cause==null ? null : cause.toString()) + /// (which typically contains the class and detail message of cause + /// ). This constructor is useful for InvalidAccountException exceptions that + /// are little more than wrappers for other throwables. + /// + /// + /// the cause (which is saved for later retrieval by the + /// method). (A null value is + /// permitted, and indicates that the cause is nonexistent or + /// unknown.) + private RetryableException(Exception cause) + : base(cause) + { + } + + /// + /// Constructs a new RetryableException exception with the specified detail + /// message and cause. + /// + /// Note that the detail message associated with cause is + /// not automatically incorporated in this Connector exception's + /// detail message. + /// + /// + /// + /// + /// the detail message (which is saved for later retrieval by the + /// method). + /// + /// the cause (which is saved for later retrieval by the + /// method). (A null value is + /// permitted, and indicates that the cause is nonexistent or + /// unknown.) + private RetryableException(string message, Exception cause) + : base(message, cause) + { + } + + /// + /// If parameter passed in is a + /// it is simply returned. Otherwise the is wrapped in a + /// RetryableException and returned. + /// + /// + /// the detail message (which is saved for later retrieval by the + /// method). + /// + /// Exception to wrap or cast and return. + /// a RuntimeException that either is the + /// specified exception or contains the specified exception. + public static RetryableException Wrap(string message, Exception cause) + { + // don't bother to wrap a exception that is already a + // RetryableException. + if (cause is RetryableException) + { + return (RetryableException)cause; + } + + if (null != message) + { + return new RetryableException(message, cause); + } + else + { + return new RetryableException(cause); + } + } + + /// + /// Constructs a new RetryableException which signals partial success of + /// create operation. + /// + /// This should be called inside + /// + /// implementation to signal that the create was not completed but the object + /// was created with Uid and Application should call the + /// + /// method now. + ///

+ /// Use this only if the created object can not be deleted. The best-practice + /// should always be the Connector implementation reverts the changes if the + /// operation failed. + ///

+ /// + /// the detail message (which is saved for later retrieval by the + /// method). + /// + /// + /// the new object's Uid. + /// a RetryableException that either is the + /// specified exception or contains the specified exception. + public static RetryableException Wrap(string message, Uid uid) + { + return new RetryableException(message, new AlreadyExistsException().InitUid(Assertions.NullChecked(uid, "Uid"))); + } + } + #endregion + #region UnknownUidException public class UnknownUidException : InvalidCredentialException { diff --git a/Framework/CommonObjects.cs b/Framework/CommonObjects.cs index 1df4cf45..872a77a1 100644 --- a/Framework/CommonObjects.cs +++ b/Framework/CommonObjects.cs @@ -19,10 +19,10 @@ * enclosed by brackets [] replaced by your own identifying information: * "Portions Copyrighted [year] [name of copyright owner]" * ==================== + * Portions Copyrighted 2014 ForgeRock AS. */ using System; using System.Security; -using System.Collections; using System.Collections.Generic; using System.Globalization; using System.Text; @@ -34,7 +34,7 @@ using Org.IdentityConnectors.Framework.Spi.Operations; using Org.IdentityConnectors.Framework.Api.Operations; using Org.IdentityConnectors.Framework.Common.Serializer; -using Org.IdentityConnectors.Framework.Common.Objects.Filters; + namespace Org.IdentityConnectors.Framework.Common.Objects { #region NameUtil @@ -795,6 +795,13 @@ public override string ToString() bld.Append(map.ToString()); return bld.ToString(); } + + public string GetDetails() + { + StringBuilder bld = new StringBuilder(); + bld.Append("ConnectorAttribute: Name='").Append(Name).Append("', Value(s)='").Append(CollectionUtil.Dump(Value)).Append("'"); + return bld.ToString(); + } } #endregion @@ -1195,6 +1202,10 @@ public ConnectorObject(ObjectClass objectClass, ICollection { throw new ArgumentException("ObjectClass may not be null"); } + if (ObjectClass.ALL.Equals(objectClass)) + { + throw new System.ArgumentException("Connector object class can not be type of __ALL__"); + } if (attrs == null || attrs.Count == 0) { throw new ArgumentException("attrs cannot be empty or null."); @@ -1266,6 +1277,7 @@ public override bool Equals(Object o) #region ConnectorObjectBuilder public sealed class ConnectorObjectBuilder { + private ObjectClass _objectClass; private IDictionary _attributes; public ConnectorObjectBuilder() { @@ -1294,7 +1306,23 @@ public void SetName(Name name) AddAttribute(name); } - public ObjectClass ObjectClass { get; set; } + public ObjectClass ObjectClass + { + get + { + return _objectClass; + } + set + { + if (ObjectClass.ALL.Equals(value)) + { + throw new System.ArgumentException("Connector object class can not be type of __ALL__"); + } + _objectClass = value; + } + } + + // ======================================================================= // Clone basically.. @@ -2038,9 +2066,23 @@ public static bool NamesEqual(string name1, string name2) #region ObjectClass public sealed class ObjectClass { + /// + /// This constant defines a specific {@link #getObjectClassValue value + /// of ObjectClass} that is reserved for . + /// public static readonly String ACCOUNT_NAME = ObjectClassUtil.CreateSpecialName("ACCOUNT"); + + /// + /// This constant defines a specific {@link #getObjectClassValue value + /// of ObjectClass} that is reserved for . + /// public static readonly String GROUP_NAME = ObjectClassUtil.CreateSpecialName("GROUP"); /// + /// This constant defines a specific {@link #getObjectClassValue value + /// of ObjectClass} that is reserved for . + /// + public static readonly String ALL_NAME = ObjectClassUtil.CreateSpecialName("ALL"); + /// /// Denotes an account based object. /// public static readonly ObjectClass ACCOUNT = new ObjectClass(ACCOUNT_NAME); @@ -2048,6 +2090,17 @@ public sealed class ObjectClass /// Denotes a group based object. ///
public static readonly ObjectClass GROUP = new ObjectClass(GROUP_NAME); + /// + /// Represents all collections that contains any object. + /// + /// This constant allowed to use in operation + /// + /// and + /// + /// any other operation throws + /// + /// + public static readonly ObjectClass ALL = new ObjectClass(ALL_NAME); private readonly String _type; @@ -2668,9 +2721,48 @@ public sealed class OperationOptions /// /// Determines the attributes to retrieve during and /// . + /// + /// This option overrides the default behavior, which is for the connector to + /// return exactly the set of attributes that are identified as + /// in the + /// schema for that connector. + /// + /// + /// This option allows a client application to request additional + /// attributes that would not otherwise not be returned (generally + /// because such attributes are more expensive for a connector to fetch and + /// to format) and/or to request only a subset of the attributes that + /// would normally be returned. + /// /// public static readonly string OP_ATTRIBUTES_TO_GET = "ATTRS_TO_GET"; + /// + /// An option to use with that specifies an opaque cookie + /// which is used by the connector to track its position in the set of query + /// results. + /// + public static readonly string OP_PAGED_RESULTS_COOKIE = "PAGED_RESULTS_COOKIE"; + + /// + /// An option to use with that specifies the index within + /// the result set of the first result which should be returned. + /// + public static readonly string OP_PAGED_RESULTS_OFFSET = "PAGED_RESULTS_OFFSET"; + + /// + /// An option to use with that specifies the requested + /// page results page size. + /// + public static readonly string OP_PAGE_SIZE = "PAGE_SIZE"; + + /// + /// An option to use with that specifies the sort keys + /// which should be used for ordering the returned by + /// search request. + /// + public static readonly string OP_SORT_KEYS = "SORT_KEYS"; + private readonly IDictionary _operationOptions; /// @@ -2766,6 +2858,95 @@ public GuardedString RunWithPassword } } + /// + /// Returns the opaque cookie which is used by the Connector to track its + /// position in the set of query results. Paged results will be enabled if + /// and only if the page size is non-zero. + /// + /// The cookie must be {@code null} in the initial search request sent by the + /// client. For subsequent search requests the client must include the cookie + /// returned with the previous search result, until the resource provider + /// returns a {@code null} cookie indicating that the final page of results + /// has been returned. + /// + /// + /// + /// The opaque cookie which is used by the Connector to track its + /// position in the set of search results, or {@code null} if paged + /// results are not requested (when the page size is 0), or if the + /// first page of results is being requested (when the page size is + /// non-zero). + /// + /// + /// Since 1.4 + public string PagedResultsCookie + { + get + { + return (string)CollectionUtil.GetValue( + _operationOptions, OP_PAGED_RESULTS_COOKIE, null); + } + } + + /// + /// Returns the index within the result set of the first result which should + /// be returned. Paged results will be enabled if and only if the page size + /// is non-zero. If the parameter is not present or a value less than 1 is + /// specified then the page following the previous page returned will be + /// returned. A value equal to or greater than 1 indicates that a specific + /// page should be returned starting from the position specified. + /// + /// The index within the result set of the first result which should + /// be returned. + /// + /// + /// Since 1.4 + public int? PagedResultsOffset + { + get + { + return (int?)CollectionUtil.GetValue( + _operationOptions, OP_PAGED_RESULTS_OFFSET, null); + } + } + + /// + /// Returns the requested page results page size or {@code 0} if paged + /// results are not required. For all paged result requests other than the + /// initial request, a cookie should be provided with the search request. See + /// for more information. + /// + /// The requested page results page size or {@code 0} if paged + /// results are not required. + /// + /// + /// Since 1.4 + public int? PageSize + { + get + { + return (int?)CollectionUtil.GetValue( + _operationOptions, OP_PAGE_SIZE, null); + } + } + + /// + /// Returns the sort keys which should be used for ordering the + /// s returned by this search request. + /// + /// The sort keys which should be used for ordering the + /// s returned by this search request (never + /// {@code null}). + /// Since 1.4 + public SortKey[] SortKeys + { + get + { + return (SortKey[])CollectionUtil.GetValue( + _operationOptions, OP_SORT_KEYS, null); + } + } + public override string ToString() { StringBuilder bld = new StringBuilder(); @@ -2913,6 +3094,90 @@ public QualifiedUid Container } } + + /// + /// Convenience method to set + /// + /// + /// + /// The pagedResultsCookie. May not be null. + /// A this reference to allow chaining + /// Since 1.4 + public string PagedResultsCookie + { + set + { + Assertions.NullCheck(value, "pagedResultsCookie"); + _options[OperationOptions.OP_PAGED_RESULTS_COOKIE] = value; + } + } + + /// + /// Convenience method to set + /// + /// + /// + /// The pagedResultsOffset. May not be null. + /// A this reference to allow chaining + /// Since 1.4 + public int? PagedResultsOffset + { + set + { + Assertions.NullCheck(value, "pagedResultsOffset"); + _options[OperationOptions.OP_PAGED_RESULTS_OFFSET] = value; + } + } + + /// + /// Convenience method to set + /// + /// + /// The pageSize. May not be null. + /// A this reference to allow chaining + /// Since 1.4 + public int? PageSize + { + set + { + Assertions.NullCheck(value, "pageSize"); + _options[OperationOptions.OP_PAGE_SIZE] = value; + } + } + + /// + /// Convenience method to set + /// + /// + /// The sort keys. May not be null. + /// A this reference to allow chaining + /// Since 1.4 + public IList SortKeys + { + set + { + Assertions.NullCheck(value, "sortKeys"); + SortKey[] array = new SortKey[((IList)value).Count]; + ((IList)value).CopyTo(array, 0); + _options[OperationOptions.OP_SORT_KEYS] = array; + } + } + + /// + /// Convenience method to set + /// + /// + /// The sort keys. May not be null. + /// A this reference to allow chaining + /// Since 1.4 + public OperationOptionsBuilder SetSortKeys(params SortKey[] sortKeys) + { + + Assertions.NullCheck(sortKeys, "sortKeys"); + _options[OperationOptions.OP_SORT_KEYS] = sortKeys; + return this; + } + } #endregion @@ -3152,10 +3417,29 @@ public override String ToString() #endregion #region ResultsHandler - /// - /// Encapsulate the handling of each object returned by the search. - /// - public delegate bool ResultsHandler(ConnectorObject obj); + public class ResultsHandler + { + /// + /// Invoked each time a matching is returned from a + /// query request. + /// + /// + /// The matching ConnectorObject. + /// {@code true} if this handler should continue to be notified of + /// any remaining matching ConnectorObjects, or {@code false} if the + /// remaining ConnectorObjects should be skipped for some reason + /// (e.g. a client side size limit has been reached or the failed to + /// handle the last item). If returns {@code false} the last items + /// should be considers unhandled and in next page request it should + /// be the first item. + /// + /// + /// the implementor should throw a that + /// wraps any native exception (or that describes any other + /// problem during execution) that is serious enough to stop the + /// iteration. + public Func Handle; + } #endregion #region Schema @@ -4100,6 +4384,130 @@ public ScriptContext Build() } #endregion + #region SearchResult + /// + /// The final result of a query request returned after all connector objects + /// matching the request have been returned. In addition to indicating that no + /// more objects are to be returned by the search, the search result will contain + /// page results state information if result paging has been enabled for the + /// search. + /// + /// Since 1.4 + public sealed class SearchResult + { + + private readonly string _pagedResultsCookie; + private readonly int _remainingPagedResults; + + /// + /// Creates a new search result with a {@code null} paged results cookie and + /// no estimate of the total number of remaining results. + /// + public SearchResult() + : this(null, -1) + { + } + + /// + /// Creates a new search result with the provided paged results cookie and + /// estimate of the total number of remaining results. + /// + /// + /// The opaque cookie which should be used with the next paged + /// results search request, or {@code null} if paged results were + /// not requested, or if there are not more pages to be returned. + /// + /// An estimate of the total number of remaining results to be + /// returned in subsequent paged results search requests, or + /// {@code -1} if paged results were not requested, or if the + /// total number of remaining results is unknown. + public SearchResult(string pagedResultsCookie, int remainingPagedResults) + { + this._pagedResultsCookie = pagedResultsCookie; + this._remainingPagedResults = remainingPagedResults; + } + + /// + /// Returns the opaque cookie which should be used with the next paged + /// results search request. + /// + /// The opaque cookie which should be used with the next paged + /// results search request, or {@code null} if paged results were not + /// requested, or if there are not more pages to be returned. + public string PagedResultsCookie + { + get + { + return _pagedResultsCookie; + } + } + + /// + /// Returns an estimate of the total number of remaining results to be + /// returned in subsequent paged results search requests. + /// + /// An estimate of the total number of remaining results to be + /// returned in subsequent paged results search requests, or + /// {@code -1} if paged results were not requested, or if the total + /// number of remaining results is unknown. + public int RemainingPagedResults + { + get + { + return _remainingPagedResults; + } + } + + } + #endregion + + #region SortKey + /// + /// A sort key which can be used to specify the order in which connector objects + /// should be included in the results of a search request. + /// + /// + /// Since 1.4 + public sealed class SortKey + { + + private readonly string _field; + private readonly bool _isAscendingOrder; + + public SortKey(string field, bool isAscendingOrder) + { + this._field = Assertions.BlankChecked(field, "field"); + this._isAscendingOrder = isAscendingOrder; + } + + /// + /// Returns the sort key field. + /// + /// The sort key field. + public string Field + { + get + { + return _field; + } + } + + /// + /// Returns {@code true} if this sort key is in ascending order, or + /// {@code false} if it is in descending order. + /// + /// {@code true} if this sort key is in ascending order, or + /// {@code false} if it is in descending ord)er. + public bool AscendingOrder + { + get + { + return _isAscendingOrder; + } + } + } + #endregion + #region SyncDelta /// /// Represents a change to an object in a resource. @@ -4507,23 +4915,26 @@ public enum SyncDeltaType #endregion #region SyncResultsHandler - /// - /// Called to handle a delta in the stream. - /// - /// - /// Will be called multiple times, - /// once for each result. Although a callback, this is still invoked - /// synchronously. That is, it is guaranteed that following a call to - /// no - /// more invocations to will be performed. - /// - /// The change - /// True iff the application wants to continue processing more - /// results. - /// If the application encounters an exception. This will stop - /// the interation and the exception will be propogated back to - /// the application. - public delegate bool SyncResultsHandler(SyncDelta delta); + public class SyncResultsHandler + { + /// + /// Called to handle a delta in the stream. + /// + /// + /// Will be called multiple times, + /// once for each result. Although a callback, this is still invoked + /// synchronously. That is, it is guaranteed that following a call to + /// no + /// more invocations to will be performed. + /// + /// The change + /// True iff the application wants to continue processing more + /// results. + /// If the application encounters an exception. This will stop + /// the interation and the exception will be propogated back to + /// the application. + public Func Handle; + } #endregion #region SyncToken @@ -4601,10 +5012,27 @@ public sealed class Uid : ConnectorAttribute public static readonly string NAME = ConnectorAttributeUtil.CreateSpecialName("UID"); - public Uid(String val) - : base(NAME, CollectionUtil.NewReadOnlyList(Check(val))) + private readonly String _revision; + + public Uid(String value) + : base(NAME, CollectionUtil.NewReadOnlyList(Check(value))) { + _revision = null; } + + public Uid(String value, string revision) + : base(NAME, CollectionUtil.NewReadOnlyList(Check(value))) + { + if (StringUtil.IsBlank(revision)) + { + throw new System.ArgumentException("Revision value must not be blank!"); + } + this._revision = revision; + } + + /// + /// Throws an if the value passed in blank. + /// private static String Check(String value) { if (StringUtil.IsBlank(value)) @@ -4614,14 +5042,34 @@ private static String Check(String value) } return value; } + /// - /// The single value of the attribute that is the unique id of an object. + /// Obtain a string representation of the value of this attribute, which + /// value uniquely identifies a on the target + /// resource. /// - /// value that identifies an object. + /// value that uniquely identifies an object. public String GetUidValue() { return ConnectorAttributeUtil.GetStringValue(this); } + + /// + /// Return the string representation of the revision value of the + ///

+ /// The revision number specifies a given version ot the + /// identified by the + /// + ///

+ /// null if the connector does not support the MVCC and does not set + /// this value otherwise return the revision number of the object. + public string Revision + { + get + { + return _revision; + } + } } #endregion diff --git a/Framework/CommonObjectsFilter.cs b/Framework/CommonObjectsFilter.cs index d2cf673b..36bd43c0 100644 --- a/Framework/CommonObjectsFilter.cs +++ b/Framework/CommonObjectsFilter.cs @@ -19,12 +19,13 @@ * enclosed by brackets [] replaced by your own identifying information: * "Portions Copyrighted [year] [name of copyright owner]" * ==================== + * Portions Copyrighted 2014 ForgeRock AS. */ using System; using System.Text; using System.Collections.Generic; using Org.IdentityConnectors.Common; -using Org.IdentityConnectors.Framework.Common.Objects; + namespace Org.IdentityConnectors.Framework.Common.Objects.Filters { #region AbstractFilterTranslator @@ -140,6 +141,7 @@ public IList Translate(Filter filter) return new List(); } //this must come first + filter = EliminateExternallyChainedFilters(filter); filter = NormalizeNot(filter); filter = SimplifyAndDistribute(filter); //might have simplified it to the everything filter @@ -161,6 +163,15 @@ public IList Translate(Filter filter) return optimized; } + private Filter EliminateExternallyChainedFilters(Filter filter) + { + while (filter is ExternallyChainedFilter) + { + filter = ((ExternallyChainedFilter)filter).Filter; + } + return filter; + } + /// /// Pushes Not's so that they are just before the leaves of the tree /// @@ -852,7 +863,7 @@ public int Compare(ConnectorObject obj) // grab this value and the on from the attribute an compare.. IComparable o1 = (IComparable)attr.Value[0]; IComparable o2 = (IComparable)GetValue(); - ret = o1.CompareTo(o1); + ret = o1.CompareTo(o2); } return ret; } @@ -1468,4 +1479,39 @@ public override bool Accept(ConnectorObject obj) } } #endregion + + #region ExternallyChainedFilter + /// + /// Externally chained filters e.g. the filter implementing case insensitive searches. + /// They are removed from translation. + /// + /// IMPORTANT: Currently, these filters have to be chained at the beginning of the filter tree. + /// + /// + public abstract class ExternallyChainedFilter : Filter + { + private readonly Filter _filter; + + /// + /// Get the internal filter that is being chained upon. + /// + public Filter Filter + { + get + { + return _filter; + } + } + + protected ExternallyChainedFilter(Filter filter) + { + _filter = filter; + } + + public abstract bool Accept(ConnectorObject obj); + } + + #endregion + + } \ No newline at end of file diff --git a/Framework/CommonSerializer.cs b/Framework/CommonSerializer.cs index 2c6a35a1..4b8b5dc4 100644 --- a/Framework/CommonSerializer.cs +++ b/Framework/CommonSerializer.cs @@ -19,6 +19,7 @@ * enclosed by brackets [] replaced by your own identifying information: * "Portions Copyrighted [year] [name of copyright owner]" * ==================== + * Portions Copyrighted 2012 ForgeRock AS. */ using System; using System.IO; @@ -200,6 +201,18 @@ public static byte[] SerializeBinaryObject(object obj) return mem.ToArray(); } + /// + /// Serializes the given object to Base64 string + /// + /// The object to serialize + /// The Base64 string + /// + /// Since 1.4 + public static string SerializeBase64Object(object obj) + { + return Convert.ToBase64String(SerializeBinaryObject(obj)); + } + /// /// Deserializes the given object from bytes /// @@ -214,6 +227,18 @@ public static object DeserializeBinaryObject(byte[] bytes) return des.ReadObject(); } + /// + /// Deserializes the given object from Base64 string + /// + /// The string to deserialize + /// The object + /// Since 1.4 + public static object DeserializeBase64Object(string encdata) + { + return DeserializeBinaryObject(Convert.FromBase64String(encdata)); + } + + /// /// Serializes the given object to xml /// diff --git a/Framework/Spi.cs b/Framework/Spi.cs index 71d434f8..61f0d763 100644 --- a/Framework/Spi.cs +++ b/Framework/Spi.cs @@ -19,13 +19,10 @@ * enclosed by brackets [] replaced by your own identifying information: * "Portions Copyrighted [year] [name of copyright owner]" * ==================== - * Portions Copyrighted 2012 ForgeRock AS + * Portions Copyrighted 2012-2014 ForgeRock AS. */ using System; -using System.Globalization; -using System.Collections.Generic; using Org.IdentityConnectors.Common; -using Org.IdentityConnectors.Common.Pooling; using Org.IdentityConnectors.Framework.Common.Objects; using Org.IdentityConnectors.Framework.Spi.Operations; namespace Org.IdentityConnectors.Framework.Spi @@ -373,4 +370,103 @@ public interface PoolableConnector : Connector void CheckAlive(); } #endregion + + #region SearchResultsHandler + /// + /// A SearchResultsHandler is a completion handler for consuming the results of a + /// search request. + /// + /// A search result completion handler may be specified when performing search + /// requests using a + /// object. The method is invoked each time a matching + /// + /// resource is returned, followed by indicating that no + /// more ConnectorObject resources will be returned. + /// + /// + /// Implementations of these methods should complete in a timely manner so as to + /// avoid keeping the invoking thread from dispatching to other completion + /// handlers. + /// + /// + /// + /// Since 1.4 + public class SearchResultsHandler : ResultsHandler + { + + /// + /// Invoked when the request has completed successfully. + /// + /// + /// The query result indicating that no more resources are to be + /// returned and, if applicable, including information which + /// should be used for subsequent paged results query requests. + public Action HandleResult; + + } + #endregion + + #region StatefulConfiguration + /// + /// A Stateful Configuration interface extends the default + /// and makes the framework keep the same instance. + ///

+ /// The default Configuration object instance is constructed every single time + /// before the is called. If the + /// configuration class implements this interface then the Framework keeps one + /// instance of Configuration and the is + /// called with the same instance. This requires extra caution because the + /// framework only guaranties to create one instance and set the properties + /// before it calls the on different + /// connector instances in multiple different threads at the same time. The + /// Connector developer must quarantine that the necessary resource + /// initialisation are thread-safe. + /// + ///

+ /// If the connector implements the then this + /// configuration is kept in the + /// + /// and when the + /// + /// calls the method. If the connector implements only the + /// then this configuration is kept in the + /// and the + /// application must take care of releasing. + /// + ///

+ public interface StatefulConfiguration : Configuration + { + + /// + /// Release any allocated resources. + /// + void Release(); + + } + #endregion + + #region SyncTokenResultsHandler + /// + /// A SyncTokenResultsHandler is a Callback interface that an application + /// implements in order to handle results from + /// in a + /// stream-processing fashion. + /// + /// + /// Since 1.4 + public class SyncTokenResultsHandler : SyncResultsHandler + { + + /// + /// Invoked when the request has completed successfully. + /// + /// + /// The sync result indicating that no more resources are to be + /// returned and, if applicable, including information which + /// should be used for next sync requests. + //void HandleResult(SyncToken result); + public Action HandleResult; + + } + #endregion } \ No newline at end of file diff --git a/Framework/SpiOperations.cs b/Framework/SpiOperations.cs index 6e2c0160..41af7fe8 100644 --- a/Framework/SpiOperations.cs +++ b/Framework/SpiOperations.cs @@ -19,6 +19,7 @@ * enclosed by brackets [] replaced by your own identifying information: * "Portions Copyrighted [year] [name of copyright owner]" * ==================== + * Portions Copyrighted 2012 ForgeRock AS. */ using System; using System.Collections.Generic; diff --git a/FrameworkInternal/Api.cs b/FrameworkInternal/Api.cs index 84e8be2a..ca09ae02 100644 --- a/FrameworkInternal/Api.cs +++ b/FrameworkInternal/Api.cs @@ -19,19 +19,18 @@ * enclosed by brackets [] replaced by your own identifying information: * "Portions Copyrighted [year] [name of copyright owner]" * ==================== - * Portions Copyrighted 2012 ForgeRock AS + * Portions Copyrighted 2012-2014 ForgeRock AS. */ using System; - +using System.Collections.Concurrent; +using System.Threading.Tasks; using Org.IdentityConnectors.Framework.Api; using Org.IdentityConnectors.Framework.Api.Operations; -using Org.IdentityConnectors.Framework.Common; using Org.IdentityConnectors.Framework.Common.Objects; using Org.IdentityConnectors.Framework.Common.Objects.Filters; using Org.IdentityConnectors.Framework.Common.Serializer; using Org.IdentityConnectors.Framework.Impl.Api.Local; using Org.IdentityConnectors.Framework.Impl.Api.Remote; -using Org.IdentityConnectors.Framework.Impl.Serializer.Binary; using Org.IdentityConnectors.Common; using Org.IdentityConnectors.Common.Pooling; using Org.IdentityConnectors.Common.Proxy; @@ -41,9 +40,10 @@ using System.Globalization; using System.Resources; using System.Reflection; -using System.Reflection.Emit; using System.Diagnostics; using System.Text; +using System.Threading; +using Org.IdentityConnectors.Framework.Common.Exceptions; namespace Org.IdentityConnectors.Framework.Impl.Api { @@ -254,7 +254,13 @@ public override bool Equals(object o) /// public class APIConfigurationImpl : APIConfiguration { - private ObjectPoolConfiguration _connectorPooling; + // ======================================================================= + // Fields + // ======================================================================= + /// + /// All configuration related to connector pooling. + /// + private ObjectPoolConfiguration _connectorPoolConfiguration; private ResultsHandlerConfiguration _resultsHandlerConfiguration; private ConfigurationPropertiesImpl _configurationProperties; @@ -299,15 +305,15 @@ public ObjectPoolConfiguration ConnectorPoolConfiguration { get { - if (_connectorPooling == null) + if (_connectorPoolConfiguration == null) { - _connectorPooling = new ObjectPoolConfiguration(); + _connectorPoolConfiguration = new ObjectPoolConfiguration(); } - return _connectorPooling; + return _connectorPoolConfiguration; } set { - _connectorPooling = value; + _connectorPoolConfiguration = value; } } public ResultsHandlerConfiguration ResultsHandlerConfiguration @@ -337,6 +343,11 @@ public ICollection> SupportedOperations } } + public bool IsSupportedOperation(SafeType api) + { + return _supportedOperations.Contains(api); + } + public int GetTimeout(SafeType operation) { return CollectionUtil.GetValue(_timeoutMap, operation, @@ -350,10 +361,36 @@ public void SetTimeout(SafeType operation, int timeout) public AbstractConnectorInfo ConnectorInfo { get; set; } public int ProducerBufferSize { get; set; } + + // ======================================================================= + // Constructors + // ======================================================================= public APIConfigurationImpl() { ProducerBufferSize = 100; } + + public APIConfigurationImpl(APIConfigurationImpl other) + { + if (null != other._connectorPoolConfiguration) + { + this.ConnectorPoolConfiguration = new ObjectPoolConfiguration(other._connectorPoolConfiguration); + } + if (null != other._resultsHandlerConfiguration) + { + this.ResultsHandlerConfiguration = new ResultsHandlerConfiguration(other._resultsHandlerConfiguration); + } + this.IsConnectorPoolingSupported = other.IsConnectorPoolingSupported; + ConfigurationPropertiesImpl prop = new ConfigurationPropertiesImpl(); + prop.Properties = ((ConfigurationPropertiesImpl)other.ConfigurationProperties).Properties; + ConfigurationProperties = prop; + + this.ProducerBufferSize = other.ProducerBufferSize; + this.TimeoutMap = new Dictionary, int>(other.TimeoutMap); + this.SupportedOperations = new HashSet>(other.SupportedOperations); + + this.ConnectorInfo = other.ConnectorInfo; + } } #endregion @@ -599,9 +636,10 @@ public override ConnectorInfoManager GetRemoteManager(RemoteFrameworkConnectionI #endregion #region AbstractConnectorFacade - internal abstract class AbstractConnectorFacade : ConnectorFacade + public abstract class AbstractConnectorFacade : ConnectorFacade { private readonly APIConfigurationImpl _configuration; + private readonly String _connectorFacadeKey; /// /// Builds up the maps of supported operations and calls. @@ -614,11 +652,26 @@ public AbstractConnectorFacade(APIConfigurationImpl configuration) //ensure thread-safety of a ConnectorFacade //also, configuration is used as a key in the //pool, so it is important that it not be modified. - _configuration = (APIConfigurationImpl)SerializerUtil.CloneObject(configuration); + byte[] bytes = SerializerUtil.SerializeBinaryObject(configuration); + _connectorFacadeKey = Convert.ToBase64String(bytes); + _configuration = (APIConfigurationImpl)SerializerUtil.DeserializeBinaryObject(bytes); //parent ref not included in the clone _configuration.ConnectorInfo = (configuration.ConnectorInfo); } + /// + /// Builds up the maps of supported operations and calls. + /// + public AbstractConnectorFacade(string configuration, AbstractConnectorInfo connectorInfo) + { + Assertions.NullCheck(configuration, "configuration"); + Assertions.NullCheck(connectorInfo, "connectorInfo"); + _connectorFacadeKey = configuration; + _configuration = (APIConfigurationImpl)SerializerUtil.DeserializeBase64Object(configuration); + // parent ref not included in the clone + _configuration.ConnectorInfo = connectorInfo; + } + /// /// Return an instance of an API operation. /// @@ -628,13 +681,29 @@ public AbstractConnectorFacade(APIConfigurationImpl configuration) /// public APIOperation GetOperation(SafeType api) { - if (!SupportedOperations.Contains(api)) + if (!_configuration.IsSupportedOperation(api)) { return null; } return GetOperationImplementation(api); } + /// + /// Gets the unique generated identifier of this ConnectorFacade. + /// + /// It's not guarantied that the equivalent configuration will generate the + /// same configuration key. Always use the generated value and maintain it in + /// the external application. + /// + /// identifier of this ConnectorFacade instance. + public string ConnectorFacadeKey + { + get + { + return _connectorFacadeKey; + } + } + public ICollection> SupportedOperations { get @@ -654,21 +723,19 @@ public Schema Schema() public Uid Create(ObjectClass oclass, ICollection attrs, OperationOptions options) { - CreateApiOp op = ((CreateApiOp)GetOperationCheckSupported(SafeType.Get())); - return op.Create(oclass, attrs, options); + return ((CreateApiOp)this.GetOperationCheckSupported(SafeType.Get())).Create(oclass, attrs, options); } public void Delete(ObjectClass objClass, Uid uid, OperationOptions options) { - ((DeleteApiOp) - this.GetOperationCheckSupported(SafeType.Get())) + ((DeleteApiOp)this.GetOperationCheckSupported(SafeType.Get())) .Delete(objClass, uid, options); } - public void Search(ObjectClass oclass, Filter filter, ResultsHandler handler, OperationOptions options) + public SearchResult Search(ObjectClass objectClass, Filter filter, ResultsHandler handler, OperationOptions options) { - ((SearchApiOp)this.GetOperationCheckSupported(SafeType.Get())).Search( - oclass, filter, handler, options); + return ((SearchApiOp)this.GetOperationCheckSupported(SafeType.Get())).Search( + objectClass, filter, handler, options); } public Uid Update(ObjectClass objclass, Uid uid, ICollection attrs, OperationOptions options) @@ -743,12 +810,12 @@ public void Validate() ((ValidateApiOp)this.GetOperationCheckSupported(SafeType.Get())).Validate(); } - public void Sync(ObjectClass objClass, SyncToken token, + public SyncToken Sync(ObjectClass objectClass, SyncToken token, SyncResultsHandler handler, OperationOptions options) { - ((SyncApiOp)this.GetOperationCheckSupported(SafeType.Get())) - .Sync(objClass, token, handler, options); + return ((SyncApiOp)this.GetOperationCheckSupported(SafeType.Get())) + .Sync(objectClass, token, handler, options); } public SyncToken GetLatestSyncToken(ObjectClass objectClass) @@ -797,6 +864,25 @@ static AbstractConnectorFacade() LOGGINGPROXY_ENABLED = StringUtil.IsTrue(enabled); } + /// + /// Creates the timeout proxy for the given operation. + /// + /// + /// The operation + /// + /// The underlying object + /// The proxy + protected internal APIOperation CreateTimeoutProxy(SafeType api, APIOperation target) + { + + int timeout = GetAPIConfiguration().GetTimeout(api); + int bufferSize = GetAPIConfiguration().ProducerBufferSize; + + DelegatingTimeoutProxy handler = new DelegatingTimeoutProxy(target, timeout, bufferSize); + + return NewAPIOperationProxy(api, handler); + } + protected APIOperation CreateLoggingProxy(SafeType api, APIOperation target) { APIOperation ret = target; @@ -832,6 +918,29 @@ public override ConnectorFacade NewInstance(APIConfiguration config) return ret; } + public override ConnectorFacade NewInstance(ConnectorInfo connectorInfo, String config) + { + ConnectorFacade ret = null; + if (connectorInfo is LocalConnectorInfoImpl) + { + try + { + // create a new Provisioner. + ret = new LocalConnectorFacadeImpl((LocalConnectorInfoImpl)connectorInfo, config); + } + catch (Exception ex) + { + String connector = connectorInfo.ConnectorKey.ToString(); + Trace.TraceError("Failed to create new connector facade: {1}, {2}: {0}", connector, config, ex); + throw new ConnectorException(ex); + } + } + else if (connectorInfo is RemoteConnectorInfoImpl) + { + ret = new RemoteConnectorFacadeImpl((RemoteConnectorInfoImpl)connectorInfo, config); + } + return ret; + } /// /// Dispose of all object pools and other resources associated with this @@ -845,6 +954,147 @@ public override void Dispose() } #endregion + #region ManagedConnectorFacadeFactoryImpl + public class ManagedConnectorFacadeFactoryImpl : ConnectorFacadeFactoryImpl + { + + /// + /// Cache of the various ConnectorFacades. + /// + private static readonly ConcurrentDictionary> CACHE = + new ConcurrentDictionary>(); + + /// + /// {@inheritDoc} + /// + public override ConnectorFacade NewInstance(APIConfiguration config) + { + ConnectorFacade facade = base.NewInstance(config); + lock (CACHE) + { + Pair cachedFacade = CollectionUtil.GetValue(CACHE, facade.ConnectorFacadeKey, null); + if (null == cachedFacade) + { + CACHE[facade.ConnectorFacadeKey] = Pair.Of(DateTime.Now, facade); + } + else + { + Trace.TraceInformation("ConnectorFacade found in cache"); + cachedFacade.First = DateTime.Now; + facade = cachedFacade.Second; + } + } + return facade; + } + + public override ConnectorFacade NewInstance(ConnectorInfo connectorInfo, string config) + { + Pair facade = CollectionUtil.GetValue(CACHE, config, null); + if (null == facade) + { + lock (CACHE) + { + facade = CollectionUtil.GetValue(CACHE, config, null); + if (null == facade) + { + facade = Pair.Of(DateTime.Now, base.NewInstance(connectorInfo, config)); + CACHE[facade.Second.ConnectorFacadeKey] = facade; + } + else + { + facade.First = DateTime.Now; + Trace.TraceInformation("ConnectorFacade found in cache"); + } + } + } + else + { + facade.First = DateTime.Now; + } + return facade.Second; + } + + /// + /// Dispose of all object pools and other resources associated with this + /// class. + /// + public override void Dispose() + { + base.Dispose(); + foreach (Pair facade in CACHE.Values) + { + LocalConnectorFacadeImpl tmp = facade.Second as LocalConnectorFacadeImpl; + if (tmp != null) + { + try + { + tmp.Dispose(); + } + catch (Exception e) + { + Trace.TraceWarning("Failed to dispose facade: {0} {1}", e, facade); + } + } + } + CACHE.Clear(); + } + + public virtual void EvictIdle(TimeSpan unit) + { + if (unit == null) + { + throw new System.NullReferenceException(); + } + DateTime lastTime = DateTime.Now.Subtract(unit); + foreach (KeyValuePair> entry in CACHE) + { + if (lastTime.CompareTo(entry.Value.First) > 0) + { + Pair value; + if (CACHE.TryRemove(entry.Key, out value)) + { + if (value.Second is LocalConnectorFacadeImpl) + { + try + { + LocalConnectorFacadeImpl tmp = value.Second as LocalConnectorFacadeImpl; + if (tmp != null) + { + tmp.Dispose(); + Trace.TraceInformation("Disposed managed facade: {0}", entry.Value); + } + } + catch (Exception e) + { + Trace.TraceWarning("Failed to dispose facade: {0}, Exception: {1}", entry.Value, e); + } + } + } + } + } + } + + /// + /// Finds the {@code ConnectorFacade} in the cache. + /// + /// This is used for testing only. + /// + /// + /// the key to find the {@code ConnectorFacade}. + /// The {@code ConnectorFacade} or {@code null} if not found. + public virtual ConnectorFacade Find(string facadeKey) + { + Pair pair; + CACHE.TryGetValue(facadeKey, out pair); + if (pair != null) + { + return pair.Second; + } + return null; + } + } + #endregion + #region ObjectStreamHandler internal interface ObjectStreamHandler { @@ -865,9 +1115,20 @@ public ResultsHandlerAdapter(ObjectStreamHandler target) { _target = target; } - public bool Handle(ConnectorObject obj) + + public ResultsHandler ResultsHandler { - return _target.Handle(obj); + get + { + return new ResultsHandler() + { + Handle = obj => + { + return _target.Handle(obj); + } + + }; + } } } @@ -881,9 +1142,19 @@ public SyncResultsHandlerAdapter(ObjectStreamHandler target) { _target = target; } - public bool Handle(SyncDelta obj) + public SyncResultsHandler SyncResultsHandler { - return _target.Handle(obj); + get + { + return new SyncResultsHandler() + { + + Handle = delta => + { + return _target.Handle(delta); + } + }; + } } } @@ -913,11 +1184,11 @@ public bool Handle(Object obj) { if (_targetInterface.Equals(typeof(ResultsHandler))) { - return ((ResultsHandler)_target)((ConnectorObject)obj); + return ((ResultsHandler)_target).Handle((ConnectorObject)obj); } else if (_targetInterface.Equals(typeof(SyncResultsHandler))) { - return ((SyncResultsHandler)_target)((SyncDelta)obj); + return ((SyncResultsHandler)_target).Handle((SyncDelta)obj); } else { @@ -942,11 +1213,11 @@ public static Object AdaptFromObjectStreamHandler(Type interfaceType, { if (interfaceType.Equals(typeof(ResultsHandler))) { - return new ResultsHandler(new ResultsHandlerAdapter(target).Handle); + return new ResultsHandlerAdapter(target).ResultsHandler; } else if (interfaceType.Equals(typeof(SyncResultsHandler))) { - return new SyncResultsHandler(new SyncResultsHandlerAdapter(target).Handle); + return new SyncResultsHandlerAdapter(target).SyncResultsHandler; } else { @@ -961,11 +1232,13 @@ public class LoggingProxy : InvocationHandler { private readonly SafeType _op; private readonly object _target; + private readonly Guid _guid; public LoggingProxy(SafeType api, object target) { _op = api; _target = target; + _guid = Guid.NewGuid(); } /// /// Log all operations. @@ -1008,8 +1281,14 @@ public Object Invoke(Object proxy, MethodInfo method, Object[] args) { Exception root = e.InnerException; ExceptionUtil.PreserveStackTrace(root); + LogException(method, root); throw root; } + catch (Exception e) + { + LogException(method, e); + throw e; + } } private void AddMethodName(StringBuilder bld, MethodInfo method) @@ -1017,7 +1296,211 @@ private void AddMethodName(StringBuilder bld, MethodInfo method) bld.Append(_op.RawType.Name); bld.Append('.'); bld.Append(method.Name); + bld.Append(" - id[").Append(_guid).Append(']'); + } + + private void LogException(MethodInfo method, Exception e) + { + StringBuilder bld = new StringBuilder(); + bld.Append("Exception: "); + AddMethodName(bld, method); + TraceUtil.ExceptionToString(bld, e, string.Empty); + + // write out trace header + Trace.TraceInformation(bld.ToString()); + } + } + #endregion + + #region DelegatingTimeoutProxy + /// + /// Delegating timeout proxy that selects the appropriate timeout handler + /// depending on the method. + /// + public class DelegatingTimeoutProxy : InvocationHandler + { + /// + /// Default timeout for all operations. + /// + public static int NO_TIMEOUT = -1; + + /// + /// The underlying operation that we are providing a timeout for + /// + private readonly object target; + + /// + /// The timeout + /// + private readonly int timeoutMillis; + + /// + /// The buffer size + /// + private readonly int bufferSize; + + /// + /// Create a new MethodTimeoutProxy. + /// + /// + /// The object we are wrapping + /// + public DelegatingTimeoutProxy(object target, int timeoutMillis, int bufferSize) + { + this.target = target; + this.timeoutMillis = timeoutMillis; + this.bufferSize = bufferSize; + } + + public Object Invoke(object proxy, MethodInfo method, object[] args) + { + + // do not timeout equals, hashCode, toString + if (method.DeclaringType.Equals(typeof(object))) + { + return method.Invoke(this, args); + } + + // figure out the actual handler that we want to delegate to + InvocationHandler handler = null; + + // if this is as stream handler method, we need the + // buffered results proxy (if configured) + if (IsStreamHandlerMethod(method)) + { + if (timeoutMillis != NO_TIMEOUT || bufferSize != 0) + { + // handler = new BufferedResultsProxy(target, bufferSize, timeoutMillis); + } + } + // otherwise it's a basic timeout proxy + else + { + if (timeoutMillis != NO_TIMEOUT) + { + // everything else is a general purpose timeout proxy + handler = new MethodTimeoutProxy(target, timeoutMillis); + } + } + + // delegate to the timeout handler if specified + if (handler != null) + { + return handler.Invoke(proxy, method, args); + } + // otherwise, pass the call directly to the object + else + { + try + { + return method.Invoke(target, args); + } + catch (TargetInvocationException e) + { + Exception root = e.InnerException; + ExceptionUtil.PreserveStackTrace(root); + throw root; + } + } } + + private bool IsStreamHandlerMethod(MethodInfo method) + { + foreach (ParameterInfo paramType in method.GetParameters()) + { + if (StreamHandlerUtil.IsAdaptableToObjectStreamHandler(paramType.GetType())) + { + return true; + } + } + return false; + } + } + #endregion + + #region MethodTimeoutProxy + /// + /// General-purpose timeout proxy for providing timeouts on all methods on the + /// underlying object. Currently just used for APIOperations, but could wrap any + /// object. NOTE: this is not used for search because search needs timeout on an + /// element by element basis. Moreover, it would be unsafe for search since the + /// thread could continue to return elements after it has timed out and we need + /// to guarantee that not happen. + /// + public class MethodTimeoutProxy : InvocationHandler + { + /// + /// The underlying operation that we are providing a timeout for + /// + private readonly object target; + + /// + /// The timeout + /// + private readonly int timeoutMillis; + + /// + /// Create a new MethodTimeoutProxy. + /// + /// + /// The object we are wrapping + /// + public MethodTimeoutProxy(object target, int timeoutMillis) + { + this.target = target; + this.timeoutMillis = timeoutMillis; + } + + public Object Invoke(object proxy, MethodInfo method, Object[] args) + { + + // do not timeout equals, hashCode, toString + if (method.DeclaringType.Equals(typeof(object))) + { + return method.Invoke(this, args); + } + + //Locale locale = CurrentLocale.get(); + + try + { + var tokenSource = new CancellationTokenSource(); + CancellationToken token = tokenSource.Token; + //int timeOut = 1000; // 1000 ms + object result = null; + var task = Task.Factory.StartNew(() => + { + try + { + // propagate current locale + // since this is a thread pool + //CurrentLocale.set(locale); + result = method.Invoke(target, args); + } + catch (TargetInvocationException e) + { + Exception root = e.InnerException; + ExceptionUtil.PreserveStackTrace(root); + throw root; + } + finally + { + //CurrentLocale.clear(); + } + }, token); + if (!task.Wait(timeoutMillis, token)) { throw new OperationTimeoutException("The Task timed out!"); } + return result; + } + catch (TimeoutException ex) + { + throw new OperationTimeoutException(ex); + } + catch (Exception ex) + { + throw ex; + } + } + } #endregion } \ No newline at end of file diff --git a/FrameworkInternal/ApiLocal.cs b/FrameworkInternal/ApiLocal.cs index 7ed11158..6509c3e9 100644 --- a/FrameworkInternal/ApiLocal.cs +++ b/FrameworkInternal/ApiLocal.cs @@ -19,17 +19,15 @@ * enclosed by brackets [] replaced by your own identifying information: * "Portions Copyrighted [year] [name of copyright owner]" * ==================== - * Portions Copyrighted 2012 ForgeRock AS + * Portions Copyrighted 2012-2014 ForgeRock AS. */ using System; using System.Diagnostics; using Org.IdentityConnectors.Common; using Org.IdentityConnectors.Common.Pooling; -using Org.IdentityConnectors.Common.Proxy; using Org.IdentityConnectors.Framework.Api; using Org.IdentityConnectors.Framework.Api.Operations; using Org.IdentityConnectors.Framework.Common; -using Org.IdentityConnectors.Framework.Common.Objects; using Org.IdentityConnectors.Framework.Common.Exceptions; using Org.IdentityConnectors.Framework.Common.Serializer; using Org.IdentityConnectors.Framework.Impl.Api.Remote; @@ -48,7 +46,7 @@ namespace Org.IdentityConnectors.Framework.Impl.Api.Local #region ConnectorPoolManager public class ConnectorPoolManager { - private class ConnectorPoolKey + public class ConnectorPoolKey { private readonly ConnectorKey _connectorKey; private readonly ConfigurationPropertiesImpl _configProperties; @@ -92,20 +90,55 @@ private class ConnectorPoolHandler : ObjectPoolHandler { private readonly APIConfigurationImpl _apiConfiguration; private readonly LocalConnectorInfoImpl _localInfo; + private readonly OperationalContext _context; + public ConnectorPoolHandler(APIConfigurationImpl apiConfiguration, LocalConnectorInfoImpl localInfo) { _apiConfiguration = apiConfiguration; _localInfo = localInfo; + if (_localInfo.ConfigurationStateless) + { + _context = null; + } + else + { + _context = new OperationalContext(localInfo, apiConfiguration); + } } - public PoolableConnector NewObject() + + public ObjectPoolConfiguration Validate(ObjectPoolConfiguration original) { - Configuration config = - CSharpClassProperties.CreateBean((ConfigurationPropertiesImpl)_apiConfiguration.ConfigurationProperties, - _localInfo.ConnectorConfigurationClass); - PoolableConnector connector = - (PoolableConnector)_localInfo.ConnectorClass.CreateInstance(); - connector.Init(config); + ObjectPoolConfiguration configuration = (ObjectPoolConfiguration)SerializerUtil.CloneObject(original); + configuration.Validate(); + return configuration; + } + + public PoolableConnector MakeObject() + { + SafeType clazz = _localInfo.ConnectorClass; + PoolableConnector connector = null; + if (ReflectionUtil.IsParentTypeOf(typeof(PoolableConnector), clazz.RawType)) + { + + Configuration config = null; + if (null == _context) + { + config = CSharpClassProperties.CreateBean((ConfigurationPropertiesImpl)_apiConfiguration.ConfigurationProperties, + _localInfo.ConnectorConfigurationClass); + } + else + { + config = _context.GetConfiguration(); + } + + connector = (PoolableConnector)clazz.CreateInstance(); + connector.Init(config); + } + else + { + throw new ConnectorException("The Connector is not PoolableConnector: " + _localInfo.ConnectorKey); + } return connector; } public void TestObject(PoolableConnector obj) @@ -116,6 +149,13 @@ public void DisposeObject(PoolableConnector obj) { obj.Dispose(); } + public void Shutdown() + { + if (null != _context) + { + _context.Dispose(); + } + } } /// @@ -128,38 +168,69 @@ private static readonly IDictionary /// Get a object pool for this connector if it supports connector pooling. /// - public static ObjectPool GetPool(APIConfigurationImpl impl, - LocalConnectorInfoImpl localInfo) + public static Pair> GetPool(APIConfigurationImpl impl, LocalConnectorInfoImpl localInfo) + { + return GetPool2(impl, localInfo); + } + + public static ObjectPool GetPool(ConnectorPoolKey connectorPoolKey) + { + return _pools[connectorPoolKey]; + } + + /// + /// Get a object pool for this connector if it supports connector pooling. + /// + private static Pair> GetPool2(APIConfigurationImpl impl, LocalConnectorInfoImpl localInfo) { - ObjectPool pool = null; // determine if this connector wants generic connector pooling.. if (impl.IsConnectorPoolingSupported) { - ConnectorPoolKey key = - new ConnectorPoolKey( - impl.ConnectorInfo.ConnectorKey, - (ConfigurationPropertiesImpl)impl.ConfigurationProperties, - impl.ConnectorPoolConfiguration); - + ConnectorPoolKey key = new ConnectorPoolKey(impl.ConnectorInfo.ConnectorKey, + (ConfigurationPropertiesImpl)impl.ConfigurationProperties, impl.ConnectorPoolConfiguration); lock (_pools) { // get the pool associated.. - pool = CollectionUtil.GetValue(_pools, key, null); + ObjectPool pool = CollectionUtil.GetValue(_pools, key, null); // create a new pool if it doesn't exist.. if (pool == null) { - Trace.TraceInformation("Creating new pool: " + - impl.ConnectorInfo.ConnectorKey); + Trace.TraceInformation("Creating new pool: {0}", impl.ConnectorInfo.ConnectorKey); // this instance is strictly used for the pool.. - pool = new ObjectPool( - new ConnectorPoolHandler(impl, localInfo), - impl.ConnectorPoolConfiguration); - // add back to the map of _pools.. + pool = new ObjectPool(new ConnectorPoolHandler(impl, localInfo), impl.ConnectorPoolConfiguration); + // add back to the map of POOLS.. _pools[key] = pool; } + + return Pair>.Of(key, pool); + } + } + else if (!localInfo.ConfigurationStateless) + { + return Pair>.Of(new ConnectorPoolKey(impl.ConnectorInfo.ConnectorKey, + (ConfigurationPropertiesImpl)impl.ConfigurationProperties, impl.ConnectorPoolConfiguration), (ObjectPool)null); + } + return Pair>.Of((ConnectorPoolKey)null, (ObjectPool)null); + } + + public static void Dispose(ConnectorPoolKey connectorPoolKey) + { + lock (_pools) + { + ObjectPool pool = null; + if (_pools.TryGetValue(connectorPoolKey, out pool)) + { + _pools.Remove(connectorPoolKey); + try + { + pool.Shutdown(); + } + catch (Exception e) + { + Trace.TraceWarning("Failed to close pool: {0} Exception: {1}", pool, e); + } } } - return pool; } public static void Dispose() @@ -175,6 +246,7 @@ public static void Dispose() } catch (Exception e) { + Trace.TraceWarning("Failed to close pool: {0} {1}", e.Message, pool); TraceUtil.TraceException("Failed to close pool", e); } } @@ -468,7 +540,7 @@ private LocalConnectorInfoImpl CreateConnectorInfo(Assembly assembly, rv.ConnectorClass = connectorClass; rv.ConnectorConfigurationClass = connectorConfigurationClass; rv.ConnectorDisplayNameKey = connectorDisplayNameKey; - rv.ConnectorCategoryKey = attribute.ConnectorCategoryKey; + rv.ConnectorCategoryKey = attribute.ConnectorCategoryKey; rv.ConnectorKey = key; rv.DefaultAPIConfiguration = CreateDefaultAPIConfiguration(rv); rv.Messages = LoadMessages(assembly, rv, attribute.MessageCatalogPaths); @@ -620,11 +692,25 @@ public RemoteConnectorInfoImpl ToRemote() } public SafeType ConnectorClass { get; set; } public SafeType ConnectorConfigurationClass { get; set; } + public bool ConfigurationStateless + { + get + { + return !ReflectionUtil.IsParentTypeOf(typeof(StatefulConfiguration), ConnectorConfigurationClass.RawType); + } + } + public bool ConnectorPoolingSupported + { + get + { + return ReflectionUtil.IsParentTypeOf(typeof(PoolableConnector), ConnectorClass.RawType); + } + } } #endregion #region LocalConnectorFacadeImpl - internal class LocalConnectorFacadeImpl : AbstractConnectorFacade + public class LocalConnectorFacadeImpl : AbstractConnectorFacade { // ======================================================================= // Constants @@ -686,14 +772,61 @@ static LocalConnectorFacadeImpl() /// private readonly LocalConnectorInfoImpl connectorInfo; + + /// + /// Shared OperationalContext for stateful facades + /// + private readonly ConnectorOperationalContext operationalContext; + /// /// Builds up the maps of supported operations and calls. /// - public LocalConnectorFacadeImpl(LocalConnectorInfoImpl connectorInfo, - APIConfigurationImpl apiConfiguration) + public LocalConnectorFacadeImpl(LocalConnectorInfoImpl connectorInfo, APIConfigurationImpl apiConfiguration) : base(apiConfiguration) { this.connectorInfo = connectorInfo; + if (connectorInfo.ConfigurationStateless && !connectorInfo.ConnectorPoolingSupported) + { + operationalContext = null; + } + else + { + operationalContext = new ConnectorOperationalContext(connectorInfo, GetAPIConfiguration()); + } + } + + public LocalConnectorFacadeImpl(LocalConnectorInfoImpl connectorInfo, string configuration) + : base(configuration, connectorInfo) + { + this.connectorInfo = connectorInfo; + if (connectorInfo.ConfigurationStateless && !connectorInfo.ConnectorPoolingSupported) + { + operationalContext = null; + } + else + { + operationalContext = new ConnectorOperationalContext(connectorInfo, GetAPIConfiguration()); + } + } + + public void Dispose() + { + if (null != operationalContext) + { + operationalContext.Dispose(); + } + } + + protected internal ConnectorOperationalContext OperationalContext + { + get + { + if (null == operationalContext) + { + return new ConnectorOperationalContext(connectorInfo, GetAPIConfiguration()); + } + return operationalContext; + } } // ======================================================================= @@ -717,13 +850,9 @@ protected override APIOperation GetOperationImplementation(SafeType.Get()]; - ConnectorOperationalContext context = - new ConnectorOperationalContext(connectorInfo, - GetAPIConfiguration(), - GetPool()); ConnectorAPIOperationRunnerProxy handler = - new ConnectorAPIOperationRunnerProxy(context, constructor); + new ConnectorAPIOperationRunnerProxy(OperationalContext, constructor); proxy = new GetImpl((SearchApiOp)NewAPIOperationProxy(SafeType.Get(), handler)); } @@ -731,27 +860,19 @@ protected override APIOperation GetOperationImplementation(SafeType GetPool() - { - return ConnectorPoolManager.GetPool(GetAPIConfiguration(), connectorInfo); - } } #endregion @@ -1047,7 +1168,7 @@ private PooledObject BorrowObjectNoTest() else if (_activeObjects.Count < _config.MaxObjects) { pooledConn = - new PooledObject(_handler.NewObject()); + new PooledObject(_handler.MakeObject()); } //if there's an object available, return it @@ -1096,7 +1217,14 @@ public void Shutdown() //if there are any active objects still //going, leave them alone so they can return //gracefully - EvictIdleObjects(); + try + { + EvictIdleObjects(); + } + finally + { + _handler.Shutdown(); + } //wake anyone up who was waiting on an object Monitor.PulseAll(LOCK); } @@ -1194,9 +1322,54 @@ private void DisposeNoException(T obj) #region ObjectPoolHandler public interface ObjectPoolHandler where T : class { - T NewObject(); + /// + /// Validates, copies and updates the original + /// {@code ObjectPoolConfiguration}. + ///

+ /// This class can validate and if necessary it changes the {@code original} + /// configuration. + ///

+ /// + /// custom configured instance. + /// new instance of the {@code original} config. + ObjectPoolConfiguration Validate(ObjectPoolConfiguration original); + + /// + /// Makes a new instance of the pooled object. + ///

+ /// This method is called whenever a new instance is needed. + ///

+ /// new instance of T. + T MakeObject(); // T NewObject(); + + /// + /// Tests the borrowed object. + ///

+ /// This method is invoked on head instances to make sure they can be + /// borrowed from the pool. + ///

+ /// + /// the pooled object. void TestObject(T obj); + + /// + /// Disposes the object. + ///

+ /// This method is invoked on every instance when it is being "dropped" from + /// the pool (whether due to the response from , + /// or for reasons specific to the pool implementation.) + ///

+ /// + /// The "dropped" object. void DisposeObject(T obj); + + /// + /// Releases any allocated resources. + ///

+ /// Existing active objects will remain alive and be allowed to shutdown + /// gracefully, but no more objects will be allocated. + ///

+ void Shutdown(); } #endregion } \ No newline at end of file diff --git a/FrameworkInternal/ApiLocalOperations.cs b/FrameworkInternal/ApiLocalOperations.cs index 651e19d2..069c9c5b 100644 --- a/FrameworkInternal/ApiLocalOperations.cs +++ b/FrameworkInternal/ApiLocalOperations.cs @@ -19,11 +19,11 @@ * enclosed by brackets [] replaced by your own identifying information: * "Portions Copyrighted [year] [name of copyright owner]" * ==================== + * Portions Copyrighted 2014 ForgeRock AS. */ using System; using Org.IdentityConnectors.Common; -using Org.IdentityConnectors.Common.Pooling; using Org.IdentityConnectors.Common.Proxy; using Org.IdentityConnectors.Common.Script; using Org.IdentityConnectors.Common.Security; @@ -38,6 +38,8 @@ using System.Collections.Generic; using System.Linq; using Org.IdentityConnectors.Framework.Api; +using System.Text; +using System.Diagnostics; namespace Org.IdentityConnectors.Framework.Impl.Api.Local.Operations { @@ -168,7 +170,7 @@ public Object Invoke(Object proxy, MethodInfo method, object[] args) } object ret = null; Connector connector = null; - ObjectPool pool = _context.GetPool(); + ObjectPool pool = _context.Pool; // get the connector class.. SafeType connectorClazz = _context.GetConnectorClass(); try @@ -218,7 +220,7 @@ public Object Invoke(Object proxy, MethodInfo method, object[] args) } catch (Exception e) { - //don't let pool exceptions propogate or mask + //don't let pool exceptions propagate or mask //other exceptions. do log it though. TraceUtil.TraceException(null, e); } @@ -255,27 +257,65 @@ public Object Invoke(Object proxy, MethodInfo method, object[] args) ///
public class ConnectorOperationalContext : OperationalContext { - private readonly ObjectPool _pool; + /// + /// Pool Key for Connectors + /// + private ConnectorPoolManager.ConnectorPoolKey connectorPoolKey; - public ConnectorOperationalContext(LocalConnectorInfoImpl connectorInfo, - APIConfigurationImpl apiConfiguration, - ObjectPool pool) + public ConnectorOperationalContext(LocalConnectorInfoImpl connectorInfo, APIConfigurationImpl apiConfiguration) : base(connectorInfo, apiConfiguration) { - _pool = pool; } - public ObjectPool GetPool() + public ObjectPool Pool { - return _pool; - } + get + { + if (apiConfiguration.IsConnectorPoolingSupported) + { + if (null == connectorPoolKey) + { + Pair> pool = ConnectorPoolManager.GetPool(apiConfiguration, connectorInfo); + connectorPoolKey = pool.First; + return pool.Second; + } + else + { + ObjectPool pool = ConnectorPoolManager.GetPool(connectorPoolKey); + if (null == pool) + { + // + Pair> poolPair = ConnectorPoolManager.GetPool(apiConfiguration, connectorInfo); + + connectorPoolKey = poolPair.First; + pool = poolPair.Second; + } + return pool; + } + } + else + { + return null; + } + } + } public SafeType GetConnectorClass() { return GetConnectorInfo().ConnectorClass; } + public override void Dispose() + { + base.Dispose(); + if (null != connectorPoolKey) + { + ConnectorPoolManager.Dispose(connectorPoolKey); + connectorPoolKey = null; + } + } + } #endregion @@ -299,6 +339,10 @@ public AuthenticationImpl(ConnectorOperationalContext context, public Uid Authenticate(ObjectClass objectClass, String username, GuardedString password, OperationOptions options) { Assertions.NullCheck(objectClass, "objectClass"); + if (ObjectClass.ALL.Equals(objectClass)) + { + throw new System.NotSupportedException("Operation is not allowed on __ALL__ object class"); + } Assertions.NullCheck(username, "username"); Assertions.NullCheck(password, "password"); //convert null into empty @@ -330,6 +374,10 @@ public ResolveUsernameImpl(ConnectorOperationalContext context, public Uid ResolveUsername(ObjectClass objectClass, String username, OperationOptions options) { Assertions.NullCheck(objectClass, "objectClass"); + if (ObjectClass.ALL.Equals(objectClass)) + { + throw new System.NotSupportedException("Operation is not allowed on __ALL__ object class"); + } Assertions.NullCheck(username, "username"); //convert null into empty if (options == null) @@ -358,34 +406,40 @@ public CreateImpl(ConnectorOperationalContext context, /// Calls the create method on the Connector side. ///
/// - public Uid Create(ObjectClass oclass, ICollection attributes, OperationOptions options) + public Uid Create(ObjectClass objectClass, ICollection createAttributes, OperationOptions options) { - Assertions.NullCheck(oclass, "oclass"); - Assertions.NullCheck(attributes, "attributes"); + Assertions.NullCheck(objectClass, "objectClass"); + if (ObjectClass.ALL.Equals(objectClass)) + { + throw new System.NotSupportedException("Operation is not allowed on __ALL__ object class"); + } + Assertions.NullCheck(createAttributes, "createAttributes"); + // check to make sure there's not a uid.. + if (ConnectorAttributeUtil.GetUidAttribute(createAttributes) != null) + { + throw new InvalidAttributeValueException("Parameter 'createAttributes' contains a uid."); + } //convert null into empty if (options == null) { options = new OperationOptionsBuilder().Build(); } + // validate input.. HashSet dups = new HashSet(); - foreach (ConnectorAttribute attr in attributes) + foreach (ConnectorAttribute attr in createAttributes) { if (dups.Contains(attr.Name)) { - throw new ArgumentException("Duplicate attribute name exists: " + attr.Name); + throw new InvalidAttributeValueException("Duplicate attribute name exists: " + attr.Name); } dups.Add(attr.Name); } - if (oclass == null) - { - throw new ArgumentException("Required attribute ObjectClass not found!"); - } Connector connector = GetConnector(); - ObjectNormalizerFacade normalizer = GetNormalizer(oclass); + ObjectNormalizerFacade normalizer = GetNormalizer(objectClass); ICollection normalizedAttributes = - normalizer.NormalizeAttributes(attributes); + normalizer.NormalizeAttributes(createAttributes); // create the object.. - Uid ret = ((CreateOp)connector).Create(oclass, attributes, options); + Uid ret = ((CreateOp)connector).Create(objectClass, normalizedAttributes, options); return (Uid)normalizer.NormalizeAttribute(ret); } } @@ -407,9 +461,13 @@ public DeleteImpl(ConnectorOperationalContext context, /// Calls the delete method on the Connector side. /// /// - public void Delete(ObjectClass objClass, Uid uid, OperationOptions options) + public void Delete(ObjectClass objectClass, Uid uid, OperationOptions options) { - Assertions.NullCheck(objClass, "objClass"); + Assertions.NullCheck(objectClass, "objectClass"); + if (ObjectClass.ALL.Equals(objectClass)) + { + throw new System.NotSupportedException("Operation is not allowed on __ALL__ object class"); + } Assertions.NullCheck(uid, "uid"); //convert null into empty if (options == null) @@ -417,9 +475,9 @@ public void Delete(ObjectClass objClass, Uid uid, OperationOptions options) options = new OperationOptionsBuilder().Build(); } Connector connector = GetConnector(); - ObjectNormalizerFacade normalizer = GetNormalizer(objClass); + ObjectNormalizerFacade normalizer = GetNormalizer(objectClass); // delete the object.. - ((DeleteOp)connector).Delete(objClass, + ((DeleteOp)connector).Delete(objectClass, (Uid)normalizer.NormalizeAttribute(uid), options); } @@ -505,16 +563,25 @@ public SearchAttributesToGetResultsHandler( this._handler = handler; } - public bool Handle(ConnectorObject obj) + public ResultsHandler ResultsHandler { - // clone the object and reduce the attributes only the set of - // attributes. - return _handler(ReduceToAttrsToGet(obj)); + get + { + return new ResultsHandler() + { + Handle = obj => + { + // clone the object and reduce the attributes only the set of + // attributes. + return _handler.Handle(ReduceToAttrsToGet(obj)); + } + }; + } } } #endregion - #region SearchAttributesToGetResultsHandler + #region SyncAttributesToGetResultsHandler public sealed class SyncAttributesToGetResultsHandler : AttributesToGetResultsHandler { @@ -534,17 +601,26 @@ public SyncAttributesToGetResultsHandler( this._handler = handler; } - public bool Handle(SyncDelta delta) + public SyncResultsHandler SyncResultsHandler { - SyncDeltaBuilder bld = new SyncDeltaBuilder(); - bld.Uid = delta.Uid; - bld.Token = delta.Token; - bld.DeltaType = delta.DeltaType; - if (delta.Object != null) + get { - bld.Object = ReduceToAttrsToGet(delta.Object); + return new SyncResultsHandler() + { + Handle = delta => + { + SyncDeltaBuilder bld = new SyncDeltaBuilder(); + bld.Uid = delta.Uid; + bld.Token = delta.Token; + bld.DeltaType = delta.DeltaType; + if (delta.Object != null) + { + bld.Object = ReduceToAttrsToGet(delta.Object); + } + return _handler.Handle(bld.Build()); + } + }; } - return _handler(bld.Build()); } } #endregion @@ -555,7 +631,7 @@ public sealed class DuplicateFilteringResultsHandler // ======================================================================= // Fields // ======================================================================= - private readonly ResultsHandler _handler; + private readonly SearchResultsHandler _handler; private readonly HashSet _visitedUIDs = new HashSet(); private bool _stillHandling; @@ -567,7 +643,7 @@ public sealed class DuplicateFilteringResultsHandler /// Filter chain for producers. /// /// Producer to filter. - public DuplicateFilteringResultsHandler(ResultsHandler handler) + public DuplicateFilteringResultsHandler(SearchResultsHandler handler) { // there must be a producer.. if (handler == null) @@ -577,18 +653,33 @@ public DuplicateFilteringResultsHandler(ResultsHandler handler) this._handler = handler; } - public bool Handle(ConnectorObject obj) + public SearchResultsHandler ResultsHandler { - String uid = - obj.Uid.GetUidValue(); - if (!_visitedUIDs.Add(uid)) + get { - //we've already seen this - don't pass it - //throw - return true; + return new SearchResultsHandler() + { + Handle = obj => + { + String uid = + obj.Uid.GetUidValue(); + if (!_visitedUIDs.Add(uid)) + { + //we've already seen this - don't pass it + //throw + return true; + } + _stillHandling = _handler.Handle(obj); + return _stillHandling; + }, + HandleResult = result => + { + _handler.HandleResult(result); + } + + }; } - _stillHandling = _handler(obj); - return _stillHandling; + } public bool IsStillHandling @@ -627,25 +718,34 @@ public FilteredResultsHandler(ResultsHandler handler, Filter filter) } this.handler = handler; // use a default pass through filter.. - this.filter = filter == null ? new PassThruFilter() : filter; + this.filter = filter == null ? new PassThroughFilter() : filter; } - public bool Handle(ConnectorObject obj) + public ResultsHandler ResultsHandler { - if (filter.Accept(obj)) - { - return handler(obj); - } - else + get { - return true; + return new ResultsHandler + { + Handle = obj => + { + if (filter.Accept(obj)) + { + return handler.Handle(obj); + } + else + { + return true; + } + } + }; } } /// - /// Use a pass thru filter to use if a null filter is provided. + /// Use a pass through filter to use if a null filter is provided. /// - class PassThruFilter : Filter + class PassThroughFilter : Filter { public bool Accept(ConnectorObject obj) { @@ -667,10 +767,19 @@ public class GetImpl : GetApiOp private class ResultAdapter { private IList _list = new List(); - public bool Handle(ConnectorObject obj) + public ResultsHandler ResultsHandler { - _list.Add(obj); - return false; + get + { + return new ResultsHandler() + { + Handle = obj => + { + _list.Add(obj); + return false; + } + }; + } } public ConnectorObject GetResult() { @@ -683,9 +792,13 @@ public GetImpl(SearchApiOp search) this.op = search; } - public ConnectorObject GetObject(ObjectClass objClass, Uid uid, OperationOptions options) + public ConnectorObject GetObject(ObjectClass objectClass, Uid uid, OperationOptions options) { - Assertions.NullCheck(objClass, "objClass"); + Assertions.NullCheck(objectClass, "objectClass"); + if (ObjectClass.ALL.Equals(objectClass)) + { + throw new System.NotSupportedException("Operation is not allowed on __ALL__ object class"); + } Assertions.NullCheck(uid, "uid"); //convert null into empty if (options == null) @@ -694,7 +807,7 @@ public ConnectorObject GetObject(ObjectClass objClass, Uid uid, OperationOptions } Filter filter = FilterBuilder.EqualTo(uid); ResultAdapter adapter = new ResultAdapter(); - op.Search(objClass, filter, new ResultsHandler(adapter.Handle), options); + op.Search(objectClass, filter, adapter.ResultsHandler, options); return adapter.GetResult(); } } @@ -703,44 +816,91 @@ public ConnectorObject GetObject(ObjectClass objClass, Uid uid, OperationOptions #region OperationalContext /// /// NOTE: internal class, public only for unit tests - /// OperationalContext - base class for operations that do not - /// require a connection. + /// OperationalContext - base class for operations that do not require a + /// connector instance. /// public class OperationalContext { + /// /// ConnectorInfo /// - private readonly LocalConnectorInfoImpl connectorInfo; + protected readonly LocalConnectorInfoImpl connectorInfo; /// /// Contains the . /// - private readonly APIConfigurationImpl apiConfiguration; + protected readonly APIConfigurationImpl apiConfiguration; + private volatile Configuration configuration; - public OperationalContext(LocalConnectorInfoImpl connectorInfo, - APIConfigurationImpl apiConfiguration) + /// + /// Creates a new OperationalContext but it does not initiates the + /// Configuration because the method must do it + /// when it's called from a block where the classloader of the Thread is set + /// to Connector. + /// + /// + /// + public OperationalContext(LocalConnectorInfoImpl connectorInfo, APIConfigurationImpl apiConfiguration) { this.connectorInfo = connectorInfo; this.apiConfiguration = apiConfiguration; } + /* + * This method must be called when the Bundle ClassLoader is the Thread + * Context ClassLoader. + */ public Configuration GetConfiguration() { - return CSharpClassProperties.CreateBean((ConfigurationPropertiesImpl)this.apiConfiguration.ConfigurationProperties, - connectorInfo.ConnectorConfigurationClass); + + if (null == configuration) + { + lock (this) + { + if (null == configuration) + { + this.configuration = CSharpClassProperties.CreateBean((ConfigurationPropertiesImpl)this.apiConfiguration.ConfigurationProperties, + connectorInfo.ConnectorConfigurationClass); + } + } + } + return configuration; } protected LocalConnectorInfoImpl GetConnectorInfo() { + return connectorInfo; - } + } public ResultsHandlerConfiguration getResultsHandlerConfiguration() { - return apiConfiguration.ResultsHandlerConfiguration; + return new ResultsHandlerConfiguration(apiConfiguration.ResultsHandlerConfiguration); + } + + public virtual void Dispose() + { + if (configuration is StatefulConfiguration) + { + // dispose it not supposed to throw, but just in case, + // catch the exception and log it so we know about it + // but don't let the exception prevent additional + // cleanup that needs to happen + try + { + StatefulConfiguration config = (StatefulConfiguration)configuration; + configuration = null; + config.Release(); + } + catch (Exception e) + { + // log this though + Trace.TraceWarning(e.Message); + } + } } } #endregion @@ -760,13 +920,21 @@ public NormalizingResultsHandler(ResultsHandler target, _normalizer = normalizer; } - - public bool Handle(ConnectorObject obj) + public ResultsHandler ResultsHandler { - ConnectorObject normalized = _normalizer.NormalizeObject(obj); - return _target(normalized); - } + get + { + return new ResultsHandler() + { + Handle = obj => + { + ConnectorObject normalized = _normalizer.NormalizeObject(obj); + return _target.Handle(normalized); + } + }; + } + } } #endregion @@ -785,11 +953,102 @@ public NormalizingSyncResultsHandler(SyncResultsHandler target, _normalizer = normalizer; } + public SyncResultsHandler SyncResultsHandler + { + get + { + return new SyncResultsHandler() + { + + Handle = delta => + { + SyncDelta normalized = _normalizer.NormalizeSyncDelta(delta); + return _target.Handle(normalized); + } + }; + } + } + } + #endregion + + #region CaseNormalizer + public sealed class CaseNormalizer : AttributeNormalizer + { + public static ObjectNormalizerFacade CreateCaseNormalizerFacade(ObjectClass oclass) + { + return new ObjectNormalizerFacade(oclass, new CaseNormalizer()); + } + + public ConnectorAttribute NormalizeAttribute(ObjectClass oclass, ConnectorAttribute attribute) + { + // Trace.TraceInformation("Starting CaseNormalizer.NormalizeAttribute({0}, {1})", oclass, attribute.GetDetails()); + ConnectorAttribute rv = attribute; + bool converted = false; + + IList values = rv.Value; + if (values != null) + { + IList newValues = new List(); + + foreach (object value in values) + { + if (value is string) + { + newValues.Add(((string)value).ToUpper()); + converted = true; + } + else + { + newValues.Add(value); + } + } + + if (converted) // only when something changed; to save a few cpu cycles... + { + rv = ConnectorAttributeBuilder.Build(attribute.Name, newValues); + } + } + + // Trace.TraceInformation("Finishing CaseNormalizer.NormalizeAttribute, converted = {0}, return value = {1}", converted, rv.GetDetails()); + return rv; + } + } + #endregion + + #region NormalizingFilter + /// + /// Proxy the filter to filter based on object normalized version. + /// Similar to ObjectNormalizerFacade.NormalizeFilter, + /// but this one DOES NOT expect that it gets object to be accepted/rejected + /// in normalized form - it normalizes the object just before deciding. + /// Currently used for case insensitive filtering. + /// + public sealed class NormalizingFilter : ExternallyChainedFilter + { + private readonly ObjectNormalizerFacade _normalizationFacade; - public bool Handle(SyncDelta delta) + public NormalizingFilter(Filter filter, ObjectNormalizerFacade facade) + : base(facade.NormalizeFilter(filter)) { - SyncDelta normalized = _normalizer.NormalizeSyncDelta(delta); - return _target(normalized); + _normalizationFacade = facade; + } + + /// + /// Return the decision based on normalized version of the object. + /// + /// + public override bool Accept(ConnectorObject obj) + { + bool result = Filter.Accept(_normalizationFacade.NormalizeObject(obj)); + // Trace.TraceInformation("NormalizingFilter.Accept returns {0} for {1}", result, obj.GetAttributeByName("__NAME__")); + return result; + } + + public override string ToString() + { + StringBuilder bld = new StringBuilder(); + bld.Append("NORMALIZE USING ").Append(_normalizationFacade).Append(": ").Append(Filter); + return bld.ToString(); } } #endregion @@ -1050,6 +1309,10 @@ public Object RunScriptOnConnector(ScriptContext request, private Assembly[] BuildReferenceList(Assembly assembly) { List list = new List(); + foreach (var assemblyName in assembly.GetReferencedAssemblies()) + { + list.Add(Assembly.Load(assemblyName)); + } // Just add the connector itself. list.Add(assembly); return list.ToArray(); @@ -1101,9 +1364,13 @@ public SearchImpl(ConnectorOperationalContext context, /// . /// /// - public void Search(ObjectClass oclass, Filter originalFilter, ResultsHandler handler, OperationOptions options) + public SearchResult Search(ObjectClass objectClass, Filter originalFilter, ResultsHandler handler, OperationOptions options) { - Assertions.NullCheck(oclass, "oclass"); + Assertions.NullCheck(objectClass, "objectClass"); + if (ObjectClass.ALL.Equals(objectClass)) + { + throw new System.NotSupportedException("Operation is not allowed on __ALL__ object class"); + } Assertions.NullCheck(handler, "handler"); //convert null into empty if (options == null) @@ -1114,21 +1381,28 @@ public void Search(ObjectClass oclass, Filter originalFilter, ResultsHandler han ResultsHandlerConfiguration hdlCfg = null != GetOperationalContext() ? GetOperationalContext().getResultsHandlerConfiguration() : new ResultsHandlerConfiguration(); ResultsHandler handlerChain = handler; - Filter finalFilter = originalFilter; + Filter actualFilter = originalFilter; // actualFilter is used for chaining filters - it points to the filter where new filters should be chained + + if (hdlCfg.EnableFilteredResultsHandler && hdlCfg.EnableCaseInsensitiveFilter && actualFilter != null) + { + Trace.TraceInformation("Creating case insensitive filter"); + ObjectNormalizerFacade normalizer = CaseNormalizer.CreateCaseNormalizerFacade(objectClass); + actualFilter = new NormalizingFilter(actualFilter, normalizer); + } if (hdlCfg.EnableNormalizingResultsHandler) { - ObjectNormalizerFacade normalizer = GetNormalizer(oclass); + ObjectNormalizerFacade normalizer = GetNormalizer(objectClass); //chain a normalizing handler (must come before //filter handler) - ResultsHandler normalizingHandler = new NormalizingResultsHandler(handler, normalizer).Handle; - Filter normalizedFilter = normalizer.NormalizeFilter(originalFilter); + ResultsHandler normalizingHandler = new NormalizingResultsHandler(handler, normalizer).ResultsHandler; // chain a filter handler.. if (hdlCfg.EnableFilteredResultsHandler) { // chain a filter handler.. - handlerChain = new FilteredResultsHandler(normalizingHandler, normalizedFilter).Handle; - finalFilter = normalizedFilter; + Filter normalizedFilter = normalizer.NormalizeFilter(actualFilter); + handlerChain = new FilteredResultsHandler(normalizingHandler, normalizedFilter).ResultsHandler; + actualFilter = normalizedFilter; } else { @@ -1138,8 +1412,7 @@ public void Search(ObjectClass oclass, Filter originalFilter, ResultsHandler han else if (hdlCfg.EnableFilteredResultsHandler) { // chain a filter handler.. - handlerChain = new FilteredResultsHandler(handlerChain, originalFilter).Handle; - finalFilter = originalFilter; + handlerChain = new FilteredResultsHandler(handlerChain, actualFilter).ResultsHandler; } //get the IList interface that this type implements @@ -1161,9 +1434,23 @@ public void Search(ObjectClass oclass, Filter originalFilter, ResultsHandler han if (attrsToGet != null && attrsToGet.Length > 0 && hdlCfg.EnableAttributesToGetSearchResultsHandler) { handlerChain = new SearchAttributesToGetResultsHandler( - handlerChain, attrsToGet).Handle; + handlerChain, attrsToGet).ResultsHandler; } - searcher.RawSearch(GetConnector(), oclass, finalFilter, handlerChain, options); + SearchResult result = null; + SearchResultsHandler innreHandler = new SearchResultsHandler() + { + Handle = obj => + { + return handlerChain.Handle(obj); + }, + + HandleResult = obj => + { + result = obj; + } + }; + searcher.RawSearch(GetConnector(), objectClass, actualFilter, innreHandler, options); + return result; } } #endregion @@ -1187,7 +1474,7 @@ internal interface RawSearcher void RawSearch(Object search, ObjectClass oclass, Filter filter, - ResultsHandler handler, + SearchResultsHandler handler, OperationOptions options); } #endregion @@ -1198,7 +1485,7 @@ internal class RawSearcherImpl : RawSearcher where T : class public void RawSearch(Object search, ObjectClass oclass, Filter filter, - ResultsHandler handler, + SearchResultsHandler handler, OperationOptions options) { RawSearch((SearchOp)search, oclass, filter, handler, options); @@ -1220,10 +1507,9 @@ public void RawSearch(Object search, public static void RawSearch(SearchOp search, ObjectClass oclass, Filter filter, - ResultsHandler handler, + SearchResultsHandler handler, OperationOptions options) { - FilterTranslator translator = search.CreateFilterTranslator(oclass, options); IList queries = @@ -1242,7 +1528,7 @@ public static void RawSearch(SearchOp search, if (eliminateDups) { dups = new DuplicateFilteringResultsHandler(handler); - handler = dups.Handle; + handler = dups.ResultsHandler; } foreach (T query in queries) { @@ -1274,12 +1560,10 @@ public SyncImpl(ConnectorOperationalContext context, { } - public void Sync(ObjectClass objClass, SyncToken token, - SyncResultsHandler handler, - OperationOptions options) + public SyncToken Sync(ObjectClass objectClass, SyncToken token, SyncResultsHandler handler, OperationOptions options) { //token is allowed to be null, objClass and handler must not be null - Assertions.NullCheck(objClass, "objClass"); + Assertions.NullCheck(objectClass, "objectClass"); Assertions.NullCheck(handler, "handler"); //convert null into empty if (options == null) @@ -1290,15 +1574,29 @@ public void Sync(ObjectClass objClass, SyncToken token, string[] attrsToGet = options.AttributesToGet; if (attrsToGet != null && attrsToGet.Length > 0) { - handler = new SyncAttributesToGetResultsHandler( - handler, attrsToGet).Handle; + handler = new SyncAttributesToGetResultsHandler(handler, attrsToGet).SyncResultsHandler; } //chain a normalizing results handler - ObjectNormalizerFacade normalizer = - GetNormalizer(objClass); - handler = new NormalizingSyncResultsHandler(handler, normalizer).Handle; - ((SyncOp)GetConnector()).Sync(objClass, token, handler, options); + if (GetConnector() is AttributeNormalizer) + { + handler = new NormalizingSyncResultsHandler(handler, GetNormalizer(objectClass)).SyncResultsHandler; + } + + SyncToken result = null; + ((SyncOp)GetConnector()).Sync(objectClass, token, new SyncTokenResultsHandler() + { + Handle = delta => + { + return handler.Handle(delta); + }, + HandleResult = obj => + { + result = obj; + } + }, options); + return result; } + public SyncToken GetLatestSyncToken(ObjectClass objectClass) { return ((SyncOp)GetConnector()).GetLatestSyncToken(objectClass); @@ -1387,13 +1685,13 @@ public Uid Update(ObjectClass objclass, return (Uid)normalizer.NormalizeAttribute(ret); } - public Uid AddAttributeValues(ObjectClass objclass, + public Uid AddAttributeValues(ObjectClass objectClass, Uid uid, ICollection valuesToAdd, OperationOptions options) { // validate all the parameters.. - ValidateInput(objclass, uid, valuesToAdd, true); + ValidateInput(objectClass, uid, valuesToAdd, true); //cast null as empty if (options == null) { @@ -1401,7 +1699,7 @@ public Uid AddAttributeValues(ObjectClass objclass, } ObjectNormalizerFacade normalizer = - GetNormalizer(objclass); + GetNormalizer(objectClass); uid = (Uid)normalizer.NormalizeAttribute(uid); valuesToAdd = normalizer.NormalizeAttributes(valuesToAdd); @@ -1411,24 +1709,24 @@ public Uid AddAttributeValues(ObjectClass objclass, { UpdateAttributeValuesOp valueOp = (UpdateAttributeValuesOp)op; - ret = valueOp.AddAttributeValues(objclass, uid, valuesToAdd, options); + ret = valueOp.AddAttributeValues(objectClass, uid, valuesToAdd, options); } else { ICollection replaceAttributes = - FetchAndMerge(objclass, uid, valuesToAdd, true, options); - ret = op.Update(objclass, uid, replaceAttributes, options); + FetchAndMerge(objectClass, uid, valuesToAdd, true, options); + ret = op.Update(objectClass, uid, replaceAttributes, options); } return (Uid)normalizer.NormalizeAttribute(ret); } - public Uid RemoveAttributeValues(ObjectClass objclass, + public Uid RemoveAttributeValues(ObjectClass objectClass, Uid uid, ICollection valuesToRemove, OperationOptions options) { // validate all the parameters.. - ValidateInput(objclass, uid, valuesToRemove, true); + ValidateInput(objectClass, uid, valuesToRemove, true); //cast null as empty if (options == null) { @@ -1436,7 +1734,7 @@ public Uid RemoveAttributeValues(ObjectClass objclass, } ObjectNormalizerFacade normalizer = - GetNormalizer(objclass); + GetNormalizer(objectClass); uid = (Uid)normalizer.NormalizeAttribute(uid); valuesToRemove = normalizer.NormalizeAttributes(valuesToRemove); @@ -1446,13 +1744,13 @@ public Uid RemoveAttributeValues(ObjectClass objclass, { UpdateAttributeValuesOp valueOp = (UpdateAttributeValuesOp)op; - ret = valueOp.RemoveAttributeValues(objclass, uid, valuesToRemove, options); + ret = valueOp.RemoveAttributeValues(objectClass, uid, valuesToRemove, options); } else { ICollection replaceAttributes = - FetchAndMerge(objclass, uid, valuesToRemove, false, options); - ret = op.Update(objclass, uid, replaceAttributes, options); + FetchAndMerge(objectClass, uid, valuesToRemove, false, options); + ret = op.Update(objectClass, uid, replaceAttributes, options); } return (Uid)normalizer.NormalizeAttribute(ret); } @@ -1570,15 +1868,19 @@ private ConnectorObject GetConnectorObject(ObjectClass oclass, Uid uid, Operatio /// /// Makes things easier if you can trust the input. /// - public static void ValidateInput(ObjectClass objclass, + public static void ValidateInput(ObjectClass objectClass, Uid uid, - ICollection attrs, bool isDelta) + ICollection replaceAttributes, bool isDelta) { Assertions.NullCheck(uid, "uid"); - Assertions.NullCheck(objclass, "objclass"); - Assertions.NullCheck(attrs, "attrs"); + Assertions.NullCheck(objectClass, "objectClass"); + if (ObjectClass.ALL.Equals(objectClass)) + { + throw new System.NotSupportedException("Operation is not allowed on __ALL__ object class"); + } + Assertions.NullCheck(replaceAttributes, "replaceAttributes"); // check to make sure there's not a uid.. - if (ConnectorAttributeUtil.GetUidAttribute(attrs) != null) + if (ConnectorAttributeUtil.GetUidAttribute(replaceAttributes) != null) { throw new ArgumentException( "Parameter 'attrs' contains a uid."); @@ -1586,7 +1888,7 @@ public static void ValidateInput(ObjectClass objclass, // check for things only valid during ADD/DELETE if (isDelta) { - foreach (ConnectorAttribute attr in attrs) + foreach (ConnectorAttribute attr in replaceAttributes) { Assertions.NullCheck(attr, "attr"); // make sure that none of the values are null.. diff --git a/FrameworkInternal/ApiRemote.cs b/FrameworkInternal/ApiRemote.cs index d06762ed..aa37d8ff 100644 --- a/FrameworkInternal/ApiRemote.cs +++ b/FrameworkInternal/ApiRemote.cs @@ -19,9 +19,10 @@ * enclosed by brackets [] replaced by your own identifying information: * "Portions Copyrighted [year] [name of copyright owner]" * ==================== - * Portions Copyrighted 2012 ForgeRock AS + * Portions Copyrighted 2012-2014 ForgeRock AS. */ using System; +using System.Collections; using System.Collections.Generic; using System.IO; using System.Net; @@ -31,21 +32,17 @@ using System.Net.Security; using System.Net.Sockets; using System.Security.Cryptography.X509Certificates; -using System.Threading; using Org.IdentityConnectors.Common; using Org.IdentityConnectors.Common.Proxy; using Org.IdentityConnectors.Framework.Api; using Org.IdentityConnectors.Framework.Api.Operations; using Org.IdentityConnectors.Framework.Common.Exceptions; -using Org.IdentityConnectors.Framework.Common.Objects; -using Org.IdentityConnectors.Framework.Common.Objects.Filters; using Org.IdentityConnectors.Framework.Common.Serializer; -using Org.IdentityConnectors.Framework.Impl.Api; -using Org.IdentityConnectors.Framework.Impl.Api.Local.Operations; using Org.IdentityConnectors.Framework.Impl.Api.Remote.Messages; -using System.Diagnostics; + namespace Org.IdentityConnectors.Framework.Impl.Api.Remote { + #region RemoteFrameworkConnection public class RemoteFrameworkConnection : IDisposable { private TcpClient _socket; @@ -152,7 +149,9 @@ public object ReadObject() return _decoder.ReadObject(); } } + #endregion + #region RemoteConnectorInfoImpl /// /// internal class, public only for unit tests /// @@ -169,7 +168,9 @@ public RemoteConnectorInfoImpl() public RemoteFrameworkConnectionInfo RemoteConnectionInfo { get; set; } } + #endregion + #region RemoteConnectorInfoManagerImpl public class RemoteConnectorInfoManagerImpl : ConnectorInfoManager { private IList _connectorInfo; @@ -247,41 +248,75 @@ public IList ConnectorInfos } } } + #endregion - internal class RemoteConnectorFacadeImpl : AbstractConnectorFacade + #region RemoteConnectorFacadeImpl + /// + /// Implements all the methods of the facade + /// + public class RemoteConnectorFacadeImpl : AbstractConnectorFacade { + + internal readonly string remoteConnectorFacadeKey; + /// /// Builds up the maps of supported operations and calls. /// public RemoteConnectorFacadeImpl(APIConfigurationImpl configuration) - : base(configuration) + : base(GenerateRemoteConnectorFacadeKey(configuration), configuration.ConnectorInfo) + { + // Restore the original configuration settings + GetAPIConfiguration().ProducerBufferSize = configuration.ProducerBufferSize; + GetAPIConfiguration().TimeoutMap = configuration.TimeoutMap; + remoteConnectorFacadeKey = ConnectorFacadeKey; + } + + public RemoteConnectorFacadeImpl(RemoteConnectorInfoImpl connectorInfo, string configuration) + : base(configuration, connectorInfo) + { + remoteConnectorFacadeKey = GenerateRemoteConnectorFacadeKey(GetAPIConfiguration()); + } + + private static string GenerateRemoteConnectorFacadeKey(APIConfigurationImpl configuration) { + APIConfigurationImpl copy = new APIConfigurationImpl(configuration); + copy.ProducerBufferSize = 0; + copy.TimeoutMap = new Dictionary, int>(); + return SerializerUtil.SerializeBase64Object(copy); } protected override APIOperation GetOperationImplementation(SafeType api) { + // add remote proxy InvocationHandler handler = new RemoteOperationInvocationHandler( - GetAPIConfiguration(), - api); + (RemoteConnectorInfoImpl)GetAPIConfiguration().ConnectorInfo, remoteConnectorFacadeKey, api); APIOperation proxy = NewAPIOperationProxy(api, handler); - // add logging.. + // now wrap the proxy in the appropriate timeout proxy + proxy = CreateTimeoutProxy(api, proxy); + // add logging proxy proxy = CreateLoggingProxy(api, proxy); + return proxy; } } + #endregion + #region RemoteOperationInvocationHandler /// /// Invocation handler for all of our operations /// public class RemoteOperationInvocationHandler : InvocationHandler { - private readonly APIConfigurationImpl _configuration; + private readonly RemoteConnectorInfoImpl _connectorInfo; + private readonly String _connectorFacadeKey; private readonly SafeType _operation; - public RemoteOperationInvocationHandler(APIConfigurationImpl configuration, + public RemoteOperationInvocationHandler(RemoteConnectorInfoImpl connectorInfo, + String connectorFacadeKey, SafeType operation) { - _configuration = configuration; + _connectorInfo = connectorInfo; + _connectorFacadeKey = connectorFacadeKey; _operation = operation; } @@ -303,13 +338,10 @@ public Object Invoke(Object proxy, MethodInfo method, Object[] args) ExtractStreamHandler(ReflectionUtil.GetParameterTypes(method), simpleMarshallArgs); //build the request object - RemoteConnectorInfoImpl connectorInfo = - (RemoteConnectorInfoImpl)_configuration.ConnectorInfo; RemoteFrameworkConnectionInfo connectionInfo = - connectorInfo.RemoteConnectionInfo; + _connectorInfo.RemoteConnectionInfo; OperationRequest request = new OperationRequest( - connectorInfo.ConnectorKey, - _configuration, + _connectorInfo.ConnectorKey, _connectorFacadeKey, _operation, method.Name, simpleMarshallArgs); @@ -426,4 +458,229 @@ private static ObjectStreamHandler ExtractStreamHandler(Type[] paramTypes, return rv; } } + #endregion + + #region RemoteWrappedException + /// + /// RemoteWrappedException wraps every exception which are received from Remote + /// Connector Server. + ///

+ /// This Exception is not allowed to use in Connectors!!! + ///

+ /// + /// + /// + /// This type of exception is not allowed to be serialise because this exception + /// represents any after deserialization. + /// + /// This code example show how to get the remote stack trace and how to use the + /// same catches to handle the exceptions regardless its origin. + /// + ///

+    /// 
+    ///  String stackTrace = null;
+    ///  try {
+    ///      try {
+    ///          facade.GetObject(ObjectClass.ACCOUNT, uid, null);
+    ///      } catch (RemoteWrappedException e) {
+    ///          stackTrace = e.StackTrace;
+    ///      }
+    ///  } catch (Throwable t) {
+    ///      
+    ///  }
+    /// 
+    /// 
+ ///
+ /// Since 1.4 + public sealed class RemoteWrappedException : ConnectorException + { + public const string FIELD_CLASS = "class"; + public const string FIELD_MESSAGE = "message"; + public const string FIELD_CAUSE = "cause"; + public const string FIELD_STACK_TRACE = "stackTrace"; + + private readonly string stackTrace; + + /// + ///
+        ///     
+        ///         {
+        ///              "class": "org.identityconnectors.framework.common.exceptions.ConnectorIOException",
+        ///              "message": "Sample Error Message",
+        ///              "cause": {
+        ///                  "class": "java.net.SocketTimeoutException",
+        ///                  "message": "Sample Error Message",
+        ///                  "cause": {
+        ///                      "class": "edu.example.CustomException",
+        ///                      "message": "Sample Error Message"
+        ///                  }
+        ///              },
+        ///              "stackTrace": "full stack trace for logging"
+        ///          }
+        ///     
+        /// 
+ ///
+ private IDictionary exception = null; + + /// + internal RemoteWrappedException(IDictionary exception) + : base((string)exception[FIELD_MESSAGE]) + { + this.exception = exception; + this.stackTrace = (string)exception[FIELD_STACK_TRACE]; + } + + public RemoteWrappedException(string throwableClass, string message, RemoteWrappedException cause, string stackTrace) + : base(message) + { + exception = new Dictionary(4); + exception[FIELD_CLASS] = Assertions.BlankChecked(throwableClass, "throwableClass"); + exception[FIELD_MESSAGE] = message; + if (null != cause) + { + exception[FIELD_CAUSE] = cause.exception; + } + if (null != stackTrace) + { + exception[FIELD_STACK_TRACE] = stackTrace; + } + this.stackTrace = stackTrace; + } + + /// + /// Gets the class name of the original exception. + /// + /// This value is constructed by {@code Exception.Type.FullName}. + /// + /// name of the original exception. + public string ExceptionClass + { + get + { + return (string)exception[FIELD_CLASS]; + } + } + + /// + /// Checks if the exception is the expected class. + /// + /// + /// the expected throwable class. + /// {@code true} if the class name are equals. + public bool Is(Type expected) + { + if (null == expected) + { + return false; + } + string className = ((string)exception[FIELD_CLASS]); + //The .NET Type.FullName property will not always yield results identical to the Java Class.getName method: + string classExpected = expected.FullName; + return classExpected.Equals(className, StringComparison.CurrentCultureIgnoreCase); + } + + /// + /// Returns the cause of original throwable or {@code null} if the cause is + /// nonexistent or unknown. (The cause is the throwable that caused the + /// original throwable to get thrown.) + /// + /// the cause of this throwable or {@code null} if the cause is + /// nonexistent or unknown. + public RemoteWrappedException Cause + { + get + { + object o = exception[FIELD_CAUSE]; + if (o is IDictionary) + { + return new RemoteWrappedException((IDictionary)o); + } + else + { + return null; + } + } + } + + public override string StackTrace + { + get + { + if (null == stackTrace) + { + return base.StackTrace; + } + else + { + return stackTrace; + } + } + } + + public string ReadStackTrace() + { + return stackTrace; + } + + /// + /// Wraps the Throwable into a RemoteWrappedException instance. + /// + /// + /// Exception to wrap or cast and return. + /// a RemoteWrappedException that either is the + /// specified exception or contains the specified exception. + public static RemoteWrappedException Wrap(Exception ex) + { + if (null == ex) + { + return null; + } + // don't bother to wrap a exception that is already a + // RemoteWrappedException. + if (ex is RemoteWrappedException) + { + return (RemoteWrappedException)ex; + } + return new RemoteWrappedException(convert(ex)); + } + + /// + /// Converts the throwable object to a new Map object that representing + /// itself. + /// + /// + /// the {@code Throwable} to be converted + /// the Map representing the throwable. + public static Dictionary convert(Exception throwable) + { + Dictionary exception = null; + if (null != throwable) + { + exception = new Dictionary(4); + //The .NET Type.FullName property will not always yield results identical to the Java Class.getName method: + exception[FIELD_CLASS] = throwable.GetType().FullName; + exception[FIELD_MESSAGE] = throwable.Message; + if (null != throwable.InnerException) + { + exception[FIELD_CAUSE] = buildCause(throwable.InnerException); + } + exception[FIELD_STACK_TRACE] = throwable.StackTrace; + } + return exception; + } + + private static IDictionary buildCause(Exception throwable) + { + IDictionary cause = new Dictionary(null != throwable.InnerException ? 3 : 2); + //The .NET Type.FullName property will not always yield results identical to the Java Class.getName method: + cause[FIELD_CLASS] = throwable.GetType().FullName; + cause[FIELD_MESSAGE] = throwable.Message; + if (null != throwable.InnerException) + { + cause[FIELD_CAUSE] = buildCause(throwable.InnerException); + } + return cause; + } + } + #endregion } \ No newline at end of file diff --git a/FrameworkInternal/ApiRemoteMessages.cs b/FrameworkInternal/ApiRemoteMessages.cs index 37a74eab..445afac6 100644 --- a/FrameworkInternal/ApiRemoteMessages.cs +++ b/FrameworkInternal/ApiRemoteMessages.cs @@ -19,6 +19,7 @@ * enclosed by brackets [] replaced by your own identifying information: * "Portions Copyrighted [year] [name of copyright owner]" * ==================== + * Portions Copyrighted 2014 ForgeRock AS. */ using System; using System.Collections.Generic; @@ -27,6 +28,7 @@ using Org.IdentityConnectors.Framework.Api.Operations; namespace Org.IdentityConnectors.Framework.Impl.Api.Remote.Messages { + #region HelloRequest /// /// internal class, public only for unit tests /// @@ -69,7 +71,9 @@ public bool isConnectorInfo() return checkInfoLevel(CONNECTOR_INFO); } } + #endregion + #region HelloResponse /// /// internal class, public only for unit tests /// @@ -149,14 +153,18 @@ public IDictionary ServerInfo return null; } } + #endregion + #region Message /// /// internal class, public only for unit tests /// public interface Message { } + #endregion + #region OperationRequest /// /// internal class, public only for unit tests /// @@ -170,7 +178,7 @@ public class OperationRequest : Message /// /// The configuration information to use. /// - private readonly APIConfigurationImpl _configuration; + private readonly String _connectorFacadeKey; /// /// The operation to perform. @@ -197,13 +205,13 @@ public class OperationRequest : Message private readonly IList _arguments; public OperationRequest(ConnectorKey key, - APIConfigurationImpl apiConfiguration, + String connectorFacadeKey, SafeType operation, string operationMethodName, IList arguments) { _connectorKey = key; - _configuration = apiConfiguration; + _connectorFacadeKey = connectorFacadeKey; _operation = operation; _operationMethodName = operationMethodName; _arguments = CollectionUtil.NewReadOnlyList(arguments); @@ -217,11 +225,11 @@ public ConnectorKey ConnectorKey } } - public APIConfigurationImpl Configuration + public String ConnectorFacadeKey { get { - return _configuration; + return _connectorFacadeKey; } } @@ -249,7 +257,9 @@ public IList Arguments } } } + #endregion + #region OperationRequestMoreData /// /// internal class, public only for unit tests /// @@ -259,7 +269,9 @@ public OperationRequestMoreData() { } } + #endregion + #region OperationRequestStopData /// /// internal class, public only for unit tests /// @@ -269,7 +281,9 @@ public OperationRequestStopData() { } } + #endregion + #region OperationResponseEnd /// /// internal class, public only for unit tests /// @@ -279,22 +293,24 @@ public OperationResponseEnd() { } } + #endregion + #region OperationResponsePart /// /// internal class, public only for unit tests /// public class OperationResponsePart : Message { - private Exception _exception; + private RemoteWrappedException _exception; private Object _result; public OperationResponsePart(Exception ex, Object result) { - _exception = ex; + _exception = RemoteWrappedException.Wrap(ex); _result = result; } - public Exception Exception + public RemoteWrappedException Exception { get { @@ -310,7 +326,9 @@ public Object Result } } } + #endregion + #region OperationResponsePause /// /// internal class, public only for unit tests /// @@ -320,7 +338,9 @@ public OperationResponsePause() { } } + #endregion + #region EchoMessage public class EchoMessage : Message { private object _object; @@ -345,4 +365,5 @@ public string ObjectXml } } } + #endregion } \ No newline at end of file diff --git a/FrameworkInternal/ExceptionUtil.cs b/FrameworkInternal/ExceptionUtil.cs index 725cfa8d..353e36e9 100644 --- a/FrameworkInternal/ExceptionUtil.cs +++ b/FrameworkInternal/ExceptionUtil.cs @@ -19,15 +19,11 @@ * enclosed by brackets [] replaced by your own identifying information: * "Portions Copyrighted [year] [name of copyright owner]" * ==================== + * Portions Copyrighted 2014 ForgeRock AS. */ using System; -using System.Collections.Generic; -using System.Linq; -using System.Text; using System.Reflection; -using System.Diagnostics; using Org.IdentityConnectors.Common; -using System.Runtime.CompilerServices; namespace Org.IdentityConnectors.Framework.Impl { diff --git a/FrameworkInternal/Resources.resx b/FrameworkInternal/Resources.resx index 489464ee..e9b34bad 100644 --- a/FrameworkInternal/Resources.resx +++ b/FrameworkInternal/Resources.resx @@ -121,387 +121,420 @@ <?xml version='1.0' encoding='UTF-8'?> -<!--=======================================================--> -<!--= =--> -<!--= DTD for Connector Objects =--> -<!--= =--> -<!--=======================================================--> - -<!--=======================================================--> -<!--= =--> -<!--= All XML Objects =--> -<!--= =--> -<!--=======================================================--> - -<!ENTITY % exceptionTypes - "AlreadyExistsException | ConfigurationException | ConnectionBrokenException - | ConnectionFailedException | ConnectorIOException | InvalidPasswordException - | UnknownUidException | InvalidCredentialException | PermissionDeniedException - | ConnectorSecurityException | OperationTimeoutException | ConnectorException - | RuntimeException | Exception | Throwable | PasswordExpiredException | IllegalArgumentException - "> - -<!ENTITY % messageTypes - "HelloRequest | HelloResponse | OperationRequest | OperationResponseEnd | - OperationResponsePart | OperationRequestMoreData | OperationRequestStopData | - OperationResponsePause | EchoMessage - "> - -<!ENTITY % filterTypes - "AndFilter | ContainsFilter | EndsWithFilter | EqualsFilter | - GreaterThanFilter | GreaterThanOrEqualFilter | LessThanFilter | - LessThanOrEqualFilter | NotFilter | OrFilter | StartsWithFilter | - ContainsAllValuesFilter - "> - -<!ENTITY % attributeTypes - "Attribute | Uid | Name"> - -<!ENTITY % primitiveTypes - "null | Array | Boolean | boolean | Character | char | Integer | - int | Long | long | Float | float | Double | double | String | - URI | File | BigDecimal | BigInteger | ByteArray | Class | - Map | List | Set | Locale | GuardedByteArray | GuardedString - "> - -<!ENTITY % xmlObject - "%primitiveTypes; | %exceptionTypes; | %messageTypes; | %filterTypes; | %attributeTypes; | -ObjectPoolConfiguration | ResultsHandlerConfiguration | ConfigurationProperty | ConfigurationProperties | -APIConfiguration | ConnectorMessages | ConnectorKey | ConnectorInfo | -UpdateApiOpType | AttributeInfo | ConnectorObject | ObjectClass | -ObjectClassInfo | Schema | Script | ScriptContext | OperationOptions | -OperationOptionInfo | SyncDeltaType | SyncToken | SyncDelta | QualifiedUid -"> - - - -<!--=======================================================--> -<!--= =--> -<!--= Top Level Element for object streams =--> -<!--= =--> -<!--=======================================================--> - - -<!ELEMENT MultiObject (( - %xmlObject; -)*)> - - -<!--=======================================================--> -<!--= =--> -<!--= Primitives =--> -<!--= =--> -<!--=======================================================--> - - - -<!ELEMENT null EMPTY> -<!ELEMENT Array ((%xmlObject;)*)> -<!ATTLIST Array - componentType CDATA #REQUIRED -> -<!ELEMENT Boolean (#PCDATA)> -<!ELEMENT boolean (#PCDATA)> -<!ELEMENT Character (#PCDATA)> -<!ELEMENT char (#PCDATA)> -<!ELEMENT Integer (#PCDATA)> -<!ELEMENT int (#PCDATA)> -<!ELEMENT Long (#PCDATA)> -<!ELEMENT long (#PCDATA)> -<!ELEMENT Float (#PCDATA)> -<!ELEMENT float (#PCDATA)> -<!ELEMENT Double (#PCDATA)> -<!ELEMENT double (#PCDATA)> -<!ELEMENT String (#PCDATA)> -<!ELEMENT URI (#PCDATA)> -<!ELEMENT File (#PCDATA)> -<!ELEMENT BigDecimal EMPTY> -<!ATTLIST BigDecimal - unscaled CDATA #REQUIRED - scale CDATA #REQUIRED -> -<!ELEMENT BigInteger (#PCDATA)> -<!ELEMENT ByteArray (#PCDATA)> -<!ELEMENT Class (#PCDATA)> -<!ELEMENT Map ((MapEntry)*)> -<!ATTLIST Map - caseInsensitive CDATA #IMPLIED -> -<!ELEMENT MapEntry ((%xmlObject;),(%xmlObject;))> -<!ELEMENT Keys ((%xmlObject;)*)> -<!ELEMENT List ((%xmlObject;)*)> -<!ELEMENT Set ((%xmlObject;)*)> -<!ATTLIST Set - caseInsensitive CDATA #IMPLIED -> -<!ELEMENT Locale EMPTY> -<!ATTLIST Locale - language CDATA #IMPLIED - country CDATA #IMPLIED - variant CDATA #IMPLIED -> -<!ELEMENT GuardedByteArray (#PCDATA)> -<!ELEMENT GuardedString (#PCDATA)> - -<!--=======================================================--> -<!--= =--> -<!--= APIConfiguration =--> -<!--= =--> -<!--=======================================================--> - -<!ELEMENT ObjectPoolConfiguration EMPTY> -<!ATTLIST ObjectPoolConfiguration - maxObjects CDATA #IMPLIED - maxIdle CDATA #IMPLIED - maxWait CDATA #IMPLIED - minEvictableIdleTimeMillis CDATA #IMPLIED - minIdle CDATA #IMPLIED -> - -<!ELEMENT ResultsHandlerConfiguration EMPTY> -<!ATTLIST ResultsHandlerConfiguration - enableNormalizingResultsHandler CDATA #IMPLIED - enableFilteredResultsHandler CDATA #IMPLIED - enableCaseInsensitiveFilter CDATA #IMPLIED - enableAttributesToGetSearchResultsHandler CDATA #IMPLIED -> - -<!ELEMENT ConfigurationProperty (value,operations)> -<!ATTLIST ConfigurationProperty - order CDATA #IMPLIED - confidential CDATA #IMPLIED - required CDATA #IMPLIED - name CDATA #REQUIRED - helpMessageKey CDATA #REQUIRED - displayMessageKey CDATA #REQUIRED - groupMessageKey CDATA #REQUIRED - type CDATA #REQUIRED -> -<!ELEMENT value (%xmlObject;)> -<!ELEMENT operations (Class)*> -<!ELEMENT ConfigurationProperties ((ConfigurationProperty)*)> - -<!ELEMENT APIConfiguration (connectorPoolConfiguration,resultsHandlerConfiguration,ConfigurationProperties,timeoutMap,SupportedOperations)> -<!ATTLIST APIConfiguration - connectorPoolingSupported CDATA #REQUIRED - producerBufferSize CDATA #REQUIRED -> -<!ELEMENT connectorPoolConfiguration ((ObjectPoolConfiguration))> -<!ELEMENT resultsHandlerConfiguration ((ResultsHandlerConfiguration))> -<!ELEMENT timeoutMap (Map)> -<!ELEMENT SupportedOperations ((Class)*)> -<!ELEMENT ConnectorMessages (catalogs)> -<!ELEMENT catalogs (Map)> -<!ELEMENT ConnectorKey EMPTY> -<!ATTLIST ConnectorKey - bundleName CDATA #REQUIRED - bundleVersion CDATA #REQUIRED - connectorName CDATA #REQUIRED -> -<!ELEMENT ConnectorInfo (ConnectorKey,ConnectorMessages,APIConfiguration)> -<!ATTLIST ConnectorInfo - connectorDisplayNameKey CDATA #REQUIRED - connectorCategoryKey CDATA #REQUIRED -> - -<!--=======================================================--> -<!--= =--> -<!--= Common Objects =--> -<!--= =--> -<!--=======================================================--> -<!ELEMENT Attribute (Values)?> -<!ELEMENT Values ((%xmlObject;)*)> -<!ATTLIST Attribute - name CDATA #REQUIRED -> - -<!ELEMENT Uid (#PCDATA)> -<!ELEMENT Name (#PCDATA)> - - - -<!ELEMENT UpdateApiOpType (#PCDATA)> - - -<!ELEMENT AlreadyExistsException EMPTY> -<!ATTLIST AlreadyExistsException - message CDATA #IMPLIED -> -<!ELEMENT ConfigurationException EMPTY> -<!ATTLIST ConfigurationException - message CDATA #IMPLIED -> -<!ELEMENT ConnectionBrokenException EMPTY> -<!ATTLIST ConnectionBrokenException - message CDATA #IMPLIED -> -<!ELEMENT ConnectionFailedException EMPTY> -<!ATTLIST ConnectionFailedException - message CDATA #IMPLIED -> -<!ELEMENT ConnectorIOException EMPTY> -<!ATTLIST ConnectorIOException - message CDATA #IMPLIED -> -<!ELEMENT InvalidPasswordException EMPTY> -<!ATTLIST InvalidPasswordException - message CDATA #IMPLIED -> -<!ELEMENT PasswordExpiredException (Uid?)> -<!ATTLIST PasswordExpiredException - message CDATA #IMPLIED -> -<!ELEMENT UnknownUidException EMPTY> -<!ATTLIST UnknownUidException - message CDATA #IMPLIED -> -<!ELEMENT InvalidCredentialException EMPTY> -<!ATTLIST InvalidCredentialException - message CDATA #IMPLIED -> -<!ELEMENT PermissionDeniedException EMPTY> -<!ATTLIST PermissionDeniedException - message CDATA #IMPLIED -> -<!ELEMENT ConnectorSecurityException EMPTY> -<!ATTLIST ConnectorSecurityException - message CDATA #IMPLIED -> -<!ELEMENT OperationTimeoutException EMPTY> -<!ATTLIST OperationTimeoutException - message CDATA #IMPLIED -> -<!ELEMENT ConnectorException EMPTY> -<!ATTLIST ConnectorException - message CDATA #IMPLIED -> -<!ELEMENT IllegalArgumentException EMPTY> -<!ATTLIST IllegalArgumentException - message CDATA #IMPLIED -> -<!ELEMENT RuntimeException EMPTY> -<!ATTLIST RuntimeException - message CDATA #IMPLIED -> -<!ELEMENT Exception EMPTY> -<!ATTLIST Exception - message CDATA #IMPLIED -> -<!ELEMENT Throwable EMPTY> -<!ATTLIST Throwable - message CDATA #IMPLIED -> -<!ELEMENT AttributeInfo (AttributeInfoFlag*)> -<!ATTLIST AttributeInfo - name CDATA #REQUIRED - type CDATA #REQUIRED -> -<!ELEMENT AttributeInfoFlag EMPTY> -<!ATTLIST AttributeInfoFlag - value ( REQUIRED | MULTIVALUED | NOT_CREATABLE | NOT_UPDATEABLE | NOT_READABLE | NOT_RETURNED_BY_DEFAULT ) #REQUIRED -> -<!ELEMENT ConnectorObject (ObjectClass,Attributes)> -<!ELEMENT Attributes ((%attributeTypes;)*)> - -<!ELEMENT ObjectClass EMPTY> -<!ATTLIST ObjectClass - type CDATA #REQUIRED -> - -<!ELEMENT ObjectClassInfo (AttributeInfos)> -<!ATTLIST ObjectClassInfo - type CDATA #REQUIRED - container CDATA #IMPLIED -> -<!ELEMENT AttributeInfos ((AttributeInfo)*)> - -<!ELEMENT Schema (ObjectClassInfos,OperationOptionInfos,objectClassesByOperation,optionsByOperation)> -<!ELEMENT ObjectClassInfos ((ObjectClassInfo)*)> -<!ELEMENT OperationOptionInfos ((OperationOptionInfo)*)> -<!ELEMENT objectClassesByOperation (Map)> -<!ELEMENT optionsByOperation (Map)> - -<!ELEMENT scriptText (#PCDATA)> -<!ELEMENT scriptArguments (Map)> - -<!ELEMENT Script (scriptText)> -<!ATTLIST Script - scriptLanguage CDATA #REQUIRED -> - -<!ELEMENT ScriptContext (scriptArguments,scriptText)> -<!ATTLIST ScriptContext - scriptLanguage CDATA #REQUIRED -> - -<!ELEMENT OperationOptions (options)> -<!ELEMENT options (Map)> -<!ELEMENT OperationOptionInfo EMPTY> -<!ATTLIST OperationOptionInfo - name CDATA #REQUIRED - type CDATA #REQUIRED -> -<!ELEMENT SyncDeltaType EMPTY> -<!ATTLIST SyncDeltaType - value ( CREATE_OR_UPDATE | DELETE ) #REQUIRED -> -<!ELEMENT SyncToken (value)> -<!ELEMENT SyncDelta (SyncDeltaType,SyncToken,PreviousUid,Uid,ConnectorObject?)> - -<!ELEMENT PreviousUid (#PCDATA)> - -<!ELEMENT QualifiedUid (ObjectClass,Uid)> - - -<!--=======================================================--> -<!--= =--> -<!--= Filters =--> -<!--= =--> -<!--=======================================================--> - - -<!ELEMENT attribute (%attributeTypes;)> -<!ELEMENT AndFilter ((%filterTypes;),(%filterTypes;))> -<!ELEMENT ContainsFilter (attribute)> -<!ELEMENT EndsWithFilter (attribute)> -<!ELEMENT EqualsFilter (attribute)> -<!ELEMENT GreaterThanFilter (attribute)> -<!ELEMENT GreaterThanOrEqualFilter (attribute)> -<!ELEMENT LessThanFilter (attribute)> -<!ELEMENT LessThanOrEqualFilter (attribute)> -<!ELEMENT NotFilter (%filterTypes;)> -<!ELEMENT OrFilter ((%filterTypes;),(%filterTypes;))> -<!ELEMENT StartsWithFilter (attribute)> -<!ELEMENT ContainsAllValuesFilter (attribute)> - -<!--=======================================================--> -<!--= =--> -<!--= Messages =--> -<!--= =--> -<!--=======================================================--> -<!ELEMENT HelloRequest EMPTY> -<!ATTLIST HelloRequest - infoLevel CDATA #REQUIRED -> -<!ELEMENT serverInfoMap (Map)> -<!ELEMENT ConnectorKeys ((ConnectorKey)*)> -<!ELEMENT ConnectorInfos ((ConnectorInfo)*)> -<!ELEMENT exception (%exceptionTypes;)> -<!ELEMENT HelloResponse (exception,serverInfoMap,ConnectorInfos,ConnectorKeys)> -<!ELEMENT OperationRequest (ConnectorKey,APIConfiguration,Arguments)> -<!ATTLIST OperationRequest - operation CDATA #REQUIRED - operationMethodName CDATA #REQUIRED -> -<!ELEMENT Arguments ((%xmlObject;)*)> -<!ELEMENT OperationResponseEnd EMPTY> -<!ELEMENT OperationResponsePart (exception,result)> -<!ELEMENT result ((%xmlObject;)*)> -<!ELEMENT OperationRequestMoreData EMPTY> -<!ELEMENT OperationRequestStopData EMPTY> -<!ELEMENT OperationResponsePause EMPTY> -<!ELEMENT EchoMessage (value,objectXml?)> -<!ELEMENT objectXml (#PCDATA)> + <!--=======================================================--> + <!--= =--> + <!--= DTD for Connector Objects =--> + <!--= =--> + <!--=======================================================--> + + <!--=======================================================--> + <!--= =--> + <!--= All XML Objects =--> + <!--= =--> + <!--=======================================================--> + + <!ENTITY % exceptionTypes + "AlreadyExistsException | ConfigurationException | ConnectionBrokenException + | ConnectionFailedException | ConnectorIOException | InvalidPasswordException + | UnknownUidException | InvalidCredentialException | PermissionDeniedException + | ConnectorSecurityException | OperationTimeoutException | InvalidAttributeValueException + | PreconditionRequiredException | PreconditionFailedException | RetryableException + | RemoteWrappedException | ConnectorException + | RuntimeException | Exception | Throwable | PasswordExpiredException | IllegalArgumentException + "> + + <!ENTITY % messageTypes + "HelloRequest | HelloResponse | OperationRequest | OperationResponseEnd | + OperationResponsePart | OperationRequestMoreData | OperationRequestStopData | + OperationResponsePause | EchoMessage + "> + + <!ENTITY % filterTypes + "AndFilter | ContainsFilter | EndsWithFilter | EqualsFilter | + GreaterThanFilter | GreaterThanOrEqualFilter | LessThanFilter | + LessThanOrEqualFilter | NotFilter | OrFilter | StartsWithFilter | + ContainsAllValuesFilter + "> + + <!ENTITY % attributeTypes + "Attribute | Uid | Name"> + + <!ENTITY % primitiveTypes + "null | Array | Boolean | boolean | Character | char | Integer | + int | Long | long | Float | float | Double | double | String | + URI | File | BigDecimal | BigInteger | ByteArray | Class | + Map | List | Set | Locale | GuardedByteArray | GuardedString + "> + + <!ENTITY % xmlObject + "%primitiveTypes; | %exceptionTypes; | %messageTypes; | %filterTypes; | %attributeTypes; | + ObjectPoolConfiguration | ResultsHandlerConfiguration | ConfigurationProperty | ConfigurationProperties | + APIConfiguration | ConnectorMessages | ConnectorKey | ConnectorInfo | + UpdateApiOpType | AttributeInfo | ConnectorObject | ObjectClass | + ObjectClassInfo | Schema | Script | ScriptContext | OperationOptions | + OperationOptionInfo | SyncDeltaType | SyncToken | SyncDelta | QualifiedUid + "> + + + + <!--=======================================================--> + <!--= =--> + <!--= Top Level Element for object streams =--> + <!--= =--> + <!--=======================================================--> + + + <!ELEMENT MultiObject (( + %xmlObject; + )*)> + + + <!--=======================================================--> + <!--= =--> + <!--= Primitives =--> + <!--= =--> + <!--=======================================================--> + + + + <!ELEMENT null EMPTY> + <!ELEMENT Array ((%xmlObject;)*)> + <!ATTLIST Array + componentType CDATA #REQUIRED + > + <!ELEMENT Boolean (#PCDATA)> + <!ELEMENT boolean (#PCDATA)> + <!ELEMENT Character (#PCDATA)> + <!ELEMENT char (#PCDATA)> + <!ELEMENT Integer (#PCDATA)> + <!ELEMENT int (#PCDATA)> + <!ELEMENT Long (#PCDATA)> + <!ELEMENT long (#PCDATA)> + <!ELEMENT Float (#PCDATA)> + <!ELEMENT float (#PCDATA)> + <!ELEMENT Double (#PCDATA)> + <!ELEMENT double (#PCDATA)> + <!ELEMENT String (#PCDATA)> + <!ELEMENT URI (#PCDATA)> + <!ELEMENT File (#PCDATA)> + <!ELEMENT BigDecimal EMPTY> + <!ATTLIST BigDecimal + unscaled CDATA #REQUIRED + scale CDATA #REQUIRED + > + <!ELEMENT BigInteger (#PCDATA)> + <!ELEMENT ByteArray (#PCDATA)> + <!ELEMENT Class (#PCDATA)> + <!ELEMENT Map ((MapEntry)*)> + <!ATTLIST Map + caseInsensitive CDATA #IMPLIED + > + <!ELEMENT MapEntry ((%xmlObject;),(%xmlObject;))> + <!ELEMENT Keys ((%xmlObject;)*)> + <!ELEMENT List ((%xmlObject;)*)> + <!ELEMENT Set ((%xmlObject;)*)> + <!ATTLIST Set + caseInsensitive CDATA #IMPLIED + > + <!ELEMENT Locale EMPTY> + <!ATTLIST Locale + language CDATA #IMPLIED + country CDATA #IMPLIED + variant CDATA #IMPLIED + > + <!ELEMENT GuardedByteArray (#PCDATA)> + <!ELEMENT GuardedString (#PCDATA)> + + <!--=======================================================--> + <!--= =--> + <!--= APIConfiguration =--> + <!--= =--> + <!--=======================================================--> + + <!ELEMENT ObjectPoolConfiguration EMPTY> + <!ATTLIST ObjectPoolConfiguration + maxObjects CDATA #IMPLIED + maxIdle CDATA #IMPLIED + maxWait CDATA #IMPLIED + minEvictableIdleTimeMillis CDATA #IMPLIED + minIdle CDATA #IMPLIED + > + + <!ELEMENT ResultsHandlerConfiguration EMPTY> + <!ATTLIST ResultsHandlerConfiguration + enableNormalizingResultsHandler CDATA #IMPLIED + enableFilteredResultsHandler CDATA #IMPLIED + enableCaseInsensitiveFilter CDATA #IMPLIED + enableAttributesToGetSearchResultsHandler CDATA #IMPLIED + > + + <!ELEMENT ConfigurationProperty (value,operations)> + <!ATTLIST ConfigurationProperty + order CDATA #IMPLIED + confidential CDATA #IMPLIED + required CDATA #IMPLIED + name CDATA #REQUIRED + helpMessageKey CDATA #REQUIRED + displayMessageKey CDATA #REQUIRED + groupMessageKey CDATA #REQUIRED + type CDATA #REQUIRED + > + <!ELEMENT value (%xmlObject;)> + <!ELEMENT operations (Class)*> + <!ELEMENT ConfigurationProperties ((ConfigurationProperty)*)> + + <!ELEMENT APIConfiguration (connectorPoolConfiguration,resultsHandlerConfiguration,ConfigurationProperties,timeoutMap,SupportedOperations)> + <!ATTLIST APIConfiguration + connectorPoolingSupported CDATA #REQUIRED + producerBufferSize CDATA #REQUIRED + > + <!ELEMENT connectorPoolConfiguration ((ObjectPoolConfiguration))> + <!ELEMENT resultsHandlerConfiguration ((ResultsHandlerConfiguration))> + <!ELEMENT timeoutMap (Map)> + <!ELEMENT SupportedOperations ((Class)*)> + <!ELEMENT ConnectorMessages (catalogs)> + <!ELEMENT catalogs (Map)> + <!ELEMENT ConnectorKey EMPTY> + <!ATTLIST ConnectorKey + bundleName CDATA #REQUIRED + bundleVersion CDATA #REQUIRED + connectorName CDATA #REQUIRED + > + <!ELEMENT ConnectorInfo (ConnectorKey,ConnectorMessages,APIConfiguration)> + <!ATTLIST ConnectorInfo + connectorDisplayNameKey CDATA #REQUIRED + connectorCategoryKey CDATA #REQUIRED + > + + <!--=======================================================--> + <!--= =--> + <!--= Common Objects =--> + <!--= =--> + <!--=======================================================--> + <!ELEMENT Attribute (Values)?> + <!ELEMENT Values ((%xmlObject;)*)> + <!ATTLIST Attribute + name CDATA #REQUIRED + > + + <!ELEMENT Uid (#PCDATA)> + <!ATTLIST Uid + uid CDATA #IMPLIED + revision CDATA #IMPLIED + > + <!ELEMENT Name (#PCDATA)> + + + + <!ELEMENT UpdateApiOpType (#PCDATA)> + + + <!ELEMENT AlreadyExistsException EMPTY> + <!ATTLIST AlreadyExistsException + message CDATA #IMPLIED + > + <!ELEMENT ConfigurationException EMPTY> + <!ATTLIST ConfigurationException + message CDATA #IMPLIED + > + <!ELEMENT ConnectionBrokenException EMPTY> + <!ATTLIST ConnectionBrokenException + message CDATA #IMPLIED + > + <!ELEMENT ConnectionFailedException EMPTY> + <!ATTLIST ConnectionFailedException + message CDATA #IMPLIED + > + <!ELEMENT ConnectorIOException EMPTY> + <!ATTLIST ConnectorIOException + message CDATA #IMPLIED + > + <!ELEMENT InvalidPasswordException EMPTY> + <!ATTLIST InvalidPasswordException + message CDATA #IMPLIED + > + <!ELEMENT PasswordExpiredException (Uid?)> + <!ATTLIST PasswordExpiredException + message CDATA #IMPLIED + > + <!ELEMENT UnknownUidException EMPTY> + <!ATTLIST UnknownUidException + message CDATA #IMPLIED + > + <!ELEMENT InvalidCredentialException EMPTY> + <!ATTLIST InvalidCredentialException + message CDATA #IMPLIED + > + <!ELEMENT PermissionDeniedException EMPTY> + <!ATTLIST PermissionDeniedException + message CDATA #IMPLIED + > + <!ELEMENT ConnectorSecurityException EMPTY> + <!ATTLIST ConnectorSecurityException + message CDATA #IMPLIED + > + <!ELEMENT OperationTimeoutException EMPTY> + <!ATTLIST OperationTimeoutException + message CDATA #IMPLIED + > + <!ELEMENT InvalidAttributeValueException EMPTY> + <!ATTLIST InvalidAttributeValueException + message CDATA #IMPLIED + > + <!ELEMENT PreconditionFailedException EMPTY> + <!ATTLIST PreconditionFailedException + message CDATA #IMPLIED + > + <!ELEMENT PreconditionRequiredException EMPTY> + <!ATTLIST PreconditionRequiredException + message CDATA #IMPLIED + > + <!ELEMENT RetryableException EMPTY> + <!ATTLIST RetryableException + message CDATA #IMPLIED + > + <!ELEMENT RemoteWrappedException (RemoteWrappedException?)> + <!ATTLIST RemoteWrappedException + class CDATA #IMPLIED + message CDATA #IMPLIED + stackTrace CDATA #IMPLIED + > + <!ELEMENT ConnectorException EMPTY> + <!ATTLIST ConnectorException + message CDATA #IMPLIED + > + <!ELEMENT IllegalArgumentException EMPTY> + <!ATTLIST IllegalArgumentException + message CDATA #IMPLIED + > + <!ELEMENT RuntimeException EMPTY> + <!ATTLIST RuntimeException + message CDATA #IMPLIED + > + <!ELEMENT Exception EMPTY> + <!ATTLIST Exception + message CDATA #IMPLIED + > + <!ELEMENT Throwable EMPTY> + <!ATTLIST Throwable + message CDATA #IMPLIED + > + <!ELEMENT AttributeInfo (AttributeInfoFlag*)> + <!ATTLIST AttributeInfo + name CDATA #REQUIRED + type CDATA #REQUIRED + > + <!ELEMENT AttributeInfoFlag EMPTY> + <!ATTLIST AttributeInfoFlag + value ( REQUIRED | MULTIVALUED | NOT_CREATABLE | NOT_UPDATEABLE | NOT_READABLE | NOT_RETURNED_BY_DEFAULT ) #REQUIRED + > + <!ELEMENT ConnectorObject (ObjectClass,Attributes)> + <!ELEMENT Attributes ((%attributeTypes;)*)> + + <!ELEMENT ObjectClass EMPTY> + <!ATTLIST ObjectClass + type CDATA #REQUIRED + > + + <!ELEMENT ObjectClassInfo (AttributeInfos)> + <!ATTLIST ObjectClassInfo + type CDATA #REQUIRED + container CDATA #IMPLIED + > + <!ELEMENT AttributeInfos ((AttributeInfo)*)> + + <!ELEMENT Schema (ObjectClassInfos,OperationOptionInfos,objectClassesByOperation,optionsByOperation)> + <!ELEMENT ObjectClassInfos ((ObjectClassInfo)*)> + <!ELEMENT OperationOptionInfos ((OperationOptionInfo)*)> + <!ELEMENT objectClassesByOperation (Map)> + <!ELEMENT optionsByOperation (Map)> + + <!ELEMENT scriptText (#PCDATA)> + <!ELEMENT scriptArguments (Map)> + + <!ELEMENT Script (scriptText)> + <!ATTLIST Script + scriptLanguage CDATA #REQUIRED + > + + <!ELEMENT ScriptContext (scriptArguments,scriptText)> + <!ATTLIST ScriptContext + scriptLanguage CDATA #REQUIRED + > + + <!ELEMENT OperationOptions (options)> + <!ELEMENT options (Map)> + <!ELEMENT OperationOptionInfo EMPTY> + <!ATTLIST OperationOptionInfo + name CDATA #REQUIRED + type CDATA #REQUIRED + > + <!ELEMENT SyncDeltaType EMPTY> + <!ATTLIST SyncDeltaType + value ( CREATE_OR_UPDATE | DELETE ) #REQUIRED + > + <!ELEMENT SyncToken (value)> + <!ELEMENT SyncDelta (SyncDeltaType,SyncToken,PreviousUid,Uid,ConnectorObject?)> + + <!ELEMENT PreviousUid (#PCDATA)> + <!ATTLIST PreviousUid + uid CDATA #IMPLIED + revision CDATA #IMPLIED + > + + <!ELEMENT QualifiedUid (ObjectClass,Uid)> + + + <!--=======================================================--> + <!--= =--> + <!--= Filters =--> + <!--= =--> + <!--=======================================================--> + + + <!ELEMENT attribute (%attributeTypes;)> + <!ELEMENT AndFilter ((%filterTypes;),(%filterTypes;))> + <!ELEMENT ContainsFilter (attribute)> + <!ELEMENT EndsWithFilter (attribute)> + <!ELEMENT EqualsFilter (attribute)> + <!ELEMENT GreaterThanFilter (attribute)> + <!ELEMENT GreaterThanOrEqualFilter (attribute)> + <!ELEMENT LessThanFilter (attribute)> + <!ELEMENT LessThanOrEqualFilter (attribute)> + <!ELEMENT NotFilter (%filterTypes;)> + <!ELEMENT OrFilter ((%filterTypes;),(%filterTypes;))> + <!ELEMENT StartsWithFilter (attribute)> + <!ELEMENT ContainsAllValuesFilter (attribute)> + + <!--=======================================================--> + <!--= =--> + <!--= Messages =--> + <!--= =--> + <!--=======================================================--> + <!ELEMENT HelloRequest EMPTY> + <!ATTLIST HelloRequest + infoLevel CDATA #REQUIRED + > + <!ELEMENT serverInfoMap (Map)> + <!ELEMENT ConnectorKeys ((ConnectorKey)*)> + <!ELEMENT ConnectorInfos ((ConnectorInfo)*)> + <!ELEMENT exception (%exceptionTypes;)> + <!ELEMENT HelloResponse (exception,serverInfoMap,ConnectorInfos,ConnectorKeys)> + <!ELEMENT OperationRequest (ConnectorKey,Arguments)> + <!ATTLIST OperationRequest + operation CDATA #REQUIRED + operationMethodName CDATA #REQUIRED + connectorFacadeKey CDATA #REQUIRED + > + <!ELEMENT Arguments ((%xmlObject;)*)> + <!ELEMENT OperationResponseEnd EMPTY> + <!ELEMENT OperationResponsePart (exception,result)> + <!ELEMENT result ((%xmlObject;)*)> + <!ELEMENT OperationRequestMoreData EMPTY> + <!ELEMENT OperationRequestStopData EMPTY> + <!ELEMENT OperationResponsePause EMPTY> + <!ELEMENT EchoMessage (value,objectXml?)> + <!ELEMENT objectXml (#PCDATA)> Test Framework Value diff --git a/FrameworkInternal/Security.cs b/FrameworkInternal/Security.cs index c4712bf5..e16a75bc 100644 --- a/FrameworkInternal/Security.cs +++ b/FrameworkInternal/Security.cs @@ -19,11 +19,9 @@ * enclosed by brackets [] replaced by your own identifying information: * "Portions Copyrighted [year] [name of copyright owner]" * ==================== + * Portions Copyrighted 2014 ForgeRock AS. */ -using System; -using Org.IdentityConnectors.Common.Security; -using System.Text; using System.Security.Cryptography; namespace Org.IdentityConnectors.Common.Security.Impl diff --git a/FrameworkInternal/Serializer.cs b/FrameworkInternal/Serializer.cs index 23a770d3..8cb2ba4b 100644 --- a/FrameworkInternal/Serializer.cs +++ b/FrameworkInternal/Serializer.cs @@ -19,12 +19,11 @@ * enclosed by brackets [] replaced by your own identifying information: * "Portions Copyrighted [year] [name of copyright owner]" * ==================== - * Portions Copyrighted 2012 ForgeRock AS + * Portions Copyrighted 2012-2014 ForgeRock AS. */ using System; using System.IO; using System.Collections.Generic; -using System.Security; using Org.IdentityConnectors.Common; using Org.IdentityConnectors.Common.Pooling; using Org.IdentityConnectors.Common.Security; @@ -39,7 +38,6 @@ using Org.IdentityConnectors.Framework.Impl.Api.Remote.Messages; using Org.IdentityConnectors.Framework.Impl.Serializer.Binary; using Org.IdentityConnectors.Framework.Impl.Serializer.Xml; -using System.Linq; using System.Globalization; namespace Org.IdentityConnectors.Framework.Impl.Serializer { @@ -306,6 +304,11 @@ internal interface ObjectEncoder /// void WriteDoubleContents(double v); + /// + /// Writes the value in-line. + /// + void WriteByteContents(byte v); + /// /// Special case for byte [] that uses base64 encoding for XML /// @@ -557,6 +560,8 @@ static Primitives() { HANDLERS.Add(new BooleanHandler(typeof(bool?), "Boolean")); HANDLERS.Add(new BooleanHandler(typeof(bool), "boolean")); + HANDLERS.Add(new ByteHandler(typeof(byte?), "Byte")); + HANDLERS.Add(new ByteHandler(typeof(byte), "byte")); HANDLERS.Add(new CharacterHandler(typeof(char?), "Character")); HANDLERS.Add(new CharacterHandler(typeof(char), "char")); HANDLERS.Add(new IntegerHandler(typeof(int?), "Integer")); @@ -794,6 +799,24 @@ public override void Serialize(Object obj, ObjectEncoder encoder) encoder.WriteStringContents(val.Value); } } + private class ByteHandler : AbstractObjectSerializationHandler + { + public ByteHandler(Type objectType, String serialType) + : base(objectType, serialType) + { + + } + public override Object Deserialize(ObjectDecoder decoder) + { + return decoder.ReadByteArrayContents(); + } + + public override void Serialize(Object obj, ObjectEncoder encoder) + { + byte val = (byte)obj; + encoder.WriteByteContents(val); + } + } private class ByteArrayHandler : AbstractObjectSerializationHandler { public ByteArrayHandler() @@ -1702,11 +1725,16 @@ static CommonObjectHandlers() HANDLERS.Add(new ConnectionBrokenExceptionHandler()); HANDLERS.Add(new ConnectionFailedExceptionHandler()); HANDLERS.Add(new ConnectorIOExceptionHandler()); + HANDLERS.Add(new InvalidAttributeValueExceptionHandler()); HANDLERS.Add(new PasswordExpiredExceptionHandler()); HANDLERS.Add(new InvalidPasswordExceptionHandler()); HANDLERS.Add(new UnknownUidExceptionHandler()); HANDLERS.Add(new InvalidCredentialExceptionHandler()); HANDLERS.Add(new PermissionDeniedExceptionHandler()); + HANDLERS.Add(new PreconditionFailedExceptionHandler()); + HANDLERS.Add(new PreconditionRequiredExceptionHandler()); + HANDLERS.Add(new RemoteWrappedExceptionHandler()); + HANDLERS.Add(new RetryableExceptionHandler()); HANDLERS.Add(new ConnectorSecurityExceptionHandler()); HANDLERS.Add(new OperationTimeoutExceptionHandler()); HANDLERS.Add(new ConnectorExceptionHandler()); @@ -1719,6 +1747,8 @@ static CommonObjectHandlers() HANDLERS.Add(new ConnectorObjectHandler()); HANDLERS.Add(new NameHandler()); HANDLERS.Add(new ObjectClassHandler()); + HANDLERS.Add(new SearchResultHandler()); + HANDLERS.Add(new SortKeyHandler()); HANDLERS.Add(new ObjectClassInfoHandler()); HANDLERS.Add(new SchemaHandler()); HANDLERS.Add(new UidHandler()); @@ -1795,6 +1825,18 @@ protected override ConnectorIOException CreateException(String msg) return new ConnectorIOException(msg); } } + private class InvalidAttributeValueExceptionHandler : AbstractExceptionHandler + { + public InvalidAttributeValueExceptionHandler() + : base("InvalidAttributeValueException") + { + + } + protected override InvalidAttributeValueException CreateException(String msg) + { + return new InvalidAttributeValueException(msg); + } + } private class PasswordExpiredExceptionHandler : AbstractExceptionHandler { public PasswordExpiredExceptionHandler() @@ -1907,6 +1949,70 @@ protected override ConnectorException CreateException(String msg) return new ConnectorException(msg); } } + private class PreconditionFailedExceptionHandler : AbstractExceptionHandler + { + public PreconditionFailedExceptionHandler() + : base("PreconditionFailedException") + { + + } + protected override PreconditionFailedException CreateException(String msg) + { + return new PreconditionFailedException(msg); + } + } + private class PreconditionRequiredExceptionHandler : AbstractExceptionHandler + { + public PreconditionRequiredExceptionHandler() + : base("PreconditionRequiredException") + { + + } + protected override PreconditionRequiredException CreateException(String msg) + { + return new PreconditionRequiredException(msg); + } + } + private class RemoteWrappedExceptionHandler : AbstractObjectSerializationHandler + { + public RemoteWrappedExceptionHandler() + : base(typeof(RemoteWrappedException), "RemoteWrappedException") + { + + } + public override Object Deserialize(ObjectDecoder decoder) + { + //The .NET Type.FullName property will not always yield results identical to the Java Class.getName method: + string throwableClass = decoder.ReadStringField(RemoteWrappedException.FIELD_CLASS, typeof(ConnectorException).FullName); + string message = decoder.ReadStringField(RemoteWrappedException.FIELD_MESSAGE, null); + RemoteWrappedException cause = (RemoteWrappedException)decoder.ReadObjectField("RemoteWrappedException", typeof(RemoteWrappedException), null); + string stackTrace = decoder.ReadStringField(RemoteWrappedException.FIELD_STACK_TRACE, null); + + return new RemoteWrappedException(throwableClass, message, cause, stackTrace); + } + + public override void Serialize(Object obj, ObjectEncoder encoder) + { + RemoteWrappedException val = (RemoteWrappedException)obj; + encoder.WriteStringField(RemoteWrappedException.FIELD_CLASS, val.ExceptionClass); + encoder.WriteStringField(RemoteWrappedException.FIELD_MESSAGE, val.Message); + encoder.WriteObjectField("RemoteWrappedException", val.InnerException, true); + encoder.WriteStringField(RemoteWrappedException.FIELD_STACK_TRACE, val.ReadStackTrace()); + } + } + private class RetryableExceptionHandler : AbstractExceptionHandler + { + public RetryableExceptionHandler() + : base("RetryableException") + { + + } + protected override RetryableException CreateException(String msg) + { + return RetryableException.Wrap(msg, (Exception)null); + } + } + //RuntimeException, Exception, and Throwable //when going from Java to C#, these always become //Exception. @@ -2274,14 +2380,20 @@ public UidHandler() } public override Object Deserialize(ObjectDecoder decoder) { - String str = decoder.ReadStringContents(); - return new Uid(str); + String val = decoder.ReadStringField("uid", null); + String revision = decoder.ReadStringField("revision", null); + if (null == revision) { + return new Uid(val); + } else { + return new Uid(val, revision); + } } public override void Serialize(Object obj, ObjectEncoder encoder) { Uid val = (Uid)obj; - encoder.WriteStringContents(val.GetUidValue()); + encoder.WriteStringField("uid", val.GetUidValue()); + encoder.WriteStringField("revision", val.Revision); } } private class ScriptHandler : AbstractObjectSerializationHandler @@ -2356,6 +2468,44 @@ public override void Serialize(Object obj, ObjectEncoder encoder) encoder.WriteObjectField("options", val.Options, false); } } + private class SearchResultHandler : AbstractObjectSerializationHandler + { + public SearchResultHandler() + : base(typeof(SearchResult), "SearchResult") + { + + } + public override Object Deserialize(ObjectDecoder decoder) + { + return new SearchResult(decoder.ReadStringField("pagedResultsCookie", null), decoder.ReadIntField("remainingPagedResults", -1)); + } + + public override void Serialize(Object obj, ObjectEncoder encoder) + { + SearchResult val = (SearchResult)obj; + encoder.WriteStringField("pagedResultsCookie", val.PagedResultsCookie); + encoder.WriteIntField("remainingPagedResults", val.RemainingPagedResults); + } + } + private class SortKeyHandler : AbstractObjectSerializationHandler + { + public SortKeyHandler() + : base(typeof(Org.IdentityConnectors.Framework.Common.Objects.SortKey), "SortKey") + { + + } + public override Object Deserialize(ObjectDecoder decoder) + { + return new Org.IdentityConnectors.Framework.Common.Objects.SortKey(decoder.ReadStringField("field", null), decoder.ReadBooleanField("isAscending", true)); + } + + public override void Serialize(Object obj, ObjectEncoder encoder) + { + Org.IdentityConnectors.Framework.Common.Objects.SortKey val = (Org.IdentityConnectors.Framework.Common.Objects.SortKey)obj; + encoder.WriteStringField("field", val.Field); + encoder.WriteBooleanField("isAscending", val.AscendingOrder); + } + } private class OperationOptionInfoHandler : AbstractObjectSerializationHandler { public OperationOptionInfoHandler() @@ -2779,8 +2929,8 @@ public override sealed Object Deserialize(ObjectDecoder decoder) { ConnectorKey connectorKey = (ConnectorKey)decoder.ReadObjectField("ConnectorKey", typeof(ConnectorKey), null); - APIConfigurationImpl configuration = - (APIConfigurationImpl)decoder.ReadObjectField("APIConfiguration", typeof(APIConfigurationImpl), null); + String connectorFacadeKey = + decoder.ReadStringField("connectorFacadeKey", null); Type operation = decoder.ReadClassField("operation", null); string operationMethodName = @@ -2788,7 +2938,7 @@ public override sealed Object Deserialize(ObjectDecoder decoder) IList arguments = (IList) decoder.ReadObjectField("Arguments", typeof(IList), null); return new OperationRequest(connectorKey, - configuration, + connectorFacadeKey, SafeType.ForRawType(operation), operationMethodName, arguments); @@ -2802,10 +2952,10 @@ public override sealed void Serialize(Object obj, ObjectEncoder encoder) val.Operation.RawType); encoder.WriteStringField("operationMethodName", val.OperationMethodName); + encoder.WriteStringField("connectorFacadeKey", + val.ConnectorFacadeKey); encoder.WriteObjectField("ConnectorKey", - val.ConnectorKey, true); - encoder.WriteObjectField("APIConfiguration", - val.Configuration, true); + val.ConnectorKey, true); encoder.WriteObjectField("Arguments", val.Arguments, true); } diff --git a/FrameworkInternal/SerializerBinary.cs b/FrameworkInternal/SerializerBinary.cs index 7f1714a0..eb72f585 100644 --- a/FrameworkInternal/SerializerBinary.cs +++ b/FrameworkInternal/SerializerBinary.cs @@ -19,13 +19,13 @@ * enclosed by brackets [] replaced by your own identifying information: * "Portions Copyrighted [year] [name of copyright owner]" * ==================== + * Portions Copyrighted 2014 ForgeRock AS. */ using System; using System.Collections.Generic; using Org.IdentityConnectors.Common; using Org.IdentityConnectors.Framework.Common.Exceptions; using Org.IdentityConnectors.Framework.Common.Serializer; -using Org.IdentityConnectors.Framework.Impl.Serializer; using System.IO; using System.Text; namespace Org.IdentityConnectors.Framework.Impl.Serializer.Binary @@ -266,7 +266,7 @@ private Stream GetCurrentOutput() internal class BinaryObjectEncoder : ObjectEncoder, BinaryObjectSerializer { - public const int ENCODING_VERSION = 1; + public const int ENCODING_VERSION = 2; public const int OBJECT_MAGIC = 0xFAFB; @@ -315,6 +315,13 @@ public void WriteBooleanField(String fieldName, bool v) _internalEncoder.EndField(); } + public void WriteByteContents(byte v) + { + _internalEncoder.StartAnonymousField(); + _internalEncoder.WriteByte(v); + _internalEncoder.EndField(); + } + public void WriteByteArrayContents(byte[] v) { _internalEncoder.StartAnonymousField(); @@ -642,7 +649,7 @@ public long ReadLong() ((long)(_long_buf[4] & 0xff) << 24) | ((long)(_long_buf[5] & 0xff) << 16) | ((long)(_long_buf[6] & 0xff) << 8) | - ((long)(_long_buf[7] & 0xff))); + ((_long_buf[7] & 0xff))); } public double ReadDouble() diff --git a/FrameworkInternal/SerializerXml.cs b/FrameworkInternal/SerializerXml.cs index 5cb1ce44..e740bf03 100644 --- a/FrameworkInternal/SerializerXml.cs +++ b/FrameworkInternal/SerializerXml.cs @@ -19,6 +19,7 @@ * enclosed by brackets [] replaced by your own identifying information: * "Portions Copyrighted [year] [name of copyright owner]" * ==================== + * Portions Copyrighted 2014 ForgeRock AS. */ using System; using System.Collections.Generic; @@ -64,6 +65,11 @@ public void WriteBooleanField(String fieldName, bool v) WriteAttributeInternal(fieldName, EncodeBoolean(v)); } + public void WriteByteContents(byte v) + { + WriteStringContentsInternal(EncodeByte(v)); + } + public void WriteByteArrayContents(byte[] v) { WriteStringContentsInternal(EncodeByteArray(v)); @@ -156,6 +162,11 @@ internal static String EncodeBoolean(bool b) return b.ToString(); } + private static String EncodeByte(byte singleByte) + { + return Convert.ToString(singleByte); + } + private static String EncodeByteArray(byte[] bytes) { return Convert.ToBase64String(bytes); diff --git a/FrameworkInternal/Server.cs b/FrameworkInternal/Server.cs index 7f7a1aa3..e906cdff 100644 --- a/FrameworkInternal/Server.cs +++ b/FrameworkInternal/Server.cs @@ -19,7 +19,7 @@ * enclosed by brackets [] replaced by your own identifying information: * "Portions Copyrighted [year] [name of copyright owner]" * ==================== - * Portions Copyrighted 2012 ForgeRock AS + * Portions Copyrighted 2012-2014 ForgeRock AS. */ using System; using System.Collections.Generic; @@ -27,7 +27,6 @@ using System.Globalization; using System.Net; using System.Net.Security; -using System.Security; using System.Security.Cryptography.X509Certificates; using System.Net.Sockets; using System.IO; @@ -42,13 +41,11 @@ using Org.IdentityConnectors.Framework.Api.Operations; using Org.IdentityConnectors.Framework.Common.Objects; using Org.IdentityConnectors.Framework.Common.Exceptions; -using Org.IdentityConnectors.Framework.Common.Objects.Filters; using Org.IdentityConnectors.Framework.Common.Serializer; using Org.IdentityConnectors.Framework.Server; using Org.IdentityConnectors.Framework.Impl.Api; using Org.IdentityConnectors.Framework.Impl.Api.Remote.Messages; using Org.IdentityConnectors.Framework.Impl.Api.Local; -using Org.IdentityConnectors.Framework.Impl.Api.Local.Operations; using Org.IdentityConnectors.Framework.Impl.Api.Remote; namespace Org.IdentityConnectors.Framework.Server @@ -62,6 +59,8 @@ public abstract class ConnectorServer private const String IMPL_NAME = "Org.IdentityConnectors.Framework.Impl.Server.ConnectorServerImpl"; + protected internal static readonly TraceSource logger = new TraceSource("ConnectorServer"); + /// /// The port to listen on; /// @@ -88,6 +87,11 @@ private const String IMPL_NAME /// private int _maxWorkers = 100; + /// + /// The maximum time in minutes a facade can be inactive. + /// + private int _maxFacadeLifeTime = 60; + /// /// The network interface address to use. /// @@ -190,6 +194,26 @@ public int MinWorkers } } + /// + /// Returns the max inactive lifetime of + /// to allow. + /// + /// The max inactive lifetime of + /// to + /// allow. + public virtual int MaxFacadeLifeTime + { + get + { + return _maxFacadeLifeTime; + } + set + { + AssertNotStarted(); + _maxFacadeLifeTime = value; + } + } + /// /// Returns the network interface address to bind to. /// @@ -211,9 +235,9 @@ public IPAddress IfAddress } /// - /// Returns true iff we are to use SSL. + /// Returns true if we are to use SSL. /// - /// true iff we are to use SSL. + /// true if we are to use SSL. public bool UseSSL { get @@ -295,20 +319,22 @@ public String KeyHash abstract public void Stop(); /// - /// Return true iff the server is started. + /// Return true if the server is started. /// /// /// Note that started is a /// logical state (start method has been called). It does not necessarily /// reflect the health of the server /// - /// true iff the server is started. + /// true if the server is started. abstract public bool IsStarted(); } } namespace Org.IdentityConnectors.Framework.Impl.Server { + + #region ConnectionProcessor public class ConnectionProcessor { private class RemoteResultsHandler : ObjectStreamHandler @@ -346,10 +372,6 @@ public bool Handle(Object obj) { throw new BrokenConnectionException(e); } - catch (Exception) - { - throw; - } } } @@ -709,14 +731,10 @@ private APIOperation GetAPIOperation(OperationRequest request) throw new Exception("No such connector: " + request.ConnectorKey); } - APIConfigurationImpl config = - request.Configuration; - - //re-wire the configuration with its connector info - config.ConnectorInfo = (AbstractConnectorInfo)info; + String connectorFacadeKey = request.ConnectorFacadeKey; ConnectorFacade facade = - ConnectorFacadeFactory.GetInstance().NewInstance(config); + ConnectorFacadeFactory.GetManagedInstance().NewInstance(info, connectorFacadeKey); return facade.GetOperation(request.Operation); } @@ -737,7 +755,9 @@ public IOException GetIOException() } } + #endregion + #region ConnectionListener class ConnectionListener { /// @@ -908,7 +928,9 @@ public void Shutdown() } } } + #endregion + #region RequestStats internal class RequestStats { public RequestStats() @@ -918,7 +940,9 @@ public RequestStats() public long StartTimeMillis { get; set; } public long RequestID { get; set; } } + #endregion + #region ConnectorServerImpl public class ConnectorServerImpl : ConnectorServer { @@ -926,6 +950,7 @@ private readonly IDictionary _pendingRequests = CollectionUtil.NewIdentityDictionary(); private ConnectionListener _listener; private Object COUNT_LOCK = new Object(); + private Timer _timer = null; private long _startDate = 0; private long _requestCount = 0; @@ -1060,6 +1085,13 @@ public override void Start() ConnectionListener listener = new ConnectionListener(this, socket); listener.Start(); _listener = listener; + + var statusChecker = new FacadeDisposer(new TimeSpan(0, MaxFacadeLifeTime, 0)); + // Create an inferred delegate that invokes methods for the timer. + TimerCallback tcb = statusChecker.Run; + + _timer = new Timer(tcb, null, new TimeSpan(0, MaxFacadeLifeTime, 0), + new TimeSpan(0, Math.Min(MaxFacadeLifeTime, 10), 0)); } private TcpListener CreateServerSocket() @@ -1085,7 +1117,35 @@ public override void Stop() _listener = null; } _startDate = 0; - ConnectorFacadeFactory.GetInstance().Dispose(); + if (null != _timer) + { + _timer.Dispose(); + _timer = null; + } + ConnectorFacadeFactory.GetManagedInstance().Dispose(); + } + + internal class FacadeDisposer + { + private readonly TimeSpan _delay; + private int _sequence = 0; + + public FacadeDisposer(TimeSpan unit) + { + this._delay = unit; + } + + public void Run(Object stateInfo) + { + logger.TraceEvent(TraceEventType.Verbose, _sequence++, + "Invoking Managed ConnectorFacade Disposer : {0:yyyy/MM/dd H:mm:ss zzz}", DateTime.Now); + ConnectorFacadeFactory factory = ConnectorFacadeFactory.GetManagedInstance(); + if (factory is ManagedConnectorFacadeFactoryImpl) + { + ((ManagedConnectorFacadeFactoryImpl)factory).EvictIdle(_delay); + } + } } } + #endregion } \ No newline at end of file diff --git a/FrameworkInternal/Test.cs b/FrameworkInternal/Test.cs index fbc41fb9..9d963431 100644 --- a/FrameworkInternal/Test.cs +++ b/FrameworkInternal/Test.cs @@ -112,18 +112,38 @@ private static bool IsConnectorPoolingSupported(SafeType clazz) /// The result handler /// The options - may be null - will /// be cast to an empty OperationOptions - public void Search(SearchOp search, - ObjectClass oclass, + public SearchResult Search(SearchOp search, + ObjectClass objectClass, Filter filter, ResultsHandler handler, OperationOptions options) where T : class { + Assertions.NullCheck(objectClass, "objectClass"); + if (ObjectClass.ALL.Equals(objectClass)) + { + throw new System.NotSupportedException("Operation is not allowed on __ALL__ object class"); + } + Assertions.NullCheck(handler, "handler"); + //convert null into empty if (options == null) { options = new OperationOptionsBuilder().Build(); } - RawSearcherImpl.RawSearch( - search, oclass, filter, handler, options); + + SearchResult result = null; + RawSearcherImpl.RawSearch(search, objectClass, filter, new SearchResultsHandler() + { + Handle = obj => + { + return handler.Handle(obj); + }, + HandleResult = obj => + { + result = obj; + } + + }, options); + return result != null ? result : new SearchResult(); } public ConnectorMessages CreateDummyMessages() diff --git a/FrameworkTests/ConnectorFacadeExceptionTests.cs b/FrameworkTests/ConnectorFacadeExceptionTests.cs index d700e512..af2130be 100644 --- a/FrameworkTests/ConnectorFacadeExceptionTests.cs +++ b/FrameworkTests/ConnectorFacadeExceptionTests.cs @@ -19,11 +19,9 @@ * enclosed by brackets [] replaced by your own identifying information: * "Portions Copyrighted [year] [name of copyright owner]" * ==================== + * Portions Copyrighted 2014 ForgeRock AS. */ using System; -using System.Collections.Generic; -using System.Linq; -using System.Text; using NUnit.Framework; using System.Diagnostics; using Org.IdentityConnectors.Framework.Api; diff --git a/FrameworkTests/ConnectorFacadeTests.cs b/FrameworkTests/ConnectorFacadeTests.cs index dd85f884..d303f9a0 100644 --- a/FrameworkTests/ConnectorFacadeTests.cs +++ b/FrameworkTests/ConnectorFacadeTests.cs @@ -19,6 +19,7 @@ * enclosed by brackets [] replaced by your own identifying information: * "Portions Copyrighted [year] [name of copyright owner]" * ==================== + * Portions Copyrighted 2014 ForgeRock AS. */ using System; @@ -29,10 +30,9 @@ using Org.IdentityConnectors.Common.Security; using Org.IdentityConnectors.Framework.Api; using Org.IdentityConnectors.Framework.Api.Operations; -using Org.IdentityConnectors.Framework.Common; +using Org.IdentityConnectors.Framework.Common.Exceptions; using Org.IdentityConnectors.Framework.Common.Objects; using Org.IdentityConnectors.Framework.Spi; -using Org.IdentityConnectors.Framework.Spi.Operations; using Org.IdentityConnectors.Test.Common; namespace FrameworkTests @@ -182,6 +182,23 @@ public void AuthenticateCallPattern() }); } + [Test] + [ExpectedException(typeof(NotSupportedException))] + public void AuthenticateAllCallPattern() + { + TestCallPattern(new TestOperationPattern() + { + MakeCall = facade => + { + facade.Authenticate(ObjectClass.ALL, "dfadf", new GuardedString(), null); + }, + CheckCalls = calls => + { + Assert.Fail("Should not get here.."); + } + }); + } + [Test] public void ResolveUsernameCallPattern() { @@ -198,6 +215,23 @@ public void ResolveUsernameCallPattern() }); } + [Test] + [ExpectedException(typeof(NotSupportedException))] + public void ResolveUsernameAllCallPattern() + { + TestCallPattern(new TestOperationPattern() + { + MakeCall = facade => + { + facade.ResolveUsername(ObjectClass.ALL, "dfadf", null); + }, + CheckCalls = calls => + { + Assert.Fail("Should not get here.."); + } + }); + } + [Test] public void CreateCallPattern() { @@ -234,8 +268,26 @@ public void CreateWithOutObjectClassPattern() } [Test] - [ExpectedException(typeof(ArgumentException))] - public void createDuplicatConnectorAttributesPattern() + [ExpectedException(typeof(NotSupportedException))] + public void CreateAllCallPattern() + { + TestCallPattern(new TestOperationPattern() + { + MakeCall = facade => + { + ICollection attrs = CollectionUtil.NewReadOnlySet(); + facade.Create(ObjectClass.ALL, attrs, null); + }, + CheckCalls = calls => + { + Assert.Fail("Should not get here.."); + } + }); + } + + [Test] + [ExpectedException(typeof(InvalidAttributeValueException))] + public void CreateDuplicatConnectorAttributesPattern() { TestCallPattern(new TestOperationPattern() { @@ -271,6 +323,25 @@ public void UpdateCallPattern() }); } + [Test] + [ExpectedException(typeof(NotSupportedException))] + public void UpdateAllCallPattern() + { + TestCallPattern(new TestOperationPattern() + { + MakeCall = facade => + { + ICollection attrs = new HashSet(); + attrs.Add(ConnectorAttributeBuilder.Build("accountid")); + facade.Update(ObjectClass.ALL, NewUid(0), attrs, null); + }, + CheckCalls = calls => + { + Assert.Fail("Should not get here.."); + } + }); + } + [Test] public void DeleteCallPattern() { @@ -287,6 +358,23 @@ public void DeleteCallPattern() }); } + [Test] + [ExpectedException(typeof(NotSupportedException))] + public void DeleteAllCallPattern() + { + TestCallPattern(new TestOperationPattern() + { + MakeCall = facade => + { + facade.Delete(ObjectClass.ALL, NewUid(0), null); + }, + CheckCalls = calls => + { + Assert.Fail("Should not get here.."); + } + }); + } + [Test] public void SearchCallPattern() { @@ -295,9 +383,12 @@ public void SearchCallPattern() MakeCall = facade => { // create an empty results handler.. - ResultsHandler rh = obj => + ResultsHandler rh = new ResultsHandler() { - return true; + Handle = obj => { + return true; + } + }; // call the search method.. facade.Search(ObjectClass.ACCOUNT, null, rh, null); @@ -310,6 +401,32 @@ public void SearchCallPattern() }); } + [Test] + [ExpectedException(typeof(NotSupportedException))] + public void SearchAllCallPattern() + { + TestCallPattern(new TestOperationPattern() + { + MakeCall = facade => + { + // create an empty results handler.. + ResultsHandler rh = new ResultsHandler() + { + Handle = obj => + { + return true; + } + }; + // call the search method.. + facade.Search(ObjectClass.ALL, null, rh, null); + }, + CheckCalls = calls => + { + Assert.Fail("Should not get here.."); + } + }); + } + [Test] public void GetCallPattern() { @@ -329,6 +446,110 @@ public void GetCallPattern() }); } + [Test] + [ExpectedException(typeof(NotSupportedException))] + public void GetAllCallPattern() + { + TestCallPattern(new TestOperationPattern() + { + MakeCall = facade => + { + // create an empty results handler.. + // call the search method.. + facade.GetObject(ObjectClass.ALL, NewUid(0), null); + }, + CheckCalls = calls => + { + Assert.Fail("Should not get here.."); + } + }); + } + + [Test] + public virtual void GetLatestSyncTokenCallPattern() + { + TestCallPattern(new TestOperationPattern() + { + MakeCall = facade => + { + // call the getLatestSyncToken method.. + facade.GetLatestSyncToken(ObjectClass.ALL); + }, + CheckCalls = calls => + { + Assert.AreEqual("GetLatestSyncToken", GetAndRemoveMethodName(calls)); + } + }); + } + + [Test] + public virtual void GetLatestSyncTokenAllCallPattern() + { + TestCallPattern(new TestOperationPattern() + { + MakeCall = facade => + { + // call the getLatestSyncToken method.. + facade.GetLatestSyncToken(ObjectClass.ALL); + }, + CheckCalls = calls => + { + Assert.AreEqual("GetLatestSyncToken", GetAndRemoveMethodName(calls)); + } + }); + } + + + [Test] + public virtual void SyncCallPattern() + { + TestCallPattern(new TestOperationPattern() + { + MakeCall = facade => + { + // create an empty results handler.. + SyncResultsHandler rh = new SyncResultsHandler() + { + Handle = obj => + { + return true; + } + }; + // call the sync method.. + facade.Sync(ObjectClass.ACCOUNT, new SyncToken(1), rh, null); + }, + CheckCalls = calls => + { + Assert.AreEqual("Sync", GetAndRemoveMethodName(calls)); + } + }); + } + + [Test] + public virtual void SyncAllCallPattern() + { + TestCallPattern(new TestOperationPattern() + { + MakeCall = facade => + { + // create an empty results handler.. + SyncResultsHandler rh = new SyncResultsHandler() + { + Handle = obj => + { + return true; + } + }; + // call the sync method.. + facade.Sync(ObjectClass.ALL, new SyncToken(1), rh, null); + }, + CheckCalls = calls => + { + Assert.AreEqual("Sync", GetAndRemoveMethodName(calls)); + } + }); + } + [Test] public void TestOpCallPattern() { diff --git a/FrameworkTests/ConnectorInfoManagerTests.cs b/FrameworkTests/ConnectorInfoManagerTests.cs index 44f5b9fc..b291220e 100644 --- a/FrameworkTests/ConnectorInfoManagerTests.cs +++ b/FrameworkTests/ConnectorInfoManagerTests.cs @@ -19,7 +19,7 @@ * enclosed by brackets [] replaced by your own identifying information: * "Portions Copyrighted [year] [name of copyright owner]" * ==================== - * Portions Copyrighted 2012 ForgeRock AS + * Portions Copyrighted 2012-2014 ForgeRock AS. */ using System; using NUnit.Framework; @@ -31,13 +31,14 @@ using Org.IdentityConnectors.Framework.Api.Operations; using Org.IdentityConnectors.Framework.Common.Exceptions; using Org.IdentityConnectors.Framework.Common.Objects; +using Org.IdentityConnectors.Framework.Common.Objects.Filters; +using Org.IdentityConnectors.Framework.Impl.Api.Remote; +using ICF = Org.IdentityConnectors.Framework.Common.Objects; using Org.IdentityConnectors.Framework.Server; using Org.IdentityConnectors.Framework.Impl.Api; using Org.IdentityConnectors.Framework.Impl.Api.Local; -using System.Security; using System.Threading; using System.Globalization; -using System.Net; using System.Net.Security; using System.Security.Cryptography.X509Certificates; namespace FrameworkTests @@ -45,7 +46,7 @@ namespace FrameworkTests [TestFixture] public class ConnectorInfoManagerTests { - private static ConnectorInfo FindConnectorInfo + protected static ConnectorInfo FindConnectorInfo (ConnectorInfoManager manager, string version, string connectorName) @@ -156,7 +157,7 @@ public void TestValidate() FindConnectorInfo(manager, "1.0.0.0", "org.identityconnectors.testconnector.TstConnector"); - + Assert.IsNotNull(info); APIConfiguration api = info.CreateDefaultAPIConfiguration(); ConfigurationProperties props = api.ConfigurationProperties; @@ -208,8 +209,9 @@ public void TestSearchWithManyResults() FindConnectorInfo(manager, "1.0.0.0", "org.identityconnectors.testconnector.TstConnector"); - + Assert.IsNotNull(info); APIConfiguration api = info.CreateDefaultAPIConfiguration(); + api.ProducerBufferSize = 0; ConfigurationProperties props = api.ConfigurationProperties; ConfigurationProperty property = props.GetProperty("numResults"); @@ -222,14 +224,18 @@ public void TestSearchWithManyResults() IList results = new List(); - facade.Search(ObjectClass.ACCOUNT, null, - obj => - { - results.Add(obj); - return true; - }, null); + SearchResult searchResult = facade.Search(ObjectClass.ACCOUNT, null, new ResultsHandler() + { + Handle = + obj => + { + results.Add(obj); + return true; + } + }, null); Assert.AreEqual(1000, results.Count); + Assert.AreEqual(0, searchResult.RemainingPagedResults); for (int i = 0; i < results.Count; i++) { ConnectorObject obj = results[i]; @@ -239,21 +245,24 @@ public void TestSearchWithManyResults() results.Clear(); - facade.Search(ObjectClass.ACCOUNT, null, - obj => - { - if (results.Count < 500) - { - results.Add(obj); - return true; - } - else - { - return false; - } - }, null); + searchResult = facade.Search(ObjectClass.ACCOUNT, null, new ResultsHandler() + { + Handle = obj => + { + if (results.Count < 500) + { + results.Add(obj); + return true; + } + else + { + return false; + } + } + }, null); Assert.AreEqual(500, results.Count); + Assert.IsTrue(500 == searchResult.RemainingPagedResults || 401 == searchResult.RemainingPagedResults); for (int i = 0; i < results.Count; i++) { ConnectorObject obj = results[i]; @@ -280,7 +289,7 @@ public void TestSyncWithManyResults() FindConnectorInfo(manager, "1.0.0.0", "org.identityconnectors.testconnector.TstConnector"); - + Assert.IsNotNull(info); APIConfiguration api = info.CreateDefaultAPIConfiguration(); ConfigurationProperties props = api.ConfigurationProperties; @@ -296,10 +305,13 @@ public void TestSyncWithManyResults() Assert.AreEqual("mylatest", latest.Value); IList results = new List(); - facade.Sync(ObjectClass.ACCOUNT, null, obj => + facade.Sync(ObjectClass.ACCOUNT, null, new SyncResultsHandler() { - results.Add(obj); - return true; + Handle = obj => + { + results.Add(obj); + return true; + } }, null); Assert.AreEqual(1000, results.Count); @@ -313,19 +325,22 @@ public void TestSyncWithManyResults() results.Clear(); facade.Sync(ObjectClass.ACCOUNT, - null, - obj => + null, new SyncResultsHandler() { - if (results.Count < 500) + Handle = obj => { - results.Add(obj); - return true; + if (results.Count < 500) + { + results.Add(obj); + return true; + } + else + { + return false; + } } - else - { - return false; - } - }, null); + } + , null); Assert.AreEqual(500, results.Count); for (int i = 0; i < results.Count; i++) @@ -336,6 +351,31 @@ public void TestSyncWithManyResults() } } + [Test] + public void TestSyncTokenResults() + { + foreach (ConnectorFacade facade in CreateStateFulFacades()) + { + Uid uid = facade.Create(ObjectClass.ACCOUNT, CollectionUtil.NewReadOnlySet(), null); + + SyncToken latest = facade.GetLatestSyncToken(ObjectClass.ACCOUNT); + Assert.AreEqual(uid.GetUidValue(), latest.Value); + + for (int i = 0; i < 10; i++) + { + SyncToken lastToken = facade.Sync(ObjectClass.ACCOUNT, null, new SyncResultsHandler() + { + Handle = obj => + { + return true; + } + }, null); + Assert.IsNotNull(lastToken); + Assert.AreEqual(lastToken.Value, latest.Value); + } + } + } + [Test] public void TestConnectionPooling() { @@ -396,6 +436,7 @@ public void TestScripting() FindConnectorInfo(manager, "1.0.0.0", "org.identityconnectors.testconnector.TstConnector"); + Assert.IsNotNull(info); APIConfiguration api = info.CreateDefaultAPIConfiguration(); @@ -423,7 +464,7 @@ public void TestScripting() { String SCRIPT = "import org.identityconnectors.testconnector\n" + - "TstConnector.getVersion()"; + "TstConnector.GetVersion()"; builder.ScriptText = (SCRIPT); String result = (String)facade.RunScriptOnConnector(builder.Build(), null); @@ -467,6 +508,300 @@ public void TestScripting() } } + [Test] + public void TestConnectorContext() + { + ConnectorPoolManager.Dispose(); + ConnectorInfoManager manager = GetConnectorInfoManager(); + ConnectorInfo info1 = FindConnectorInfo(manager, "1.0.0.0", "org.identityconnectors.testconnector.TstStatefulConnector"); + Assert.IsNotNull(info1); + + APIConfiguration config = info1.CreateDefaultAPIConfiguration(); + + config.ConnectorPoolConfiguration.MinIdle = 0; + config.ConnectorPoolConfiguration.MaxIdle = 0; + + ConnectorFacade facade1 = ConnectorFacadeFactory.GetInstance().NewInstance(config); + + ICollection attrs = CollectionUtil.NewReadOnlySet(); + string uid = facade1.Create(ObjectClass.ACCOUNT, attrs, null).GetUidValue(); + Assert.AreEqual(facade1.Create(ObjectClass.ACCOUNT, attrs, null).GetUidValue(), uid); + Assert.AreEqual(facade1.Create(ObjectClass.ACCOUNT, attrs, null).GetUidValue(), uid); + Assert.AreEqual(facade1.Create(ObjectClass.ACCOUNT, attrs, null).GetUidValue(), uid); + Assert.AreEqual(facade1.Create(ObjectClass.ACCOUNT, attrs, null).GetUidValue(), uid); + Assert.AreEqual(facade1.Create(ObjectClass.ACCOUNT, attrs, null).GetUidValue(), uid); + Assert.AreEqual(facade1.Create(ObjectClass.ACCOUNT, attrs, null).GetUidValue(), uid); + + config = info1.CreateDefaultAPIConfiguration(); + config.ConnectorPoolConfiguration.MinIdle = 1; + config.ConnectorPoolConfiguration.MaxIdle = 2; + facade1 = ConnectorFacadeFactory.GetInstance().NewInstance(config); + uid = facade1.Create(ObjectClass.ACCOUNT, attrs, null).GetUidValue(); + Assert.AreEqual(facade1.Create(ObjectClass.ACCOUNT, attrs, null).GetUidValue(), uid); + Assert.AreEqual(facade1.Create(ObjectClass.ACCOUNT, attrs, null).GetUidValue(), uid); + Assert.AreEqual(facade1.Create(ObjectClass.ACCOUNT, attrs, null).GetUidValue(), uid); + Assert.AreEqual(facade1.Create(ObjectClass.ACCOUNT, attrs, null).GetUidValue(), uid); + Assert.AreEqual(facade1.Create(ObjectClass.ACCOUNT, attrs, null).GetUidValue(), uid); + Assert.AreEqual(facade1.Create(ObjectClass.ACCOUNT, attrs, null).GetUidValue(), uid); + } + + [Test] + public void TestPagedSearch() + { + ConnectorPoolManager.Dispose(); + ConnectorInfoManager manager = GetConnectorInfoManager(); + ConnectorInfo info1 = FindConnectorInfo(manager, "1.0.0.0", "org.identityconnectors.testconnector.TstStatefulPoolableConnector"); + Assert.IsNotNull(info1); + + APIConfiguration config = info1.CreateDefaultAPIConfiguration(); + config.ProducerBufferSize = 0; + + config.ConnectorPoolConfiguration.MinIdle = 1; + config.ConnectorPoolConfiguration.MaxIdle = 2; + + ConnectorFacade facade1 = ConnectorFacadeFactory.GetInstance().NewInstance(config); + + OperationOptionsBuilder builder = new OperationOptionsBuilder(); + builder.PageSize = 10; + builder.SetSortKeys(new ICF.SortKey(Name.NAME, true)); + + SearchResult searchResult = null; + ISet UIDs = new HashSet(); + + int iteration = 0; + do + { + + if (null != searchResult) + { + builder.PagedResultsCookie = searchResult.PagedResultsCookie; + } + + int size = 0; + searchResult = facade1.Search(ObjectClass.ACCOUNT, null, new ResultsHandler() + { + + Handle = obj => + { + if (size >= 10) + { + Assert.Fail("More then 10 objects was handled!"); + } + size++; + if (UIDs.Contains(obj.Uid)) + { + Assert.Fail("Duplicate Entry in results"); + } + return UIDs.Add(obj.Uid); + } + }, builder.Build()); + iteration++; + Assert.IsNotNull(searchResult); + Assert.AreEqual(searchResult.RemainingPagedResults, 100 - (iteration * 10)); + + } while (searchResult.PagedResultsCookie != null); + + // Search with paged results offset + + builder = new OperationOptionsBuilder(); + builder.PageSize = 10; + builder.PagedResultsOffset = 5; + builder.SetSortKeys(new ICF.SortKey(Name.NAME, true)); + + searchResult = null; + + UIDs.Clear(); + Filter filter = FilterBuilder.EqualTo(ConnectorAttributeBuilder.BuildEnabled(true)); + + iteration = 0; + do + { + + if (null != searchResult) + { + builder.PagedResultsCookie = searchResult.PagedResultsCookie; + } + + int size = 0; + searchResult = facade1.Search(ObjectClass.ACCOUNT, filter, new ResultsHandler() + { + Handle = obj => + { + if (size >= 10) + { + Assert.Fail("More then 10 objects was handled!"); + } + size++; + if (UIDs.Contains(obj.Uid)) + { + Assert.Fail("Duplicate Entry in results"); + } + return UIDs.Add(obj.Uid); + } + }, builder.Build()); + iteration++; + Assert.IsNotNull(searchResult); + Assert.AreEqual(searchResult.RemainingPagedResults, Math.Max(50 - (iteration * 15), 0)); + + } while (searchResult.PagedResultsCookie != null); + } + + [Test] + public void TestTimeout() + { + ConnectorInfoManager manager = GetConnectorInfoManager(); + ConnectorInfo info1 = FindConnectorInfo(manager, "1.0.0.0", "org.identityconnectors.testconnector.TstConnector"); + Assert.IsNotNull(info1); + + APIConfiguration config = info1.CreateDefaultAPIConfiguration(); + config.SetTimeout(SafeType.ForRawType(typeof(CreateApiOp)), 5000); + config.SetTimeout(SafeType.ForRawType(typeof(SearchApiOp)), 5000); + ConfigurationProperties props = config.ConfigurationProperties; + ConfigurationProperty property = props.GetProperty("numResults"); + // 1000 is several times the remote size between pauses + property.Value = 2; + OperationOptionsBuilder opBuilder = new OperationOptionsBuilder(); + opBuilder.SetOption("delay", 10000); + + ConnectorFacade facade1 = ConnectorFacadeFactory.GetInstance().NewInstance(config); + + ICollection attrs = CollectionUtil.NewReadOnlySet(); + try + { + facade1.Create(ObjectClass.ACCOUNT, attrs, opBuilder.Build()).GetUidValue(); + Assert.Fail("expected timeout"); + } + catch (OperationTimeoutException) + { + // expected + } + //catch (RemoteWrappedException e) + //{ + // Assert.IsTrue(e.Is(typeof(OperationTimeoutException))); + //} + + try + { + facade1.Search(ObjectClass.ACCOUNT, null, new ResultsHandler() + { + Handle = obj => + { + return true; + } + }, opBuilder.Build()); + Assert.Fail("expected timeout"); + } + catch (OperationTimeoutException) + { + // expected + } + //catch (RemoteWrappedException e) + //{ + // Assert.IsTrue(e.Is(typeof(OperationTimeoutException))); + //} + } + + [Test] + public void TestMVCCControl() + { + + foreach (ConnectorFacade facade in CreateStateFulFacades()) + { + + + Uid uid = facade.Create(ObjectClass.ACCOUNT, CollectionUtil.NewReadOnlySet(), null); + + + if (facade is LocalConnectorFacadeImpl) + { + try + { + facade.Delete(ObjectClass.ACCOUNT, uid, null); + } + catch (PreconditionRequiredException) + { + // Expected + } + catch (Exception) + { + Assert.Fail("Expecting PreconditionRequiredException"); + } + try + { + facade.Delete(ObjectClass.ACCOUNT, new Uid(uid.GetUidValue(), "0"), null); + } + catch (PreconditionFailedException) + { + // Expected + } + catch (Exception) + { + Assert.Fail("Expecting PreconditionFailedException"); + } + facade.Delete(ObjectClass.ACCOUNT, new Uid(uid.GetUidValue(), uid.GetUidValue()), null); + } + else + { + try + { + facade.Delete(ObjectClass.ACCOUNT, uid, null); + } + catch (RemoteWrappedException e) + { + if (!e.Is(typeof(PreconditionRequiredException))) + { + Assert.Fail("Expecting PreconditionRequiredException"); + } + } + catch (Exception) + { + Assert.Fail("Expecting RemoteWrappedException"); + } + try + { + facade.Delete(ObjectClass.ACCOUNT, new Uid(uid.GetUidValue(), "0"), null); + } + catch (RemoteWrappedException e) + { + if (!e.Is(typeof(PreconditionFailedException))) + { + Assert.Fail("Expecting PreconditionFailedException"); + } + } + catch (Exception) + { + Assert.Fail("Expecting RemoteWrappedException"); + } + facade.Delete(ObjectClass.ACCOUNT, new Uid(uid.GetUidValue(), uid.GetUidValue()), null); + } + } + } + + public IList CreateStateFulFacades() + { + IList test = new List(2); + + ConnectorInfoManager manager = GetConnectorInfoManager(); + ConnectorInfo info = FindConnectorInfo(manager, "1.0.0.0", "org.identityconnectors.testconnector.TstStatefulConnector"); + Assert.IsNotNull(info); + + APIConfiguration config = info.CreateDefaultAPIConfiguration(); + + test.Add(ConnectorFacadeFactory.GetInstance().NewInstance(config)); + + info = FindConnectorInfo(manager, "1.0.0.0", "org.identityconnectors.testconnector.TstStatefulPoolableConnector"); + Assert.IsNotNull(info); + + config = info.CreateDefaultAPIConfiguration(); + + config.ConnectorPoolConfiguration.MinIdle = 0; + config.ConnectorPoolConfiguration.MaxIdle = 0; + + test.Add(ConnectorFacadeFactory.GetInstance().NewInstance(config)); + + return test; + } + protected virtual ConnectorInfoManager GetConnectorInfoManager() { ConnectorInfoManagerFactory fact = ConnectorInfoManagerFactory.GetInstance(); @@ -531,6 +866,64 @@ protected override void ShutdownConnnectorInfoManager() _server = null; } } + + [Test, Explicit] + [Category("LongRunning")] + public virtual void TestFacadeEviction() + { + ConnectorServer server = ConnectorServer.NewInstance(); + try + { + GuardedString str = new GuardedString(); + str.AppendChar('c'); + str.AppendChar('h'); + str.AppendChar('a'); + str.AppendChar('n'); + str.AppendChar('g'); + str.AppendChar('e'); + str.AppendChar('i'); + str.AppendChar('t'); + +#if DEBUG + const int PORT = 58760; +#else + const int PORT = 58761; +#endif + + server.MaxFacadeLifeTime = 1; + server.Port = PORT; + server.IfAddress = (IOUtil.GetIPAddress("127.0.0.1")); + server.KeyHash = str.GetBase64SHA1Hash(); + server.Start(); + + RemoteFrameworkConnectionInfo connInfo = + new RemoteFrameworkConnectionInfo("127.0.0.1", PORT, str, false, null, 0); + ConnectorInfoManager remoteManager = + ConnectorInfoManagerFactory.GetInstance().GetRemoteManager(connInfo); + + ConnectorInfo remoteInfo = + FindConnectorInfo(remoteManager, "1.0.0.0", "org.identityconnectors.testconnector.TstConnector"); + + ConnectorFacade remoteFacade = ConnectorFacadeFactory.GetInstance(). + NewInstance(remoteInfo.CreateDefaultAPIConfiguration()); + + ManagedConnectorFacadeFactoryImpl managedFactory = + (ManagedConnectorFacadeFactoryImpl)ConnectorFacadeFactory.GetManagedInstance(); + + // Assert it's empty + Assert.IsNull(managedFactory.Find(remoteFacade.ConnectorFacadeKey)); + remoteFacade.Schema(); + // Assert it has one item + Assert.IsNotNull(managedFactory.Find(remoteFacade.ConnectorFacadeKey)); + Thread.Sleep(new TimeSpan(0, 2, 0)); + // Assert it's empty + Assert.IsNull(managedFactory.Find(remoteFacade.ConnectorFacadeKey)); + } + finally + { + server.Stop(); + } + } } internal class MyCertificateValidationCallback diff --git a/FrameworkTests/ExceptionUtilTests.cs b/FrameworkTests/ExceptionUtilTests.cs index dc9a4ad3..5159c149 100644 --- a/FrameworkTests/ExceptionUtilTests.cs +++ b/FrameworkTests/ExceptionUtilTests.cs @@ -19,13 +19,10 @@ * enclosed by brackets [] replaced by your own identifying information: * "Portions Copyrighted [year] [name of copyright owner]" * ==================== + * Portions Copyrighted 2014 ForgeRock AS. */ using System; -using System.Collections.Generic; -using System.Linq; -using System.Text; using NUnit.Framework; -using Org.IdentityConnectors.Test.Common; using System.Diagnostics; using Org.IdentityConnectors.Framework.Impl; diff --git a/FrameworkTests/FilterTranslatorTests.cs b/FrameworkTests/FilterTranslatorTests.cs index 7d024f7a..173646c8 100644 --- a/FrameworkTests/FilterTranslatorTests.cs +++ b/FrameworkTests/FilterTranslatorTests.cs @@ -19,11 +19,11 @@ * enclosed by brackets [] replaced by your own identifying information: * "Portions Copyrighted [year] [name of copyright owner]" * ==================== + * Portions Copyrighted 2014 ForgeRock AS. */ using System; using System.Collections.Generic; using NUnit.Framework; -using Org.IdentityConnectors.Common; using Org.IdentityConnectors.Framework.Common.Objects; using Org.IdentityConnectors.Framework.Common.Objects.Filters; namespace FrameworkTests diff --git a/FrameworkTests/FrameworkTests.csproj b/FrameworkTests/FrameworkTests.csproj index ec3bde17..bf660f45 100644 --- a/FrameworkTests/FrameworkTests.csproj +++ b/FrameworkTests/FrameworkTests.csproj @@ -89,6 +89,7 @@ + diff --git a/FrameworkTests/GuardedByteArrayTests.cs b/FrameworkTests/GuardedByteArrayTests.cs index 966d90bf..32d4ffdf 100644 --- a/FrameworkTests/GuardedByteArrayTests.cs +++ b/FrameworkTests/GuardedByteArrayTests.cs @@ -19,11 +19,9 @@ * enclosed by brackets [] replaced by your own identifying information: * "Portions Copyrighted [year] [name of copyright owner]" * ==================== + * Portions Copyrighted 2014 ForgeRock AS. */ using System; -using System.Security; -using System.Runtime.InteropServices; -using System.Text; using Org.IdentityConnectors.Common.Security; using Org.IdentityConnectors.Framework.Common.Serializer; using NUnit.Framework; diff --git a/FrameworkTests/GuardedStringTests.cs b/FrameworkTests/GuardedStringTests.cs index 9b671e4f..07896ab5 100644 --- a/FrameworkTests/GuardedStringTests.cs +++ b/FrameworkTests/GuardedStringTests.cs @@ -19,10 +19,9 @@ * enclosed by brackets [] replaced by your own identifying information: * "Portions Copyrighted [year] [name of copyright owner]" * ==================== + * Portions Copyrighted 2014 ForgeRock AS. */ using System; -using System.Security; -using System.Runtime.InteropServices; using System.Text; using Org.IdentityConnectors.Common.Security; using Org.IdentityConnectors.Framework.Common.Serializer; diff --git a/FrameworkTests/MockConnector.cs b/FrameworkTests/MockConnector.cs index 3809ab80..a63ef2e6 100644 --- a/FrameworkTests/MockConnector.cs +++ b/FrameworkTests/MockConnector.cs @@ -19,6 +19,7 @@ * enclosed by brackets [] replaced by your own identifying information: * "Portions Copyrighted [year] [name of copyright owner]" * ==================== + * Portions Copyrighted 2014 ForgeRock AS. */ using System; @@ -125,7 +126,7 @@ public static void AddCall(string methodName, params object[] args) public class MockAllOpsConnector : MockConnector, CreateOp, DeleteOp, UpdateOp, SearchOp, UpdateAttributeValuesOp, AuthenticateOp, - ResolveUsernameOp, TestOp, ScriptOnConnectorOp, ScriptOnResourceOp + ResolveUsernameOp, TestOp, ScriptOnConnectorOp, ScriptOnResourceOp, SyncOp { public object RunScriptOnConnector(ScriptContext request, @@ -224,6 +225,22 @@ public void Test() { AddCall("Test"); } + + public void Sync(ObjectClass objectClass, SyncToken token, SyncResultsHandler handler, OperationOptions options) + { + Assert.IsNotNull(objectClass); + Assert.IsNotNull(token); + Assert.IsNotNull(handler); + Assert.IsNotNull(options); + AddCall("Sync", objectClass, token, handler, options); + } + + public SyncToken GetLatestSyncToken(ObjectClass objectClass) + { + Assert.IsNotNull(objectClass); + AddCall("GetLatestSyncToken", objectClass); + return new SyncToken(0); + } } public class MockUpdateConnector : Connector, UpdateOp, SearchOp @@ -294,7 +311,7 @@ public void ExecuteQuery(ObjectClass oclass, string query, ResultsHandler handle { foreach (ConnectorObject obj in objects) { - if (!handler(obj)) + if (!handler.Handle(obj)) { break; } diff --git a/FrameworkTests/ObjectPoolTests.cs b/FrameworkTests/ObjectPoolTests.cs index bfdccdd4..957c1404 100644 --- a/FrameworkTests/ObjectPoolTests.cs +++ b/FrameworkTests/ObjectPoolTests.cs @@ -19,6 +19,7 @@ * enclosed by brackets [] replaced by your own identifying information: * "Portions Copyrighted [year] [name of copyright owner]" * ==================== + * Portions Copyrighted 2014 ForgeRock AS. */ using System; using System.Threading; @@ -26,8 +27,6 @@ using Org.IdentityConnectors.Common.Pooling; using Org.IdentityConnectors.Framework.Common.Exceptions; -using Org.IdentityConnectors.Framework.Common.Objects; -using Org.IdentityConnectors.Framework.Spi; using Org.IdentityConnectors.Framework.Impl.Api.Local; namespace FrameworkTests @@ -67,7 +66,7 @@ private class MyTestConnectionFactory : ObjectPoolHandler private bool _createBadConnection = false; private int _totalCreatedConnections = 0; - public MyTestConnection NewObject() + public MyTestConnection MakeObject() { _totalCreatedConnections++; MyTestConnection rv = new MyTestConnection(); @@ -101,6 +100,15 @@ public bool CreateBadConnection _createBadConnection = value; } } + + public ObjectPoolConfiguration Validate(ObjectPoolConfiguration original) + { + return original; + } + + public void Shutdown() + { + } } private class MyTestThread diff --git a/FrameworkTests/ObjectSerializationTests.cs b/FrameworkTests/ObjectSerializationTests.cs index e5872370..e4b0908b 100644 --- a/FrameworkTests/ObjectSerializationTests.cs +++ b/FrameworkTests/ObjectSerializationTests.cs @@ -19,7 +19,7 @@ * enclosed by brackets [] replaced by your own identifying information: * "Portions Copyrighted [year] [name of copyright owner]" * ==================== - * Portions Copyrighted 2012-2013 ForgeRock AS + * Portions Copyrighted 2012-2014 ForgeRock AS. */ using System; using System.IO; @@ -27,10 +27,8 @@ using NUnit.Framework; using System.Collections.Generic; using System.Globalization; -using System.Security; using System.Linq; using System.Xml; -using Org.IdentityConnectors.Framework.Impl.Serializer.Binary; using Org.IdentityConnectors.Common; using Org.IdentityConnectors.Common.Pooling; using Org.IdentityConnectors.Common.Security; @@ -44,7 +42,7 @@ using Org.IdentityConnectors.Framework.Impl.Api; using Org.IdentityConnectors.Framework.Impl.Api.Remote; using Org.IdentityConnectors.Framework.Impl.Api.Remote.Messages; -using Org.IdentityConnectors.Framework.Impl.Serializer.Xml; + namespace FrameworkTests { [TestFixture] @@ -960,7 +958,7 @@ public void TestOperationRequest() OperationRequest(new ConnectorKey("my bundle", "my version", "my connector"), - apiImpl, + SerializerUtil.SerializeBase64Object(apiImpl), SafeType.Get(), "mymethodName", args); @@ -968,7 +966,7 @@ public void TestOperationRequest() Assert.AreEqual("my bundle", v2.ConnectorKey.BundleName); Assert.AreEqual("my version", v2.ConnectorKey.BundleVersion); Assert.AreEqual("my connector", v2.ConnectorKey.ConnectorName); - Assert.IsNotNull(v2.Configuration); + Assert.IsNotNull(v2.ConnectorFacadeKey); Assert.AreEqual(SafeType.Get(), v2.Operation); Assert.AreEqual("mymethodName", v2.OperationMethodName); Assert.IsTrue( @@ -986,7 +984,7 @@ public void TestOperationResponseEnd() [Test] public void TestOperationResponsePart() { - Exception ex = new Exception("foo"); + Exception ex = new Exception("foo", new ArgumentException("Cause")); OperationResponsePart v1 = new OperationResponsePart(ex, "bar"); OperationResponsePart v2 = (OperationResponsePart)CloneObject(v1); Assert.IsNotNull(v2.Exception); diff --git a/FrameworkTests/PropertyBagTests.cs b/FrameworkTests/PropertyBagTests.cs index c14b13de..05426ea5 100644 --- a/FrameworkTests/PropertyBagTests.cs +++ b/FrameworkTests/PropertyBagTests.cs @@ -19,11 +19,10 @@ * enclosed by brackets [] replaced by your own identifying information: * "Portions Copyrighted [year] [name of copyright owner]" * ==================== + * Portions Copyrighted 2014 ForgeRock AS. */ using System; using System.Collections.Generic; -using System.Linq; -using System.Text; using NUnit.Framework; using Org.IdentityConnectors.Test.Common; using Org.IdentityConnectors.Common; diff --git a/FrameworkTests/ProxyTests.cs b/FrameworkTests/ProxyTests.cs index 734f0b92..968986bb 100644 --- a/FrameworkTests/ProxyTests.cs +++ b/FrameworkTests/ProxyTests.cs @@ -19,14 +19,14 @@ * enclosed by brackets [] replaced by your own identifying information: * "Portions Copyrighted [year] [name of copyright owner]" * ==================== + * Portions Copyrighted 2014 ForgeRock AS. */ using System; using NUnit.Framework; using System.Reflection; -using System.Reflection.Emit; using Org.IdentityConnectors.Common.Proxy; using System.Collections.Generic; -using Org.IdentityConnectors.Common; + namespace FrameworkTests { public interface MyTestInterface diff --git a/FrameworkTests/SafeTypeTest.cs b/FrameworkTests/SafeTypeTest.cs index 5b126a2f..441b629a 100644 --- a/FrameworkTests/SafeTypeTest.cs +++ b/FrameworkTests/SafeTypeTest.cs @@ -19,8 +19,9 @@ * enclosed by brackets [] replaced by your own identifying information: * "Portions Copyrighted [year] [name of copyright owner]" * ==================== + * Portions Copyrighted 2014 ForgeRock AS. */ -using System; + using NUnit.Framework; using Org.IdentityConnectors.Common; using Org.IdentityConnectors.Framework.Api.Operations; diff --git a/FrameworkTests/ScriptTests.cs b/FrameworkTests/ScriptTests.cs index 8444de67..697ea8fe 100644 --- a/FrameworkTests/ScriptTests.cs +++ b/FrameworkTests/ScriptTests.cs @@ -19,11 +19,11 @@ * enclosed by brackets [] replaced by your own identifying information: * "Portions Copyrighted [year] [name of copyright owner]" * ==================== + * Portions Copyrighted 2014 ForgeRock AS. */ using System; using System.Collections.Generic; using System.Reflection; -using Org.IdentityConnectors.Common; using Org.IdentityConnectors.Common.Script; using Org.IdentityConnectors.Framework.Common.Objects; using NUnit.Framework; @@ -53,7 +53,7 @@ public void testShellScripting() ScriptExecutorFactory factory = ScriptExecutorFactory.NewInstance("Shell"); ScriptExecutor exe = factory.NewScriptExecutor(new Assembly[0], "echo bob", false); IDictionary vals = new Dictionary(); - Assert.AreEqual(0, exe.Execute(vals)); + Assert.AreEqual(0, ((IDictionary)exe.Execute(vals))["exitCode"]); } [Test] [ExpectedException(typeof(ArgumentException))] diff --git a/FrameworkTests/StringUtilTests.cs b/FrameworkTests/StringUtilTests.cs new file mode 100755 index 00000000..94fc6007 --- /dev/null +++ b/FrameworkTests/StringUtilTests.cs @@ -0,0 +1,224 @@ +/* + * ==================== + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER. + * + * Copyright 2008-2009 Sun Microsystems, Inc. All rights reserved. + * + * The contents of this file are subject to the terms of the Common Development + * and Distribution License("CDDL") (the "License"). You may not use this file + * except in compliance with the License. + * + * You can obtain a copy of the License at + * http://opensource.org/licenses/cddl1.php + * See the License for the specific language governing permissions and limitations + * under the License. + * + * When distributing the Covered Code, include this CDDL Header Notice in each file + * and include the License file at http://opensource.org/licenses/cddl1.php. + * If applicable, add the following below this CDDL Header, with the fields + * enclosed by brackets [] replaced by your own identifying information: + * "Portions Copyrighted [year] [name of copyright owner]" + * ==================== + * Portions Copyrighted 2014 ForgeRock AS. + */ + +using System; +using System.Collections.Generic; +using System.Text; +using Org.IdentityConnectors.Common; +using NUnit.Framework; + +namespace FrameworkTests +{ + /// + /// Description of ScriptTests. + /// + [TestFixture] + public class StringUtilTests + { + [Test] + public virtual void TestIndexOfDigit() + { + int test = 0; + const string TEST0 = null; + const string TEST1 = "fsadlkjffj"; + const string TEST2 = "abac2dafj"; + const string TEST3 = "fa323jf4af"; + + test = StringUtil.IndexOfDigit(TEST0); + Assert.AreEqual(test, -1); + test = StringUtil.IndexOfDigit(TEST1); + Assert.AreEqual(test, -1); + test = StringUtil.IndexOfDigit(TEST2); + Assert.AreEqual(test, 4); + test = StringUtil.IndexOfDigit(TEST3); + Assert.AreEqual(test, 2); + } + + [Test] + public virtual void TestIndexOfNonDigit() + { + int test = 0; + const string TEST0 = null; + const string TEST1 = "2131398750976"; + const string TEST2 = "21312a9320484"; + const string TEST3 = "32323aa323435"; + + test = StringUtil.IndexOfNonDigit(TEST0); + Assert.AreEqual(test, -1); + test = StringUtil.IndexOfNonDigit(TEST1); + Assert.AreEqual(test, -1); + test = StringUtil.IndexOfNonDigit(TEST2); + Assert.AreEqual(test, 5); + test = StringUtil.IndexOfNonDigit(TEST3); + Assert.AreEqual(test, 5); + } + + [Test] + public virtual void TestSubDigitString() + { + } + + [Test] + public virtual void TestStripXmlAttribute() + { + string[][] DATA = new string[][] { new string[] { null, null, null }, new string[] { "attr='fads'", "attr", "" }, new string[] { "at1='fasd' at1=''", "at1", "" } }; + string tst = null; + for (int i = 0; i < DATA.Length; i++) + { + tst = StringUtil.StripXmlAttribute(DATA[i][0], DATA[i][1]); + Assert.AreEqual(tst, DATA[i][2]); + } + } + + /// + /// Make sure it removes '\n'. + /// + [Test] + public virtual void TestStripNewlines() + { + string[][] TESTS = new string[][] { new string[] { null, null }, new string[] { "afdslf\n", "afdslf" }, new string[] { "afds\nfadkfj", "afdsfadkfj" }, new string[] { "afds \nfadkfj", "afds fadkfj" }, new string[] { "afds\n fadkfj", "afds fadkfj" } }; + string tmp; + foreach (string[] data in TESTS) + { + tmp = StringUtil.StripNewlines(data[0]); + Assert.AreEqual(tmp, data[1]); + } + } + + [Test] + public virtual void TestStripXmlComments() + { + string[][] DATA = new string[][] { new string[] { null, null }, new string[] { "", "" }, new string[] { "test data", "test data" }, new string[] { "", "test data-->" }, new string[] { "test data ", "test data " }, new string[] { " test data", " test data" }, new string[] { " test data", " test data" } }; + + string tst = null; + for (int i = 0; i < DATA.Length; i++) + { + tst = StringUtil.StripXmlComments(DATA[i][0]); + Assert.AreEqual(tst, DATA[i][1]); + } + } + + /// + /// Tests the methods on the + /// arguments provided. + /// + internal static void ParseLineTest(char textQ, char fieldD, IList values) + { + string csv = CreateCSVLine(textQ, fieldD, values); + IList parsedValues = StringUtil.ParseLine(csv, fieldD, textQ); + Assert.AreEqual(parsedValues, ToStringList(values)); + } + + /// + /// Create a CSV line based on the values provided. + /// + internal static string CreateCSVLine(char textQ, char fieldD, IList values) + { + StringBuilder bld = new StringBuilder(); + bool first = true; + foreach (object o in values) + { + // apply field delimiter.. + if (first) + { + first = false; + } + else + { + bld.Append(fieldD); + } + // if its a string add the text qualifiers.. + // don't bother escape text qualifiers in the string yet.. + if (o is string) + { + bld.Append(textQ); + } + bld.Append(o); + if (o is string) + { + bld.Append(textQ); + } + } + return bld.ToString(); + } + + /// + /// Converts a of objects to a of s. + /// + internal static IList ToStringList(IList list) + { + IList ret = new List(); + foreach (object o in list) + { + ret.Add(o.ToString()); + } + return ret; + } + + internal static IList RandomList(Random r, int size, char[] invalid, char valid) + { + IList ret = new List(); + for (int i = 0; i < size; i++) + { + object add; + if (r.Next(100) % 2 == 0) + { + add = r.Next(); + } + else if (r.Next(100) % 2 == 0) + { + add = r.NextDouble(); + } + else + { + string str = StringUtil.RandomString(r, r.Next(30)); + foreach (char c in invalid) + { + // replace all w/ 'a'.. + str = str.Replace(c, valid); + } + add = str; + } + ret.Add(add); + } + return ret; + } + + [Test] + public virtual void TestRandomString() + { + // just execute it because it doesn't really matter.. + string s = StringUtil.RandomString(); + Assert.IsTrue(s.Length < 257); + } + + [Test] + public virtual void testEndsWith() + { + Assert.IsTrue(StringUtil.EndsWith("afdsf", 'f')); + Assert.IsFalse(StringUtil.EndsWith(null, 'f')); + Assert.IsFalse(StringUtil.EndsWith("fadsfkj", 'f')); + } + } +} \ No newline at end of file diff --git a/FrameworkTests/TestHelperTests.cs b/FrameworkTests/TestHelperTests.cs index de48d413..19497c28 100644 --- a/FrameworkTests/TestHelperTests.cs +++ b/FrameworkTests/TestHelperTests.cs @@ -19,13 +19,12 @@ * enclosed by brackets [] replaced by your own identifying information: * "Portions Copyrighted [year] [name of copyright owner]" * ==================== + * Portions Copyrighted 2014 ForgeRock AS. */ using System; using System.IO; using System.Xml; using System.Collections.Generic; -using System.Linq; - using NUnit.Framework; using Org.IdentityConnectors.Framework.Common.Objects; diff --git a/FrameworkTests/UpdateImplTests.cs b/FrameworkTests/UpdateImplTests.cs index 9af682e6..013b7ece 100644 --- a/FrameworkTests/UpdateImplTests.cs +++ b/FrameworkTests/UpdateImplTests.cs @@ -19,14 +19,13 @@ * enclosed by brackets [] replaced by your own identifying information: * "Portions Copyrighted [year] [name of copyright owner]" * ==================== + * Portions Copyrighted 2014 ForgeRock AS. */ using System; using System.Security; using NUnit.Framework; using System.Collections.Generic; using Org.IdentityConnectors.Common; -using Org.IdentityConnectors.Framework.Api; -using Org.IdentityConnectors.Framework.Api.Operations; using Org.IdentityConnectors.Framework.Impl.Api.Local.Operations; using Org.IdentityConnectors.Framework.Common.Objects; namespace FrameworkTests diff --git a/PowerShellScriptExecutorFactory/PowerShellScriptExecutorFactory.cs b/PowerShellScriptExecutorFactory/PowerShellScriptExecutorFactory.cs index 5b98e401..c4810972 100644 --- a/PowerShellScriptExecutorFactory/PowerShellScriptExecutorFactory.cs +++ b/PowerShellScriptExecutorFactory/PowerShellScriptExecutorFactory.cs @@ -1,7 +1,7 @@ /** * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER. * -* Copyright (c) 2012 ForgeRock AS. All Rights Reserved +* Copyright (c) 2012-2014 ForgeRock AS. All Rights Reserved * * The contents of this file are subject to the terms * of the Common Development and Distribution License @@ -22,13 +22,12 @@ * "Portions Copyrighted [year] [name of copyright owner]" * * -* * Author: Gael Allioux +* Author: Gael Allioux */ using System; using System.Collections.Generic; using System.Collections.ObjectModel; -using System.Text; using System.Reflection; using System.Management.Automation; using System.Management.Automation.Runspaces; diff --git a/Service/Program.cs b/Service/Program.cs index 376a9585..20101101 100644 --- a/Service/Program.cs +++ b/Service/Program.cs @@ -19,16 +19,14 @@ * enclosed by brackets [] replaced by your own identifying information: * "Portions Copyrighted [year] [name of copyright owner]" * ==================== + * Portions Copyrighted 2014 ForgeRock AS. */ using System; -using System.ComponentModel; using System.Configuration; using System.Configuration.Install; using System.Collections.Generic; -using System.Security; using System.Reflection; using System.ServiceProcess; -using System.Text; using Org.IdentityConnectors.Common.Security; using System.Security.Cryptography.X509Certificates; diff --git a/Service/ProjectInstaller.cs b/Service/ProjectInstaller.cs index 86bde3ee..7c547c36 100644 --- a/Service/ProjectInstaller.cs +++ b/Service/ProjectInstaller.cs @@ -19,9 +19,9 @@ * enclosed by brackets [] replaced by your own identifying information: * "Portions Copyrighted [year] [name of copyright owner]" * ==================== + * Portions Copyrighted 2014 ForgeRock AS. */ -using System; -using System.Collections.Generic; + using System.ComponentModel; using System.Configuration.Install; using System.ServiceProcess; diff --git a/Service/Service.cs b/Service/Service.cs index b79cd3f3..96a542b7 100644 --- a/Service/Service.cs +++ b/Service/Service.cs @@ -19,19 +19,16 @@ * enclosed by brackets [] replaced by your own identifying information: * "Portions Copyrighted [year] [name of copyright owner]" * ==================== + * Portions Copyrighted 2014 ForgeRock AS. */ using System; -using System.Collections.Generic; using System.Collections.Specialized; -using System.ComponentModel; using System.Configuration; -using System.Data; using System.Diagnostics; using System.IO; using System.Reflection; using System.Security.Cryptography.X509Certificates; using System.ServiceProcess; -using System.Text; using Org.IdentityConnectors.Common; using Org.IdentityConnectors.Framework.Server; @@ -44,6 +41,7 @@ public class Service : ServiceBase private const string PROP_CERTSTORE = "connectorserver.certificatestorename"; private const string PROP_IFADDRESS = "connectorserver.ifaddress"; public const string PROP_KEY = "connectorserver.key"; + public const string PROP_FACADE_LIFETIME = "connectorserver.maxFacadeLifeTime"; private ConnectorServer _server; @@ -145,6 +143,11 @@ protected override void OnStart(string[] args) _server.IfAddress = IOUtil.GetIPAddress(ifaddress); } + String facedeLifeTimeStr = settings.Get(PROP_FACADE_LIFETIME); + if (facedeLifeTimeStr != null) + { + _server.MaxFacadeLifeTime = Int32.Parse(facedeLifeTimeStr); + } _server.Start(); Trace.TraceInformation("Started connector server"); } diff --git a/Service/app.config b/Service/app.config index e2e4e967..8e50e8e7 100644 --- a/Service/app.config +++ b/Service/app.config @@ -17,6 +17,7 @@ + Debug + false @@ -33,7 +34,7 @@ - @@ -41,7 +42,7 @@ - + @@ -52,15 +53,20 @@ + Lines="@(ShellScriptExecutorFactoryFiles->'<File Source="%(Fullpath)" Name="%(Filename)%(Extension)" />')"/> + + + + + + + - - - + @@ -77,11 +83,13 @@ - - - + + - + diff --git a/ServiceInstall/File.bottom b/ServiceInstall/File.bottom index 9e4c6103..034b63c4 100644 --- a/ServiceInstall/File.bottom +++ b/ServiceInstall/File.bottom @@ -1,7 +1,4 @@ - - - - - - - + + + + \ No newline at end of file diff --git a/ServiceInstall/File.top b/ServiceInstall/File.top index fba68484..2083c8d6 100644 --- a/ServiceInstall/File.top +++ b/ServiceInstall/File.top @@ -1,21 +1,24 @@ - + - - - - - - - - - - + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/ServiceInstall/Setup.wxs b/ServiceInstall/Setup.wxs index c9de0961..3de8eece 100644 --- a/ServiceInstall/Setup.wxs +++ b/ServiceInstall/Setup.wxs @@ -1,7 +1,7 @@ - + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + text/microsoft-resx + + + 2.0 + + + System.Resources.ResXResourceReader, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + <?xml version="1.0" encoding="utf-8" ?> +<configuration> + <runtime> + <loadFromRemoteSources enabled="true"/> + </runtime> + <connectionStrings> + <!-- Example connection to a SQL Server Database on localhost. --> + <!-- <add name="ExampleConnectionString" + connectionString="Data Source=.;Initial Catalog=DBName;Integrated Security=True" + providerName="System.Data.SqlClient" /> --> + </connectionStrings> + <appSettings> + <!-- access these values via the property: + System.Configuration.ConfigurationManager.AppSettings[key] + --> + <add key="connectorserver.port" value="8759" /> + <add key="connectorserver.usessl" value="false" /> + <add key="connectorserver.certificatestorename" value="ConnectorServerSSLCertificate" /> + <add key="connectorserver.ifaddress" value="0.0.0.0" /> + <add key="connectorserver.maxFacadeLifeTime" value="0" /> + <add key="connectorserver.key" value="lmA6bMfENJGlIDbfrVtklXFK32s=" /> + <!-- + Enable/Disable the logging proxy for all operations. + --> + <add key="logging.proxy" value="false"/> + </appSettings> + <system.diagnostics> + <trace autoflush="true" indentsize="4"> + <listeners> + <remove name="Default" /> + <add name="console" /> + <add name="file" /> + </listeners> + </trace> + <sources> + <source name="ConnectorServer" switchName="switch1"> + <listeners> + <remove name="Default" /> + <add name="file" /> + </listeners> + </source> + <source name="MyClass2" switchName="switch2"> + <listeners> + <remove name="Default" /> + <add name="file" /> + </listeners> + </source> + </sources> + <switches> + <add name="switch1" value="Information"/> + <add name="switch2" value="Warning"/> + </switches> + <sharedListeners> + <add name="console" + type="System.Diagnostics.ConsoleTraceListener"> + </add> + <add name="file" + type="System.Diagnostics.TextWriterTraceListener" + initializeData="connectorserver.log" + traceOutputOptions="DateTime"> + <filter type="System.Diagnostics.EventTypeFilter" initializeData="Information"/> + </add> + </sharedListeners> + </system.diagnostics> + +</configuration> + + \ No newline at end of file diff --git a/Service/Service.csproj b/Service/Service.csproj index 199bcfaf..f730b4b1 100644 --- a/Service/Service.csproj +++ b/Service/Service.csproj @@ -92,6 +92,11 @@ Component + + True + True + Resources.resx + Component @@ -117,4 +122,10 @@ Framework + + + ResXFileCodeGenerator + Resources.Designer.cs + + \ No newline at end of file From 33ed36af48d88be11c7b79a8a958b237929d36de Mon Sep 17 00:00:00 2001 From: Laszlo Hordos Date: Thu, 9 Oct 2014 12:59:32 +0000 Subject: [PATCH 321/342] Branch framework-1.4.1.x --- BooScriptExecutorFactory/version.template | 2 +- Common/version.template | 2 +- Framework/version.template | 2 +- FrameworkInternal/version.template | 2 +- FrameworkTests/version.template | 2 +- PowerShellScriptExecutorFactory/version.template | 2 +- Service/version.template | 2 +- ShellScriptExecutorFactory/version.template | 2 +- TestCommon/version.template | 2 +- 9 files changed, 9 insertions(+), 9 deletions(-) diff --git a/BooScriptExecutorFactory/version.template b/BooScriptExecutorFactory/version.template index 34c32671..2f5d8109 100644 --- a/BooScriptExecutorFactory/version.template +++ b/BooScriptExecutorFactory/version.template @@ -1 +1 @@ -1.4.1.0 \ No newline at end of file +1.4.2.0 \ No newline at end of file diff --git a/Common/version.template b/Common/version.template index 34c32671..2f5d8109 100644 --- a/Common/version.template +++ b/Common/version.template @@ -1 +1 @@ -1.4.1.0 \ No newline at end of file +1.4.2.0 \ No newline at end of file diff --git a/Framework/version.template b/Framework/version.template index 34c32671..2f5d8109 100644 --- a/Framework/version.template +++ b/Framework/version.template @@ -1 +1 @@ -1.4.1.0 \ No newline at end of file +1.4.2.0 \ No newline at end of file diff --git a/FrameworkInternal/version.template b/FrameworkInternal/version.template index 34c32671..2f5d8109 100644 --- a/FrameworkInternal/version.template +++ b/FrameworkInternal/version.template @@ -1 +1 @@ -1.4.1.0 \ No newline at end of file +1.4.2.0 \ No newline at end of file diff --git a/FrameworkTests/version.template b/FrameworkTests/version.template index 34c32671..2f5d8109 100644 --- a/FrameworkTests/version.template +++ b/FrameworkTests/version.template @@ -1 +1 @@ -1.4.1.0 \ No newline at end of file +1.4.2.0 \ No newline at end of file diff --git a/PowerShellScriptExecutorFactory/version.template b/PowerShellScriptExecutorFactory/version.template index 34c32671..2f5d8109 100644 --- a/PowerShellScriptExecutorFactory/version.template +++ b/PowerShellScriptExecutorFactory/version.template @@ -1 +1 @@ -1.4.1.0 \ No newline at end of file +1.4.2.0 \ No newline at end of file diff --git a/Service/version.template b/Service/version.template index 34c32671..2f5d8109 100644 --- a/Service/version.template +++ b/Service/version.template @@ -1 +1 @@ -1.4.1.0 \ No newline at end of file +1.4.2.0 \ No newline at end of file diff --git a/ShellScriptExecutorFactory/version.template b/ShellScriptExecutorFactory/version.template index 34c32671..2f5d8109 100644 --- a/ShellScriptExecutorFactory/version.template +++ b/ShellScriptExecutorFactory/version.template @@ -1 +1 @@ -1.4.1.0 \ No newline at end of file +1.4.2.0 \ No newline at end of file diff --git a/TestCommon/version.template b/TestCommon/version.template index 34c32671..2f5d8109 100644 --- a/TestCommon/version.template +++ b/TestCommon/version.template @@ -1 +1 @@ -1.4.1.0 \ No newline at end of file +1.4.2.0 \ No newline at end of file From 38e29bbe99f1d237a87a338bb3b798020190d927 Mon Sep 17 00:00:00 2001 From: Laszlo Hordos Date: Fri, 24 Oct 2014 07:34:52 +0000 Subject: [PATCH 322/342] OPENICF-317 CR-4972 Fix SyncDelta and SyncDeltaBuilder should enforce the presence of Uid and ObjectClass --- Framework/CommonObjects.cs | 6 ++---- FrameworkInternal/ApiLocal.cs | 10 ++++++++++ ServiceInstall/ServiceInstall.wixproj | 3 ++- 3 files changed, 14 insertions(+), 5 deletions(-) diff --git a/Framework/CommonObjects.cs b/Framework/CommonObjects.cs index b21c889d..4121a109 100644 --- a/Framework/CommonObjects.cs +++ b/Framework/CommonObjects.cs @@ -928,10 +928,7 @@ public override string ToString() // poor man's consistent toString impl.. StringBuilder bld = new StringBuilder(); bld.Append("ConnectorAttribute: "); - IDictionary map = new Dictionary(); - map["Name"] = Name; - map["Value"] = Value; - bld.Append(map.ToString()); + bld.Append(Name).Append(" = ").Append(CollectionUtil.Dump(Value)); return bld.ToString(); } @@ -5009,6 +5006,7 @@ internal SyncDelta(SyncToken token, SyncDeltaType deltaType, { Assertions.NullCheck(token, "token"); Assertions.NullCheck(deltaType, "deltaType"); + Assertions.NullCheck(objectClass, "objectClass"); Assertions.NullCheck(uid, "uid"); //do not allow previous Uid for anything else than create or update diff --git a/FrameworkInternal/ApiLocal.cs b/FrameworkInternal/ApiLocal.cs index af640024..725d9439 100644 --- a/FrameworkInternal/ApiLocal.cs +++ b/FrameworkInternal/ApiLocal.cs @@ -520,6 +520,16 @@ private IList ProcessAssembly(Assembly assembly) catch (Exception e) { TraceUtil.TraceException("Unable to load assembly: " + assembly.FullName + ". Assembly will be ignored.", e); + if (e is System.Reflection.ReflectionTypeLoadException) + { + var typeLoadException = e as ReflectionTypeLoadException; + var loaderExceptions = typeLoadException.LoaderExceptions; + foreach (var item in loaderExceptions) + { + TraceUtil.TraceException(" - loader exception: " + item, item); + } + } + return rv; } foreach (Type type in types) diff --git a/ServiceInstall/ServiceInstall.wixproj b/ServiceInstall/ServiceInstall.wixproj index 18f76183..3e0b546e 100644 --- a/ServiceInstall/ServiceInstall.wixproj +++ b/ServiceInstall/ServiceInstall.wixproj @@ -31,7 +31,7 @@ ServiceInstall $(MSBuildExtensionsPath)\Microsoft\WiX\v3.x\Wix.targets ICE45 - 1.4.1.0-SNAPSHOT + 1.4.2.0-SNAPSHOT prompt @@ -54,6 +54,7 @@ true True False + 1.4.2.0 From 7e07b624b7735b1e3414d4550d2274a43b67a4b9 Mon Sep 17 00:00:00 2001 From: Laszlo Hordos Date: Wed, 17 Dec 2014 09:49:26 +0000 Subject: [PATCH 323/342] OPENICF-330 CR-5634 Fix OR Queries against LDAP with not-found _id values return no results --- FrameworkInternal/ApiLocalOperations.cs | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) diff --git a/FrameworkInternal/ApiLocalOperations.cs b/FrameworkInternal/ApiLocalOperations.cs index 46052f23..64512234 100644 --- a/FrameworkInternal/ApiLocalOperations.cs +++ b/FrameworkInternal/ApiLocalOperations.cs @@ -634,7 +634,7 @@ public sealed class DuplicateFilteringResultsHandler private readonly SearchResultsHandler _handler; private readonly HashSet _visitedUIDs = new HashSet(); - private bool _stillHandling; + private bool _stillHandling = true; // ======================================================================= // Constructors @@ -1592,10 +1592,17 @@ public SyncToken Sync(ObjectClass objectClass, SyncToken token, SyncResultsHandl } SyncToken result = null; + Boolean doAll = ObjectClass.ALL.Equals(objectClass); ((SyncOp)GetConnector()).Sync(objectClass, token, new SyncTokenResultsHandler() { Handle = delta => { + if (doAll && SyncDeltaType.DELETE.Equals(delta.DeltaType) + && null == delta.ObjectClass) + { + throw new ConnectorException( + "Sync '__ALL__' operation requires the connector to set 'objectClass' parameter for sync event."); + } return handler.Handle(delta); }, HandleResult = obj => From 1e4858cdd75c5d5943fbd6dffad520d72302cf62 Mon Sep 17 00:00:00 2001 From: Laszlo Hordos Date: Wed, 17 Dec 2014 10:12:05 +0000 Subject: [PATCH 324/342] OPENICF-335 CR-5716 Revert OPENICF-317 fix which cause incompatibility in framework --- Framework/CommonObjects.cs | 1 - FrameworkTests/ConnectorFacadeTests.cs | 27 ++++++++++++++++++++++++++ FrameworkTests/MockConnector.cs | 21 ++++++++++++++++++++ 3 files changed, 48 insertions(+), 1 deletion(-) diff --git a/Framework/CommonObjects.cs b/Framework/CommonObjects.cs index 4121a109..dd648b40 100644 --- a/Framework/CommonObjects.cs +++ b/Framework/CommonObjects.cs @@ -5006,7 +5006,6 @@ internal SyncDelta(SyncToken token, SyncDeltaType deltaType, { Assertions.NullCheck(token, "token"); Assertions.NullCheck(deltaType, "deltaType"); - Assertions.NullCheck(objectClass, "objectClass"); Assertions.NullCheck(uid, "uid"); //do not allow previous Uid for anything else than create or update diff --git a/FrameworkTests/ConnectorFacadeTests.cs b/FrameworkTests/ConnectorFacadeTests.cs index d303f9a0..17b596b1 100644 --- a/FrameworkTests/ConnectorFacadeTests.cs +++ b/FrameworkTests/ConnectorFacadeTests.cs @@ -550,6 +550,33 @@ public virtual void SyncAllCallPattern() }); } + [Test] + [ExpectedException(typeof(ConnectorException), ExpectedMessage="Sync '__ALL__' operation requires.*", MatchType = MessageMatch.Regex)] + public virtual void SyncAllCallFailPattern() { + TestCallPattern(new TestOperationPattern() + { + MakeCall = facade => + { + // create an empty results handler.. + SyncResultsHandler rh = new SyncResultsHandler() + { + Handle = obj => + { + return true; + } + }; + // call the sync method.. + var builder = new OperationOptionsBuilder(); + builder.Options["FAIL_DELETE"] = true; + facade.Sync(ObjectClass.ALL, new SyncToken(1), rh, builder.Build()); + }, + CheckCalls = calls => + { + Assert.AreEqual("Sync", GetAndRemoveMethodName(calls)); + } + }); + } + [Test] public void TestOpCallPattern() { diff --git a/FrameworkTests/MockConnector.cs b/FrameworkTests/MockConnector.cs index a63ef2e6..b802801a 100644 --- a/FrameworkTests/MockConnector.cs +++ b/FrameworkTests/MockConnector.cs @@ -203,6 +203,11 @@ public void ExecuteQuery(ObjectClass oclass, string query, Assert.IsNotNull(handler); Assert.IsNotNull(options); AddCall("ExecuteQuery", oclass, query, handler, options); + if (null != options.PageSize && options.PageSize > 0) + { + // This is a pages search request + ((SearchResultsHandler)handler).HandleResult(new SearchResult("TOKEN==", 100)); + } } public Uid Authenticate(ObjectClass objectClass, string username, GuardedString password, @@ -233,6 +238,22 @@ public void Sync(ObjectClass objectClass, SyncToken token, SyncResultsHandler ha Assert.IsNotNull(handler); Assert.IsNotNull(options); AddCall("Sync", objectClass, token, handler, options); + if (ObjectClass.ALL.Equals(objectClass)) + { + if (null != CollectionUtil.GetValue(options.Options, "FAIL_DELETE", null)) + { + //Require ObjectClass when delta is 'delete' + var builder = new SyncDeltaBuilder(); + builder.DeltaType = SyncDeltaType.DELETE; + builder.Uid = new Uid("DELETED"); + builder.Token = new SyncToken(99); + handler.Handle(builder.Build()); + } + else + { + ((SyncTokenResultsHandler)handler).HandleResult(new SyncToken(100)); + } + } } public SyncToken GetLatestSyncToken(ObjectClass objectClass) From 79a53e179abcbd2619286f4a6945c127b6b94754 Mon Sep 17 00:00:00 2001 From: Laszlo Hordos Date: Wed, 15 Apr 2015 06:07:19 +0000 Subject: [PATCH 325/342] OPENICF-378 CR-6620 Fix Cannot change trace logger to VisualBasic FileLogTraceListener --- Framework.targets | 6 +++--- FrameworkInternal/Test.cs | 10 ++-------- FrameworkTests/VersionRangeTests.cs | 10 +++++----- .../ShellScriptExecutorFactory.cs | 2 +- 4 files changed, 11 insertions(+), 17 deletions(-) diff --git a/Framework.targets b/Framework.targets index c7339ff5..3ff384a0 100644 --- a/Framework.targets +++ b/Framework.targets @@ -24,7 +24,7 @@ $(MSBuildProjectDirectory)\version.template $(MSBuildProjectDirectory)\version.txt - ForgeRock AS + ForgeRock Copyright 2008-2009 Sun Microsystems, Inc. All rights reserved. $(MSBuildProjectDirectory)\..\Dist @@ -52,11 +52,11 @@ diff --git a/FrameworkInternal/Test.cs b/FrameworkInternal/Test.cs index 5f511af5..7282e64a 100644 --- a/FrameworkInternal/Test.cs +++ b/FrameworkInternal/Test.cs @@ -121,14 +121,8 @@ public APIConfiguration CreateTestConfiguration(SafeType connectorCla rawConnectorClass.Name)); ; APIConfigurationImpl impl = LocalConnectorInfoManagerImpl.CreateDefaultAPIConfiguration(rv); rv.DefaultAPIConfiguration = impl; - if (false) - { - rv.Messages = CreateDummyMessages(); - } - else - { - rv.Messages = LocalConnectorInfoManagerImpl.LoadMessages(assembly, rv, attribute.MessageCatalogPaths); - } + rv.Messages = LocalConnectorInfoManagerImpl.LoadMessages(assembly, rv, attribute.MessageCatalogPaths); + ConfigurationPropertiesImpl configProps = (ConfigurationPropertiesImpl)impl.ConfigurationProperties; string fullPrefix = StringUtil.IsBlank(prefix) ? null : prefix + "."; diff --git a/FrameworkTests/VersionRangeTests.cs b/FrameworkTests/VersionRangeTests.cs index b5c17a0d..732fbb23 100755 --- a/FrameworkTests/VersionRangeTests.cs +++ b/FrameworkTests/VersionRangeTests.cs @@ -76,7 +76,7 @@ public virtual void TestValidSyntax() VersionRange.Parse("(1.1.0.0)"); Assert.Fail("Invalid syntax not failed"); } - catch (System.FormatException e) + catch (System.FormatException) { // ok } @@ -85,7 +85,7 @@ public virtual void TestValidSyntax() VersionRange.Parse("1.1.0.0,1.1)]"); Assert.Fail("Invalid syntax not failed"); } - catch (System.ArgumentException e) + catch (System.ArgumentException) { // ok } @@ -94,7 +94,7 @@ public virtual void TestValidSyntax() VersionRange.Parse("(1.1.0.0-1.1)"); Assert.Fail("Invalid syntax not failed"); } - catch (System.ArgumentException e) + catch (System.ArgumentException) { // ok } @@ -103,7 +103,7 @@ public virtual void TestValidSyntax() VersionRange.Parse("1.1.0.0,1.1"); Assert.Fail("Invalid syntax not failed"); } - catch (System.ArgumentException e) + catch (System.ArgumentException) { // ok } @@ -112,7 +112,7 @@ public virtual void TestValidSyntax() VersionRange.Parse("( , 1.1)"); Assert.Fail("Invalid syntax not failed"); } - catch (System.ArgumentException e) + catch (System.ArgumentException) { // ok } diff --git a/ShellScriptExecutorFactory/ShellScriptExecutorFactory.cs b/ShellScriptExecutorFactory/ShellScriptExecutorFactory.cs index 056fab1f..19a66798 100644 --- a/ShellScriptExecutorFactory/ShellScriptExecutorFactory.cs +++ b/ShellScriptExecutorFactory/ShellScriptExecutorFactory.cs @@ -192,7 +192,7 @@ public object Execute(IDictionary arguments) { File.Delete(fn); } - catch (Exception e) + catch (Exception) { } From 4401de814e2cf8082d15c0ab8009bf980b4c7752 Mon Sep 17 00:00:00 2001 From: Laszlo Hordos Date: Wed, 17 Jun 2015 11:18:38 +0000 Subject: [PATCH 326/342] OPENICF-424 CR-7254 Add facility to notify application from Connector about configuration change --- Common/AsyncHandler.cs | 58 ++ Common/CollectionUtil.cs | 138 ++++- Common/Common.csproj | 1 + Common/Security.cs | 45 +- Framework/Api.cs | 46 +- Framework/ApiOperations.cs | 37 +- Framework/Common.cs | 170 +++++- Framework/CommonObjects.cs | 103 +++- Framework/CommonObjectsFilter.cs | 487 ++++++++++++++- Framework/Spi.cs | 32 +- Framework/SpiOperations.cs | 33 +- FrameworkInternal/Api.cs | 106 +++- FrameworkInternal/ApiLocal.cs | 195 ++++-- FrameworkInternal/ApiLocalOperations.cs | 283 +++++++-- FrameworkInternal/ApiRemote.cs | 7 +- FrameworkInternal/Serializer.cs | 18 +- FrameworkInternal/Test.cs | 6 +- FrameworkTests/ConnectorInfoManagerTests.cs | 71 ++- FrameworkTests/FrameworkTests.csproj | 15 + FrameworkTests/packages.config | 8 + TestBundles/TestBundleV1/TestBundleV1.csproj | 5 +- TestBundles/TestBundleV1/TestConnector.cs | 602 +++++++++++++++++-- 22 files changed, 2248 insertions(+), 218 deletions(-) create mode 100755 Common/AsyncHandler.cs create mode 100755 FrameworkTests/packages.config diff --git a/Common/AsyncHandler.cs b/Common/AsyncHandler.cs new file mode 100755 index 00000000..397dfc9a --- /dev/null +++ b/Common/AsyncHandler.cs @@ -0,0 +1,58 @@ +/* + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER. + * + * Copyright (c) 2015 ForgeRock AS. All rights reserved. + * + * The contents of this file are subject to the terms + * of the Common Development and Distribution License + * (the License). You may not use this file except in + * compliance with the License. + * + * You can obtain a copy of the License at + * http://forgerock.org/license/CDDLv1.0.html + * See the License for the specific language governing + * permission and limitations under the License. + * + * When distributing Covered Code, include this CDDL + * Header Notice in each file and include the License file + * at http://forgerock.org/license/CDDLv1.0.html + * If applicable, add the following below the CDDL Header, + * with the fields enclosed by brackets [] replaced by + * your own identifying information: + * "Portions Copyrighted [year] [name of copyright owner]" + */ + +namespace Org.IdentityConnectors.Common +{ + /// + /// A completion handler for consuming errors which occur during the execution of + /// asynchronous tasks. + /// + /// + /// The type of error consumed by the handler. + public interface IFailureHandler + { + /// + /// Invoked when the asynchronous task has failed. + /// + /// + /// The error indicating why the asynchronous task has failed. + void HandleError(TE error); + } + + /// + /// A completion handler for consuming the results of asynchronous tasks. + /// + /// + /// The type of result consumed by the handler. + public interface ISuccessHandler + { + /// + /// Invoked when the asynchronous task has completed successfully. + /// + /// + /// The result of the asynchronous task. + void HandleResult(TV result); + } + +} \ No newline at end of file diff --git a/Common/CollectionUtil.cs b/Common/CollectionUtil.cs index 98dc1a56..941d973a 100644 --- a/Common/CollectionUtil.cs +++ b/Common/CollectionUtil.cs @@ -19,6 +19,7 @@ * enclosed by brackets [] replaced by your own identifying information: * "Portions Copyrighted [year] [name of copyright owner]" * ==================== + * Portions Copyrighted 2015 ForgeRock AS. */ using System; using System.Runtime.CompilerServices; @@ -335,11 +336,31 @@ public static int GetKeyValuePairHashCode(KeyValuePair pair) /// The second collection /// public static bool SetsEqual(ICollection collection1, - ICollection collection2) + ICollection collection2) { - if (collection1 == null || collection1 == null) + return SetsEqual(collection1, collection2, false); + } + + private static bool _SetsEqual(ICollection collection1, + ICollection collection2, Boolean equalsIgnoreCase) + { + return SetsEqual(collection1, collection2, equalsIgnoreCase); + } + + /// + /// Returns true if the two sets contain the same elements. This is only for + /// sets and dictionaries. Does not work for Lists or Arrays. + /// + /// The first collection + /// The second collection + /// + /// + public static bool SetsEqual(ICollection collection1, + ICollection collection2, Boolean equalsIgnoreCase) + { + if (collection1 == null || collection2 == null) { - return collection1 == null && collection1 == null; + return collection1 == null && collection2== null; } if (collection1.Count != collection2.Count) { @@ -688,11 +709,11 @@ public static ICollection NewSet(IEnumerable collection) /// /// Returns a modifiable set, after first copying the array. /// - /// An array maybe null. + /// An array maybe null. /// a modifiable set, after first copying the array. public static ICollection NewSet(params T[] items) { - return NewSet((IEnumerable)items); + return NewSet((IEnumerable)items); } /// @@ -700,14 +721,14 @@ public static ICollection NewSet(params T[] items) /// /// A collection. May be null. /// a modifiable set, after first copying the collection. - public static ICollection NewSet(IEnumerable collection) + public static ICollection NewSet(IEnumerable collection) { - ICollection rv = new HashSet(); + ICollection rv = new HashSet(); if (collection != null) { foreach (T element in collection) { - rv.Add((U)(object)element); + rv.Add((TU)(object)element); } } return rv; @@ -830,23 +851,35 @@ private static ICollection NewReadOnlySet(params T[] args) return new ReadOnlyCollection(list); } - public static bool DictionariesEqual(IDictionary m1, - IDictionary m2) + public static bool DictionariesEqual(IDictionary m1, + IDictionary m2) + { + return DictionariesEqual(m1, m2, false); + } + + private static bool _DictionariesEqual(IDictionary m1, + IDictionary m2, Boolean equalsIgnoreCase) + { + return DictionariesEqual(m1, m2, equalsIgnoreCase); + } + + public static bool DictionariesEqual(IDictionary m1, + IDictionary m2, Boolean equalsIgnoreCase) { if (m1.Count != m2.Count) { return false; } - foreach (KeyValuePair entry1 in m1) + foreach (KeyValuePair entry1 in m1) { - K key1 = entry1.Key; - V val1 = entry1.Value; + TK key1 = entry1.Key; + TV val1 = entry1.Value; if (!m2.ContainsKey(key1)) { return false; } Object val2 = m2[key1]; - if (!CollectionUtil.Equals(val1, val2)) + if (!CollectionUtil.Equals(val1, val2, equalsIgnoreCase)) { return false; } @@ -855,7 +888,19 @@ public static bool DictionariesEqual(IDictionary m1, } public static bool ListsEqual(IList v1, - IList v2) + IList v2) + { + return ListsEqual(v1, v2, false); + } + + private static bool _ListsEqual(IList v1, + IList v2, Boolean equalsIgnoreCase) + { + return ListsEqual(v1, v2, equalsIgnoreCase); + } + + public static bool ListsEqual(IList v1, + IList v2, Boolean equalsIgnoreCase) { if (v1.Count != v2.Count) { @@ -871,6 +916,18 @@ public static bool ListsEqual(IList v1, return true; } + /// + /// Forces the compare of two comparable objects and removes any warnings + /// generated by the compiler. + /// + /// the integer value of o1.compareTo(o2). + public static int ForceCompare(object o1, object o2) + { + IComparable t1 = (IComparable)o1; + T t2 = (T)o2; + return t1.CompareTo(t2); + } + /// /// hashCode function that properly handles arrays, /// collections, maps, collections of arrays, and maps of arrays. @@ -893,7 +950,7 @@ public static int GetHashCode(Object o) Object el = array.GetValue(i); unchecked { - rv += CollectionUtil.GetHashCode(el); + rv += GetHashCode(el); } } return rv; @@ -955,6 +1012,33 @@ public static int GetHashCode(Object o) /// The second object. May be null. /// true if the two objects are equal. public new static bool Equals(Object o1, Object o2) + { + return Equals(o1, o2, false); + } + + + /// + /// Equality function that properly handles arrays, + /// lists, maps, lists of arrays, and maps of arrays. + /// + /// + /// + /// NOTE: For Sets, this relies on the equals method + /// of the Set to do the right thing. This is a reasonable + /// assumption since, in order for Sets to behave + /// properly as Sets, their values must already have + /// a proper implementation of equals. (Or they must + /// be specialized Sets that define a custom comparator that + /// knows how to do the right thing). The same holds true for Map keys. + /// Map values, on the other hand, are compared (so Map values + /// can be arrays). + /// + /// + /// The first object. May be null. + /// The second object. May be null. + /// If true the String and Character comparison is case-ignore + /// true if the two objects are equal. + public static bool Equals(Object o1, Object o2, Boolean equalsIgnoreCase) { if (o1 == o2) { //same object or both null @@ -988,7 +1072,7 @@ public static int GetHashCode(Object o) { Object el1 = array1.GetValue(i); Object el2 = array2.GetValue(i); - if (!CollectionUtil.Equals(el1, el2)) + if (!Equals(el1, el2, equalsIgnoreCase)) { return false; } @@ -1008,11 +1092,11 @@ public static int GetHashCode(Object o) Type collectionUtil = typeof(CollectionUtil); - MethodInfo info = collectionUtil.GetMethod("ListsEqual"); + MethodInfo info = collectionUtil.GetMethod("_ListsEqual", BindingFlags.Static | BindingFlags.NonPublic); info = info.MakeGenericMethod(genericArguments); - Object rv = info.Invoke(null, new object[] { o1, o2 }); + Object rv = info.Invoke(null, new object[] { o1, o2, equalsIgnoreCase }); return (bool)rv; } else if (ReflectionUtil.IsParentTypeOf(typeof(IDictionary<,>), o1.GetType())) @@ -1028,11 +1112,11 @@ public static int GetHashCode(Object o) Type collectionUtil = typeof(CollectionUtil); - MethodInfo info = collectionUtil.GetMethod("DictionariesEqual"); + MethodInfo info = collectionUtil.GetMethod("_DictionariesEqual", BindingFlags.Static | BindingFlags.NonPublic); info = info.MakeGenericMethod(genericArguments); - Object rv = info.Invoke(null, new object[] { o1, o2 }); + Object rv = info.Invoke(null, new object[] { o1, o2 , equalsIgnoreCase}); return (bool)rv; } else if (ReflectionUtil.IsParentTypeOf(typeof(ICollection<>), o1.GetType())) @@ -1048,13 +1132,21 @@ public static int GetHashCode(Object o) Type collectionUtil = typeof(CollectionUtil); - MethodInfo info = collectionUtil.GetMethod("SetsEqual"); + MethodInfo info = collectionUtil.GetMethod("_SetsEqual", BindingFlags.Static | BindingFlags.NonPublic); info = info.MakeGenericMethod(genericArguments); - Object rv = info.Invoke(null, new object[] { o1, o2 }); + Object rv = info.Invoke(null, new object[] { o1, o2, equalsIgnoreCase }); return (bool)rv; } + else if (equalsIgnoreCase && o1 is string && o2 is string) + { + return ((string)o1).Equals((string)o2, StringComparison.CurrentCultureIgnoreCase); + } + else if (equalsIgnoreCase && o1 is char && o2 is char) + { + return char.ToLower((char)o1) == char.ToLower((char)o2); + } else { return o1.Equals(o2); diff --git a/Common/Common.csproj b/Common/Common.csproj index facde6be..8744fb39 100644 --- a/Common/Common.csproj +++ b/Common/Common.csproj @@ -78,6 +78,7 @@ + diff --git a/Common/Security.cs b/Common/Security.cs index a332fa12..23dac12b 100644 --- a/Common/Security.cs +++ b/Common/Security.cs @@ -19,9 +19,10 @@ * enclosed by brackets [] replaced by your own identifying information: * "Portions Copyrighted [year] [name of copyright owner]" * ==================== - * Portions Copyrighted 2014 ForgeRock AS. + * Portions Copyrighted 2014-2015 ForgeRock AS. */ using System; +using System.Linq; using System.Security; using System.Security.Cryptography; using System.Runtime.InteropServices; @@ -726,6 +727,8 @@ protected override void FreeMemory() /// public static class SecurityUtil { + public static string[] LowerHexArray = Enumerable.Range(0, 256).Select(v => v.ToString("x2")).ToArray(); + public static string[] UpperHexArray = Enumerable.Range(0, 256).Select(v => v.ToString("X2")).ToArray(); /// /// Converts chars to bytes without using any external functions @@ -760,8 +763,8 @@ public static UnmanagedArray CharsToBytes(UnmanagedArray chars) /// This guarantees the caller that they only /// need to cleanup the input and result. /// - /// The chars - /// The bytes + /// The bytes + /// The chars public static UnmanagedArray BytesToChars(UnmanagedArray bytes) { UnmanagedCharArray chars = new UnmanagedCharArray(bytes.Length / 2); @@ -803,6 +806,42 @@ public unsafe static string ComputeBase64SHA1Hash(UnmanagedArray input) } } + /// + /// Computes the Hex encoded SHA1 hash of the input. + /// + /// + /// The input bytes. + /// + /// {@code true} converts to lowercase or {@code false} to + /// uppercase + /// the hash (computed from the input bytes). + /// since 1.5 + public static string ComputeHexSHA1Hash(byte[] bytes, bool toLowerCase) + { + SHA1 hasher = SHA1.Create(); + byte[] data = hasher.ComputeHash(bytes); + return BytesToHex(data, toLowerCase); + } + + /// + /// Computes the Hex encoded input. + /// + /// + /// The input bytes to convert to Hex characters + /// + /// {@code true} converts to lowercase or {@code false} to + /// uppercase + /// A String containing hexadecimal characters + /// since 1.5 + public static string BytesToHex(byte[] bytes, bool toLowerCase) + { + StringBuilder hexChars = new StringBuilder(bytes.Length * 2); + string[] hexArray = toLowerCase ? LowerHexArray : UpperHexArray; + foreach (var v in bytes) + hexChars.Append(hexArray[v]); + return hexChars.ToString(); + } + /// /// Copies an unmanaged byte array into a managed byte array. /// diff --git a/Framework/Api.cs b/Framework/Api.cs index 77df8987..ecfb8cdd 100644 --- a/Framework/Api.cs +++ b/Framework/Api.cs @@ -19,7 +19,7 @@ * enclosed by brackets [] replaced by your own identifying information: * "Portions Copyrighted [year] [name of copyright owner]" * ==================== - * Portions Copyrighted 2012-2014 ForgeRock AS. + * Portions Copyrighted 2012-2015 ForgeRock AS. */ using System; using System.Collections.Generic; @@ -31,6 +31,8 @@ using Org.IdentityConnectors.Framework.Api.Operations; using Org.IdentityConnectors.Framework.Common; using Org.IdentityConnectors.Framework.Common.Objects; +using Org.IdentityConnectors.Framework.Spi; +using Org.IdentityConnectors.Framework.Spi.Operations; namespace Org.IdentityConnectors.Framework.Api { @@ -58,6 +60,19 @@ public interface APIConfiguration /// ConfigurationProperties ConfigurationProperties { get; } + /// + /// Add a configuration change listener callback handler. + /// + /// This callback handler will be notified when connector push an event back + /// to application to notify the initial configuration has to be changed + /// before next time creating a new Connectorfacade in order to continue + /// operate properly. + /// + /// + /// the callback handler to receive the change event. + /// a closeable to unregister the change listener. + IConfigurationPropertyChangeListener ChangeListener { set; } + /// /// Determines if this uses the framework's connector /// pooling. @@ -107,7 +122,7 @@ public interface APIConfiguration /// /// Sets the size of the buffer for the support - /// and what the results of the producer buffered. + /// and what the results of the producer buffered. /// /// /// default is 100, if size is set to zero or less will disable @@ -222,17 +237,38 @@ public interface ConfigurationProperty } #endregion + #region IConfigurationPropertyChangeListener + /// + /// A ConfigurationPropertyChangeListener receives the change of + /// ConfigurationProperty. + /// + /// since 1.5 + public interface IConfigurationPropertyChangeListener + { + + /// + /// Receives notification that an ConfigurationProperty has been changed to + /// connector. + /// + /// + /// containing the property name and value of the + /// ConfigurationProperty that was changed. + void ConfigurationPropertyChange(IList changes); + + } + #endregion + #region ConnectorFacade /// /// Main interface through which an application invokes Connector operations. /// Represents at the API level a specific instance of a Connector that has been /// configured in a specific way. /// - /// + public interface ConnectorFacade : CreateApiOp, IConnectorEventSubscriptionApiOp, DeleteApiOp, SearchApiOp, UpdateApiOp, SchemaApiOp, AuthenticationApiOp, ResolveUsernameApiOp, GetApiOp, ValidateApiOp, TestApiOp, ScriptOnConnectorApiOp, ScriptOnResourceApiOp, - SyncApiOp + ISyncEventSubscriptionApiOp, SyncApiOp { /// diff --git a/Framework/ApiOperations.cs b/Framework/ApiOperations.cs index a5166e72..fa93a059 100644 --- a/Framework/ApiOperations.cs +++ b/Framework/ApiOperations.cs @@ -19,7 +19,7 @@ * enclosed by brackets [] replaced by your own identifying information: * "Portions Copyrighted [year] [name of copyright owner]" * ==================== - * Portions Copyrighted 2012-2014 ForgeRock AS. + * Portions Copyrighted 2012-2015 ForgeRock AS. */ using System; using System.Collections.Generic; @@ -42,13 +42,24 @@ public interface AuthenticationApiOp : APIOperation /// /// Most basic authentication available. /// + /// /// string that represents the account or user id. /// string that represents the password for the account or user. + /// /// iff the credentials do not pass authentication otherwise /// nothing. Uid Authenticate(ObjectClass objectClass, string username, GuardedString password, OperationOptions options); } + #region IConnectorEventSubscriptionApiOp + /// + /// since 1.5 + public interface IConnectorEventSubscriptionApiOp : APIOperation + { + ISubscription Subscribe(ObjectClass objectClass, Filter eventFilter, IObserver handler, OperationOptions operationOptions); + } + #endregion + public interface ResolveUsernameApiOp : APIOperation { /// @@ -82,9 +93,10 @@ public interface CreateApiOp : APIOperation /// ObjectClass attribute and that there are no duplicate name'd /// attributes. /// + /// /// ConnectorAttribtes to create the object. /// Unique id for the created object. - Uid Create(ObjectClass oclass, ICollection attrs, OperationOptions options); + Uid Create(ObjectClass objectClass, ICollection attrs, OperationOptions options); } /// @@ -98,7 +110,7 @@ public interface DeleteApiOp : APIOperation /// /// The type of object to delete. /// The unique identitfier for the object to delete. - /// Throws UnknowUid if the object does not exist. + /// Throws UnknowUid if the object does not exist. void Delete(ObjectClass objectClass, Uid uid, OperationOptions options); } @@ -333,6 +345,25 @@ public interface SyncApiOp : APIOperation SyncToken GetLatestSyncToken(ObjectClass objectClass); } + #region ISyncEventSubscriptionApiOp + /// + /// since 1.5 + public interface ISyncEventSubscriptionApiOp : APIOperation + { + /// + /// Create a subscription to a given sync topic. + /// + /// + /// + /// + /// + /// @return + /// + /// when the operation failed to create subscription. + ISubscription Subscribe(ObjectClass objectClass, SyncToken token, IObserver handler, OperationOptions operationOptions); + } + #endregion + /// /// Updates a . /// diff --git a/Framework/Common.cs b/Framework/Common.cs index 69010416..22a691a1 100644 --- a/Framework/Common.cs +++ b/Framework/Common.cs @@ -19,7 +19,7 @@ * enclosed by brackets [] replaced by your own identifying information: * "Portions Copyrighted [year] [name of copyright owner]" * ==================== - * Portions Copyrighted 2014 ForgeRock AS. + * Portions Copyrighted 2014-2015 ForgeRock AS. */ using System; using System.Collections; @@ -28,12 +28,143 @@ using System.Collections.Generic; using Org.IdentityConnectors.Common; using Org.IdentityConnectors.Common.Security; +using Org.IdentityConnectors.Framework.Api; using Org.IdentityConnectors.Framework.Spi; using Org.IdentityConnectors.Framework.Api.Operations; using Org.IdentityConnectors.Framework.Spi.Operations; using Org.IdentityConnectors.Framework.Common.Objects; +using Org.IdentityConnectors.Framework.Common.Objects.Filters; + namespace Org.IdentityConnectors.Framework.Common { + #region ConnectorKeyRange + /// + /// A ConnectorKeyRange identifies a range of ConnectorKeys. + /// + /// The uniquely identifies one connector with exact + /// meanwhile this class has a + /// which can match multiple. + /// + /// + /// since 1.5 + public sealed class ConnectorKeyRange + { + private readonly string _bundleName; + private readonly VersionRange _bundleVersionRange; + private readonly string _connectorName; + + public string BundleName + { + get + { + return _bundleName; + } + } + + public VersionRange BundleVersionRange + { + get + { + return _bundleVersionRange; + } + } + + public string ConnectorName + { + get + { + return _connectorName; + } + } + + public bool IsInRange(ConnectorKey connectorKey) + { + return !_bundleVersionRange.Empty && _bundleName.Equals(connectorKey.BundleName) && _connectorName.Equals(connectorKey.ConnectorName) && _bundleVersionRange.IsInRange(Version.Parse(connectorKey.BundleVersion)); + } + + public ConnectorKey ExactConnectorKey + { + get + { + if (_bundleVersionRange.Exact) + { + return new ConnectorKey(_bundleName, _bundleVersionRange.Floor.ToString(), _connectorName); + } + else + { + throw new ArgumentException("BundleVersion is not exact version"); + } + } + } + + private ConnectorKeyRange(string bundleName, VersionRange bundleVersionRange, string connectorName) + { + _bundleName = bundleName; + _bundleVersionRange = bundleVersionRange; + _connectorName = connectorName; + } + + public override bool Equals(object o) + { + if (this == o) + { + return true; + } + if (!(o is ConnectorKeyRange)) + { + return false; + } + + ConnectorKeyRange that = (ConnectorKeyRange)o; + + return _bundleName.Equals(that._bundleName) && _bundleVersionRange.Equals(that._bundleVersionRange) && _connectorName.Equals(that._connectorName); + } + + public override int GetHashCode() + { + int result = _bundleName.GetHashCode(); + result = 31 * result + _bundleVersionRange.GetHashCode(); + result = 31 * result + _connectorName.GetHashCode(); + return result; + } + + public static Builder NewBuilder() + { + return new Builder(); + } + + public class Builder + { + private string _bundleName; + private string _bundleVersion; + private string _connectorName; + + public virtual Builder SetBundleName(string bundleName) + { + _bundleName = Assertions.BlankChecked(bundleName, "bundleName"); + return this; + } + + public virtual Builder SetBundleVersion(string bundleVersion) + { + _bundleVersion = Assertions.BlankChecked(bundleVersion, "bundleVersion"); + return this; + } + + public virtual Builder SetConnectorName(string connectorName) + { + _connectorName = Assertions.BlankChecked(connectorName, "connectorName"); + return this; + } + + public virtual ConnectorKeyRange Build() + { + return new ConnectorKeyRange(_bundleName, VersionRange.Parse(_bundleVersion), _connectorName); + } + } + } + #endregion + #region FrameworkInternalBridge internal static class FrameworkInternalBridge { @@ -100,6 +231,10 @@ static FrameworkUtil() SafeType.Get(); temp[SafeType.Get()] = SafeType.Get(); + temp[SafeType.Get()] = + SafeType.Get(); + temp[SafeType.Get()] = + SafeType.Get(); SPI_TO_API = CollectionUtil.NewReadOnlyDictionary(temp); CONFIG_SUPPORTED_TYPES = CollectionUtil.NewReadOnlySet @@ -480,6 +615,39 @@ public static void CheckOperationOptionValue(Object val) } } + /// + /// Check if + /// was invoked from + /// or + /// . + /// + /// + /// the query parameter of + /// + /// {@code null} if invoked from + /// + /// or value if invoked + /// + /// + /// since 1.5 + public static Uid GetUidIfGetOperation(Filter filter) + { + Uid uidToGet = null; + var equalsFilter = filter as EqualsFilter; + if (equalsFilter != null) + { + if (equalsFilter.GetAttribute() is Uid) + { + uidToGet = (Uid)equalsFilter.GetAttribute(); + } + else if (equalsFilter.GetAttribute().@Is(Uid.NAME)) + { + uidToGet = new Uid(ConnectorAttributeUtil.GetStringValue(equalsFilter.GetAttribute())); + } + } + return uidToGet; + } + /// /// Returns the version of the framework. /// diff --git a/Framework/CommonObjects.cs b/Framework/CommonObjects.cs index dd648b40..c06e4a76 100644 --- a/Framework/CommonObjects.cs +++ b/Framework/CommonObjects.cs @@ -19,15 +19,16 @@ * enclosed by brackets [] replaced by your own identifying information: * "Portions Copyrighted [year] [name of copyright owner]" * ==================== - * Portions Copyrighted 2014 ForgeRock AS. + * Portions Copyrighted 2014-2015 ForgeRock AS. */ using System; using System.Linq; using System.Security; using System.Collections.Generic; +using System.Collections.ObjectModel; using System.Globalization; using System.Text; - +using System.Threading; using Org.IdentityConnectors.Common; using Org.IdentityConnectors.Common.Security; @@ -629,7 +630,7 @@ public static GuardedString GetCurrentPasswordValue(ICollection - /// Get the password expired attribute from a of + /// Get the password expired attribute from a of /// s. /// /// set of attribute to find the expired password @@ -2337,9 +2338,9 @@ public sealed class ObjectClass /// Represents all collections that contains any object. /// /// This constant allowed to use in operation - /// + /// /// and - /// + /// /// any other operation throws /// /// @@ -4976,6 +4977,96 @@ public static SortKey ReverseOrder(SortKey key) } #endregion + #region ISubscription + /// + /// A SubscriptionHandler represents a subscription to an asynchronous event + /// channel. + /// + /// since 1.5 + public interface ISubscription : IDisposable + { + /// + /// Indicates whether this Subscription is currently unsubscribed. + /// + /// true if this Subscription is currently + /// unsubscribed, false otherwise + bool Unsubscribed { get; } + } + + public class CancellationSubscription : ISubscription + { + private readonly CancellationTokenSource _cts; + + /// + /// Gets the used by this CancellationSubscription. + /// + /// + public CancellationToken Token + { + get + { + return _cts.Token; + } + } + + /// + /// Gets a value that indicates whether the object is disposed. + /// + /// + public bool IsDisposed + { + get + { + return _cts.IsCancellationRequested; + } + } + + /// + /// Initializes a new instance of the + /// class that uses an existing . + /// + /// + /// used for cancellation. + /// is null. + public CancellationSubscription(CancellationTokenSource cts) + { + if (cts == null) + throw new ArgumentNullException("cts"); + _cts = cts; + } + + /// + /// Initializes a new instance of the class that uses a + /// new . + /// + /// + public CancellationSubscription() + : this(new CancellationTokenSource()) + { + } + + /// + /// Cancels the underlying . + /// + /// + public void Dispose() + { + _cts.Cancel(); + } + + /// + /// Indicates whether this Subscription is currently unsubscribed. + /// + /// true if this Subscription is currently + /// unsubscribed, false otherwise + public bool Unsubscribed + { + get { return IsDisposed; } + } + } + + #endregion + #region SyncDelta /// /// Represents a change to an object in a resource. @@ -5597,7 +5688,7 @@ public String GetUidValue() ///

/// The revision number specifies a given version ot the /// identified by the - /// + /// ///

/// null if the connector does not support the MVCC and does not set /// this value otherwise return the revision number of the object. diff --git a/Framework/CommonObjectsFilter.cs b/Framework/CommonObjectsFilter.cs index c1c32e13..fd899616 100644 --- a/Framework/CommonObjectsFilter.cs +++ b/Framework/CommonObjectsFilter.cs @@ -25,6 +25,7 @@ using System.Linq; using System.Text; using System.Collections.Generic; +using System.Threading; using Org.IdentityConnectors.Common; namespace Org.IdentityConnectors.Framework.Common.Objects.Filters @@ -796,7 +797,7 @@ public override bool Accept(ConnectorObject obj) return result; } - public override R Accept(FilterVisitor v, P p) + public override TR Accept(FilterVisitor v, TP p) { return v.VisitAndFilter(p, this); } @@ -904,7 +905,7 @@ public bool IsPresent(ConnectorObject obj) return obj.GetAttributeByName(_attribute.Name) != null; } public abstract bool Accept(ConnectorObject obj); - public abstract R Accept(FilterVisitor v, P p); + public abstract TR Accept(FilterVisitor v, TP p); } #endregion @@ -1493,6 +1494,437 @@ public static Filter Not(Filter filter) { return new NotFilter(filter); } + + /// + /// Creates a new presence filter using the provided attribute name. + /// + /// + /// The name of field within the which must be + /// present. + /// The newly created {@code presence} filter. + public static Filter Present(string attributeName) + { + return new PresenceFilter(attributeName); + } + } + #endregion + + #region FilteredResultsHandlerVisitor + + public enum FilterResult + { + False, + True, + Undefined + } + + static class EnumExtensionMethods + { + public static bool ToBoolean(this FilterResult instance) + { + return instance == FilterResult.True; // UNDEFINED collapses to FALSE. + } + + } + + /// + /// A FilteredResultsHandlerVisitor can accept the + /// . It + /// can be used for case-sensitive and case-ignore mode to accept the + /// and values. + /// + /// + /// since 1.5 + public class FilteredResultsHandlerVisitor : FilterVisitor + { + + public static readonly FilteredResultsHandlerVisitor DefaultCaseSensitiveVisitor = new FilteredResultsHandlerVisitor(false); + public static readonly FilteredResultsHandlerVisitor DefaultCaseIgnoreVisitor = new FilteredResultsHandlerVisitor(true); + + private FilterResult valueOf(bool b) + { + return b ? FilterResult.True : FilterResult.False; + } + + public static Filter WrapFilter(Filter nestedFilter, bool caseIgnore) + { + return null == nestedFilter ? null : new InnerVisitorFilter(nestedFilter, caseIgnore); + } + + private sealed class InnerVisitorFilter : Filter + { + private readonly Filter _nestedFilter; + private readonly bool _caseIgnore; + + public InnerVisitorFilter(Filter nestedFilter, bool caseIgnore) + { + _nestedFilter = nestedFilter; + _caseIgnore = caseIgnore; + } + + public bool Accept(ConnectorObject obj) + { + return _nestedFilter.Accept(_caseIgnore ? DefaultCaseIgnoreVisitor : DefaultCaseSensitiveVisitor, obj).ToBoolean(); + } + + public TR Accept(FilterVisitor v, TP p) + { + return v.VisitExtendedFilter(p, this); + } + } + + private readonly bool _caseIgnore; + + public FilteredResultsHandlerVisitor(bool caseIgnore) + { + _caseIgnore = caseIgnore; + } + + public virtual FilterResult VisitAndFilter(ConnectorObject connectorObject, AndFilter filter) + { + FilterResult result = FilterResult.True; + foreach (Filter subFilter in filter.Filters) + { + FilterResult r = subFilter.Accept(this, connectorObject); + if (r.CompareTo(result) < 0) + { + result = r; + } + if (result == FilterResult.False) + { + break; + } + } + return result; + } + + public virtual FilterResult VisitContainsFilter(ConnectorObject connectorObject, ContainsFilter filter) + { + FilterResult result = FilterResult.Undefined; + String valueAssertion = ExpectSingleValue(connectorObject, filter.Name, typeof(String)); + if (null != valueAssertion) + { + if (_caseIgnore) + { + result = valueOf(valueAssertion.ToLower(Thread.CurrentThread.CurrentUICulture).Contains(filter.GetValue().ToLower(Thread.CurrentThread.CurrentUICulture))); + } + else + { + result = valueOf(valueAssertion.Contains(filter.GetValue())); + } + } + return result; + } + + public virtual FilterResult VisitContainsAllValuesFilter(ConnectorObject connectorObject, ContainsAllValuesFilter filter) + { + FilterResult result = FilterResult.Undefined; + ConnectorAttribute attribute = connectorObject.GetAttributeByName(filter.Name); + IList attributeValues = null; + if (null != attribute && null != (attributeValues = attribute.Value)) + { + IList filterValues = filter.GetAttribute().Value; + if (filterValues.Count == 0) + { + result = FilterResult.True; + } + else if (attributeValues.Count == 0) + { + result = FilterResult.False; + } + else if (_caseIgnore) + { + bool stillContains = true; + foreach (object o in filter.GetAttribute().Value) + { + bool found = false; + if (o is string) + { + foreach (object c in attributeValues) + { + if (c is string && ((string)c).Equals((string)o, StringComparison.CurrentCultureIgnoreCase)) + { + found = true; + break; + } + } + } + else if (o is char) + { + foreach (object c in attributeValues) + { + if (c is char && char.ToUpper((char)c) != char.ToUpper((char)o)) + { + found = true; + break; + } + } + } + else + { + result = valueOf(filter.GetAttribute().Value.Except(attributeValues).Any()); + break; + } + if (!(stillContains = stillContains && found)) + { + break; + } + } + result = valueOf(stillContains); + } + else + { + result = valueOf(filter.GetAttribute().Value.Except(attributeValues).Any()); + } + } + return result; + } + public virtual FilterResult VisitEqualsFilter(ConnectorObject connectorObject, EqualsFilter filter) + { + FilterResult result = FilterResult.Undefined; + ConnectorAttribute attribute = connectorObject.GetAttributeByName(filter.Name); + if (null != attribute) + { + IList attributeValues = attribute.Value; + IList filterValues = filter.GetAttribute().Value; + result = valueOf(CollectionUtil.Equals(attributeValues, filterValues, _caseIgnore)); + } + return result; + } + public virtual FilterResult VisitExtendedFilter(ConnectorObject connectorObject, Filter filter) + { + FilterResult result = FilterResult.Undefined; + if (filter is PresenceFilter) + { + result = valueOf(null != connectorObject.GetAttributeByName(((PresenceFilter)filter).Name)); + } + return result; + } + public virtual FilterResult VisitGreaterThanFilter(ConnectorObject connectorObject, GreaterThanFilter filter) + { + FilterResult result = FilterResult.Undefined; + Object valueAssertion = ExpectSingleValue(connectorObject, filter.Name); + if (null != valueAssertion) + { + if (!(valueAssertion is IComparable)) + { + throw new ArgumentException("Attribute value " + filter.Name + " must be comparable! Found" + valueAssertion.GetType()); + } + object filterValue = filter.GetValue(); + if (_caseIgnore && filterValue is String) + { + var s = valueAssertion as string; + if (s != null) + { + result = valueOf(String.Compare(s, (string)filterValue, StringComparison.OrdinalIgnoreCase) > 0); + } + } + else if (_caseIgnore && filterValue is char) + { + var c = valueAssertion as char?; + if (c != null) + { + result = valueOf((Char.ToLower((char)c)) - (Char.ToLower((char)filterValue)) > 0); + } + } + else + { + result = valueOf(CollectionUtil.ForceCompare(valueAssertion, filterValue) > 0); + } + } + return result; + } + + + public virtual FilterResult VisitGreaterThanOrEqualFilter(ConnectorObject connectorObject, GreaterThanOrEqualFilter filter) + { + FilterResult result = FilterResult.Undefined; + object valueAssertion = ExpectSingleValue(connectorObject, filter.Name); + if (null != valueAssertion) + { + if (!(valueAssertion is IComparable)) + { + throw new ArgumentException("Attribute value " + filter.Name + " must be comparable! Found" + valueAssertion.GetType()); + } + object filterValue = filter.GetValue(); + if (_caseIgnore && filterValue is string) + { + var s = valueAssertion as string; + if (s != null) + { + result = valueOf(String.Compare(s, (string)filterValue, StringComparison.OrdinalIgnoreCase) >= 0); + } + } + else if (_caseIgnore && filterValue is char) + { + var c = valueAssertion as char?; + if (c != null) + { + result = valueOf((Char.ToLower((char)c)) - (Char.ToLower((char)filterValue)) >= 0); + } + } + else + { + result = valueOf(CollectionUtil.ForceCompare(valueAssertion, filterValue) >= 0); + } + } + return result; + } + public virtual FilterResult VisitLessThanFilter(ConnectorObject connectorObject, LessThanFilter filter) + { + FilterResult result = FilterResult.Undefined; + object valueAssertion = ExpectSingleValue(connectorObject, filter.Name); + if (null != valueAssertion) + { + if (!(valueAssertion is IComparable)) + { + throw new ArgumentException("Attribute value " + filter.Name + " must be comparable! Found" + valueAssertion.GetType()); + } + object filterValue = filter.GetValue(); + if (_caseIgnore && filterValue is string) + { + var s = valueAssertion as string; + if (s != null) + { + result = valueOf(String.Compare(s, (string)filterValue, StringComparison.OrdinalIgnoreCase) < 0); + } + } + else if (_caseIgnore && filterValue is char?) + { + var c = valueAssertion as char?; + if (c != null) + { + result = valueOf((Char.ToLower((char)c)) - (Char.ToLower((char)filterValue)) < 0); + } + } + else + { + result = valueOf(CollectionUtil.ForceCompare(valueAssertion, filterValue) < 0); + } + } + return result; + } + public virtual FilterResult VisitLessThanOrEqualFilter(ConnectorObject connectorObject, LessThanOrEqualFilter filter) + { + FilterResult result = FilterResult.Undefined; + object valueAssertion = ExpectSingleValue(connectorObject, filter.Name); + if (null != valueAssertion) + { + if (!(valueAssertion is IComparable)) + { + throw new ArgumentException("Attribute value " + filter.Name + " must be comparable! Found" + valueAssertion.GetType()); + } + object filterValue = filter.GetValue(); + if (_caseIgnore && filterValue is string) + { + var s = valueAssertion as string; + if (s != null) + { + result = valueOf(String.Compare(s, (string)filterValue, StringComparison.OrdinalIgnoreCase) <= 0); + } + } + else if (_caseIgnore && filterValue is char?) + { + var c = valueAssertion as char?; + if (c != null) + { + result = valueOf((Char.ToLower((char)c)) - (Char.ToLower((char)filterValue)) <= 0); + } + } + else + { + result = valueOf(CollectionUtil.ForceCompare(valueAssertion, filterValue) <= 0); + } + } + return result; + } + public virtual FilterResult VisitNotFilter(ConnectorObject connectorObject, NotFilter filter) + { + switch (filter.Filter.Accept(this, connectorObject)) + { + case FilterResult.False: + return FilterResult.True; + case FilterResult.Undefined: + return FilterResult.Undefined; + default: // TRUE + return FilterResult.False; + } + } + + public virtual FilterResult VisitOrFilter(ConnectorObject connectorObject, OrFilter filter) + { + FilterResult result = FilterResult.False; + foreach (Filter subFilter in filter.Filters) + { + FilterResult r = subFilter.Accept(this, connectorObject); + if (r.CompareTo(result) > 0) + { + result = r; + } + if (result == FilterResult.True) + { + break; + } + } + return result; + } + + public virtual FilterResult VisitStartsWithFilter(ConnectorObject connectorObject, StartsWithFilter filter) + { + FilterResult result = FilterResult.Undefined; + String valueAssertion = ExpectSingleValue(connectorObject, filter.Name, typeof(String)); + if (null != valueAssertion) + { + if (_caseIgnore) + { + result = valueOf(valueAssertion.ToLower(Thread.CurrentThread.CurrentUICulture).StartsWith(filter.GetValue().ToLower(Thread.CurrentThread.CurrentUICulture))); + } + else + { + result = valueOf(valueAssertion.StartsWith(filter.GetValue())); + } + } + return result; + } + + public virtual FilterResult VisitEndsWithFilter(ConnectorObject connectorObject, EndsWithFilter filter) + { + FilterResult result = FilterResult.Undefined; + String valueAssertion = ExpectSingleValue(connectorObject, filter.Name, typeof(String)); + if (null != valueAssertion) + { + if (_caseIgnore) + { + result = valueOf(valueAssertion.ToLower(Thread.CurrentThread.CurrentUICulture).EndsWith(filter.GetValue().ToLower(Thread.CurrentThread.CurrentUICulture))); + } + else + { + result = valueOf(valueAssertion.EndsWith(filter.GetValue())); + } + } + return result; + } + + protected internal virtual T ExpectSingleValue(ConnectorObject connectorObject, string attributeName, Type expect) + { + object o = ExpectSingleValue(connectorObject, attributeName); + if (null != o && expect.IsInstanceOfType(o)) + { + return (T)o; + } + return default(T); + } + + protected internal virtual object ExpectSingleValue(ConnectorObject connectorObject, string attributeName) + { + ConnectorAttribute attr = connectorObject.GetAttributeByName(attributeName); + if (null != attr && null != attr.Value && attr.Value.Count() == 1) + { + return attr.Value[0]; + } + return null; + } + } #endregion @@ -1522,7 +1954,7 @@ public GreaterThanFilter(ConnectorAttribute attr) /// public override bool Accept(ConnectorObject obj) { - return IsPresent(obj) && this.Compare(obj) > 0; + return IsPresent(obj) && Compare(obj) > 0; } public override R Accept(FilterVisitor v, P p) @@ -1558,7 +1990,7 @@ public GreaterThanOrEqualFilter(ConnectorAttribute attr) /// public override bool Accept(ConnectorObject obj) { - return IsPresent(obj) && this.Compare(obj) >= 0; + return IsPresent(obj) && Compare(obj) >= 0; } public override R Accept(FilterVisitor v, P p) @@ -1798,6 +2230,53 @@ public override string ToString() } #endregion + #region PresenceFilter + /// + /// A PresenceFilter determines if the attribute provided is present in the + /// + /// + /// since 1.5 + public class PresenceFilter : Filter + { + + private readonly string _name; + + public PresenceFilter(string attributeName) + { + this._name = attributeName; + if (StringUtil.IsBlank(_name)) + { + throw new ArgumentException("Attribute name not be null!"); + } + } + + /// + /// Name of the attribute to find in the . + /// + public virtual string Name + { + get + { + return _name; + } + } + + /// + /// Determines if the attribute provided is present in the + /// . + /// + public virtual bool Accept(ConnectorObject obj) + { + return obj.GetAttributeByName(_name) != null; + } + + public virtual R Accept(FilterVisitor v, P p) + { + return v.VisitExtendedFilter(p, this); + } + } + #endregion + #region SingleValueAttributeFilter /// /// Get a single value out of the attribute to test w/. diff --git a/Framework/Spi.cs b/Framework/Spi.cs index f685c07e..dfaab7ea 100644 --- a/Framework/Spi.cs +++ b/Framework/Spi.cs @@ -19,7 +19,7 @@ * enclosed by brackets [] replaced by your own identifying information: * "Portions Copyrighted [year] [name of copyright owner]" * ==================== - * Portions Copyrighted 2012-2014 ForgeRock AS. + * Portions Copyrighted 2012-2015 ForgeRock AS. */ using System; using Org.IdentityConnectors.Common; @@ -147,10 +147,40 @@ public interface Configuration #region AbstractConfiguration public abstract class AbstractConfiguration : Configuration { + public interface IConfigurationChangeCallback + { + void NotifyUpdate(); + } + + private IConfigurationChangeCallback _callback; public ConnectorMessages ConnectorMessages { get; set; } public abstract void Validate(); + + public void AddChangeCallback(IConfigurationChangeCallback handler) + { + if (null != _callback) + { + throw new InvalidOperationException("Configuration change update handler has been set"); + } + _callback = handler; + } + + protected internal virtual void NotifyConfigurationUpdate() + { + if (null != _callback) + { + try + { + _callback.NotifyUpdate(); + } + catch (Exception) + { + // Ignored + } + } + } } #endregion diff --git a/Framework/SpiOperations.cs b/Framework/SpiOperations.cs index 41af7fe8..36516aa2 100644 --- a/Framework/SpiOperations.cs +++ b/Framework/SpiOperations.cs @@ -19,7 +19,7 @@ * enclosed by brackets [] replaced by your own identifying information: * "Portions Copyrighted [year] [name of copyright owner]" * ==================== - * Portions Copyrighted 2012 ForgeRock AS. + * Portions Copyrighted 2012-2015 ForgeRock AS. */ using System; using System.Collections.Generic; @@ -55,6 +55,7 @@ public interface AuthenticateOp : SPIOperation /// most common is . /// /// + /// /// the name based credential for authentication. /// the password based credential for authentication. /// iff native authentication fails. If a native exception if @@ -62,6 +63,17 @@ public interface AuthenticateOp : SPIOperation Uid Authenticate(ObjectClass objectClass, String username, GuardedString password, OperationOptions options); } + #region IConnectorEventSubscriptionOp + /// + /// A ConnectorEventSubscriptionOp. + /// + /// since 1.5 + public interface IConnectorEventSubscriptionOp : SPIOperation + { + ISubscription Subscribe(ObjectClass objectClass, Filter eventFilter, IObserver observer, OperationOptions operationOptions); + } + #endregion + public interface ResolveUsernameOp : SPIOperation { /// @@ -232,7 +244,7 @@ public interface ScriptOnResourceOp : SPIOperation Object RunScriptOnResource(ScriptContext request, OperationOptions options); } - + #region SearchOp /// /// Implement this interface to allow the Connector to search for resource /// objects. @@ -276,6 +288,20 @@ public interface SearchOp : SPIOperation where T : class /// about this ever being null. void ExecuteQuery(ObjectClass oclass, T query, ResultsHandler handler, OperationOptions options); } + #endregion + + #region ISyncEventSubscriptionOp + /// + /// ASyncEventSubscriptionOp. + /// + /// since 1.5 + public interface ISyncEventSubscriptionOp : SPIOperation + { + ISubscription Subscribe(ObjectClass objectClass, SyncToken token, IObserver asyncHandler, OperationOptions operationOptions); + } + #endregion + + #region SyncOp /// /// Receive synchronization events from the resource. /// @@ -307,6 +333,7 @@ void Sync(ObjectClass objClass, SyncToken token, /// The latest token or null if there is no sync data. SyncToken GetLatestSyncToken(ObjectClass objectClass); } + #endregion /// /// The developer of a Connector should implement either this interface or the @@ -461,7 +488,7 @@ Uid RemoveAttributeValues(ObjectClass objclass, /// /// Tests the connector . - /// + /// /// Unlike validation performed by , testing a configuration /// checks that any pieces of environment referred by the configuration are available. /// For example, the connector could make a physical connection to a host specified diff --git a/FrameworkInternal/Api.cs b/FrameworkInternal/Api.cs index a6bc2fa5..2664f6b8 100644 --- a/FrameworkInternal/Api.cs +++ b/FrameworkInternal/Api.cs @@ -37,6 +37,7 @@ using Org.IdentityConnectors.Common.Security; using System.Linq; using System.Collections.Generic; +using System.Configuration; using System.Globalization; using System.Resources; using System.Reflection; @@ -44,6 +45,7 @@ using System.Text; using System.Threading; using Org.IdentityConnectors.Framework.Common.Exceptions; +using ConfigurationProperty = Org.IdentityConnectors.Framework.Api.ConfigurationProperty; namespace Org.IdentityConnectors.Framework.Impl.Api { @@ -55,6 +57,25 @@ public class ConfigurationPropertyImpl : ConfigurationProperty { private ICollection> _operations; + public ConfigurationPropertyImpl() + { + } + + public ConfigurationPropertyImpl(ConfigurationPropertyImpl parent) + { + _operations = parent.Operations; + Parent = null; + Order = parent.Order; + Name = parent.Name; + HelpMessageKey = parent.HelpMessageKey; + DisplayMessageKey = parent.DisplayMessageKey; + Value = parent.Value; + GroupMessageKey = parent.GroupMessageKey; + ValueType = parent.ValueType; + IsConfidential = parent.IsConfidential; + IsRequired = parent.IsRequired; + } + public ICollection> Operations { get @@ -360,6 +381,8 @@ public void SetTimeout(SafeType operation, int timeout) public AbstractConnectorInfo ConnectorInfo { get; set; } + public IConfigurationPropertyChangeListener ChangeListener { get; set; } + public int ProducerBufferSize { get; set; } // ======================================================================= @@ -374,22 +397,23 @@ public APIConfigurationImpl(APIConfigurationImpl other) { if (null != other._connectorPoolConfiguration) { - this.ConnectorPoolConfiguration = new ObjectPoolConfiguration(other._connectorPoolConfiguration); + ConnectorPoolConfiguration = new ObjectPoolConfiguration(other._connectorPoolConfiguration); } if (null != other._resultsHandlerConfiguration) { - this.ResultsHandlerConfiguration = new ResultsHandlerConfiguration(other._resultsHandlerConfiguration); + ResultsHandlerConfiguration = new ResultsHandlerConfiguration(other._resultsHandlerConfiguration); } - this.IsConnectorPoolingSupported = other.IsConnectorPoolingSupported; + IsConnectorPoolingSupported = other.IsConnectorPoolingSupported; ConfigurationPropertiesImpl prop = new ConfigurationPropertiesImpl(); prop.Properties = ((ConfigurationPropertiesImpl)other.ConfigurationProperties).Properties; ConfigurationProperties = prop; - this.ProducerBufferSize = other.ProducerBufferSize; - this.TimeoutMap = new Dictionary, int>(other.TimeoutMap); - this.SupportedOperations = new HashSet>(other.SupportedOperations); + ProducerBufferSize = other.ProducerBufferSize; + TimeoutMap = new Dictionary, int>(other.TimeoutMap); + SupportedOperations = new HashSet>(other.SupportedOperations); - this.ConnectorInfo = other.ConnectorInfo; + ConnectorInfo = other.ConnectorInfo; + ChangeListener = other.ChangeListener; } } #endregion @@ -656,13 +680,19 @@ public AbstractConnectorFacade(APIConfigurationImpl configuration) _connectorFacadeKey = Convert.ToBase64String(bytes); _configuration = (APIConfigurationImpl)SerializerUtil.DeserializeBinaryObject(bytes); //parent ref not included in the clone - _configuration.ConnectorInfo = (configuration.ConnectorInfo); + _configuration.ConnectorInfo = configuration.ConnectorInfo; + _configuration.ChangeListener = configuration.ChangeListener; ; + } + + public AbstractConnectorFacade(string configuration, AbstractConnectorInfo connectorInfo) + : this(configuration, connectorInfo, null) + { } /// /// Builds up the maps of supported operations and calls. /// - public AbstractConnectorFacade(string configuration, AbstractConnectorInfo connectorInfo) + public AbstractConnectorFacade(string configuration, AbstractConnectorInfo connectorInfo, IConfigurationPropertyChangeListener changeListener) { Assertions.NullCheck(configuration, "configuration"); Assertions.NullCheck(connectorInfo, "connectorInfo"); @@ -670,6 +700,7 @@ public AbstractConnectorFacade(string configuration, AbstractConnectorInfo conne _configuration = (APIConfigurationImpl)SerializerUtil.DeserializeBase64Object(configuration); // parent ref not included in the clone _configuration.ConnectorInfo = connectorInfo; + _configuration.ChangeListener = changeListener; } /// @@ -717,30 +748,30 @@ public ICollection> SupportedOperations // ======================================================================= public Schema Schema() { - return ((SchemaApiOp)this.GetOperationCheckSupported(SafeType.Get())) + return ((SchemaApiOp)GetOperationCheckSupported(SafeType.Get())) .Schema(); } public Uid Create(ObjectClass oclass, ICollection attrs, OperationOptions options) { - return ((CreateApiOp)this.GetOperationCheckSupported(SafeType.Get())).Create(oclass, attrs, options); + return ((CreateApiOp)GetOperationCheckSupported(SafeType.Get())).Create(oclass, attrs, options); } public void Delete(ObjectClass objClass, Uid uid, OperationOptions options) { - ((DeleteApiOp)this.GetOperationCheckSupported(SafeType.Get())) + ((DeleteApiOp)GetOperationCheckSupported(SafeType.Get())) .Delete(objClass, uid, options); } public SearchResult Search(ObjectClass objectClass, Filter filter, ResultsHandler handler, OperationOptions options) { - return ((SearchApiOp)this.GetOperationCheckSupported(SafeType.Get())).Search( + return ((SearchApiOp)GetOperationCheckSupported(SafeType.Get())).Search( objectClass, filter, handler, options); } public Uid Update(ObjectClass objclass, Uid uid, ICollection attrs, OperationOptions options) { - return ((UpdateApiOp)this.GetOperationCheckSupported(SafeType.Get())) + return ((UpdateApiOp)GetOperationCheckSupported(SafeType.Get())) .Update(objclass, uid, attrs, options); } @@ -750,7 +781,7 @@ public Uid AddAttributeValues( ICollection attrs, OperationOptions options) { - return ((UpdateApiOp)this.GetOperationCheckSupported(SafeType.Get())) + return ((UpdateApiOp)GetOperationCheckSupported(SafeType.Get())) .AddAttributeValues(objclass, uid, attrs, options); } @@ -760,70 +791,79 @@ public Uid RemoveAttributeValues( ICollection attrs, OperationOptions options) { - return ((UpdateApiOp)this.GetOperationCheckSupported(SafeType.Get())) + return ((UpdateApiOp)GetOperationCheckSupported(SafeType.Get())) .RemoveAttributeValues(objclass, uid, attrs, options); } public Uid Authenticate(ObjectClass objectClass, String username, GuardedString password, OperationOptions options) { - return ((AuthenticationApiOp)this - .GetOperationCheckSupported(SafeType.Get())).Authenticate( + return ((AuthenticationApiOp)GetOperationCheckSupported(SafeType.Get())).Authenticate( objectClass, username, password, options); } public Uid ResolveUsername(ObjectClass objectClass, String username, OperationOptions options) { - return ((ResolveUsernameApiOp)this - .GetOperationCheckSupported(SafeType.Get())).ResolveUsername( + return ((ResolveUsernameApiOp)GetOperationCheckSupported(SafeType.Get())).ResolveUsername( objectClass, username, options); } public ConnectorObject GetObject(ObjectClass objClass, Uid uid, OperationOptions options) { - return ((GetApiOp)this.GetOperationCheckSupported(SafeType.Get())) + return ((GetApiOp)GetOperationCheckSupported(SafeType.Get())) .GetObject(objClass, uid, options); } public Object RunScriptOnConnector(ScriptContext request, OperationOptions options) { - return ((ScriptOnConnectorApiOp)this - .GetOperationCheckSupported(SafeType.Get())) + return ((ScriptOnConnectorApiOp)GetOperationCheckSupported(SafeType.Get())) .RunScriptOnConnector(request, options); } public Object RunScriptOnResource(ScriptContext request, OperationOptions options) { - return ((ScriptOnResourceApiOp)this - .GetOperationCheckSupported(SafeType.Get())) + return ((ScriptOnResourceApiOp)GetOperationCheckSupported(SafeType.Get())) .RunScriptOnResource(request, options); } public void Test() { - ((TestApiOp)this.GetOperationCheckSupported(SafeType.Get())).Test(); + ((TestApiOp)GetOperationCheckSupported(SafeType.Get())).Test(); } public void Validate() { - ((ValidateApiOp)this.GetOperationCheckSupported(SafeType.Get())).Validate(); + ((ValidateApiOp)GetOperationCheckSupported(SafeType.Get())).Validate(); } public SyncToken Sync(ObjectClass objectClass, SyncToken token, SyncResultsHandler handler, OperationOptions options) { - return ((SyncApiOp)this.GetOperationCheckSupported(SafeType.Get())) + return ((SyncApiOp)GetOperationCheckSupported(SafeType.Get())) .Sync(objectClass, token, handler, options); } public SyncToken GetLatestSyncToken(ObjectClass objectClass) { - return ((SyncApiOp)this.GetOperationCheckSupported(SafeType.Get())) + return ((SyncApiOp)GetOperationCheckSupported(SafeType.Get())) .GetLatestSyncToken(objectClass); } + public ISubscription Subscribe(ObjectClass objectClass, Filter eventFilter, IObserver handler, + OperationOptions operationOptions) + { + return ((IConnectorEventSubscriptionApiOp)GetOperationCheckSupported(SafeType.Get())) + .Subscribe(objectClass, eventFilter, handler, operationOptions); + } + + public ISubscription Subscribe(ObjectClass objectClass, SyncToken token, IObserver handler, OperationOptions operationOptions) + { + return ((ISyncEventSubscriptionApiOp)GetOperationCheckSupported(SafeType.Get())) + .Subscribe(objectClass, token, handler, operationOptions); + } + private APIOperation GetOperationCheckSupported(SafeType api) { // check if this operation is supported. @@ -961,7 +1001,7 @@ public class ManagedConnectorFacadeFactoryImpl : ConnectorFacadeFactoryImpl /// /// Cache of the various ConnectorFacades. /// - private static readonly ConcurrentDictionary> CACHE = + private static readonly ConcurrentDictionary> CACHE = new ConcurrentDictionary>(); /// @@ -1021,7 +1061,7 @@ public override ConnectorFacade NewInstance(ConnectorInfo connectorInfo, string public override void Dispose() { base.Dispose(); - foreach (Pair facade in CACHE.Values) + foreach (Pair facade in CACHE.Values) { LocalConnectorFacadeImpl tmp = facade.Second as LocalConnectorFacadeImpl; if (tmp != null) @@ -1043,7 +1083,7 @@ public virtual void EvictIdle(TimeSpan unit) { if (unit == null) { - throw new System.NullReferenceException(); + throw new NullReferenceException(); } DateTime lastTime = DateTime.Now.Subtract(unit); foreach (KeyValuePair> entry in CACHE) @@ -1062,7 +1102,7 @@ public virtual void EvictIdle(TimeSpan unit) { tmp.Dispose(); Trace.TraceInformation("Disposed managed facade: {0}", entry.Value); - } + } } catch (Exception e) { diff --git a/FrameworkInternal/ApiLocal.cs b/FrameworkInternal/ApiLocal.cs index 725d9439..e093b9e6 100644 --- a/FrameworkInternal/ApiLocal.cs +++ b/FrameworkInternal/ApiLocal.cs @@ -19,7 +19,7 @@ * enclosed by brackets [] replaced by your own identifying information: * "Portions Copyrighted [year] [name of copyright owner]" * ==================== - * Portions Copyrighted 2012-2014 ForgeRock AS. + * Portions Copyrighted 2012-2015 ForgeRock AS. */ using System; using System.Diagnostics; @@ -41,6 +41,8 @@ using System.Threading; using System.IO; using System.Linq; +using Org.IdentityConnectors.Common.Proxy; + namespace Org.IdentityConnectors.Framework.Impl.Api.Local { #region ConnectorPoolManager @@ -281,7 +283,7 @@ public static ConfigurationPropertiesImpl // use the options to set internal properties.. int order = 0; String helpKey = "help_" + name; - String displKey = "display_" +name; + String displKey = "display_" + name; string grpKey = "group_" + name; bool confidential = false; bool required = false; @@ -494,8 +496,7 @@ public LocalConnectorInfoManagerImpl() { Assembly lib = Assembly.LoadFrom(file.ToString()); - CollectionUtil.AddAll(_connectorInfo, - ProcessAssembly(lib)); + CollectionUtil.AddAll(_connectorInfo, ConnectorAssemblyUtility.ProcessAssembly(lib)); } // also handle connector DLL file names with a version FileInfo[] versionedFiles = directory.GetFiles("*.Connector-*.dll"); @@ -503,12 +504,34 @@ public LocalConnectorInfoManagerImpl() { Assembly lib = Assembly.LoadFrom(versionedFile.ToString()); - CollectionUtil.AddAll(_connectorInfo, - ProcessAssembly(lib)); + CollectionUtil.AddAll(_connectorInfo, ConnectorAssemblyUtility.ProcessAssembly(lib)); } } - private IList ProcessAssembly(Assembly assembly) + public ConnectorInfo FindConnectorInfo(ConnectorKey key) + { + foreach (ConnectorInfo info in _connectorInfo) + { + if (info.ConnectorKey.Equals(key)) + { + return info; + } + } + return null; + } + + public IList ConnectorInfos + { + get + { + return CollectionUtil.AsReadOnlyList(_connectorInfo); + } + } + } + + public static class ConnectorAssemblyUtility + { + public static IList ProcessAssembly(Assembly assembly) { IList rv = new List(); @@ -524,7 +547,7 @@ private IList ProcessAssembly(Assembly assembly) { var typeLoadException = e as ReflectionTypeLoadException; var loaderExceptions = typeLoadException.LoaderExceptions; - foreach (var item in loaderExceptions) + foreach (var item in loaderExceptions) { TraceUtil.TraceException(" - loader exception: " + item, item); } @@ -541,11 +564,10 @@ private IList ProcessAssembly(Assembly assembly) { ConnectorClassAttribute attribute = (ConnectorClassAttribute)attributes[0]; - LocalConnectorInfoImpl info = - CreateConnectorInfo(assembly, type, attribute); + LocalConnectorInfoImpl info = CreateConnectorInfo(assembly, type, attribute); UriBuilder uri = new UriBuilder(assembly.CodeBase); Trace.TraceInformation("Add ConnectorInfo {0} to Local Connector Info Manager from {1}", - info.ConnectorKey, Uri.UnescapeDataString(uri.Path)); + info.ConnectorKey, Uri.UnescapeDataString(uri.Path)); rv.Add(info); } @@ -553,16 +575,16 @@ private IList ProcessAssembly(Assembly assembly) return rv; } - private LocalConnectorInfoImpl CreateConnectorInfo(Assembly assembly, - Type rawConnectorClass, - ConnectorClassAttribute attribute) + public static LocalConnectorInfoImpl CreateConnectorInfo(Assembly assembly, + Type rawConnectorClass, + ConnectorClassAttribute attribute) { String fileName = assembly.Location; if (!typeof(Connector).IsAssignableFrom(rawConnectorClass)) { String MSG = ("File " + fileName + - " declares a connector " + rawConnectorClass + - " that does not implement Connector."); + " declares a connector " + rawConnectorClass + + " that does not implement Connector."); throw new ConfigurationException(MSG); } SafeType connectorClass = @@ -571,8 +593,8 @@ private LocalConnectorInfoImpl CreateConnectorInfo(Assembly assembly, if (connectorConfigurationClass == null) { String MSG = ("File " + fileName + - " contains a ConnectorInfo attribute " + - "with no connector configuration class."); + " contains a ConnectorInfo attribute " + + "with no connector configuration class."); throw new ConfigurationException(MSG); } String connectorDisplayNameKey = @@ -586,21 +608,21 @@ private LocalConnectorInfoImpl CreateConnectorInfo(Assembly assembly, } ConnectorKey key = new ConnectorKey(assembly.GetName().Name, - assembly.GetName().Version.ToString(), - connectorClass.RawType.Namespace + "." + connectorClass.RawType.Name); + assembly.GetName().Version.ToString(), + connectorClass.RawType.Namespace + "." + connectorClass.RawType.Name); LocalConnectorInfoImpl rv = new LocalConnectorInfoImpl(); rv.ConnectorClass = connectorClass; rv.ConnectorConfigurationClass = connectorConfigurationClass; rv.ConnectorDisplayNameKey = connectorDisplayNameKey; rv.ConnectorCategoryKey = attribute.ConnectorCategoryKey; rv.ConnectorKey = key; - rv.DefaultAPIConfiguration = CreateDefaultAPIConfiguration(rv); + rv.DefaultAPIConfiguration = CreateDefaultApiConfiguration(rv); rv.Messages = LoadMessages(assembly, rv, attribute.MessageCatalogPaths); return rv; } public static APIConfigurationImpl - CreateDefaultAPIConfiguration(LocalConnectorInfoImpl localInfo) + CreateDefaultApiConfiguration(LocalConnectorInfoImpl localInfo) { SafeType connectorClass = localInfo.ConnectorClass; @@ -629,9 +651,9 @@ private static bool IsPoolingSupported(SafeType clazz) private static CultureInfo[] GetLocalizedCultures(Assembly assembly) { FileInfo assemblyFile = - new FileInfo(assembly.Location); + new FileInfo(assembly.Location); DirectoryInfo directory = - assemblyFile.Directory; + assemblyFile.Directory; IList temp = new List(); DirectoryInfo[] subdirs = directory.GetDirectories(); foreach (DirectoryInfo subdir in subdirs) @@ -666,8 +688,8 @@ private static CultureInfo[] GetLocalizedCultures(Assembly assembly) } public static ConnectorMessagesImpl LoadMessages(Assembly assembly, - LocalConnectorInfoImpl info, - String[] nameBases) + LocalConnectorInfoImpl info, + String[] nameBases) { if (nameBases == null || nameBases.Length == 0) { @@ -705,25 +727,6 @@ public static ConnectorMessagesImpl LoadMessages(Assembly assembly, return rv; } - - public ConnectorInfo FindConnectorInfo(ConnectorKey key) - { - foreach (ConnectorInfo info in _connectorInfo) - { - if (info.ConnectorKey.Equals(key)) - { - return info; - } - } - return null; - } - public IList ConnectorInfos - { - get - { - return CollectionUtil.AsReadOnlyList(_connectorInfo); - } - } } #endregion @@ -830,6 +833,11 @@ static LocalConnectorFacadeImpl() /// private readonly ConnectorOperationalContext operationalContext; + /// + /// Shared thread counter. + /// + private readonly ReferenceCounter referenceCounter = new ReferenceCounter(); + /// /// Builds up the maps of supported operations and calls. /// @@ -888,6 +896,7 @@ protected internal ConnectorOperationalContext OperationalContext protected override APIOperation GetOperationImplementation(SafeType api) { APIOperation proxy; + Boolean enableTimeoutProxy = true; //first create the inner proxy - this is the proxy that obtaining //a connection from the pool, etc //NOTE: we want to skip this part of the proxy for @@ -908,6 +917,20 @@ protected override APIOperation GetOperationImplementation(SafeType.Get(), handler)); } + else if (api.RawType == typeof(IConnectorEventSubscriptionApiOp)) + { + ConnectorAPIOperationRunnerProxy handler = new ConnectorAPIOperationRunnerProxy(OperationalContext, (opContext, connector) => new ConnectorEventSubscriptionApiOpImp( + opContext, connector, referenceCounter)); + proxy = NewAPIOperationProxy(api, handler); + enableTimeoutProxy = false; + } + else if (api.RawType == typeof(ISyncEventSubscriptionApiOp)) + { + ConnectorAPIOperationRunnerProxy handler = new ConnectorAPIOperationRunnerProxy(OperationalContext, (opContext, connector) => new SyncEventSubscriptionApiOpImpl( + opContext, connector, referenceCounter)); + proxy = NewAPIOperationProxy(api, handler); + enableTimeoutProxy = false; + } else { ConstructorInfo constructor = @@ -919,12 +942,82 @@ protected override APIOperation GetOperationImplementation(SafeType duration; + } + + public virtual void Acquire() + { + Interlocked.Increment(ref _threadCounts); + } + + public virtual void Release() + { + if (Interlocked.Decrement(ref _threadCounts) <= 0) + { + _lastUsed = DateTime.Now; + + } + } + } + + private class ReferenceCountingProxy : InvocationHandler + { + private readonly object _target; + private readonly ReferenceCounter _referenceCounter; + + public ReferenceCountingProxy(object target, ReferenceCounter referenceCounter) + { + _target = target; + _referenceCounter = referenceCounter; + } + + public object Invoke(object proxy, MethodInfo method, object[] arguments) + { + // do not log equals, hashCode, toString + if (method.DeclaringType == typeof(object)) + { + return method.Invoke(this, arguments); + } + try + { + _referenceCounter.Acquire(); + return method.Invoke(_target, arguments); + } + catch (TargetInvocationException e) + { + Exception root = e.InnerException; + ExceptionUtil.PreserveStackTrace(root); + throw root; + } + finally + { + _referenceCounter.Release(); + } + } + } } #endregion @@ -1116,7 +1209,7 @@ public ObjectPool(ObjectPoolHandler handler, /// /// Return an object to the pool /// - /// + /// public void ReturnObject(T obj) { Assertions.NullCheck(obj, "object"); @@ -1168,7 +1261,7 @@ public T BorrowObject() _handler.TestObject(rv.Object); return rv.Object; } - catch (Exception e) + catch (Exception) { //it's bad - remove from active objects lock (LOCK) @@ -1180,7 +1273,7 @@ public T BorrowObject() //immediately if (rv.IsNew) { - throw e; + throw; } } } @@ -1356,7 +1449,7 @@ private bool TooManyIdleObjects() /// /// Dispose of an object, but don't throw any exceptions /// - /// + /// private void DisposeNoException(T obj) { try diff --git a/FrameworkInternal/ApiLocalOperations.cs b/FrameworkInternal/ApiLocalOperations.cs index 64512234..2016c77a 100644 --- a/FrameworkInternal/ApiLocalOperations.cs +++ b/FrameworkInternal/ApiLocalOperations.cs @@ -19,7 +19,7 @@ * enclosed by brackets [] replaced by your own identifying information: * "Portions Copyrighted [year] [name of copyright owner]" * ==================== - * Portions Copyrighted 2014 ForgeRock AS. + * Portions Copyrighted 2014-2015 ForgeRock AS. */ using System; @@ -40,6 +40,7 @@ using Org.IdentityConnectors.Framework.Api; using System.Text; using System.Diagnostics; +using System.Threading; namespace Org.IdentityConnectors.Framework.Impl.Api.Local.Operations { @@ -148,6 +149,8 @@ internal class ConnectorAPIOperationRunnerProxy : InvocationHandler /// private readonly ConstructorInfo _runnerImplConstructor; + private readonly Func _runnerImplFunc; + /// /// Create an APIOperationRunnerProxy /// @@ -161,6 +164,13 @@ public ConnectorAPIOperationRunnerProxy(ConnectorOperationalContext context, _runnerImplConstructor = runnerImplConstructor; } + public ConnectorAPIOperationRunnerProxy(ConnectorOperationalContext context, + Func runnerImplConstructor) + { + _context = context; + _runnerImplFunc = runnerImplConstructor; + } + public Object Invoke(Object proxy, MethodInfo method, object[] args) { //do not proxy equals, hashCode, toString @@ -187,12 +197,18 @@ public Object Invoke(Object proxy, MethodInfo method, object[] args) // initialize the connector.. connector.Init(_context.GetConfiguration()); } - APIOperationRunner runner = + APIOperationRunner runner = null != _runnerImplFunc ? _runnerImplFunc(_context, connector) : (APIOperationRunner)_runnerImplConstructor.Invoke(new object[]{ _context, connector}); ret = method.Invoke(runner, args); // call out to the operation.. + if (ret is ISubscription) + { + //Dispose later + ret = new DeferredSubscriptionDisposer((ISubscription)ret, connector, pool); + connector = null; + } } catch (TargetInvocationException e) { @@ -205,48 +221,90 @@ public Object Invoke(Object proxy, MethodInfo method, object[] args) // make sure dispose of the connector properly if (connector != null) { - // determine if there was a pool.. - if (pool != null) - { - try - { - //try to return it to the pool even though an - //exception may have happened that leaves it in - //a bad state. The contract of checkAlive - //is that it will tell you if the connector is - //still valid and so we leave it up to the pool - //and connector to work it out. - pool.ReturnObject((PoolableConnector)connector); - } - catch (Exception e) - { - //don't let pool exceptions propagate or mask - //other exceptions. do log it though. - TraceUtil.TraceException(null, e); - } - } - //not pooled - just dispose - else + DisposeConnector(connector, pool); + } + } + return ret; + } + + internal static void DisposeConnector(Connector connector, ObjectPool pool) + { + // determine if there was a pool.. + if (pool != null) + { + try + { + //try to return it to the pool even though an + //exception may have happened that leaves it in + //a bad state. The contract of checkAlive + //is that it will tell you if the connector is + //still valid and so we leave it up to the pool + //and connector to work it out. + pool.ReturnObject((PoolableConnector)connector); + } + catch (Exception e) + { + //don't let pool exceptions propagate or mask + //other exceptions. do log it though. + TraceUtil.TraceException(null, e); + } + } + //not pooled - just dispose + else + { + //dispose it not supposed to throw, but just in case, + //catch the exception and log it so we know about it + //but don't let the exception prevent additional + //cleanup that needs to happen + try + { + connector.Dispose(); + } + catch (Exception e) + { + //log this though + TraceUtil.TraceException(null, e); + } + } + } + private sealed class DeferredSubscriptionDisposer : ISubscription + { + private readonly Connector _connector; + private readonly ObjectPool _poolEntry; + private readonly ISubscription _subscription; + private Int32 _active = 1; + public DeferredSubscriptionDisposer(ISubscription subscription, Connector connector, ObjectPool poolEntry) + { + _subscription = subscription; + _connector = connector; + _poolEntry = poolEntry; + } + + public void Dispose() + { + try + { + _subscription.Dispose(); + } + finally + { + if (Interlocked.CompareExchange(ref _active, 0, 1) == 1) { - //dispose it not supposed to throw, but just in case, - //catch the exception and log it so we know about it - //but don't let the exception prevent additional - //cleanup that needs to happen - try - { - connector.Dispose(); - } - catch (Exception e) - { - //log this though - TraceUtil.TraceException(null, e); - } + DisposeConnector(_connector, _poolEntry); } } } - return ret; + + public bool Unsubscribed + { + get + { + return _subscription.Unsubscribed; + } + } } } + #endregion #region ConnectorOperationalContext @@ -581,6 +639,152 @@ public ResultsHandler ResultsHandler } #endregion + #region SubscriptionImpl + + public class ConnectorEventSubscriptionApiOpImp : SubscriptionImpl, IConnectorEventSubscriptionApiOp + { + + /// + /// Creates the API operation so it can called multiple times. + /// + /// + /// + /// + public ConnectorEventSubscriptionApiOpImp(ConnectorOperationalContext context, Connector connector, LocalConnectorFacadeImpl.ReferenceCounter referenceCounter) + : base(context, connector, referenceCounter) + { + } + + public ISubscription Subscribe(ObjectClass objectClass, Filter eventFilter, IObserver handler, OperationOptions operationOptions) + { + IConnectorEventSubscriptionOp operation = ((IConnectorEventSubscriptionOp)GetConnector()); + InternalObserver observer = new InternalObserver(handler, ReferenceCounter); + try + { + ReferenceCounter.Acquire(); + return observer.Subscription(operation.Subscribe(objectClass, eventFilter, observer, operationOptions)); + } + catch (Exception e) + { + observer.OnError(e); + throw; + } + } + } + + public class SyncEventSubscriptionApiOpImpl : SubscriptionImpl, ISyncEventSubscriptionApiOp + { + + /// + /// Creates the API operation so it can called multiple times. + /// + /// + /// + /// + public SyncEventSubscriptionApiOpImpl(ConnectorOperationalContext context, Connector connector, LocalConnectorFacadeImpl.ReferenceCounter referenceCounter) + : base(context, connector, referenceCounter) + { + } + + public ISubscription Subscribe(ObjectClass objectClass, SyncToken token, IObserver handler, OperationOptions operationOptions) + { + ISyncEventSubscriptionOp operation = ((ISyncEventSubscriptionOp)GetConnector()); + InternalObserver observer = new InternalObserver(handler, ReferenceCounter); + try + { + ReferenceCounter.Acquire(); + return observer.Subscription(operation.Subscribe(objectClass, token, observer, operationOptions)); + } + catch (Exception e) + { + observer.OnError(e); + throw; + } + } + } + public class SubscriptionImpl : ConnectorAPIOperationRunner + { + + protected readonly LocalConnectorFacadeImpl.ReferenceCounter ReferenceCounter; + + /// + /// Creates the API operation so it can called multiple times. + /// + /// + /// + protected internal SubscriptionImpl(ConnectorOperationalContext context, Connector connector, LocalConnectorFacadeImpl.ReferenceCounter referenceCounter) + : base(context, connector) + { + ReferenceCounter = referenceCounter; + } + + + protected sealed class InternalObserver : IObserver + { + private readonly IObserver _observer; + private readonly CancellationSubscription _subscribed = new CancellationSubscription(); + + public InternalObserver(IObserver observer, LocalConnectorFacadeImpl.ReferenceCounter referenceCounter) + { + _observer = observer; + _subscribed.Token.Register(referenceCounter.Release); + } + + public CancellationSubscription Subscription(ISubscription subscription) + { + Assertions.NullCheck(subscription, "subscription"); + _subscribed.Token.Register(subscription.Dispose); + return _subscribed; + } + + public void OnCompleted() + { + if (!_subscribed.Unsubscribed) + { + try + { + _subscribed.Dispose(); + } + finally + { + _observer.OnCompleted(); + } + } + } + + public void OnError(Exception e) + { + if (!_subscribed.Unsubscribed) + { + try + { + _subscribed.Dispose(); + } + finally + { + _observer.OnError(e); + } + } + } + + public void OnNext(T connectorObject) + { + try + { + if (!_subscribed.Unsubscribed) + { + _observer.OnNext(connectorObject); + } + } + catch (Exception t) + { + OnError(t); + } + } + } + } + #endregion + #region SyncAttributesToGetResultsHandler public sealed class SyncAttributesToGetResultsHandler : AttributesToGetResultsHandler @@ -898,7 +1102,7 @@ public virtual void Dispose() { StatefulConfiguration config = (StatefulConfiguration)configuration; configuration = null; - config.Release(); + config.Release(); } catch (Exception e) { @@ -1615,6 +1819,7 @@ public SyncToken Sync(ObjectClass objectClass, SyncToken token, SyncResultsHandl public SyncToken GetLatestSyncToken(ObjectClass objectClass) { + Assertions.NullCheck(objectClass, "objectClass"); return ((SyncOp)GetConnector()).GetLatestSyncToken(objectClass); } } diff --git a/FrameworkInternal/ApiRemote.cs b/FrameworkInternal/ApiRemote.cs index aa37d8ff..e82ee668 100644 --- a/FrameworkInternal/ApiRemote.cs +++ b/FrameworkInternal/ApiRemote.cs @@ -19,7 +19,7 @@ * enclosed by brackets [] replaced by your own identifying information: * "Portions Copyrighted [year] [name of copyright owner]" * ==================== - * Portions Copyrighted 2012-2014 ForgeRock AS. + * Portions Copyrighted 2012-2015 ForgeRock AS. */ using System; using System.Collections; @@ -287,6 +287,11 @@ private static string GenerateRemoteConnectorFacadeKey(APIConfigurationImpl conf protected override APIOperation GetOperationImplementation(SafeType api) { + if (api.RawType == typeof(IConnectorEventSubscriptionApiOp) || api.RawType == typeof(ISyncEventSubscriptionApiOp)) + { + //Not supported remotely with legacy communication protocol + return null; + } // add remote proxy InvocationHandler handler = new RemoteOperationInvocationHandler( (RemoteConnectorInfoImpl)GetAPIConfiguration().ConnectorInfo, remoteConnectorFacadeKey, api); diff --git a/FrameworkInternal/Serializer.cs b/FrameworkInternal/Serializer.cs index fbd36623..a50d967b 100644 --- a/FrameworkInternal/Serializer.cs +++ b/FrameworkInternal/Serializer.cs @@ -19,7 +19,7 @@ * enclosed by brackets [] replaced by your own identifying information: * "Portions Copyrighted [year] [name of copyright owner]" * ==================== - * Portions Copyrighted 2012-2014 ForgeRock AS. + * Portions Copyrighted 2012-2015 ForgeRock AS. */ using System; using System.IO; @@ -1238,7 +1238,7 @@ public override Object Deserialize(ObjectDecoder decoder) public override void Serialize(Object obj, ObjectEncoder encoder) { GuardedString str = (GuardedString)obj; - str.Access( new GuardedString.LambdaAccessor( + str.Access(new GuardedString.LambdaAccessor( clearChars => { UnmanagedArray clearBytes = null; @@ -1713,8 +1713,11 @@ static OperationMappings() "ScriptOnResourceApiOp")); MAPPINGS.Add(new ObjectTypeMapperImpl(typeof(SyncApiOp), "SyncApiOp")); + MAPPINGS.Add(new ObjectTypeMapperImpl(typeof(IConnectorEventSubscriptionApiOp), + "ConnectorEventSubscriptionApiOp")); + MAPPINGS.Add(new ObjectTypeMapperImpl(typeof(ISyncEventSubscriptionApiOp), + "SyncEventSubscriptionApiOp")); } - } #endregion @@ -2387,9 +2390,12 @@ public override Object Deserialize(ObjectDecoder decoder) { String val = decoder.ReadStringField("uid", null); String revision = decoder.ReadStringField("revision", null); - if (null == revision) { + if (null == revision) + { return new Uid(val); - } else { + } + else + { return new Uid(val, revision); } } @@ -2962,7 +2968,7 @@ public override sealed void Serialize(Object obj, ObjectEncoder encoder) encoder.WriteStringField("connectorFacadeKey", val.ConnectorFacadeKey); encoder.WriteObjectField("ConnectorKey", - val.ConnectorKey, true); + val.ConnectorKey, true); encoder.WriteObjectField("Arguments", val.Arguments, true); } diff --git a/FrameworkInternal/Test.cs b/FrameworkInternal/Test.cs index 7282e64a..12a68601 100644 --- a/FrameworkInternal/Test.cs +++ b/FrameworkInternal/Test.cs @@ -19,7 +19,7 @@ * enclosed by brackets [] replaced by your own identifying information: * "Portions Copyrighted [year] [name of copyright owner]" * ==================== - * Portions Copyrighted 2014 ForgeRock AS. + * Portions Copyrighted 2014-2015 ForgeRock AS. */ using System; using System.Collections.Generic; @@ -119,9 +119,9 @@ public APIConfiguration CreateTestConfiguration(SafeType connectorCla new ConnectorKey(rawConnectorClass.Name + ".bundle", "1.0", rawConnectorClass.Name)); ; - APIConfigurationImpl impl = LocalConnectorInfoManagerImpl.CreateDefaultAPIConfiguration(rv); + APIConfigurationImpl impl = ConnectorAssemblyUtility.CreateDefaultApiConfiguration(rv); rv.DefaultAPIConfiguration = impl; - rv.Messages = LocalConnectorInfoManagerImpl.LoadMessages(assembly, rv, attribute.MessageCatalogPaths); + rv.Messages = ConnectorAssemblyUtility.LoadMessages(assembly, rv, attribute.MessageCatalogPaths); ConfigurationPropertiesImpl configProps = (ConfigurationPropertiesImpl)impl.ConfigurationProperties; diff --git a/FrameworkTests/ConnectorInfoManagerTests.cs b/FrameworkTests/ConnectorInfoManagerTests.cs index b4de8f28..8ba036e7 100644 --- a/FrameworkTests/ConnectorInfoManagerTests.cs +++ b/FrameworkTests/ConnectorInfoManagerTests.cs @@ -19,7 +19,7 @@ * enclosed by brackets [] replaced by your own identifying information: * "Portions Copyrighted [year] [name of copyright owner]" * ==================== - * Portions Copyrighted 2012-2014 ForgeRock AS. + * Portions Copyrighted 2012-2015 ForgeRock AS. */ using System; using System.Collections; @@ -41,7 +41,10 @@ using System.Threading; using System.Globalization; using System.Net.Security; +using System.Reactive.Linq; using System.Security.Cryptography.X509Certificates; +using Org.IdentityConnectors.Test.Common; + namespace FrameworkTests { [TestFixture] @@ -377,6 +380,72 @@ public void TestSyncTokenResults() } } + [Test] + public void TestSubscriptionOperation() + { + foreach (ConnectorFacade facade in CreateStateFulFacades()) + { + if (facade is LocalConnectorFacadeImpl) + { + ToListResultsHandler handler = new ToListResultsHandler(); + CountdownEvent cde = new CountdownEvent(1); + var localFacade = facade; + var connectorObjectObservable = + Observable.Create(o => localFacade.Subscribe(ObjectClass.ACCOUNT, null, o, null)); + + + var subscription = connectorObjectObservable.Subscribe( + co => + { + Console.WriteLine("Connector Event received:{0}", co.Uid.GetUidValue()); + handler.ResultsHandler.Handle(co); + }, + ex => + { + cde.Signal(); + Assert.AreEqual(handler.Objects.Count, 10, "Uncompleted subscription"); + }); + + + cde.Wait(new TimeSpan(0, 0, 25)); + subscription.Dispose(); + Assert.AreEqual(10, handler.Objects.Count); + + handler = new ToListResultsHandler(); + cde = new CountdownEvent(1); + + var syncDeltaObservable = + Observable.Create(o => localFacade.Subscribe(ObjectClass.ACCOUNT, null, o, null)); + + IDisposable[] subscriptions = new IDisposable[1]; + subscriptions[0] = syncDeltaObservable.Subscribe( + delta => + { + Console.WriteLine("Sync Event received:{0}", delta.Token.Value); + if (((int?)delta.Token.Value) > 2) + { + subscriptions[0].Dispose(); + cde.Signal(); + } + handler.ResultsHandler.Handle(delta.Object); + }, + ex => + { + cde.Signal(); + Assert.Fail("Failed Subscription", ex); + }); + + cde.Wait(new TimeSpan(0, 0, 25)); + for (int i = 0; i < 5 && !(handler.Objects.Count > 2); i++) + { + Console.WriteLine("Wait for result handler thread to complete: {0}", i); + Thread.Sleep(200); // Wait to complete all other threads + } + Assert.IsTrue(handler.Objects.Count < 10 && handler.Objects.Count > 2); + } + } + } + [Test] public void TestConnectionPooling() { diff --git a/FrameworkTests/FrameworkTests.csproj b/FrameworkTests/FrameworkTests.csproj index 4ad7f746..f7bd46a2 100644 --- a/FrameworkTests/FrameworkTests.csproj +++ b/FrameworkTests/FrameworkTests.csproj @@ -61,6 +61,20 @@ 4.0 + + ..\packages\Rx-Core.2.2.5\lib\net40\System.Reactive.Core.dll + + + ..\packages\Rx-Interfaces.2.2.5\lib\net40\System.Reactive.Interfaces.dll + + + ..\packages\Rx-Linq.2.2.5\lib\net40\System.Reactive.Linq.dll + True + + + ..\packages\Rx-PlatformServices.2.2.5\lib\net40\System.Reactive.PlatformServices.dll + True + 4.0 @@ -97,6 +111,7 @@ Always + diff --git a/FrameworkTests/packages.config b/FrameworkTests/packages.config new file mode 100755 index 00000000..c3ad3ea2 --- /dev/null +++ b/FrameworkTests/packages.config @@ -0,0 +1,8 @@ + + + + + + + + \ No newline at end of file diff --git a/TestBundles/TestBundleV1/TestBundleV1.csproj b/TestBundles/TestBundleV1/TestBundleV1.csproj index 6180ef91..a272630e 100644 --- a/TestBundles/TestBundleV1/TestBundleV1.csproj +++ b/TestBundles/TestBundleV1/TestBundleV1.csproj @@ -29,8 +29,9 @@ Library TestBundleV1 TestBundleV1.Connector - v4.0 + v4.5 OnBuildSuccess + prompt @@ -42,6 +43,7 @@ False True DEBUG;TRACE + false pdbonly @@ -53,6 +55,7 @@ False True False + false diff --git a/TestBundles/TestBundleV1/TestConnector.cs b/TestBundles/TestBundleV1/TestConnector.cs index 5a5fad55..7d6f9872 100644 --- a/TestBundles/TestBundleV1/TestConnector.cs +++ b/TestBundles/TestBundleV1/TestConnector.cs @@ -19,14 +19,19 @@ * enclosed by brackets [] replaced by your own identifying information: * "Portions Copyrighted [year] [name of copyright owner]" * ==================== - * Portions Copyrighted 2012-2014 ForgeRock AS. + * Portions Copyrighted 2012-2015 ForgeRock AS. */ using System; using System.Collections; +using System.Collections.Concurrent; using System.Collections.Generic; using System.Globalization; +using System.Linq; using System.Threading; +using System.Threading.Tasks; using Org.IdentityConnectors.Common; +using Org.IdentityConnectors.Common.Script; +using Org.IdentityConnectors.Common.Security; using Org.IdentityConnectors.Framework.Common.Objects; using ICF = Org.IdentityConnectors.Framework.Common.Objects; using Org.IdentityConnectors.Framework.Common.Objects.Filters; @@ -72,9 +77,12 @@ public int GetConnectionNumber() #endregion #region TstAbstractConnector - public abstract class TstAbstractConnector : CreateOp, SearchOp, SyncOp, DeleteOp + public abstract class TstAbstractConnector : AuthenticateOp, IConnectorEventSubscriptionOp, + CreateOp, DeleteOp, ResolveUsernameOp, SchemaOp, ScriptOnResourceOp, SearchOp, + ISyncEventSubscriptionOp, SyncOp, TestOp, UpdateOp { + internal sealed class ResourceComparator : IComparer { private readonly IList sortKeys; @@ -198,52 +206,226 @@ public void Init(Configuration cfg) Guid g = _config.Guid; } + public void Update() + { + _config.UpdateTest(); + } + + public virtual Uid Authenticate(ObjectClass objectClass, string username, GuardedString password, OperationOptions options) + { + if (_config.ReturnNullTest) + { + return null; + } + else + { + return _config.Authenticate(objectClass, username, password); + } + } + + public ISubscription Subscribe(ObjectClass objectClass, Filter eventFilter, IObserver handler, OperationOptions operationOptions) + { + + ConnectorObjectBuilder builder = new ConnectorObjectBuilder { ObjectClass = objectClass }; + + CancellationSubscription subscription = new CancellationSubscription(); + + DoPeriodicWorkAsync(runCount => + { + builder.SetUid(Convert.ToString(runCount)); + builder.SetName(Convert.ToString(runCount)); + handler.OnNext(builder.Build()); + + if (runCount >= 10) + { + // Locally stop serving subscription + handler.OnError(new ConnectorException("Subscription channel is closed")); + // The loop should be stopped from here. + return false; + } + return true; + }, new TimeSpan(0, 0, 0, 0, 500), new TimeSpan(0, 0, 1), subscription.Token); + + + return subscription; + } + + private async Task DoPeriodicWorkAsync(Func action, + TimeSpan interval, TimeSpan dueTime, CancellationToken token) + { + // Initial wait time before we begin the periodic loop. + await Task.Delay(dueTime, token); + + Int32 i = 0; + // Repeat this loop until cancelled. + while (!token.IsCancellationRequested) + { + if (action(++i)) + { + // Wait to repeat again. + await Task.Delay(interval, token); + } + else + { + break; + } + } + } + + public ISubscription Subscribe(ObjectClass objectClass, SyncToken token, IObserver handler, OperationOptions operationOptions) + { + var coBuilder = new ConnectorObjectBuilder() { ObjectClass = objectClass }; + coBuilder.SetUid("0"); + coBuilder.SetName("SYNC_EVENT"); + + SyncDeltaBuilder builder = new SyncDeltaBuilder() + { + DeltaType = SyncDeltaType.CREATE_OR_UPDATE, + Object = coBuilder.Build() + }; + + CancellationSubscription subscription = new CancellationSubscription(); + + DoPeriodicWorkAsync(runCount => + { + builder.Token = new SyncToken(runCount); + handler.OnNext(builder.Build()); + + if (runCount >= 10) + { + // Locally stop serving subscription + handler.OnError(new ConnectorException("Subscription channel is closed")); + // ScheduledFuture should be stopped from here. + return false; + } + return true; + }, new TimeSpan(0, 0, 0, 0, 500), new TimeSpan(0, 0, 1), subscription.Token); + + + return subscription; + } + + public Uid Create(ObjectClass objectClass, ICollection createAttributes, OperationOptions options) { ConnectorAttributesAccessor accessor = new ConnectorAttributesAccessor(createAttributes); - if (accessor.HasAttribute("fail")) + if (_config.ReturnNullTest) { - throw new ConnectorException("Test Exception"); + return null; } - else if (accessor.HasAttribute("exist") && accessor.FindBoolean("exist") == true) + else if (_config.IsTestObjectClass(objectClass)) { - throw new AlreadyExistsException(accessor.GetName().GetNameValue()); + return _config.GeObjectCache(objectClass).Create(createAttributes); } - else if (accessor.HasAttribute("emails")) + else { - object value = ConnectorAttributeUtil.GetSingleValue(accessor.Find("emails")); - if (value is IDictionary) + if (accessor.HasAttribute("fail")) { - return new Uid((string)((IDictionary)value)["email"]); + throw new ConnectorException("Test Exception"); } - else + else if (accessor.HasAttribute("exist") && accessor.FindBoolean("exist") == true) + { + throw new AlreadyExistsException(accessor.GetName().GetNameValue()); + } + else if (accessor.HasAttribute("emails")) { - throw new InvalidAttributeValueException("Expecting Map"); + object value = ConnectorAttributeUtil.GetSingleValue(accessor.Find("emails")); + if (value is IDictionary) + { + return new Uid((string)((IDictionary)value)["email"]); + } + else + { + throw new InvalidAttributeValueException("Expecting Map"); + } } + return new Uid(_config.Guid.ToString()); } - return new Uid(_config.Guid.ToString()); } public void Delete(ObjectClass objectClass, Uid uid, OperationOptions options) { - if (null == uid.Revision) + if (_config.ReturnNullTest) { - throw new PreconditionRequiredException("Version is required for MVCC"); + return; } - else if (_config.Guid.ToString().Equals(uid.Revision)) + if (_config.IsTestObjectClass(objectClass)) { - // Delete - String a = _config.Guid.ToString(); - String b = _config.Guid.ToString(); - String c = _config.Guid.ToString(); + _config.GeObjectCache(objectClass).Delete(uid); + } + else + { + if (null == uid.Revision) + { + throw new PreconditionRequiredException("Version is required for MVCC"); + } + else if (_config.Guid.ToString().Equals(uid.Revision)) + { + // Delete + String a = _config.Guid.ToString(); + String b = _config.Guid.ToString(); + String c = _config.Guid.ToString(); + } + else + { + throw new PreconditionFailedException("Current version of resource is 0 and not match with: " + + uid.Revision); + } + } + } + + public virtual Uid ResolveUsername(ObjectClass objectClass, string username, OperationOptions options) + { + if (_config.ReturnNullTest) + { + return null; + } + else + { + return _config.ResolveByUsername(objectClass, username); + } + } + + + public virtual Schema Schema() + { + if (_config.ReturnNullTest) + { + return null; } else { - throw new PreconditionFailedException("Current version of resource is 0 and not match with: " + uid.Revision); + SchemaBuilder builder = new SchemaBuilder(SafeType.ForRawType(GetType())); + foreach (string type in _config.TestObjectClass) + { + ObjectClassInfoBuilder classInfoBuilder = new ObjectClassInfoBuilder(); + classInfoBuilder.ObjectType = type; + classInfoBuilder.AddAttributeInfo(OperationalAttributeInfos.PASSWORD); + builder.DefineObjectClass(classInfoBuilder.Build()); + } + return builder.Build(); } } + public virtual object RunScriptOnResource(ScriptContext request, OperationOptions options) + { + if (_config.ReturnNullTest) + { + return null; + } + else + { + try + { + return ScriptExecutorFactory.NewInstance(request.ScriptLanguage).NewScriptExecutor(null, request.ScriptText, true).Execute(request.ScriptArguments); + } + catch (Exception e) + { + throw new ConnectorException(e.Message, e); + } + } + } public FilterTranslator CreateFilterTranslator(ObjectClass objectClass, OperationOptions options) { return new FilterTranslatorAnonymousInnerClassHelper(); @@ -274,20 +456,34 @@ public void ExecuteQuery(ObjectClass objectClass, Filter query, ResultsHandler h // Rebuild the full result set. SortedSet resultSet = new SortedSet(new ResourceComparator(sortKeys)); - - if (null != query) + if (_config.ReturnNullTest) + { + return; + } + else if (_config.IsTestObjectClass(objectClass)) { - foreach (ConnectorObject co in collection.Values) + Filter filter = FilteredResultsHandlerVisitor.WrapFilter(query, _config.CaseIgnore); + foreach (var connectorObject in _config.GeObjectCache(objectClass).GetIterable(filter)) { - if (query.Accept(co)) - { - resultSet.Add(co); - } + resultSet.Add(connectorObject); } } else { - resultSet.UnionWith(collection.Values); + if (null != query) + { + foreach (ConnectorObject co in collection.Values) + { + if (query.Accept(co)) + { + resultSet.Add(co); + } + } + } + else + { + resultSet.UnionWith(collection.Values); + } } // Handle the results if (null != options.PageSize) @@ -359,15 +555,71 @@ public void ExecuteQuery(ObjectClass objectClass, Filter query, ResultsHandler h public void Sync(ObjectClass objectClass, SyncToken token, SyncResultsHandler handler, OperationOptions options) { - if (handler is SyncTokenResultsHandler) + if (_config.ReturnNullTest) + { + return; + } + if (_config.IsTestObjectClass(objectClass)) + { + foreach (SyncDelta delta in _config.Sync(objectClass, (int?)token.Value)) + { + if (!handler.Handle(delta)) + { + break; + } + } + if (handler is SyncTokenResultsHandler) + { + ((SyncTokenResultsHandler)handler).HandleResult(new SyncToken(_config.LatestSyncToken)); + } + } + else { - ((SyncTokenResultsHandler)handler).HandleResult(GetLatestSyncToken(objectClass)); + if (handler is SyncTokenResultsHandler) + { + ((SyncTokenResultsHandler)handler).HandleResult(GetLatestSyncToken(objectClass)); + } } } public SyncToken GetLatestSyncToken(ObjectClass objectClass) { - return new SyncToken(_config.Guid.ToString()); + if (_config.ReturnNullTest) + { + return null; + } + else if (_config.IsTestObjectClass(objectClass)) + { + return new SyncToken(_config.LatestSyncToken); + } + else + { + return new SyncToken(_config.Guid.ToString()); + } + } + + public void Test() + { + if (_config.failValidation) + { + throw new ConnectorException("validation failed " + CultureInfo.CurrentUICulture.TwoLetterISOLanguageName); + } + } + + public Uid Update(ObjectClass objectClass, Uid uid, ICollection replaceAttributes, OperationOptions options) + { + if (_config.ReturnNullTest) + { + return null; + } + else if (_config.IsTestObjectClass(objectClass)) + { + return _config.GeObjectCache(objectClass).Update(uid, replaceAttributes); + } + else + { + throw new System.NotSupportedException("Object Update is not supported: " + objectClass.GetObjectClassValue()); + } } private static readonly SortedDictionary collection = new SortedDictionary(StringComparer.InvariantCultureIgnoreCase); @@ -390,7 +642,6 @@ static TstAbstractConnector() enabled = !enabled; } } - } #endregion @@ -456,6 +707,14 @@ public String concat(String s1, String s2) return s1 + s2; } + /// + /// Used by the script tests + /// + public void Update() + { + _config.UpdateTest(); + } + public void CheckAlive() { _myConnection.Test(); @@ -529,7 +788,7 @@ public void Sync(ObjectClass objClass, SyncToken token, } else { - break; ; + break; } } if (handler is SyncTokenResultsHandler) @@ -593,6 +852,11 @@ public override void Validate() } } + public void UpdateTest() + { + tstField = "change"; + NotifyConfigurationUpdate(); + } } #endregion @@ -628,6 +892,13 @@ public void Dispose() #region TstStatefulConnectorConfig public class TstStatefulConnectorConfig : TstConnectorConfig, StatefulConfiguration { + public Boolean CaseIgnore { get; set; } + + public String[] TestObjectClass { get; set; } + + public Boolean ReturnNullTest { get; set; } + + public String RandomString { get; set; } private Guid? guid; @@ -650,6 +921,269 @@ public void Release() { guid = null; } + + public bool IsTestObjectClass(ObjectClass objectClass) + { + return null != objectClass && null != TestObjectClass && TestObjectClass.Contains(objectClass.GetObjectClassValue(), StringComparer.OrdinalIgnoreCase); + } + + public virtual Uid ResolveByUsername(ObjectClass objectClass, string username) + { + ObjectClassCacheEntry cache; + objectCache.TryGetValue(objectClass, out cache); + if (null != cache) + { + ConnectorObjectCacheEntry entry = cache.GetByName(username); + if (null != entry) + { + return entry.ConnectorObject.Uid; + } + } + return null; + } + + public virtual Uid Authenticate(ObjectClass objectClass, string username, GuardedString password) + { + ObjectClassCacheEntry cache; + objectCache.TryGetValue(objectClass, out cache); + if (null != cache) + { + ConnectorObjectCacheEntry entry = cache.GetByName(username); + if (null != entry) + { + if (entry.Authenticate(password)) + { + return entry.ConnectorObject.Uid; + } + throw new InvalidPasswordException("Invalid Password"); + } + throw new InvalidCredentialException("Unknown username: " + username); + } + throw new InvalidCredentialException("Empty ObjectClassCache: " + objectClass.GetObjectClassValue()); + } + + internal virtual IEnumerable Sync(ObjectClass objectClass, int? token) + { + ObjectClassCacheEntry cache; + objectCache.TryGetValue(objectClass, out cache); + if (null != cache) + { + return cache.ObjectCache.Values.Where(x => + { + int rev = Convert.ToInt32(x.ConnectorObject.Uid.Revision); + return null == token || rev > token; + }).OrderBy(x => x.ConnectorObject.Uid.Revision).Select(x => + { + var builder = new SyncDeltaBuilder(); + builder.DeltaType = x.DeltaType; + builder.Token = new SyncToken(Convert.ToInt32(x.ConnectorObject.Uid.Revision)); + builder.Object = x.ConnectorObject; + return builder.Build(); + }); + } + return Enumerable.Empty(); + } + + private Int32 _revision = 0; + + internal virtual Int32 LatestSyncToken + { + get + { + return _revision; + } + } + + internal virtual Uid GetNextUid(string uid) + { + return new Uid(uid, Convert.ToString(Interlocked.Increment(ref _id))); + } + + private Int32 _id = 0; + + private Uid NewUid() + { + return GetNextUid(Convert.ToString(Interlocked.Increment(ref _id))); + } + + private readonly ConcurrentDictionary objectCache = new ConcurrentDictionary(); + + internal virtual ObjectClassCacheEntry GeObjectCache(ObjectClass objectClass) + { + ObjectClassCacheEntry cache; + objectCache.TryGetValue(objectClass, out cache); + if (null == cache) + { + cache = new ObjectClassCacheEntry(objectClass, NewUid, GetNextUid); + ObjectClassCacheEntry rv = objectCache.GetOrAdd(objectClass, cache); + if (null != rv) + { + cache = rv; + } + } + return cache; + } + + internal class ObjectClassCacheEntry + { + private readonly ObjectClass _objectClass; + private readonly ReaderWriterLockSlim _lock = new ReaderWriterLockSlim(); + private readonly ConcurrentDictionary _uniqueNameIndex = new ConcurrentDictionary(); + internal readonly ConcurrentDictionary ObjectCache = new ConcurrentDictionary(); + private readonly Func _newUid; + private readonly Func _getNextUid; + public ObjectClassCacheEntry(ObjectClass objectClass, Func newUid, Func getNextUid) + { + _objectClass = objectClass; + _newUid = newUid; + _getNextUid = getNextUid; + } + + internal virtual ConnectorObjectCacheEntry GetByName(string username) + { + ConnectorObjectCacheEntry entry = null; + string uid; + _uniqueNameIndex.TryGetValue(username, out uid); + if (null != uid) + { + ObjectCache.TryGetValue(uid, out entry); + } + return entry; + } + + public virtual Uid Create(ICollection createAttributes) + { + Name name = ConnectorAttributeUtil.GetNameFromAttributes(createAttributes); + if (name == null) + { + throw new InvalidAttributeValueException("__NAME__ Required"); + } + if (String.IsNullOrWhiteSpace(name.GetNameValue())) + { + throw new InvalidAttributeValueException("__NAME__ can not be blank"); + } + Uid uid = _newUid(); + if (_uniqueNameIndex.GetOrAdd(name.GetNameValue(), uid.GetUidValue()) == null) + { + var builder = new ConnectorObjectBuilder { ObjectClass = _objectClass }; + builder.AddAttributes(createAttributes).SetUid(uid); + + ObjectCache.TryAdd(uid.GetUidValue(), new ConnectorObjectCacheEntry(builder.Build(), _getNextUid)); + return uid; + } + else + { + throw (new AlreadyExistsException()).InitUid(new Uid(name.GetNameValue())); + } + } + + public virtual Uid Update(Uid uid, ICollection updateAttributes) + { + ConnectorObjectCacheEntry entry = null; + ObjectCache.TryGetValue(uid.GetUidValue(), out entry); + if (null == entry) + { + throw new UnknownUidException(uid, _objectClass); + } + if (_lock.TryEnterWriteLock(new TimeSpan(0, 1, 0))) + { + try + { + IDictionary attributeMap = CollectionUtil.NewCaseInsensitiveDictionary(); + foreach (ConnectorAttribute attr in entry.ConnectorObject.GetAttributes()) + { + attributeMap[attr.Name] = attr; + } + foreach (ConnectorAttribute attribute in updateAttributes) + { + if (attribute.Value == null) + { + attributeMap.Remove(attribute.Name); + } + else + { + attributeMap[attribute.Name] = attribute; + } + } + return entry.Update(attributeMap.Values); + + } + finally + { + _lock.ExitWriteLock(); + } + } + else + { + throw new ConnectorException("Failed to acquire lock", new TimeoutException("Failed to acquire lock")); + } + + } + + public virtual void Delete(Uid uid) + { + ConnectorObjectCacheEntry entry = null; + ObjectCache.TryGetValue(uid.GetUidValue(), out entry); + if (null == entry) + { + throw new UnknownUidException(uid, _objectClass); + } + + + if (_lock.TryEnterWriteLock(new TimeSpan(0, 1, 0))) + { + try + { + entry.Update(entry.ConnectorObject.GetAttributes()); + entry.DeltaType = SyncDeltaType.DELETE; + } + finally + { + _lock.ExitWriteLock(); + } + } + else + { + throw new ConnectorException("Failed to acquire lock", new TimeoutException("Failed to acquire lock")); + } + + } + public virtual IEnumerable GetIterable(Filter filter) + { + return ObjectCache.Values.Where(x => !SyncDeltaType.DELETE.Equals(x.DeltaType) && (null == filter || filter.Accept(x.ConnectorObject))).Select(x => x.ConnectorObject); + } + } + + + internal class ConnectorObjectCacheEntry + { + + internal SyncDeltaType DeltaType = SyncDeltaType.CREATE; + + internal ConnectorObject ConnectorObject { get; set; } + private readonly Func _getNextUid; + + public ConnectorObjectCacheEntry(ConnectorObject connectorConnectorObject, Func getNextUid) + { + ConnectorObject = connectorConnectorObject; + _getNextUid = getNextUid; + } + + public virtual bool Authenticate(GuardedString password) + { + ConnectorAttribute pw = ConnectorObject.GetAttributeByName(OperationalAttributes.PASSWORD_NAME); + return null != pw && null != password && ConnectorAttributeUtil.GetSingleValue(pw).Equals(password); + } + + public virtual Uid Update(ICollection updateAttributes) + { + var builder = new ConnectorObjectBuilder { ObjectClass = ConnectorObject.ObjectClass }; + builder.AddAttributes(updateAttributes).SetUid(_getNextUid(ConnectorObject.Uid.GetUidValue())); + ConnectorObject = builder.Build(); + DeltaType = SyncDeltaType.UPDATE; + return ConnectorObject.Uid; + } + } } #endregion From 9edfd26eb7c65b18a1bc1d6507bda74706f0f177 Mon Sep 17 00:00:00 2001 From: Laszlo Hordos Date: Wed, 12 Aug 2015 13:03:17 +0000 Subject: [PATCH 327/342] OPENICF-389 CR-7778 - Add .Net implementation of new communication protocol --- .../BooScriptExecutorFactory.csproj | 9 +- BooScriptExecutorFactory/version.template | 2 +- Common/Common.csproj | 11 +- Common/version.template | 2 +- ConnectorFramework.sln | 196 +- .../ConnectorServerService.Designer.cs | 37 + .../ConnectorServerService.cs | 613 ++ .../ConnectorServerService.csproj | 153 + ConnectorServerService/FR_ICF_sq_med.ico | Bin 0 -> 32038 bytes ConnectorServerService/Program.cs | 200 + ConnectorServerService/ProjectInstaller.cs | 93 + .../Properties/Resources.Designer.cs | 82 + .../Properties/Resources.resx | 212 + ConnectorServerService/packages.config | 5 + ConnectorServerService/version.template | 1 + Framework/Api.cs | 2 +- Framework/Common.cs | 62 +- Framework/Framework.csproj | 11 +- Framework/version.template | 2 +- FrameworkInternal/Api.cs | 8 +- FrameworkInternal/FrameworkInternal.csproj | 13 +- FrameworkInternal/version.template | 2 +- FrameworkProtoBuf/CommonObjectMessages.cs | 2043 +++++ FrameworkProtoBuf/FrameworkProtoBuf.csproj | 78 + FrameworkProtoBuf/OperationMessages.cs | 6949 +++++++++++++++++ FrameworkProtoBuf/RPCMessages.cs | 1950 +++++ .../protobuf/CommonObjectMessages.proto | 82 + .../protobuf/OperationMessages.proto | 251 + FrameworkProtoBuf/protobuf/RPCMessages.proto | 85 + FrameworkProtoBuf/protobuf/generate.cmd | 1 + FrameworkProtoBuf/version.template | 1 + FrameworkRPC/FrameworkRpc.csproj | 69 + FrameworkRPC/Rpc.cs | 1066 +++ FrameworkRPC/version.template | 1 + FrameworkServer/Async.cs | 784 ++ FrameworkServer/AsyncImpl.cs | 2808 +++++++ FrameworkServer/Client.cs | 580 ++ .../ConnectorEventSubscriptionApiOpImpl.cs | 625 ++ FrameworkServer/Framework.cs | 665 ++ FrameworkServer/FrameworkServer.csproj | 108 + FrameworkServer/Local.cs | 58 + FrameworkServer/Remote.cs | 2311 ++++++ FrameworkServer/Rpc.cs | 844 ++ FrameworkServer/packages.config | 4 + FrameworkServer/version.template | 1 + .../FrameworkServerTests.csproj | 134 + FrameworkServerTests/WcfServiceTests.cs | 514 ++ FrameworkServerTests/version.template | 1 + FrameworkTests/FrameworkTests.csproj | 7 +- FrameworkTests/VersionRangeTests.cs | 73 +- FrameworkTests/app.config | 19 +- FrameworkTests/packages.config | 8 +- FrameworkTests/version.template | 2 +- .../PowerShellScriptExecutorFactory.csproj | 11 +- .../version.template | 2 +- Service/FR_ICF_sq_med.ico | Bin 0 -> 32038 bytes Service/Program.cs | 2 +- Service/ProjectInstaller.cs | 6 +- Service/Properties/Resources.Designer.cs | 6 +- Service/Service.csproj | 29 +- Service/app.config | 36 +- Service/version.template | 2 +- ServiceInstall/ExtBuild.proj | 13 +- ServiceInstall/File.top | 25 +- ServiceInstall/Resources/FR_ICF_sq_med.ico | Bin 0 -> 32038 bytes ServiceInstall/Resources/SetupBanner.bmp | Bin 0 -> 85896 bytes ServiceInstall/Resources/setupDialog.bmp | Bin 0 -> 461816 bytes ServiceInstall/ServiceInstall.wixproj | 38 +- ServiceInstall/Setup.wxs | 45 +- .../ShellScriptExecutorFactory.csproj | 7 +- ShellScriptExecutorFactory/version.template | 2 +- TestBundles/TestBundleV1/TestBundleV1.csproj | 4 +- TestBundles/TestBundleV1/TestConnector.cs | 62 +- TestBundles/TestBundleV2/TestBundleV2.csproj | 7 +- TestCommon/PropertyBag.cs | 2 +- TestCommon/TestCommon.csproj | 7 +- TestCommon/version.template | 2 +- WcfServiceLibrary/ConnectorServerService.cs | 499 ++ WcfServiceLibrary/IWebSocketService.cs | 45 + WcfServiceLibrary/WcfServiceLibrary.csproj | 120 + WcfServiceLibrary/version.template | 1 + .../lib/Google.Protobuf.dll | Bin 0 -> 252928 bytes .../lib/Google.Protobuf.pdb | Bin 0 -> 808448 bytes .../Google.ProtocolBuffers.3/tools/protoc.exe | Bin 0 -> 2127872 bytes .../lib/net45/vtortola.WebSockets.Rfc6455.dll | Bin 0 -> 32768 bytes .../lib/net45/vtortola.WebSockets.dll | Bin 0 -> 51712 bytes 86 files changed, 24614 insertions(+), 197 deletions(-) create mode 100755 ConnectorServerService/ConnectorServerService.Designer.cs create mode 100755 ConnectorServerService/ConnectorServerService.cs create mode 100755 ConnectorServerService/ConnectorServerService.csproj create mode 100755 ConnectorServerService/FR_ICF_sq_med.ico create mode 100755 ConnectorServerService/Program.cs create mode 100755 ConnectorServerService/ProjectInstaller.cs create mode 100755 ConnectorServerService/Properties/Resources.Designer.cs create mode 100755 ConnectorServerService/Properties/Resources.resx create mode 100755 ConnectorServerService/packages.config create mode 100755 ConnectorServerService/version.template create mode 100755 FrameworkProtoBuf/CommonObjectMessages.cs create mode 100755 FrameworkProtoBuf/FrameworkProtoBuf.csproj create mode 100755 FrameworkProtoBuf/OperationMessages.cs create mode 100755 FrameworkProtoBuf/RPCMessages.cs create mode 100755 FrameworkProtoBuf/protobuf/CommonObjectMessages.proto create mode 100755 FrameworkProtoBuf/protobuf/OperationMessages.proto create mode 100755 FrameworkProtoBuf/protobuf/RPCMessages.proto create mode 100755 FrameworkProtoBuf/protobuf/generate.cmd create mode 100755 FrameworkProtoBuf/version.template create mode 100755 FrameworkRPC/FrameworkRpc.csproj create mode 100755 FrameworkRPC/Rpc.cs create mode 100755 FrameworkRPC/version.template create mode 100755 FrameworkServer/Async.cs create mode 100755 FrameworkServer/AsyncImpl.cs create mode 100755 FrameworkServer/Client.cs create mode 100755 FrameworkServer/ConnectorEventSubscriptionApiOpImpl.cs create mode 100755 FrameworkServer/Framework.cs create mode 100755 FrameworkServer/FrameworkServer.csproj create mode 100755 FrameworkServer/Local.cs create mode 100755 FrameworkServer/Remote.cs create mode 100755 FrameworkServer/Rpc.cs create mode 100755 FrameworkServer/packages.config create mode 100755 FrameworkServer/version.template create mode 100755 FrameworkServerTests/FrameworkServerTests.csproj create mode 100755 FrameworkServerTests/WcfServiceTests.cs create mode 100755 FrameworkServerTests/version.template create mode 100755 Service/FR_ICF_sq_med.ico create mode 100755 ServiceInstall/Resources/FR_ICF_sq_med.ico create mode 100644 ServiceInstall/Resources/SetupBanner.bmp create mode 100644 ServiceInstall/Resources/setupDialog.bmp create mode 100755 WcfServiceLibrary/ConnectorServerService.cs create mode 100755 WcfServiceLibrary/IWebSocketService.cs create mode 100755 WcfServiceLibrary/WcfServiceLibrary.csproj create mode 100755 WcfServiceLibrary/version.template create mode 100755 packages/Google.ProtocolBuffers.3/lib/Google.Protobuf.dll create mode 100755 packages/Google.ProtocolBuffers.3/lib/Google.Protobuf.pdb create mode 100755 packages/Google.ProtocolBuffers.3/tools/protoc.exe create mode 100755 packages/vtortola.WebSocketListener.2.1.8.1/lib/net45/vtortola.WebSockets.Rfc6455.dll create mode 100755 packages/vtortola.WebSocketListener.2.1.8.1/lib/net45/vtortola.WebSockets.dll diff --git a/BooScriptExecutorFactory/BooScriptExecutorFactory.csproj b/BooScriptExecutorFactory/BooScriptExecutorFactory.csproj index e52b6b73..d7bddd6f 100644 --- a/BooScriptExecutorFactory/BooScriptExecutorFactory.csproj +++ b/BooScriptExecutorFactory/BooScriptExecutorFactory.csproj @@ -21,7 +21,7 @@ "Portions Copyrighted [year] [name of copyright owner]" ==================== --> - + {0747C440-70E4-4E63-9F9D-03B3A010C991} Debug @@ -29,8 +29,9 @@ Library Org.IdentityConnectors.Common.Script.Boo Boo.ScriptExecutorFactory - BooScriptExecutorFactory - v4.0 + Boo ScriptExecutor Factory + v4.5.2 + prompt @@ -42,6 +43,7 @@ False True DEBUG;TRACE + false pdbonly @@ -53,6 +55,7 @@ true True False + false diff --git a/BooScriptExecutorFactory/version.template b/BooScriptExecutorFactory/version.template index 2f5d8109..c085cfe1 100644 --- a/BooScriptExecutorFactory/version.template +++ b/BooScriptExecutorFactory/version.template @@ -1 +1 @@ -1.4.2.0 \ No newline at end of file +1.5.0.0 \ No newline at end of file diff --git a/Common/Common.csproj b/Common/Common.csproj index 8744fb39..955da625 100644 --- a/Common/Common.csproj +++ b/Common/Common.csproj @@ -21,7 +21,7 @@ "Portions Copyrighted [year] [name of copyright owner]" ==================== --> - + {F140E8DA-52B4-4159-992A-9DA10EA8EEFB} Debug @@ -30,12 +30,13 @@ Org.IdentityConnectors.Common Common Open Connectors Common - v4.0 + v4.5.2 True False 4 false true + bin\Debug\ @@ -60,6 +61,12 @@ AnyCPU 4096 + + false + + + false + diff --git a/Common/version.template b/Common/version.template index 2f5d8109..c085cfe1 100644 --- a/Common/version.template +++ b/Common/version.template @@ -1 +1 @@ -1.4.2.0 \ No newline at end of file +1.5.0.0 \ No newline at end of file diff --git a/ConnectorFramework.sln b/ConnectorFramework.sln index 02259bb0..b2b6c27c 100644 --- a/ConnectorFramework.sln +++ b/ConnectorFramework.sln @@ -1,6 +1,8 @@ - -Microsoft Visual Studio Solution File, Format Version 11.00 -# Visual Studio 2010 + +Microsoft Visual Studio Solution File, Format Version 12.00 +# Visual Studio 2013 +VisualStudioVersion = 12.0.40418.0 +MinimumVisualStudioVersion = 10.0.40219.1 Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Common", "Common\Common.csproj", "{F140E8DA-52B4-4159-992A-9DA10EA8EEFB}" EndProject Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Framework", "Framework\Framework.csproj", "{8B24461B-456A-4032-89A1-CD418F7B5B62}" @@ -66,6 +68,7 @@ EndProject Project("{930C7802-8A8C-48F9-8165-68863BCCD9DD}") = "ServiceInstall", "ServiceInstall\ServiceInstall.wixproj", "{1CBA8F74-050C-432B-8437-08BD13BDC684}" ProjectSection(ProjectDependencies) = postProject {F140E8DA-52B4-4159-992A-9DA10EA8EEFB} = {F140E8DA-52B4-4159-992A-9DA10EA8EEFB} + {E5DCC07F-7B42-4AE9-8D6C-A15525476E0A} = {E5DCC07F-7B42-4AE9-8D6C-A15525476E0A} {8B24461B-456A-4032-89A1-CD418F7B5B62} = {8B24461B-456A-4032-89A1-CD418F7B5B62} {5b011775-b121-4eee-a410-ba2d2f5bfb8b} = {5b011775-b121-4eee-a410-ba2d2f5bfb8b} {57754ffa-bb1f-4722-a2fa-70c4f27c6784} = {57754ffa-bb1f-4722-a2fa-70c4f27c6784} @@ -74,59 +77,200 @@ Project("{930C7802-8A8C-48F9-8165-68863BCCD9DD}") = "ServiceInstall", "ServiceIn {e6a207d2-e083-41bf-b522-d9d3ec09323e} = {e6a207d2-e083-41bf-b522-d9d3ec09323e} EndProjectSection EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "FrameworkProtoBuf", "FrameworkProtoBuf\FrameworkProtoBuf.csproj", "{5A9E8C5B-4D41-4E3E-9680-6C195BFAD47A}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "FrameworkRpc", "FrameworkRPC\FrameworkRpc.csproj", "{B85C5A35-E3A2-4B04-9693-795E57D66DE2}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "ConnectorServerService", "ConnectorServerService\ConnectorServerService.csproj", "{E5DCC07F-7B42-4AE9-8D6C-A15525476E0A}" + ProjectSection(ProjectDependencies) = postProject + {F140E8DA-52B4-4159-992A-9DA10EA8EEFB} = {F140E8DA-52B4-4159-992A-9DA10EA8EEFB} + {8B24461B-456A-4032-89A1-CD418F7B5B62} = {8B24461B-456A-4032-89A1-CD418F7B5B62} + {5A9E8C5B-4D41-4E3E-9680-6C195BFAD47A} = {5A9E8C5B-4D41-4E3E-9680-6C195BFAD47A} + {B85C5A35-E3A2-4B04-9693-795E57D66DE2} = {B85C5A35-E3A2-4B04-9693-795E57D66DE2} + {5B47BEFD-C60B-4E80-943E-A7151CEEA568} = {5B47BEFD-C60B-4E80-943E-A7151CEEA568} + {D1771E11-C7D3-43FD-9D87-46F1231846F1} = {D1771E11-C7D3-43FD-9D87-46F1231846F1} + EndProjectSection +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "FrameworkServer", "FrameworkServer\FrameworkServer.csproj", "{5B47BEFD-C60B-4E80-943E-A7151CEEA568}" + ProjectSection(ProjectDependencies) = postProject + {F140E8DA-52B4-4159-992A-9DA10EA8EEFB} = {F140E8DA-52B4-4159-992A-9DA10EA8EEFB} + {8B24461B-456A-4032-89A1-CD418F7B5B62} = {8B24461B-456A-4032-89A1-CD418F7B5B62} + {5b011775-b121-4eee-a410-ba2d2f5bfb8b} = {5b011775-b121-4eee-a410-ba2d2f5bfb8b} + {5A9E8C5B-4D41-4E3E-9680-6C195BFAD47A} = {5A9E8C5B-4D41-4E3E-9680-6C195BFAD47A} + {B85C5A35-E3A2-4B04-9693-795E57D66DE2} = {B85C5A35-E3A2-4B04-9693-795E57D66DE2} + EndProjectSection +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "WcfServiceLibrary", "WcfServiceLibrary\WcfServiceLibrary.csproj", "{D1771E11-C7D3-43FD-9D87-46F1231846F1}" + ProjectSection(ProjectDependencies) = postProject + {F140E8DA-52B4-4159-992A-9DA10EA8EEFB} = {F140E8DA-52B4-4159-992A-9DA10EA8EEFB} + {8B24461B-456A-4032-89A1-CD418F7B5B62} = {8B24461B-456A-4032-89A1-CD418F7B5B62} + {5A9E8C5B-4D41-4E3E-9680-6C195BFAD47A} = {5A9E8C5B-4D41-4E3E-9680-6C195BFAD47A} + {B85C5A35-E3A2-4B04-9693-795E57D66DE2} = {B85C5A35-E3A2-4B04-9693-795E57D66DE2} + {5B47BEFD-C60B-4E80-943E-A7151CEEA568} = {5B47BEFD-C60B-4E80-943E-A7151CEEA568} + EndProjectSection +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "FrameworkServerTests", "FrameworkServerTests\FrameworkServerTests.csproj", "{46365523-FA23-4AD4-9DB8-B0E195F00571}" + ProjectSection(ProjectDependencies) = postProject + {F140E8DA-52B4-4159-992A-9DA10EA8EEFB} = {F140E8DA-52B4-4159-992A-9DA10EA8EEFB} + {8B24461B-456A-4032-89A1-CD418F7B5B62} = {8B24461B-456A-4032-89A1-CD418F7B5B62} + {5B011775-B121-4EEE-A410-BA2D2F5BFB8B} = {5B011775-B121-4EEE-A410-BA2D2F5BFB8B} + {B85C5A35-E3A2-4B04-9693-795E57D66DE2} = {B85C5A35-E3A2-4B04-9693-795E57D66DE2} + {5B47BEFD-C60B-4E80-943E-A7151CEEA568} = {5B47BEFD-C60B-4E80-943E-A7151CEEA568} + {D1771E11-C7D3-43FD-9D87-46F1231846F1} = {D1771E11-C7D3-43FD-9D87-46F1231846F1} + {E5DCC07F-7B42-4AE9-8D6C-A15525476E0A} = {E5DCC07F-7B42-4AE9-8D6C-A15525476E0A} + EndProjectSection +EndProject Global GlobalSection(SolutionConfigurationPlatforms) = preSolution Debug|Any CPU = Debug|Any CPU + Debug|Mixed Platforms = Debug|Mixed Platforms Release|Any CPU = Release|Any CPU + Release|Mixed Platforms = Release|Mixed Platforms EndGlobalSection GlobalSection(ProjectConfigurationPlatforms) = postSolution {F140E8DA-52B4-4159-992A-9DA10EA8EEFB}.Debug|Any CPU.ActiveCfg = Debug|Any CPU {F140E8DA-52B4-4159-992A-9DA10EA8EEFB}.Debug|Any CPU.Build.0 = Debug|Any CPU + {F140E8DA-52B4-4159-992A-9DA10EA8EEFB}.Debug|Mixed Platforms.ActiveCfg = Debug|Any CPU + {F140E8DA-52B4-4159-992A-9DA10EA8EEFB}.Debug|Mixed Platforms.Build.0 = Debug|Any CPU {F140E8DA-52B4-4159-992A-9DA10EA8EEFB}.Release|Any CPU.ActiveCfg = Release|Any CPU {F140E8DA-52B4-4159-992A-9DA10EA8EEFB}.Release|Any CPU.Build.0 = Release|Any CPU + {F140E8DA-52B4-4159-992A-9DA10EA8EEFB}.Release|Mixed Platforms.ActiveCfg = Release|Any CPU + {F140E8DA-52B4-4159-992A-9DA10EA8EEFB}.Release|Mixed Platforms.Build.0 = Release|Any CPU {8B24461B-456A-4032-89A1-CD418F7B5B62}.Debug|Any CPU.ActiveCfg = Debug|Any CPU {8B24461B-456A-4032-89A1-CD418F7B5B62}.Debug|Any CPU.Build.0 = Debug|Any CPU + {8B24461B-456A-4032-89A1-CD418F7B5B62}.Debug|Mixed Platforms.ActiveCfg = Debug|Any CPU + {8B24461B-456A-4032-89A1-CD418F7B5B62}.Debug|Mixed Platforms.Build.0 = Debug|Any CPU {8B24461B-456A-4032-89A1-CD418F7B5B62}.Release|Any CPU.ActiveCfg = Release|Any CPU {8B24461B-456A-4032-89A1-CD418F7B5B62}.Release|Any CPU.Build.0 = Release|Any CPU + {8B24461B-456A-4032-89A1-CD418F7B5B62}.Release|Mixed Platforms.ActiveCfg = Release|Any CPU + {8B24461B-456A-4032-89A1-CD418F7B5B62}.Release|Mixed Platforms.Build.0 = Release|Any CPU + {E6A207D2-E083-41BF-B522-D9D3EC09323E}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {E6A207D2-E083-41BF-B522-D9D3EC09323E}.Debug|Any CPU.Build.0 = Debug|Any CPU + {E6A207D2-E083-41BF-B522-D9D3EC09323E}.Debug|Mixed Platforms.ActiveCfg = Debug|Any CPU + {E6A207D2-E083-41BF-B522-D9D3EC09323E}.Debug|Mixed Platforms.Build.0 = Debug|Any CPU + {E6A207D2-E083-41BF-B522-D9D3EC09323E}.Release|Any CPU.ActiveCfg = Release|Any CPU + {E6A207D2-E083-41BF-B522-D9D3EC09323E}.Release|Any CPU.Build.0 = Release|Any CPU + {E6A207D2-E083-41BF-B522-D9D3EC09323E}.Release|Mixed Platforms.ActiveCfg = Release|Any CPU + {E6A207D2-E083-41BF-B522-D9D3EC09323E}.Release|Mixed Platforms.Build.0 = Release|Any CPU {5B011775-B121-4EEE-A410-BA2D2F5BFB8B}.Debug|Any CPU.ActiveCfg = Debug|Any CPU {5B011775-B121-4EEE-A410-BA2D2F5BFB8B}.Debug|Any CPU.Build.0 = Debug|Any CPU + {5B011775-B121-4EEE-A410-BA2D2F5BFB8B}.Debug|Mixed Platforms.ActiveCfg = Debug|Any CPU + {5B011775-B121-4EEE-A410-BA2D2F5BFB8B}.Debug|Mixed Platforms.Build.0 = Debug|Any CPU {5B011775-B121-4EEE-A410-BA2D2F5BFB8B}.Release|Any CPU.ActiveCfg = Release|Any CPU {5B011775-B121-4EEE-A410-BA2D2F5BFB8B}.Release|Any CPU.Build.0 = Release|Any CPU - {32804F5A-812C-4FA6-835C-BDAE5B24D355}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {32804F5A-812C-4FA6-835C-BDAE5B24D355}.Debug|Any CPU.Build.0 = Debug|Any CPU - {32804F5A-812C-4FA6-835C-BDAE5B24D355}.Release|Any CPU.ActiveCfg = Release|Any CPU - {32804F5A-812C-4FA6-835C-BDAE5B24D355}.Release|Any CPU.Build.0 = Release|Any CPU + {5B011775-B121-4EEE-A410-BA2D2F5BFB8B}.Release|Mixed Platforms.ActiveCfg = Release|Any CPU + {5B011775-B121-4EEE-A410-BA2D2F5BFB8B}.Release|Mixed Platforms.Build.0 = Release|Any CPU {A9D6374A-D51F-4FA3-8C02-5B1D23FAA82E}.Debug|Any CPU.ActiveCfg = Debug|Any CPU {A9D6374A-D51F-4FA3-8C02-5B1D23FAA82E}.Debug|Any CPU.Build.0 = Debug|Any CPU + {A9D6374A-D51F-4FA3-8C02-5B1D23FAA82E}.Debug|Mixed Platforms.ActiveCfg = Debug|Any CPU + {A9D6374A-D51F-4FA3-8C02-5B1D23FAA82E}.Debug|Mixed Platforms.Build.0 = Debug|Any CPU {A9D6374A-D51F-4FA3-8C02-5B1D23FAA82E}.Release|Any CPU.ActiveCfg = Release|Any CPU {A9D6374A-D51F-4FA3-8C02-5B1D23FAA82E}.Release|Any CPU.Build.0 = Release|Any CPU - {1CBA8F74-050C-432B-8437-08BD13BDC684}.Debug|Any CPU.ActiveCfg = Debug|x86 - {1CBA8F74-050C-432B-8437-08BD13BDC684}.Release|Any CPU.ActiveCfg = Release|x86 - {1CBA8F74-050C-432B-8437-08BD13BDC684}.Release|Any CPU.Build.0 = Release|x86 - {E6A207D2-E083-41BF-B522-D9D3EC09323E}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {E6A207D2-E083-41BF-B522-D9D3EC09323E}.Debug|Any CPU.Build.0 = Debug|Any CPU - {E6A207D2-E083-41BF-B522-D9D3EC09323E}.Release|Any CPU.ActiveCfg = Release|Any CPU - {E6A207D2-E083-41BF-B522-D9D3EC09323E}.Release|Any CPU.Build.0 = Release|Any CPU - {0BC2A013-56FE-46DD-90FC-2D2D57748FB6}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {0BC2A013-56FE-46DD-90FC-2D2D57748FB6}.Debug|Any CPU.Build.0 = Debug|Any CPU - {0BC2A013-56FE-46DD-90FC-2D2D57748FB6}.Release|Any CPU.ActiveCfg = Release|Any CPU - {0BC2A013-56FE-46DD-90FC-2D2D57748FB6}.Release|Any CPU.Build.0 = Release|Any CPU - {3E737796-3A83-4924-9FF1-DC542F21F59E}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {3E737796-3A83-4924-9FF1-DC542F21F59E}.Debug|Any CPU.Build.0 = Debug|Any CPU - {3E737796-3A83-4924-9FF1-DC542F21F59E}.Release|Any CPU.ActiveCfg = Release|Any CPU - {3E737796-3A83-4924-9FF1-DC542F21F59E}.Release|Any CPU.Build.0 = Release|Any CPU - {0747C440-70E4-4E63-9F9D-03B3A010C991}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {0747C440-70E4-4E63-9F9D-03B3A010C991}.Debug|Any CPU.Build.0 = Debug|Any CPU - {0747C440-70E4-4E63-9F9D-03B3A010C991}.Release|Any CPU.ActiveCfg = Release|Any CPU - {0747C440-70E4-4E63-9F9D-03B3A010C991}.Release|Any CPU.Build.0 = Release|Any CPU + {A9D6374A-D51F-4FA3-8C02-5B1D23FAA82E}.Release|Mixed Platforms.ActiveCfg = Release|Any CPU + {A9D6374A-D51F-4FA3-8C02-5B1D23FAA82E}.Release|Mixed Platforms.Build.0 = Release|Any CPU {4700690A-2D29-40A0-86AC-E5A9F71A479A}.Debug|Any CPU.ActiveCfg = Debug|Any CPU {4700690A-2D29-40A0-86AC-E5A9F71A479A}.Debug|Any CPU.Build.0 = Debug|Any CPU + {4700690A-2D29-40A0-86AC-E5A9F71A479A}.Debug|Mixed Platforms.ActiveCfg = Debug|Any CPU + {4700690A-2D29-40A0-86AC-E5A9F71A479A}.Debug|Mixed Platforms.Build.0 = Debug|Any CPU {4700690A-2D29-40A0-86AC-E5A9F71A479A}.Release|Any CPU.ActiveCfg = Release|Any CPU {4700690A-2D29-40A0-86AC-E5A9F71A479A}.Release|Any CPU.Build.0 = Release|Any CPU + {4700690A-2D29-40A0-86AC-E5A9F71A479A}.Release|Mixed Platforms.ActiveCfg = Release|Any CPU + {4700690A-2D29-40A0-86AC-E5A9F71A479A}.Release|Mixed Platforms.Build.0 = Release|Any CPU {57754FFA-BB1F-4722-A2FA-70C4F27C6784}.Debug|Any CPU.ActiveCfg = Debug|Any CPU {57754FFA-BB1F-4722-A2FA-70C4F27C6784}.Debug|Any CPU.Build.0 = Debug|Any CPU + {57754FFA-BB1F-4722-A2FA-70C4F27C6784}.Debug|Mixed Platforms.ActiveCfg = Debug|Any CPU + {57754FFA-BB1F-4722-A2FA-70C4F27C6784}.Debug|Mixed Platforms.Build.0 = Debug|Any CPU {57754FFA-BB1F-4722-A2FA-70C4F27C6784}.Release|Any CPU.ActiveCfg = Release|Any CPU {57754FFA-BB1F-4722-A2FA-70C4F27C6784}.Release|Any CPU.Build.0 = Release|Any CPU + {57754FFA-BB1F-4722-A2FA-70C4F27C6784}.Release|Mixed Platforms.ActiveCfg = Release|Any CPU + {57754FFA-BB1F-4722-A2FA-70C4F27C6784}.Release|Mixed Platforms.Build.0 = Release|Any CPU + {0747C440-70E4-4E63-9F9D-03B3A010C991}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {0747C440-70E4-4E63-9F9D-03B3A010C991}.Debug|Any CPU.Build.0 = Debug|Any CPU + {0747C440-70E4-4E63-9F9D-03B3A010C991}.Debug|Mixed Platforms.ActiveCfg = Debug|Any CPU + {0747C440-70E4-4E63-9F9D-03B3A010C991}.Debug|Mixed Platforms.Build.0 = Debug|Any CPU + {0747C440-70E4-4E63-9F9D-03B3A010C991}.Release|Any CPU.ActiveCfg = Release|Any CPU + {0747C440-70E4-4E63-9F9D-03B3A010C991}.Release|Any CPU.Build.0 = Release|Any CPU + {0747C440-70E4-4E63-9F9D-03B3A010C991}.Release|Mixed Platforms.ActiveCfg = Release|Any CPU + {0747C440-70E4-4E63-9F9D-03B3A010C991}.Release|Mixed Platforms.Build.0 = Release|Any CPU + {32804F5A-812C-4FA6-835C-BDAE5B24D355}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {32804F5A-812C-4FA6-835C-BDAE5B24D355}.Debug|Any CPU.Build.0 = Debug|Any CPU + {32804F5A-812C-4FA6-835C-BDAE5B24D355}.Debug|Mixed Platforms.ActiveCfg = Debug|Any CPU + {32804F5A-812C-4FA6-835C-BDAE5B24D355}.Debug|Mixed Platforms.Build.0 = Debug|Any CPU + {32804F5A-812C-4FA6-835C-BDAE5B24D355}.Release|Any CPU.ActiveCfg = Release|Any CPU + {32804F5A-812C-4FA6-835C-BDAE5B24D355}.Release|Any CPU.Build.0 = Release|Any CPU + {32804F5A-812C-4FA6-835C-BDAE5B24D355}.Release|Mixed Platforms.ActiveCfg = Release|Any CPU + {32804F5A-812C-4FA6-835C-BDAE5B24D355}.Release|Mixed Platforms.Build.0 = Release|Any CPU + {0BC2A013-56FE-46DD-90FC-2D2D57748FB6}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {0BC2A013-56FE-46DD-90FC-2D2D57748FB6}.Debug|Any CPU.Build.0 = Debug|Any CPU + {0BC2A013-56FE-46DD-90FC-2D2D57748FB6}.Debug|Mixed Platforms.ActiveCfg = Debug|Any CPU + {0BC2A013-56FE-46DD-90FC-2D2D57748FB6}.Debug|Mixed Platforms.Build.0 = Debug|Any CPU + {0BC2A013-56FE-46DD-90FC-2D2D57748FB6}.Release|Any CPU.ActiveCfg = Release|Any CPU + {0BC2A013-56FE-46DD-90FC-2D2D57748FB6}.Release|Any CPU.Build.0 = Release|Any CPU + {0BC2A013-56FE-46DD-90FC-2D2D57748FB6}.Release|Mixed Platforms.ActiveCfg = Release|Any CPU + {0BC2A013-56FE-46DD-90FC-2D2D57748FB6}.Release|Mixed Platforms.Build.0 = Release|Any CPU + {3E737796-3A83-4924-9FF1-DC542F21F59E}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {3E737796-3A83-4924-9FF1-DC542F21F59E}.Debug|Any CPU.Build.0 = Debug|Any CPU + {3E737796-3A83-4924-9FF1-DC542F21F59E}.Debug|Mixed Platforms.ActiveCfg = Debug|Any CPU + {3E737796-3A83-4924-9FF1-DC542F21F59E}.Debug|Mixed Platforms.Build.0 = Debug|Any CPU + {3E737796-3A83-4924-9FF1-DC542F21F59E}.Release|Any CPU.ActiveCfg = Release|Any CPU + {3E737796-3A83-4924-9FF1-DC542F21F59E}.Release|Any CPU.Build.0 = Release|Any CPU + {3E737796-3A83-4924-9FF1-DC542F21F59E}.Release|Mixed Platforms.ActiveCfg = Release|Any CPU + {3E737796-3A83-4924-9FF1-DC542F21F59E}.Release|Mixed Platforms.Build.0 = Release|Any CPU + {1CBA8F74-050C-432B-8437-08BD13BDC684}.Debug|Any CPU.ActiveCfg = Debug|x86 + {1CBA8F74-050C-432B-8437-08BD13BDC684}.Debug|Mixed Platforms.ActiveCfg = Debug|x86 + {1CBA8F74-050C-432B-8437-08BD13BDC684}.Debug|Mixed Platforms.Build.0 = Debug|x86 + {1CBA8F74-050C-432B-8437-08BD13BDC684}.Release|Any CPU.ActiveCfg = Release|x86 + {1CBA8F74-050C-432B-8437-08BD13BDC684}.Release|Any CPU.Build.0 = Release|x86 + {1CBA8F74-050C-432B-8437-08BD13BDC684}.Release|Mixed Platforms.ActiveCfg = Release|x86 + {1CBA8F74-050C-432B-8437-08BD13BDC684}.Release|Mixed Platforms.Build.0 = Release|x86 + {5A9E8C5B-4D41-4E3E-9680-6C195BFAD47A}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {5A9E8C5B-4D41-4E3E-9680-6C195BFAD47A}.Debug|Any CPU.Build.0 = Debug|Any CPU + {5A9E8C5B-4D41-4E3E-9680-6C195BFAD47A}.Debug|Mixed Platforms.ActiveCfg = Debug|Any CPU + {5A9E8C5B-4D41-4E3E-9680-6C195BFAD47A}.Debug|Mixed Platforms.Build.0 = Debug|Any CPU + {5A9E8C5B-4D41-4E3E-9680-6C195BFAD47A}.Release|Any CPU.ActiveCfg = Release|Any CPU + {5A9E8C5B-4D41-4E3E-9680-6C195BFAD47A}.Release|Any CPU.Build.0 = Release|Any CPU + {5A9E8C5B-4D41-4E3E-9680-6C195BFAD47A}.Release|Mixed Platforms.ActiveCfg = Release|Any CPU + {5A9E8C5B-4D41-4E3E-9680-6C195BFAD47A}.Release|Mixed Platforms.Build.0 = Release|Any CPU + {B85C5A35-E3A2-4B04-9693-795E57D66DE2}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {B85C5A35-E3A2-4B04-9693-795E57D66DE2}.Debug|Any CPU.Build.0 = Debug|Any CPU + {B85C5A35-E3A2-4B04-9693-795E57D66DE2}.Debug|Mixed Platforms.ActiveCfg = Debug|Any CPU + {B85C5A35-E3A2-4B04-9693-795E57D66DE2}.Debug|Mixed Platforms.Build.0 = Debug|Any CPU + {B85C5A35-E3A2-4B04-9693-795E57D66DE2}.Release|Any CPU.ActiveCfg = Release|Any CPU + {B85C5A35-E3A2-4B04-9693-795E57D66DE2}.Release|Any CPU.Build.0 = Release|Any CPU + {B85C5A35-E3A2-4B04-9693-795E57D66DE2}.Release|Mixed Platforms.ActiveCfg = Release|Any CPU + {B85C5A35-E3A2-4B04-9693-795E57D66DE2}.Release|Mixed Platforms.Build.0 = Release|Any CPU + {E5DCC07F-7B42-4AE9-8D6C-A15525476E0A}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {E5DCC07F-7B42-4AE9-8D6C-A15525476E0A}.Debug|Any CPU.Build.0 = Debug|Any CPU + {E5DCC07F-7B42-4AE9-8D6C-A15525476E0A}.Debug|Mixed Platforms.ActiveCfg = Debug|Any CPU + {E5DCC07F-7B42-4AE9-8D6C-A15525476E0A}.Debug|Mixed Platforms.Build.0 = Debug|Any CPU + {E5DCC07F-7B42-4AE9-8D6C-A15525476E0A}.Release|Any CPU.ActiveCfg = Release|Any CPU + {E5DCC07F-7B42-4AE9-8D6C-A15525476E0A}.Release|Any CPU.Build.0 = Release|Any CPU + {E5DCC07F-7B42-4AE9-8D6C-A15525476E0A}.Release|Mixed Platforms.ActiveCfg = Release|Any CPU + {E5DCC07F-7B42-4AE9-8D6C-A15525476E0A}.Release|Mixed Platforms.Build.0 = Release|Any CPU + {5B47BEFD-C60B-4E80-943E-A7151CEEA568}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {5B47BEFD-C60B-4E80-943E-A7151CEEA568}.Debug|Any CPU.Build.0 = Debug|Any CPU + {5B47BEFD-C60B-4E80-943E-A7151CEEA568}.Debug|Mixed Platforms.ActiveCfg = Debug|Any CPU + {5B47BEFD-C60B-4E80-943E-A7151CEEA568}.Debug|Mixed Platforms.Build.0 = Debug|Any CPU + {5B47BEFD-C60B-4E80-943E-A7151CEEA568}.Release|Any CPU.ActiveCfg = Release|Any CPU + {5B47BEFD-C60B-4E80-943E-A7151CEEA568}.Release|Any CPU.Build.0 = Release|Any CPU + {5B47BEFD-C60B-4E80-943E-A7151CEEA568}.Release|Mixed Platforms.ActiveCfg = Release|Any CPU + {5B47BEFD-C60B-4E80-943E-A7151CEEA568}.Release|Mixed Platforms.Build.0 = Release|Any CPU + {D1771E11-C7D3-43FD-9D87-46F1231846F1}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {D1771E11-C7D3-43FD-9D87-46F1231846F1}.Debug|Any CPU.Build.0 = Debug|Any CPU + {D1771E11-C7D3-43FD-9D87-46F1231846F1}.Debug|Mixed Platforms.ActiveCfg = Debug|Any CPU + {D1771E11-C7D3-43FD-9D87-46F1231846F1}.Debug|Mixed Platforms.Build.0 = Debug|Any CPU + {D1771E11-C7D3-43FD-9D87-46F1231846F1}.Release|Any CPU.ActiveCfg = Release|Any CPU + {D1771E11-C7D3-43FD-9D87-46F1231846F1}.Release|Any CPU.Build.0 = Release|Any CPU + {D1771E11-C7D3-43FD-9D87-46F1231846F1}.Release|Mixed Platforms.ActiveCfg = Release|Any CPU + {D1771E11-C7D3-43FD-9D87-46F1231846F1}.Release|Mixed Platforms.Build.0 = Release|Any CPU + {46365523-FA23-4AD4-9DB8-B0E195F00571}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {46365523-FA23-4AD4-9DB8-B0E195F00571}.Debug|Any CPU.Build.0 = Debug|Any CPU + {46365523-FA23-4AD4-9DB8-B0E195F00571}.Debug|Mixed Platforms.ActiveCfg = Debug|Any CPU + {46365523-FA23-4AD4-9DB8-B0E195F00571}.Debug|Mixed Platforms.Build.0 = Debug|Any CPU + {46365523-FA23-4AD4-9DB8-B0E195F00571}.Release|Any CPU.ActiveCfg = Release|Any CPU + {46365523-FA23-4AD4-9DB8-B0E195F00571}.Release|Any CPU.Build.0 = Release|Any CPU + {46365523-FA23-4AD4-9DB8-B0E195F00571}.Release|Mixed Platforms.ActiveCfg = Release|Any CPU + {46365523-FA23-4AD4-9DB8-B0E195F00571}.Release|Mixed Platforms.Build.0 = Release|Any CPU EndGlobalSection GlobalSection(SolutionProperties) = preSolution HideSolutionNode = FALSE diff --git a/ConnectorServerService/ConnectorServerService.Designer.cs b/ConnectorServerService/ConnectorServerService.Designer.cs new file mode 100755 index 00000000..fd399166 --- /dev/null +++ b/ConnectorServerService/ConnectorServerService.Designer.cs @@ -0,0 +1,37 @@ +namespace Org.ForgeRock.OpenICF.Framework.ConnectorServerService +{ + partial class ConnectorServerService + { + /// + /// Required designer variable. + /// + private System.ComponentModel.IContainer components = null; + + /// + /// Clean up any resources being used. + /// + /// true if managed resources should be disposed; otherwise, false. + protected override void Dispose(bool disposing) + { + if (disposing && (components != null)) + { + components.Dispose(); + } + base.Dispose(disposing); + } + + #region Component Designer generated code + + /// + /// Required method for Designer support - do not modify + /// the contents of this method with the code editor. + /// + private void InitializeComponent() + { + components = new System.ComponentModel.Container(); + this.ServiceName = "OpenICFWebsocketService"; + } + + #endregion + } +} diff --git a/ConnectorServerService/ConnectorServerService.cs b/ConnectorServerService/ConnectorServerService.cs new file mode 100755 index 00000000..2f042d7a --- /dev/null +++ b/ConnectorServerService/ConnectorServerService.cs @@ -0,0 +1,613 @@ +/* + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER. + * + * Copyright (c) 2015 ForgeRock AS. All rights reserved. + * + * The contents of this file are subject to the terms + * of the Common Development and Distribution License + * (the License). You may not use this file except in + * compliance with the License. + * + * You can obtain a copy of the License at + * http://forgerock.org/license/CDDLv1.0.html + * See the License for the specific language governing + * permission and limitations under the License. + * + * When distributing Covered Code, include this CDDL + * Header Notice in each file and include the License file + * at http://forgerock.org/license/CDDLv1.0.html + * If applicable, add the following below the CDDL Header, + * with the fields enclosed by brackets [] replaced by + * your own identifying information: + * "Portions Copyrighted [year] [name of copyright owner]" + */ + +using System; +using System.Collections.Specialized; +using System.Configuration; +using System.Diagnostics; +using System.IO; +using System.Linq; +using System.Net; +using System.Net.Http.Headers; +using System.Reflection; +using System.Security.Cryptography.X509Certificates; +using System.ServiceModel; +using System.ServiceModel.Configuration; +using System.ServiceProcess; +using System.Text; +using System.Threading; +using System.Threading.Tasks; +using Org.ForgeRock.OpenICF.Common.ProtoBuf; +using Org.ForgeRock.OpenICF.Framework.Remote; +using Org.ForgeRock.OpenICF.Framework.Service.WcfServiceLibrary; +using Org.IdentityConnectors.Common; +using Org.IdentityConnectors.Framework.Common.Exceptions; +using vtortola.WebSockets; + +namespace Org.ForgeRock.OpenICF.Framework.ConnectorServerService +{ + + #region OpenICFWebsocketService + + public partial class ConnectorServerService : ServiceBase + { + public const string PropKey = "connectorserver.key"; + public const string PropFacadeLifetime = "connectorserver.maxFacadeLifeTime"; + + private Action _closeAction; + + public ConnectorServerService() + { + InitializeComponent(); + } + + public static void InitializeConnectors(AsyncLocalConnectorInfoManager manager) + { + Assembly assembly = Assembly.GetExecutingAssembly(); + FileInfo thisAssemblyFile = new FileInfo(assembly.Location); + DirectoryInfo directory = thisAssemblyFile.Directory; + if (directory == null) throw new ArgumentNullException("directory"); + Environment.CurrentDirectory = directory.FullName; + Trace.TraceInformation("Starting connector server from: " + Environment.CurrentDirectory); + + FileInfo[] files = directory.GetFiles("*.Connector.dll"); + foreach (FileInfo file in files) + { + Assembly lib = Assembly.LoadFrom(file.ToString()); + manager.AddConnectorAssembly(lib); + } + + // also handle connector DLL file names with a version + FileInfo[] versionedFiles = directory.GetFiles("*.Connector-*.dll"); + foreach (FileInfo versionedFile in versionedFiles) + { + Assembly lib = Assembly.LoadFrom(versionedFile.ToString()); + manager.AddConnectorAssembly(lib); + } + } + + protected virtual Uri[] ExtractEndPoint() + { + ServicesSection servicesSection = + ConfigurationManager.GetSection("system.serviceModel/services") as ServicesSection; + + if (servicesSection != null) + { + foreach (ServiceElement service in servicesSection.Services) + { + foreach (ServiceEndpointElement endpoint in service.Endpoints) + { + if (String.Equals(typeof (IWebSocketService).FullName, endpoint.Contract)) + { + return + (from BaseAddressElement baseAddress in service.Host.BaseAddresses + select new Uri(baseAddress.BaseAddress)).ToArray(); + } + } + } + } + throw new Exception("No BaseAddress //TODO fix message"); + } + + public void StartService(string[] args) + { + OnStart(args); + } + + protected override void OnStart(string[] args) + { + try + { + ConnectorFramework connectorFramework = new ConnectorFramework(); + InitializeConnectors(connectorFramework.LocalManager); + + NameValueCollection settings = ConfigurationManager.AppSettings; + + String keyHash = settings.Get(PropKey); + if (keyHash == null) + { + throw new Org.IdentityConnectors.Framework.Common.Exceptions.ConfigurationException( + "Missing required configuration property: " + PropKey); + } + + ClientAuthenticationValidator validator = new ClientAuthenticationValidator(); + validator.Add(new SingleTenantPrincipal(connectorFramework), keyHash); + + String facadeLifeTimeStr = settings.Get(PropFacadeLifetime); + if (facadeLifeTimeStr != null) + { + // _server.MaxFacadeLifeTime = Int32.Parse(facedeLifeTimeStr); + } + + String disableWcf = settings.Get("disableWcf"); + OperatingSystem os = Environment.OSVersion; + if (disableWcf == null && (os.Platform == PlatformID.Win32NT) && + ((os.Version.Major > 6) || ((os.Version.Major == 6) && (os.Version.Minor >= 2)))) + { + ServiceHost host = new ConnectorServiceHost(validator); + host.Open(); + + _closeAction = () => host.Close(); + Trace.TraceInformation("Started WCF connector server"); + } + else + { + Uri[] endpointUri = ExtractEndPoint(); + if (endpointUri == null) + { + throw new Org.IdentityConnectors.Framework.Common.Exceptions.ConfigurationException( + "Missing required baseAddress"); + } + VtortConnectorServiceHost host = new VtortConnectorServiceHost(validator, endpointUri); + host.Open(); + + _closeAction = () => host.Close(); + Trace.TraceInformation("Started WebSocketListener connector server"); + } + } + catch (Exception e) + { + TraceUtil.TraceException("Exception occured starting connector server", e); + throw; + } + } + + public void StopService() + { + OnStop(); + } + + protected override void OnStop() + { + try + { + Trace.TraceInformation("Stopping connector server"); + if (_closeAction != null) + { + _closeAction(); + _closeAction = null; + } + Trace.TraceInformation("Stopped connector server"); + } + catch (Exception e) + { + TraceUtil.TraceException("Exception occured stopping connector server", e); + } + } + } + + #endregion + + #region VtortConnectorServiceHost + + public class VtortConnectorServiceHost + { + private const string PropCertstore = "connectorserver.certificatestorename"; + private WebSocketListener _listener; + private readonly ClientAuthenticationValidator _validator; + private readonly Uri _endPointUri; + + public VtortConnectorServiceHost(ClientAuthenticationValidator validator, params Uri[] baseAddresses) + { + _validator = validator; + _endPointUri = baseAddresses[0]; + } + + public void Open() + { + IPAddress ipAddress = IPAddress.Any; + if (_endPointUri.IsLoopback) + { + ipAddress = IPAddress.Loopback; + } + else if (!"*".Equals(_endPointUri.DnsSafeHost)) + { + ipAddress = IOUtil.GetIPAddress(_endPointUri.DnsSafeHost); + } + + var options = new WebSocketListenerOptions() + { + NegotiationQueueCapacity = 128, + ParallelNegotiations = 16, + PingTimeout = Timeout.InfiniteTimeSpan, + SubProtocols = new[] {"v1.openicf.forgerock.org"}, + OnHttpNegotiation = (request, response) => + { + var authHeader = request.Headers["Authorization"]; + if (authHeader != null) + { + var authHeaderVal = AuthenticationHeaderValue.Parse(authHeader); + + // RFC 2617 sec 1.2, "scheme" name is case-insensitive + if (authHeaderVal.Scheme.Equals("basic", + StringComparison.OrdinalIgnoreCase) && + authHeaderVal.Parameter != null) + { + var encoding = Encoding.GetEncoding("iso-8859-1"); + var credentials = encoding.GetString(Convert.FromBase64String(authHeaderVal.Parameter)); + + int separator = credentials.IndexOf(':'); + if (separator != -1) + { + string name = credentials.Substring(0, separator); + string password = credentials.Substring(separator + 1); + + var pair = _validator.FindPrincipal(name); + if (null != pair) + { + if (ClientAuthenticationValidator.Verify(pair.Second, password)) + { + request.Items["ConnectionPrincipal"] = pair.First; + } + else + { + Trace.TraceWarning("Incorrect password - username: {0} password: {1}", name, + password); + response.Status = HttpStatusCode.Forbidden; + } + } + else + { + Trace.TraceWarning("Unknown username: {0}", name); + response.Status = HttpStatusCode.Forbidden; + } + } + else + { + Trace.TraceWarning("Invalid Basic Authorization : {0}", credentials); + response.Status = HttpStatusCode.BadRequest; + } + } + else + { + Trace.TraceWarning("Basic Authorization header expected but found{0}", authHeader); + response.Status = HttpStatusCode.BadRequest; + } + } + else + { + //401 + Realm + response.Status = HttpStatusCode.Unauthorized; + } + } + }; + _listener = new WebSocketListener(new IPEndPoint(ipAddress, _endPointUri.Port), options); + + bool useSsl = String.Equals("https", _endPointUri.Scheme, StringComparison.OrdinalIgnoreCase) || + String.Equals("wss", _endPointUri.Scheme, StringComparison.OrdinalIgnoreCase); + if (useSsl) + { + _listener.ConnectionExtensions.RegisterExtension( + new WebSocketSecureConnectionExtension(GetCertificate())); + } + + var rfc6455 = new vtortola.WebSockets.Rfc6455.WebSocketFactoryRfc6455(_listener); + _listener.Standards.RegisterStandard(rfc6455); + _listener.Start(); + Task.Run((Func) ListenAsync); + } + + public void Close() + { + if (null != _listener) + { + _listener.Stop(); + _validator.Dispose(); + } + } + + protected X509Certificate2 GetCertificate() + { + NameValueCollection settings = ConfigurationManager.AppSettings; + String storeName = settings.Get(PropCertstore); + if (storeName == null) + { + throw new Org.IdentityConnectors.Framework.Common.Exceptions.ConfigurationException( + "Missing required configuration setting: " + PropCertstore); + } + + X509Store store = new X509Store(storeName, + StoreLocation.LocalMachine); + + store.Open(OpenFlags.ReadOnly | OpenFlags.OpenExistingOnly); + X509Certificate2Collection certificates = store.Certificates; + if (certificates.Count != 1) + { + throw new Org.IdentityConnectors.Framework.Common.Exceptions.ConfigurationException( + "There is supported to be exactly one certificate in the store: " + storeName); + } + X509Certificate2 certificate = store.Certificates[0]; + store.Close(); + return certificate; + } + + private async Task ListenAsync() + { + while (_listener.IsStarted) + { + try + { + var websocket = await _listener.AcceptWebSocketAsync(CancellationToken.None) + .ConfigureAwait(false); + if (websocket != null) + { + object value; + websocket.HttpRequest.Items.TryGetValue("ConnectionPrincipal", out value); + ConnectionPrincipal connectionPrincipal = value as ConnectionPrincipal; + if (null != connectionPrincipal) + { + Task.Run(() => HandleWebSocketAsync(websocket, connectionPrincipal)).ConfigureAwait(false); + } + else + { + Trace.TraceInformation("Unidentified WebSocket from {0}", websocket.RemoteEndpoint); + websocket.Close(); + } + } + else + { + Trace.TraceInformation("Stopping Connector Server!?"); + } + } + catch (Exception e) + { + TraceUtil.TraceException("Failed to Accept WebSocket", e); + } + } + } + + private async Task HandleWebSocketAsync(WebSocket websocket, ConnectionPrincipal connectionPrincipal) + { + VtortWebsocket soWebsocket = new VtortWebsocket(websocket, connectionPrincipal); + try + { + Trace.TraceInformation("Server onConnect()"); + connectionPrincipal.OperationMessageListener.OnConnect(soWebsocket); + + while (websocket.IsConnected) + { + var message = await websocket.ReadMessageAsync(CancellationToken.None) + .ConfigureAwait(false); + if (message != null) + { + switch (message.MessageType) + { + case WebSocketMessageType.Text: + using (var sr = new StreamReader(message, Encoding.UTF8)) + { + String msgContent = await sr.ReadToEndAsync(); + connectionPrincipal.OperationMessageListener.OnMessage(soWebsocket, msgContent); + } + break; + case WebSocketMessageType.Binary: + using (var ms = new MemoryStream()) + { + await message.CopyToAsync(ms); + if (ms.Length > 0) + { + connectionPrincipal.OperationMessageListener.OnMessage(soWebsocket, ms.ToArray()); + } + } + break; + } + } + } + } + catch (Exception ex) + { + connectionPrincipal.OperationMessageListener.OnError(ex); + } + finally + { + connectionPrincipal.OperationMessageListener.OnClose(soWebsocket, 1000, ""); + //Dispose before onClose is complete ??? + websocket.Dispose(); + } + } + } + + #endregion + + #region VtortWebsocket + + public class VtortWebsocket : WebSocketConnectionHolder + { + private readonly WebSocket _webSocket; + private RemoteOperationContext _context; + private readonly ConnectionPrincipal _principal; + + public VtortWebsocket(WebSocket webSocket, ConnectionPrincipal principal) + { + _webSocket = webSocket; + _principal = principal; + Task.Factory.StartNew(WriteMessageAsync); + } + + protected override async Task WriteMessageAsync(byte[] entry, + System.Net.WebSockets.WebSocketMessageType messageType) + { + try + { + switch (messageType) + { + case System.Net.WebSockets.WebSocketMessageType.Binary: + using (var writer = _webSocket.CreateMessageWriter(WebSocketMessageType.Binary)) + { + await writer.WriteAsync(entry, 0, entry.Length); + } + break; + case System.Net.WebSockets.WebSocketMessageType.Text: + using (var writer = _webSocket.CreateMessageWriter(WebSocketMessageType.Text)) + { + await writer.WriteAsync(entry, 0, entry.Length); + } + break; + default: + throw new InvalidAttributeValueException("Unsupported WebSocketMessageType: " + messageType); + } + } + catch (Exception e) + { + TraceUtil.TraceException("Failed to write", e); + } + } + + protected override void Handshake(HandshakeMessage message) + { + _context = _principal.Handshake(this, message); + if (null != _context) + { + Trace.TraceInformation("Client Connection Handshake succeeded"); + } + else + { + Trace.TraceError("Client Connection Handshake failed - Close Connection"); + TryClose(); + } + } + + protected override void TryClose() + { + _webSocket.Close(); + } + + public override bool Operational + { + get { return _webSocket.IsConnected; } + } + + public override RemoteOperationContext RemoteConnectionContext + { + get { return _context; } + } + } + + #endregion + + /* + #region BasicAuthHttpModule + + public abstract class BasicAuthHttpModule : IHttpModule + { + private const string Realm = "OpenICF"; + + public void Init(HttpApplication context) + { + // Register event handlers + context.AuthenticateRequest += OnApplicationAuthenticateRequest; + context.EndRequest += OnApplicationEndRequest; + } + + private static void SetPrincipal(IPrincipal principal) + { + Thread.CurrentPrincipal = principal; + if (HttpContext.Current != null) + { + HttpContext.Current.User = principal; + } + } + + protected abstract ConnectionPrincipal CheckPassword(SecurityToken token); + + private void AuthenticateUser(string credentials) + { + try + { + var encoding = Encoding.GetEncoding("iso-8859-1"); + credentials = encoding.GetString(Convert.FromBase64String(credentials)); + + int separator = credentials.IndexOf(':'); + if (separator != -1) + { + string name = credentials.Substring(0, separator); + string password = credentials.Substring(separator + 1); + + UserNameSecurityToken token = new UserNameSecurityToken(name, password); + ConnectionPrincipal principal = CheckPassword(token); + + if (null != principal) + { + SetPrincipal(principal); + } + else + { + // Invalid username or password. + HttpContext.Current.Response.StatusCode = 403; + } + } + else + { + //Bad Request + HttpContext.Current.Response.StatusCode = 400; + } + } + catch (FormatException) + { + // Credentials were not formatted correctly. + HttpContext.Current.Response.StatusCode = 401; + } + } + + private void OnApplicationAuthenticateRequest(object sender, EventArgs e) + { + var request = HttpContext.Current.Request; + var authHeader = request.Headers["Authorization"]; + if (authHeader != null) + { + var authHeaderVal = AuthenticationHeaderValue.Parse(authHeader); + + // RFC 2617 sec 1.2, "scheme" name is case-insensitive + if (authHeaderVal.Scheme.Equals("basic", + StringComparison.OrdinalIgnoreCase) && + authHeaderVal.Parameter != null) + { + AuthenticateUser(authHeaderVal.Parameter); + } + } + else + { + HttpContext.Current.Response.StatusCode = 401; + } + } + + // If the request was unauthorized, add the WWW-Authenticate header + // to the response. + private static void OnApplicationEndRequest(object sender, EventArgs e) + { + var response = HttpContext.Current.Response; + if (response.StatusCode == 401) + { + response.Headers.Add("WWW-Authenticate", + string.Format("Basic realm=\"{0}\"", Realm)); + } + } + + public void Dispose() + { + } + } + + #endregion + */ +} \ No newline at end of file diff --git a/ConnectorServerService/ConnectorServerService.csproj b/ConnectorServerService/ConnectorServerService.csproj new file mode 100755 index 00000000..cd01e96f --- /dev/null +++ b/ConnectorServerService/ConnectorServerService.csproj @@ -0,0 +1,153 @@ + + + + + + Debug + AnyCPU + {E5DCC07F-7B42-4AE9-8D6C-A15525476E0A} + Exe + Properties + Org.ForgeRock.OpenICF.Framework.ConnectorServerService + ConnectorServerService + OpenICF Framework - Connector Server Service + v4.5.2 + 512 + + True + + + AnyCPU + true + full + false + bin\Debug\ + DEBUG;TRACE + prompt + 4 + + + AnyCPU + pdbonly + true + bin\Release\ + TRACE + prompt + 4 + + + + + + FR_ICF_sq_med.ico + + + + + ..\packages\Google.ProtocolBuffers.3\lib\Google.Protobuf.dll + + + + + + + + + + False + ..\packages\vtortola.WebSocketListener.2.1.8.1\lib\net45\vtortola.WebSockets.dll + + + False + ..\packages\vtortola.WebSocketListener.2.1.8.1\lib\net45\vtortola.WebSockets.Rfc6455.dll + + + False + ..\packages\Microsoft.Tpl.Dataflow.4.5.24\lib\portable-net45+win8+wpa81\System.Threading.Tasks.Dataflow.dll + + + + + Component + + + ConnectorServerService.cs + + + + Component + + + + True + True + Resources.resx + + + + + Designer + + + + + + {f140e8da-52b4-4159-992a-9da10ea8eefb} + Common + + + {8b24461b-456a-4032-89a1-cd418f7b5b62} + Framework + + + {5a9e8c5b-4d41-4e3e-9680-6c195bfad47a} + FrameworkProtoBuf + + + {b85c5a35-e3a2-4b04-9693-795e57d66de2} + FrameworkRpc + + + {5b47befd-c60b-4e80-943e-a7151ceea568} + FrameworkServer + + + {D1771E11-C7D3-43FD-9D87-46F1231846F1} + WcfServiceLibrary + + + + + + + + ResXFileCodeGenerator + Resources.Designer.cs + + + + + + + \ No newline at end of file diff --git a/ConnectorServerService/FR_ICF_sq_med.ico b/ConnectorServerService/FR_ICF_sq_med.ico new file mode 100755 index 0000000000000000000000000000000000000000..10240f4759865dd5741f62a4b15549b1e2e712c1 GIT binary patch literal 32038 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 I>*ay}189e!r~m)} literal 0 HcmV?d00001 diff --git a/ConnectorServerService/Program.cs b/ConnectorServerService/Program.cs new file mode 100755 index 00000000..f82374d8 --- /dev/null +++ b/ConnectorServerService/Program.cs @@ -0,0 +1,200 @@ +using System; +using System.Configuration; +using System.Configuration.Install; +using System.Diagnostics; +using System.IO; +using System.Reflection; +using System.ServiceProcess; +using Org.ForgeRock.OpenICF.Framework.ConnectorServerService.Properties; +using Org.IdentityConnectors.Common.Security; + +namespace Org.ForgeRock.OpenICF.Framework.ConnectorServerService +{ + internal static class Program + { + private const string Debug = "debug"; + + private static void Usage() + { + Console.WriteLine("Usage: ConnectorServer.exe [option], where command is one of the following: "); + Console.WriteLine(" /install [/serviceName ] - Installs the service."); + Console.WriteLine(" /uninstall [/serviceName ] - Uninstalls the service."); + Console.WriteLine(" /run - Runs the service from the console."); + Console.WriteLine(" /setKey [] - Sets the connector server key."); + Console.WriteLine(" /setDefaults - Sets default app.config"); + } + + /// + /// The main entry point for the application. + /// + private static void Main(string[] args) + { + if (args.Length == 0) + { + Usage(); + } + else + { + String cmd = args[0].ToLower(); + if (cmd.Equals("/setkey", StringComparison.InvariantCultureIgnoreCase)) + { + if (args.Length > 2) + { + Usage(); + return; + } + DoSetKey(args.Length > 1 ? args[1] : null); + return; + } + if (cmd.Equals("/setDefaults", StringComparison.InvariantCultureIgnoreCase)) + { + if (args.Length > 1) + { + Usage(); + return; + } + using ( + var file = new StreamWriter(AppDomain.CurrentDomain.SetupInformation.ConfigurationFile, false)) + { + file.WriteLine(Resources.ResourceManager.GetString("DefaultConfig")); + Console.WriteLine(@"Default configuration successfully restored."); + } + return; + } + if ("/install".Equals(cmd, StringComparison.InvariantCultureIgnoreCase)) + { + DoInstall(); + } + else if ("/uninstall".Equals(cmd, StringComparison.InvariantCultureIgnoreCase)) + { + DoUninstall(); + } + else if ("/run".Equals(cmd, StringComparison.InvariantCultureIgnoreCase)) + { + if (args.Length > 1 && Debug.Equals(args[1], StringComparison.InvariantCultureIgnoreCase)) + { + Process currentProcess = Process.GetCurrentProcess(); + Console.WriteLine( + @"It's time to attach with debugger to process:{0} and press any key to continue.", + currentProcess.Id); + Console.ReadKey(); + } + DoRun(); + } + else if ("/service".Equals(cmd, StringComparison.InvariantCultureIgnoreCase)) + { + ServiceBase.Run(new ServiceBase[] {new ConnectorServerService()}); + } + else + { + Usage(); + } + } + } + + private static void DoInstall() + { + TransactedInstaller ti = new TransactedInstaller(); + string[] cmdline = + { + Assembly.GetExecutingAssembly().Location + }; + AssemblyInstaller ai = new AssemblyInstaller( + cmdline[0], + new string[0]); + ti.Installers.Add(ai); + InstallContext ctx = new InstallContext("install.log", + cmdline); + ti.Context = ctx; + ti.Install(new System.Collections.Hashtable()); + } + + private static void DoUninstall() + { + TransactedInstaller ti = new TransactedInstaller(); + string[] cmdline = + { + Assembly.GetExecutingAssembly().Location + }; + AssemblyInstaller ai = new AssemblyInstaller( + cmdline[0], + new string[0]); + ti.Installers.Add(ai); + InstallContext ctx = new InstallContext("uninstall.log", + cmdline); + ti.Context = ctx; + ti.Uninstall(null); + } + + private static void DoRun() + { + ConnectorServerService svc = new ConnectorServerService(); + + svc.StartService(new String[0]); + + Console.WriteLine(@"Press q to shutdown."); + + while (true) + { + ConsoleKeyInfo info = Console.ReadKey(); + if (info.KeyChar == 'q') + { + break; + } + } + svc.StopService(); + } + + private static GuardedString ReadPassword() + { + GuardedString rv = new GuardedString(); + while (true) + { + ConsoleKeyInfo info = Console.ReadKey(true); + if (info.Key == ConsoleKey.Enter) + { + Console.WriteLine(); + rv.MakeReadOnly(); + return rv; + } + else + { + Console.Write("*"); + rv.AppendChar(info.KeyChar); + } + } + } + + private static void DoSetKey(string key) + { + GuardedString str; + if (key == null) + { + Console.Write("Please enter the new key: "); + GuardedString v1 = ReadPassword(); + Console.Write("Please confirm the new key: "); + GuardedString v2 = ReadPassword(); + if (!v1.Equals(v2)) + { + Console.WriteLine("Error: Key mismatch."); + return; + } + str = v2; + } + else + { + str = new GuardedString(); + foreach (char c in key) + { + str.AppendChar(c); + } + } + Configuration config = + ConfigurationManager.OpenExeConfiguration(ConfigurationUserLevel.None); + config.AppSettings.Settings.Remove(ConnectorServerService.PropKey); + config.AppSettings.Settings.Add(ConnectorServerService.PropKey, str.GetBase64SHA1Hash()); + config.Save(ConfigurationSaveMode.Modified); + Console.WriteLine("Key has been successfully updated."); + } + } +} \ No newline at end of file diff --git a/ConnectorServerService/ProjectInstaller.cs b/ConnectorServerService/ProjectInstaller.cs new file mode 100755 index 00000000..cf7b2df8 --- /dev/null +++ b/ConnectorServerService/ProjectInstaller.cs @@ -0,0 +1,93 @@ +/* + * ==================== + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER. + * + * Copyright 2008-2009 Sun Microsystems, Inc. All rights reserved. + * + * The contents of this file are subject to the terms of the Common Development + * and Distribution License("CDDL") (the "License"). You may not use this file + * except in compliance with the License. + * + * You can obtain a copy of the License at + * http://opensource.org/licenses/cddl1.php + * See the License for the specific language governing permissions and limitations + * under the License. + * + * When distributing the Covered Code, include this CDDL Header Notice in each file + * and include the License file at http://opensource.org/licenses/cddl1.php. + * If applicable, add the following below this CDDL Header, with the fields + * enclosed by brackets [] replaced by your own identifying information: + * "Portions Copyrighted [year] [name of copyright owner]" + * ==================== + * Portions Copyrighted 2014-2015 ForgeRock AS. + */ + +using System; +using System.ComponentModel; +using System.Configuration.Install; +using System.Diagnostics; +using System.IO; +using System.ServiceProcess; + +namespace Org.ForgeRock.OpenICF.Framework.ConnectorServerService +{ + [RunInstaller(true)] + public class ProjectInstaller : Installer + { + public static string ServiceName { get; set; } + public static string DisplayName { get; set; } + public static string Description { get; set; } + + static ProjectInstaller() + { + ServiceName = "ConnectorServerService"; + DisplayName = "OpenICF Connector Server"; + Description = "OpenICF Connector Server"; + } + + public ProjectInstaller() + { + Process pc = Process.GetCurrentProcess(); + Directory.SetCurrentDirectory + (pc.MainModule.FileName.Substring(0, + pc.MainModule.FileName.LastIndexOf(@"\", + StringComparison.CurrentCulture) + )); + + var serviceProcessInstaller = new ServiceProcessInstaller(); + var serviceInstaller = new ServiceInstaller(); + + // Here you can set properties on serviceProcessInstaller or register event handlers + serviceProcessInstaller.Account = ServiceAccount.LocalSystem; + + serviceInstaller.ServiceName = ServiceName; + serviceInstaller.Description = Description; + serviceInstaller.DisplayName = DisplayName; + serviceInstaller.StartType = ServiceStartMode.Automatic; + + Installers.AddRange(new Installer[] {serviceProcessInstaller, serviceInstaller}); + } + + protected string AppendPathParameter(string path, string parameter) + { + if (path.Length > 0 && path[0] != '"') + { + path = "\"" + path + "\""; + } + path += " " + parameter; + return path; + } + + protected override void OnBeforeInstall(System.Collections.IDictionary savedState) + { + Context.Parameters["assemblypath"] = AppendPathParameter(Context.Parameters["assemblypath"], "/service"); + base.OnBeforeInstall(savedState); + } + + protected override void OnBeforeUninstall(System.Collections.IDictionary savedState) + { + Context.Parameters["assemblypath"] = AppendPathParameter(Context.Parameters["assemblypath"], "/service"); + base.OnBeforeUninstall(savedState); + } + } +} \ No newline at end of file diff --git a/ConnectorServerService/Properties/Resources.Designer.cs b/ConnectorServerService/Properties/Resources.Designer.cs new file mode 100755 index 00000000..9a9c8e6f --- /dev/null +++ b/ConnectorServerService/Properties/Resources.Designer.cs @@ -0,0 +1,82 @@ +//------------------------------------------------------------------------------ +// +// This code was generated by a tool. +// Runtime Version:4.0.30319.34209 +// +// Changes to this file may cause incorrect behavior and will be lost if +// the code is regenerated. +// +//------------------------------------------------------------------------------ + +namespace Org.ForgeRock.OpenICF.Framework.ConnectorServerService.Properties { + using System; + + + /// + /// A strongly-typed resource class, for looking up localized strings, etc. + /// + // This class was auto-generated by the StronglyTypedResourceBuilder + // class via a tool like ResGen or Visual Studio. + // To add or remove a member, edit your .ResX file then rerun ResGen + // with the /str option, or rebuild your VS project. + [global::System.CodeDom.Compiler.GeneratedCodeAttribute("System.Resources.Tools.StronglyTypedResourceBuilder", "4.0.0.0")] + [global::System.Diagnostics.DebuggerNonUserCodeAttribute()] + [global::System.Runtime.CompilerServices.CompilerGeneratedAttribute()] + internal class Resources { + + private static global::System.Resources.ResourceManager resourceMan; + + private static global::System.Globalization.CultureInfo resourceCulture; + + [global::System.Diagnostics.CodeAnalysis.SuppressMessageAttribute("Microsoft.Performance", "CA1811:AvoidUncalledPrivateCode")] + internal Resources() { + } + + /// + /// Returns the cached ResourceManager instance used by this class. + /// + [global::System.ComponentModel.EditorBrowsableAttribute(global::System.ComponentModel.EditorBrowsableState.Advanced)] + internal static global::System.Resources.ResourceManager ResourceManager { + get { + if (object.ReferenceEquals(resourceMan, null)) { + global::System.Resources.ResourceManager temp = new global::System.Resources.ResourceManager("Org.ForgeRock.OpenICF.Framework.ConnectorServerService.Properties.Resources", typeof(Resources).Assembly); + resourceMan = temp; + } + return resourceMan; + } + } + + /// + /// Overrides the current thread's CurrentUICulture property for all + /// resource lookups using this strongly typed resource class. + /// + [global::System.ComponentModel.EditorBrowsableAttribute(global::System.ComponentModel.EditorBrowsableState.Advanced)] + internal static global::System.Globalization.CultureInfo Culture { + get { + return resourceCulture; + } + set { + resourceCulture = value; + } + } + + /// + /// Looks up a localized string similar to <?xml version="1.0" encoding="utf-8"?> + ///<configuration> + /// <appSettings> + /// <!-- Access these values via the property: + /// System.Configuration.ConfigurationManager.AppSettings[key] + /// --> + /// <add key="connectorserver.certificatestorename" value="ConnectorServerSSLCertificate" /> + /// <add key="connectorserver.maxFacadeLifeTime" value="0" /> + /// <add key="connectorserver.key" value="lmA6bMfENJGlIDbfrVtklXFK32s=" /> + /// <!-- Enable/Disable the logging proxy for all operations. --> + /// <add [rest of string was truncated]";. + /// + internal static string DefaultConfig { + get { + return ResourceManager.GetString("DefaultConfig", resourceCulture); + } + } + } +} diff --git a/ConnectorServerService/Properties/Resources.resx b/ConnectorServerService/Properties/Resources.resx new file mode 100755 index 00000000..53e13c2f --- /dev/null +++ b/ConnectorServerService/Properties/Resources.resx @@ -0,0 +1,212 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + text/microsoft-resx + + + 2.0 + + + System.Resources.ResXResourceReader, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + <?xml version="1.0" encoding="utf-8"?> +<configuration> + <appSettings> + <!-- Access these values via the property: + System.Configuration.ConfigurationManager.AppSettings[key] + --> + <add key="connectorserver.certificatestorename" value="ConnectorServerSSLCertificate" /> + <add key="connectorserver.maxFacadeLifeTime" value="0" /> + <add key="connectorserver.key" value="lmA6bMfENJGlIDbfrVtklXFK32s=" /> + <!-- Enable/Disable the logging proxy for all operations. --> + <add key="logging.proxy" value="false" /> + <add key="disableWcf" value="true" /> + </appSettings> + <startup> + <supportedRuntime version="v4.0" sku=".NETFramework,Version=v4.5.2" /> + </startup> + <system.serviceModel> + <services> + <service name="Org.ForgeRock.OpenICF.Framework.Service.WcfServiceLibrary.WcfWebsocket"> + <endpoint address="" binding="customBinding" bindingConfiguration="customWebSocket" + contract="Org.ForgeRock.OpenICF.Framework.Service.WcfServiceLibrary.IWebSocketService"> + <identity> + <dns value="localhost" /> + </identity> + </endpoint> + <host> + <baseAddresses> + <!-- Remove trailing '/' otherwise response: 405 Method Not Allowed --> + <add baseAddress="http://localhost:8759/openicf" /> + </baseAddresses> + </host> + </service> + </services> + <bindings> + <customBinding> + <binding name="customWebSocket"> + <byteStreamMessageEncoding /> + <httpTransport authenticationScheme="Basic" realm="OpenICF"> + <webSocketSettings transportUsage="Always" createNotificationOnConnection="true" + subProtocol="v1.openicf.forgerock.org" /> + </httpTransport> + </binding> + </customBinding> + </bindings> + <behaviors> + <serviceBehaviors> + <behavior> + <!-- To avoid disclosing metadata information, set the values below to false before deployment --> + <serviceMetadata httpGetEnabled="false" httpsGetEnabled="false" /> + <!-- To receive exception details in faults for debugging purposes, set the value below to true. + Set to false before deployment to avoid disclosing exception information --> + <serviceDebug includeExceptionDetailInFaults="false" /> + <!--Specify the Certificate- -> + <serviceCertificate findValue="ConnectorServerSSLCertificate" + storeLocation="LocalMachine" + x509FindType="FindBySubjectName" + storeName="ConnectorServerSSLCertificate" /--> + </behavior> + </serviceBehaviors> + </behaviors> + <!--serviceHostingEnvironment aspNetCompatibilityEnabled="true" multipleSiteBindingsEnabled="true" /--> + </system.serviceModel> + <system.diagnostics> + <trace autoflush="true" indentsize="4"> + <listeners> + <remove name="Default" /> + <add name="console" /> + <add name="file" /> + </listeners> + </trace> + <sources> + <source name="ConnectorServer" switchName="switch1"> + <listeners> + <remove name="Default" /> + <add name="file" /> + </listeners> + </source> + </sources> + <switches> + <add name="switch1" value="Information" /> + </switches> + <sharedListeners> + <add name="console" type="System.Diagnostics.ConsoleTraceListener" /> + <add name="file" type="System.Diagnostics.TextWriterTraceListener" initializeData="ConnectorServerService.log" + traceOutputOptions="DateTime"> + <filter type="System.Diagnostics.EventTypeFilter" initializeData="Information" /> + </add> + </sharedListeners> + </system.diagnostics> +</configuration> + + \ No newline at end of file diff --git a/ConnectorServerService/packages.config b/ConnectorServerService/packages.config new file mode 100755 index 00000000..6ecc2a89 --- /dev/null +++ b/ConnectorServerService/packages.config @@ -0,0 +1,5 @@ + + + + + \ No newline at end of file diff --git a/ConnectorServerService/version.template b/ConnectorServerService/version.template new file mode 100755 index 00000000..c085cfe1 --- /dev/null +++ b/ConnectorServerService/version.template @@ -0,0 +1 @@ +1.5.0.0 \ No newline at end of file diff --git a/Framework/Api.cs b/Framework/Api.cs index ecfb8cdd..ec44e054 100644 --- a/Framework/Api.cs +++ b/Framework/Api.cs @@ -71,7 +71,7 @@ public interface APIConfiguration /// /// the callback handler to receive the change event. /// a closeable to unregister the change listener. - IConfigurationPropertyChangeListener ChangeListener { set; } + IConfigurationPropertyChangeListener ChangeListener { get; set; } /// /// Determines if this uses the framework's connector diff --git a/Framework/Common.cs b/Framework/Common.cs index 22a691a1..fc37a0a6 100644 --- a/Framework/Common.cs +++ b/Framework/Common.cs @@ -37,7 +37,9 @@ namespace Org.IdentityConnectors.Framework.Common { + #region ConnectorKeyRange + /// /// A ConnectorKeyRange identifies a range of ConnectorKeys. /// @@ -49,37 +51,31 @@ namespace Org.IdentityConnectors.Framework.Common /// since 1.5 public sealed class ConnectorKeyRange { - private readonly string _bundleName; + private readonly String _bundleName; private readonly VersionRange _bundleVersionRange; - private readonly string _connectorName; + private readonly String _exactVersion; + private readonly String _connectorName; public string BundleName { - get - { - return _bundleName; - } + get { return _bundleName; } } public VersionRange BundleVersionRange { - get - { - return _bundleVersionRange; - } + get { return _bundleVersionRange; } } public string ConnectorName { - get - { - return _connectorName; - } + get { return _connectorName; } } public bool IsInRange(ConnectorKey connectorKey) { - return !_bundleVersionRange.Empty && _bundleName.Equals(connectorKey.BundleName) && _connectorName.Equals(connectorKey.ConnectorName) && _bundleVersionRange.IsInRange(Version.Parse(connectorKey.BundleVersion)); + return !_bundleVersionRange.Empty && _bundleName.Equals(connectorKey.BundleName) && + _connectorName.Equals(connectorKey.ConnectorName) && + _bundleVersionRange.IsInRange(Version.Parse(connectorKey.BundleVersion)); } public ConnectorKey ExactConnectorKey @@ -88,19 +84,17 @@ public ConnectorKey ExactConnectorKey { if (_bundleVersionRange.Exact) { - return new ConnectorKey(_bundleName, _bundleVersionRange.Floor.ToString(), _connectorName); - } - else - { - throw new ArgumentException("BundleVersion is not exact version"); + return new ConnectorKey(_bundleName, _exactVersion, _connectorName); } + throw new ArgumentException("BundleVersion is not exact version"); } } - private ConnectorKeyRange(string bundleName, VersionRange bundleVersionRange, string connectorName) + private ConnectorKeyRange(String bundleName, String bundleVersion, String connectorName) { _bundleName = bundleName; - _bundleVersionRange = bundleVersionRange; + _exactVersion = bundleVersion; + _bundleVersionRange = VersionRange.Parse(_exactVersion); _connectorName = connectorName; } @@ -115,16 +109,17 @@ public override bool Equals(object o) return false; } - ConnectorKeyRange that = (ConnectorKeyRange)o; + ConnectorKeyRange that = (ConnectorKeyRange) o; - return _bundleName.Equals(that._bundleName) && _bundleVersionRange.Equals(that._bundleVersionRange) && _connectorName.Equals(that._connectorName); + return _bundleName.Equals(that._bundleName) && _bundleVersionRange.Equals(that._bundleVersionRange) && + _connectorName.Equals(that._connectorName); } public override int GetHashCode() { int result = _bundleName.GetHashCode(); - result = 31 * result + _bundleVersionRange.GetHashCode(); - result = 31 * result + _connectorName.GetHashCode(); + result = 31*result + _bundleVersionRange.GetHashCode(); + result = 31*result + _connectorName.GetHashCode(); return result; } @@ -159,10 +154,11 @@ public virtual Builder SetConnectorName(string connectorName) public virtual ConnectorKeyRange Build() { - return new ConnectorKeyRange(_bundleName, VersionRange.Parse(_bundleVersion), _connectorName); + return new ConnectorKeyRange(_bundleName, _bundleVersion, _connectorName); } } } + #endregion #region FrameworkInternalBridge @@ -840,16 +836,16 @@ public bool IsInRange(Version version) { return false; } - if (floorVersion.CompareTo(version) >= (isFloorInclusive ? 1 : 0)) + int c = floorVersion.CompareTo(version); + if (c == 0 && isFloorInclusive) { - return false; + return true; } - if (ceilingVersion == null) + if (c < 0 && ceilingVersion != null) { - return true; + return ceilingVersion.CompareTo(version) >= (isCeilingInclusive ? 0 : 1); } - return ceilingVersion.CompareTo(version) >= (isCeilingInclusive ? 0 : 1); - + return false; } /// diff --git a/Framework/Framework.csproj b/Framework/Framework.csproj index 850a5848..0fcd0114 100644 --- a/Framework/Framework.csproj +++ b/Framework/Framework.csproj @@ -21,7 +21,7 @@ "Portions Copyrighted [year] [name of copyright owner]" ==================== --> - + {8B24461B-456A-4032-89A1-CD418F7B5B62} Debug @@ -30,12 +30,13 @@ Org.IdentityConnectors.Framework Framework Open Connectors Framework - v4.0 + v4.5.2 False False 4 false true + prompt @@ -65,6 +66,12 @@ AnyCPU 4096 + + false + + + false + diff --git a/Framework/version.template b/Framework/version.template index 2f5d8109..c085cfe1 100644 --- a/Framework/version.template +++ b/Framework/version.template @@ -1 +1 @@ -1.4.2.0 \ No newline at end of file +1.5.0.0 \ No newline at end of file diff --git a/FrameworkInternal/Api.cs b/FrameworkInternal/Api.cs index 2664f6b8..7012396e 100644 --- a/FrameworkInternal/Api.cs +++ b/FrameworkInternal/Api.cs @@ -19,7 +19,7 @@ * enclosed by brackets [] replaced by your own identifying information: * "Portions Copyrighted [year] [name of copyright owner]" * ==================== - * Portions Copyrighted 2012-2014 ForgeRock AS. + * Portions Copyrighted 2012-2015 ForgeRock AS. */ using System; using System.Collections.Concurrent; @@ -864,13 +864,15 @@ public ISubscription Subscribe(ObjectClass objectClass, SyncToken token, IObserv .Subscribe(objectClass, token, handler, operationOptions); } + protected const String Msg = "Operation ''{0}'' not supported."; + private APIOperation GetOperationCheckSupported(SafeType api) { // check if this operation is supported. if (!SupportedOperations.Contains(api)) { - String MSG = "Operation ''{0}'' not supported."; - String str = String.Format(MSG, api); + + String str = String.Format(Msg, api); throw new InvalidOperationException(str); } return GetOperationImplementation(api); diff --git a/FrameworkInternal/FrameworkInternal.csproj b/FrameworkInternal/FrameworkInternal.csproj index e9135edd..f4b090f6 100644 --- a/FrameworkInternal/FrameworkInternal.csproj +++ b/FrameworkInternal/FrameworkInternal.csproj @@ -21,7 +21,7 @@ "Portions Copyrighted [year] [name of copyright owner]" ==================== --> - + {5B011775-B121-4EEE-A410-BA2D2F5BFB8B} Debug @@ -29,13 +29,14 @@ Library Org.IdentityConnectors FrameworkInternal - FrameworkInternal - v4.0 + Open Connectors Framework Internal + v4.5.2 True False 4 false true + prompt @@ -64,6 +65,12 @@ AnyCPU 4096 + + false + + + false + diff --git a/FrameworkInternal/version.template b/FrameworkInternal/version.template index 2f5d8109..c085cfe1 100644 --- a/FrameworkInternal/version.template +++ b/FrameworkInternal/version.template @@ -1 +1 @@ -1.4.2.0 \ No newline at end of file +1.5.0.0 \ No newline at end of file diff --git a/FrameworkProtoBuf/CommonObjectMessages.cs b/FrameworkProtoBuf/CommonObjectMessages.cs new file mode 100755 index 00000000..9c727f20 --- /dev/null +++ b/FrameworkProtoBuf/CommonObjectMessages.cs @@ -0,0 +1,2043 @@ +// Generated by the protocol buffer compiler. DO NOT EDIT! +// source: CommonObjectMessages.proto +#pragma warning disable 1591, 0612, 3021 +#region Designer generated code + +using pb = global::Google.Protobuf; +using pbc = global::Google.Protobuf.Collections; +using pbr = global::Google.Protobuf.Reflection; +using scg = global::System.Collections.Generic; +namespace Org.ForgeRock.OpenICF.Common.ProtoBuf { + + [global::System.Diagnostics.DebuggerNonUserCodeAttribute()] + public static partial class CommonObjectMessages { + + #region Static variables + internal static pbr::FieldAccessorTable internal__static_Uid__FieldAccessorTable; + internal static pbr::FieldAccessorTable internal__static_BigDecimal__FieldAccessorTable; + internal static pbr::FieldAccessorTable internal__static_ConnectorKey__FieldAccessorTable; + internal static pbr::FieldAccessorTable internal__static_Locale__FieldAccessorTable; + internal static pbr::FieldAccessorTable internal__static_Script__FieldAccessorTable; + internal static pbr::FieldAccessorTable internal__static_ScriptContext__FieldAccessorTable; + internal static pbr::FieldAccessorTable internal__static_SearchResult__FieldAccessorTable; + internal static pbr::FieldAccessorTable internal__static_SortKey__FieldAccessorTable; + internal static pbr::FieldAccessorTable internal__static_SyncToken__FieldAccessorTable; + internal static pbr::FieldAccessorTable internal__static_SyncDelta__FieldAccessorTable; + internal static pbr::FieldAccessorTable internal__static_ConnectorObject__FieldAccessorTable; + internal static pbr::FieldAccessorTable internal__static_QualifiedUid__FieldAccessorTable; + #endregion + #region Descriptor + public static pbr::FileDescriptor Descriptor { + get { return descriptor; } + } + private static pbr::FileDescriptor descriptor; + + static CommonObjectMessages() { + byte[] descriptorData = global::System.Convert.FromBase64String( + string.Concat( + "ChpDb21tb25PYmplY3RNZXNzYWdlcy5wcm90byImCgNVaWQSDQoFdmFsdWUY", + "ASABKAkSEAoIcmV2aXNpb24YAiABKAkiLQoKQmlnRGVjaW1hbBIQCgh1bnNj", + "YWxlZBgBIAEoCRINCgVzY2FsZRgCIAEoBSJQCgxDb25uZWN0b3JLZXkSEgoK", + "YnVuZGxlTmFtZRgBIAEoCRIVCg1idW5kbGVWZXJzaW9uGAIgASgJEhUKDWNv", + "bm5lY3Rvck5hbWUYAyABKAkiPAoGTG9jYWxlEhAKCGxhbmd1YWdlGAEgASgJ", + "Eg8KB2NvdW50cnkYAiABKAkSDwoHdmFyaWFudBgDIAEoCSI0CgZTY3JpcHQS", + "FgoOc2NyaXB0TGFuZ3VhZ2UYASABKAkSEgoKc2NyaXB0VGV4dBgCIAEoCSJB", + "Cg1TY3JpcHRDb250ZXh0EhcKBnNjcmlwdBgBIAEoCzIHLlNjcmlwdBIXCg9z", + "Y3JpcHRBcmd1bWVudHMYAiABKAwiSQoMU2VhcmNoUmVzdWx0EhoKEnBhZ2Vk", + "UmVzdWx0c0Nvb2tpZRgBIAEoCRIdChVyZW1haW5pbmdQYWdlZFJlc3VsdHMY", + "AiABKAUiLQoHU29ydEtleRINCgVmaWVsZBgBIAEoCRITCgtpc0FzY2VuZGlu", + "ZxgCIAEoCCIaCglTeW5jVG9rZW4SDQoFdmFsdWUYASABKAwi+gEKCVN5bmNE", + "ZWx0YRIZCgV0b2tlbhgBIAEoCzIKLlN5bmNUb2tlbhIrCglkZWx0YVR5cGUY", + "AiABKA4yGC5TeW5jRGVsdGEuU3luY0RlbHRhVHlwZRIZCgtwcmV2aW91c1Vp", + "ZBgDIAEoCzIELlVpZBITCgtvYmplY3RDbGFzcxgEIAEoCRIRCgN1aWQYBSAB", + "KAsyBC5VaWQSFwoPY29ubmVjdG9yT2JqZWN0GAYgASgMIkkKDVN5bmNEZWx0", + "YVR5cGUSFAoQQ1JFQVRFX09SX1VQREFURRAAEgoKBkRFTEVURRABEgoKBkNS", + "RUFURRACEgoKBlVQREFURRADIjkKD0Nvbm5lY3Rvck9iamVjdBITCgtvYmpl", + "Y3RDbGFzcxgBIAEoCRIRCglhdHRyaXV0ZXMYAiABKAwiNgoMUXVhbGlmaWVk", + "VWlkEhMKC29iamVjdENsYXNzGAEgASgJEhEKA3VpZBgCIAEoCzIELlVpZEIo", + "qgIlT3JnLkZvcmdlUm9jay5PcGVuSUNGLkNvbW1vbi5Qcm90b0J1ZmIGcHJv", + "dG8z")); + descriptor = pbr::FileDescriptor.InternalBuildGeneratedFileFrom(descriptorData, + new pbr::FileDescriptor[] { + }); + internal__static_Uid__FieldAccessorTable = + new pbr::FieldAccessorTable(typeof(global::Org.ForgeRock.OpenICF.Common.ProtoBuf.Uid), descriptor.MessageTypes[0], + new string[] { "Value", "Revision", }, new string[] { }); + internal__static_BigDecimal__FieldAccessorTable = + new pbr::FieldAccessorTable(typeof(global::Org.ForgeRock.OpenICF.Common.ProtoBuf.BigDecimal), descriptor.MessageTypes[1], + new string[] { "Unscaled", "Scale", }, new string[] { }); + internal__static_ConnectorKey__FieldAccessorTable = + new pbr::FieldAccessorTable(typeof(global::Org.ForgeRock.OpenICF.Common.ProtoBuf.ConnectorKey), descriptor.MessageTypes[2], + new string[] { "BundleName", "BundleVersion", "ConnectorName", }, new string[] { }); + internal__static_Locale__FieldAccessorTable = + new pbr::FieldAccessorTable(typeof(global::Org.ForgeRock.OpenICF.Common.ProtoBuf.Locale), descriptor.MessageTypes[3], + new string[] { "Language", "Country", "Variant", }, new string[] { }); + internal__static_Script__FieldAccessorTable = + new pbr::FieldAccessorTable(typeof(global::Org.ForgeRock.OpenICF.Common.ProtoBuf.Script), descriptor.MessageTypes[4], + new string[] { "ScriptLanguage", "ScriptText", }, new string[] { }); + internal__static_ScriptContext__FieldAccessorTable = + new pbr::FieldAccessorTable(typeof(global::Org.ForgeRock.OpenICF.Common.ProtoBuf.ScriptContext), descriptor.MessageTypes[5], + new string[] { "Script", "ScriptArguments", }, new string[] { }); + internal__static_SearchResult__FieldAccessorTable = + new pbr::FieldAccessorTable(typeof(global::Org.ForgeRock.OpenICF.Common.ProtoBuf.SearchResult), descriptor.MessageTypes[6], + new string[] { "PagedResultsCookie", "RemainingPagedResults", }, new string[] { }); + internal__static_SortKey__FieldAccessorTable = + new pbr::FieldAccessorTable(typeof(global::Org.ForgeRock.OpenICF.Common.ProtoBuf.SortKey), descriptor.MessageTypes[7], + new string[] { "Field", "IsAscending", }, new string[] { }); + internal__static_SyncToken__FieldAccessorTable = + new pbr::FieldAccessorTable(typeof(global::Org.ForgeRock.OpenICF.Common.ProtoBuf.SyncToken), descriptor.MessageTypes[8], + new string[] { "Value", }, new string[] { }); + internal__static_SyncDelta__FieldAccessorTable = + new pbr::FieldAccessorTable(typeof(global::Org.ForgeRock.OpenICF.Common.ProtoBuf.SyncDelta), descriptor.MessageTypes[9], + new string[] { "Token", "DeltaType", "PreviousUid", "ObjectClass", "Uid", "ConnectorObject", }, new string[] { }); + internal__static_ConnectorObject__FieldAccessorTable = + new pbr::FieldAccessorTable(typeof(global::Org.ForgeRock.OpenICF.Common.ProtoBuf.ConnectorObject), descriptor.MessageTypes[10], + new string[] { "ObjectClass", "Attriutes", }, new string[] { }); + internal__static_QualifiedUid__FieldAccessorTable = + new pbr::FieldAccessorTable(typeof(global::Org.ForgeRock.OpenICF.Common.ProtoBuf.QualifiedUid), descriptor.MessageTypes[11], + new string[] { "ObjectClass", "Uid", }, new string[] { }); + } + #endregion + + } + #region Messages + [global::System.Diagnostics.DebuggerNonUserCodeAttribute()] + public sealed partial class Uid : pb::IMessage { + private static readonly pb::MessageParser _parser = new pb::MessageParser(() => new Uid()); + public static pb::MessageParser Parser { get { return _parser; } } + + private static readonly string[] _fieldNames = new string[] { "revision", "value" }; + private static readonly uint[] _fieldTags = new uint[] { 18, 10 }; + public static pbr::MessageDescriptor Descriptor { + get { return global::Org.ForgeRock.OpenICF.Common.ProtoBuf.CommonObjectMessages.Descriptor.MessageTypes[0]; } + } + + pbr::FieldAccessorTable pb::IReflectedMessage.Fields { + get { return global::Org.ForgeRock.OpenICF.Common.ProtoBuf.CommonObjectMessages.internal__static_Uid__FieldAccessorTable; } + } + + private bool _frozen = false; + public bool IsFrozen { get { return _frozen; } } + + public Uid() { + OnConstruction(); + } + + partial void OnConstruction(); + + public Uid(Uid other) : this() { + value_ = other.value_; + revision_ = other.revision_; + } + + public Uid Clone() { + return new Uid(this); + } + + public void Freeze() { + if (IsFrozen) { + return; + } + _frozen = true; + } + + public const int ValueFieldNumber = 1; + private string value_ = ""; + public string Value { + get { return value_; } + set { + pb::Freezable.CheckMutable(this); + value_ = value ?? ""; + } + } + + public const int RevisionFieldNumber = 2; + private string revision_ = ""; + public string Revision { + get { return revision_; } + set { + pb::Freezable.CheckMutable(this); + revision_ = value ?? ""; + } + } + + public override bool Equals(object other) { + return Equals(other as Uid); + } + + public bool Equals(Uid other) { + if (ReferenceEquals(other, null)) { + return false; + } + if (ReferenceEquals(other, this)) { + return true; + } + if (Value != other.Value) return false; + if (Revision != other.Revision) return false; + return true; + } + + public override int GetHashCode() { + int hash = 1; + if (Value.Length != 0) hash ^= Value.GetHashCode(); + if (Revision.Length != 0) hash ^= Revision.GetHashCode(); + return hash; + } + + public override string ToString() { + return pb::JsonFormatter.Default.Format(this); + } + + public void WriteTo(pb::CodedOutputStream output) { + if (Value.Length != 0) { + output.WriteRawTag(10); + output.WriteString(Value); + } + if (Revision.Length != 0) { + output.WriteRawTag(18); + output.WriteString(Revision); + } + } + + public int CalculateSize() { + int size = 0; + if (Value.Length != 0) { + size += 1 + pb::CodedOutputStream.ComputeStringSize(Value); + } + if (Revision.Length != 0) { + size += 1 + pb::CodedOutputStream.ComputeStringSize(Revision); + } + return size; + } + + public void MergeFrom(Uid other) { + if (other == null) { + return; + } + if (other.Value.Length != 0) { + Value = other.Value; + } + if (other.Revision.Length != 0) { + Revision = other.Revision; + } + } + + public void MergeFrom(pb::CodedInputStream input) { + uint tag; + while (input.ReadTag(out tag)) { + switch(tag) { + case 0: + throw pb::InvalidProtocolBufferException.InvalidTag(); + default: + if (pb::WireFormat.IsEndGroupTag(tag)) { + return; + } + break; + case 10: { + Value = input.ReadString(); + break; + } + case 18: { + Revision = input.ReadString(); + break; + } + } + } + } + + } + + [global::System.Diagnostics.DebuggerNonUserCodeAttribute()] + public sealed partial class BigDecimal : pb::IMessage { + private static readonly pb::MessageParser _parser = new pb::MessageParser(() => new BigDecimal()); + public static pb::MessageParser Parser { get { return _parser; } } + + private static readonly string[] _fieldNames = new string[] { "scale", "unscaled" }; + private static readonly uint[] _fieldTags = new uint[] { 16, 10 }; + public static pbr::MessageDescriptor Descriptor { + get { return global::Org.ForgeRock.OpenICF.Common.ProtoBuf.CommonObjectMessages.Descriptor.MessageTypes[1]; } + } + + pbr::FieldAccessorTable pb::IReflectedMessage.Fields { + get { return global::Org.ForgeRock.OpenICF.Common.ProtoBuf.CommonObjectMessages.internal__static_BigDecimal__FieldAccessorTable; } + } + + private bool _frozen = false; + public bool IsFrozen { get { return _frozen; } } + + public BigDecimal() { + OnConstruction(); + } + + partial void OnConstruction(); + + public BigDecimal(BigDecimal other) : this() { + unscaled_ = other.unscaled_; + scale_ = other.scale_; + } + + public BigDecimal Clone() { + return new BigDecimal(this); + } + + public void Freeze() { + if (IsFrozen) { + return; + } + _frozen = true; + } + + public const int UnscaledFieldNumber = 1; + private string unscaled_ = ""; + public string Unscaled { + get { return unscaled_; } + set { + pb::Freezable.CheckMutable(this); + unscaled_ = value ?? ""; + } + } + + public const int ScaleFieldNumber = 2; + private int scale_; + public int Scale { + get { return scale_; } + set { + pb::Freezable.CheckMutable(this); + scale_ = value; + } + } + + public override bool Equals(object other) { + return Equals(other as BigDecimal); + } + + public bool Equals(BigDecimal other) { + if (ReferenceEquals(other, null)) { + return false; + } + if (ReferenceEquals(other, this)) { + return true; + } + if (Unscaled != other.Unscaled) return false; + if (Scale != other.Scale) return false; + return true; + } + + public override int GetHashCode() { + int hash = 1; + if (Unscaled.Length != 0) hash ^= Unscaled.GetHashCode(); + if (Scale != 0) hash ^= Scale.GetHashCode(); + return hash; + } + + public override string ToString() { + return pb::JsonFormatter.Default.Format(this); + } + + public void WriteTo(pb::CodedOutputStream output) { + if (Unscaled.Length != 0) { + output.WriteRawTag(10); + output.WriteString(Unscaled); + } + if (Scale != 0) { + output.WriteRawTag(16); + output.WriteInt32(Scale); + } + } + + public int CalculateSize() { + int size = 0; + if (Unscaled.Length != 0) { + size += 1 + pb::CodedOutputStream.ComputeStringSize(Unscaled); + } + if (Scale != 0) { + size += 1 + pb::CodedOutputStream.ComputeInt32Size(Scale); + } + return size; + } + + public void MergeFrom(BigDecimal other) { + if (other == null) { + return; + } + if (other.Unscaled.Length != 0) { + Unscaled = other.Unscaled; + } + if (other.Scale != 0) { + Scale = other.Scale; + } + } + + public void MergeFrom(pb::CodedInputStream input) { + uint tag; + while (input.ReadTag(out tag)) { + switch(tag) { + case 0: + throw pb::InvalidProtocolBufferException.InvalidTag(); + default: + if (pb::WireFormat.IsEndGroupTag(tag)) { + return; + } + break; + case 10: { + Unscaled = input.ReadString(); + break; + } + case 16: { + Scale = input.ReadInt32(); + break; + } + } + } + } + + } + + [global::System.Diagnostics.DebuggerNonUserCodeAttribute()] + public sealed partial class ConnectorKey : pb::IMessage { + private static readonly pb::MessageParser _parser = new pb::MessageParser(() => new ConnectorKey()); + public static pb::MessageParser Parser { get { return _parser; } } + + private static readonly string[] _fieldNames = new string[] { "bundleName", "bundleVersion", "connectorName" }; + private static readonly uint[] _fieldTags = new uint[] { 10, 18, 26 }; + public static pbr::MessageDescriptor Descriptor { + get { return global::Org.ForgeRock.OpenICF.Common.ProtoBuf.CommonObjectMessages.Descriptor.MessageTypes[2]; } + } + + pbr::FieldAccessorTable pb::IReflectedMessage.Fields { + get { return global::Org.ForgeRock.OpenICF.Common.ProtoBuf.CommonObjectMessages.internal__static_ConnectorKey__FieldAccessorTable; } + } + + private bool _frozen = false; + public bool IsFrozen { get { return _frozen; } } + + public ConnectorKey() { + OnConstruction(); + } + + partial void OnConstruction(); + + public ConnectorKey(ConnectorKey other) : this() { + bundleName_ = other.bundleName_; + bundleVersion_ = other.bundleVersion_; + connectorName_ = other.connectorName_; + } + + public ConnectorKey Clone() { + return new ConnectorKey(this); + } + + public void Freeze() { + if (IsFrozen) { + return; + } + _frozen = true; + } + + public const int BundleNameFieldNumber = 1; + private string bundleName_ = ""; + public string BundleName { + get { return bundleName_; } + set { + pb::Freezable.CheckMutable(this); + bundleName_ = value ?? ""; + } + } + + public const int BundleVersionFieldNumber = 2; + private string bundleVersion_ = ""; + public string BundleVersion { + get { return bundleVersion_; } + set { + pb::Freezable.CheckMutable(this); + bundleVersion_ = value ?? ""; + } + } + + public const int ConnectorNameFieldNumber = 3; + private string connectorName_ = ""; + public string ConnectorName { + get { return connectorName_; } + set { + pb::Freezable.CheckMutable(this); + connectorName_ = value ?? ""; + } + } + + public override bool Equals(object other) { + return Equals(other as ConnectorKey); + } + + public bool Equals(ConnectorKey other) { + if (ReferenceEquals(other, null)) { + return false; + } + if (ReferenceEquals(other, this)) { + return true; + } + if (BundleName != other.BundleName) return false; + if (BundleVersion != other.BundleVersion) return false; + if (ConnectorName != other.ConnectorName) return false; + return true; + } + + public override int GetHashCode() { + int hash = 1; + if (BundleName.Length != 0) hash ^= BundleName.GetHashCode(); + if (BundleVersion.Length != 0) hash ^= BundleVersion.GetHashCode(); + if (ConnectorName.Length != 0) hash ^= ConnectorName.GetHashCode(); + return hash; + } + + public override string ToString() { + return pb::JsonFormatter.Default.Format(this); + } + + public void WriteTo(pb::CodedOutputStream output) { + if (BundleName.Length != 0) { + output.WriteRawTag(10); + output.WriteString(BundleName); + } + if (BundleVersion.Length != 0) { + output.WriteRawTag(18); + output.WriteString(BundleVersion); + } + if (ConnectorName.Length != 0) { + output.WriteRawTag(26); + output.WriteString(ConnectorName); + } + } + + public int CalculateSize() { + int size = 0; + if (BundleName.Length != 0) { + size += 1 + pb::CodedOutputStream.ComputeStringSize(BundleName); + } + if (BundleVersion.Length != 0) { + size += 1 + pb::CodedOutputStream.ComputeStringSize(BundleVersion); + } + if (ConnectorName.Length != 0) { + size += 1 + pb::CodedOutputStream.ComputeStringSize(ConnectorName); + } + return size; + } + + public void MergeFrom(ConnectorKey other) { + if (other == null) { + return; + } + if (other.BundleName.Length != 0) { + BundleName = other.BundleName; + } + if (other.BundleVersion.Length != 0) { + BundleVersion = other.BundleVersion; + } + if (other.ConnectorName.Length != 0) { + ConnectorName = other.ConnectorName; + } + } + + public void MergeFrom(pb::CodedInputStream input) { + uint tag; + while (input.ReadTag(out tag)) { + switch(tag) { + case 0: + throw pb::InvalidProtocolBufferException.InvalidTag(); + default: + if (pb::WireFormat.IsEndGroupTag(tag)) { + return; + } + break; + case 10: { + BundleName = input.ReadString(); + break; + } + case 18: { + BundleVersion = input.ReadString(); + break; + } + case 26: { + ConnectorName = input.ReadString(); + break; + } + } + } + } + + } + + [global::System.Diagnostics.DebuggerNonUserCodeAttribute()] + public sealed partial class Locale : pb::IMessage { + private static readonly pb::MessageParser _parser = new pb::MessageParser(() => new Locale()); + public static pb::MessageParser Parser { get { return _parser; } } + + private static readonly string[] _fieldNames = new string[] { "country", "language", "variant" }; + private static readonly uint[] _fieldTags = new uint[] { 18, 10, 26 }; + public static pbr::MessageDescriptor Descriptor { + get { return global::Org.ForgeRock.OpenICF.Common.ProtoBuf.CommonObjectMessages.Descriptor.MessageTypes[3]; } + } + + pbr::FieldAccessorTable pb::IReflectedMessage.Fields { + get { return global::Org.ForgeRock.OpenICF.Common.ProtoBuf.CommonObjectMessages.internal__static_Locale__FieldAccessorTable; } + } + + private bool _frozen = false; + public bool IsFrozen { get { return _frozen; } } + + public Locale() { + OnConstruction(); + } + + partial void OnConstruction(); + + public Locale(Locale other) : this() { + language_ = other.language_; + country_ = other.country_; + variant_ = other.variant_; + } + + public Locale Clone() { + return new Locale(this); + } + + public void Freeze() { + if (IsFrozen) { + return; + } + _frozen = true; + } + + public const int LanguageFieldNumber = 1; + private string language_ = ""; + public string Language { + get { return language_; } + set { + pb::Freezable.CheckMutable(this); + language_ = value ?? ""; + } + } + + public const int CountryFieldNumber = 2; + private string country_ = ""; + public string Country { + get { return country_; } + set { + pb::Freezable.CheckMutable(this); + country_ = value ?? ""; + } + } + + public const int VariantFieldNumber = 3; + private string variant_ = ""; + public string Variant { + get { return variant_; } + set { + pb::Freezable.CheckMutable(this); + variant_ = value ?? ""; + } + } + + public override bool Equals(object other) { + return Equals(other as Locale); + } + + public bool Equals(Locale other) { + if (ReferenceEquals(other, null)) { + return false; + } + if (ReferenceEquals(other, this)) { + return true; + } + if (Language != other.Language) return false; + if (Country != other.Country) return false; + if (Variant != other.Variant) return false; + return true; + } + + public override int GetHashCode() { + int hash = 1; + if (Language.Length != 0) hash ^= Language.GetHashCode(); + if (Country.Length != 0) hash ^= Country.GetHashCode(); + if (Variant.Length != 0) hash ^= Variant.GetHashCode(); + return hash; + } + + public override string ToString() { + return pb::JsonFormatter.Default.Format(this); + } + + public void WriteTo(pb::CodedOutputStream output) { + if (Language.Length != 0) { + output.WriteRawTag(10); + output.WriteString(Language); + } + if (Country.Length != 0) { + output.WriteRawTag(18); + output.WriteString(Country); + } + if (Variant.Length != 0) { + output.WriteRawTag(26); + output.WriteString(Variant); + } + } + + public int CalculateSize() { + int size = 0; + if (Language.Length != 0) { + size += 1 + pb::CodedOutputStream.ComputeStringSize(Language); + } + if (Country.Length != 0) { + size += 1 + pb::CodedOutputStream.ComputeStringSize(Country); + } + if (Variant.Length != 0) { + size += 1 + pb::CodedOutputStream.ComputeStringSize(Variant); + } + return size; + } + + public void MergeFrom(Locale other) { + if (other == null) { + return; + } + if (other.Language.Length != 0) { + Language = other.Language; + } + if (other.Country.Length != 0) { + Country = other.Country; + } + if (other.Variant.Length != 0) { + Variant = other.Variant; + } + } + + public void MergeFrom(pb::CodedInputStream input) { + uint tag; + while (input.ReadTag(out tag)) { + switch(tag) { + case 0: + throw pb::InvalidProtocolBufferException.InvalidTag(); + default: + if (pb::WireFormat.IsEndGroupTag(tag)) { + return; + } + break; + case 10: { + Language = input.ReadString(); + break; + } + case 18: { + Country = input.ReadString(); + break; + } + case 26: { + Variant = input.ReadString(); + break; + } + } + } + } + + } + + [global::System.Diagnostics.DebuggerNonUserCodeAttribute()] + public sealed partial class Script : pb::IMessage